Craig Rodrigues!

Learning to Code: Week 3 - Vigenere Cipher

Another week down! Here is what happened.

5/26/16

Randomly looked at Slack’s job requirements:

I worked on greedy.c

PROGRAM GOAL:

Write a program that first asks the user how much change is owed and then spits out the minimum number of coins with which said change can be made. Assume that the only coins available are quarters (25¢), dimes (10¢), nickels (5¢), and pennies (1¢).

We ask that you use GetFloat so that you can handle dollars and cents, albeit sans dollar sign. In other words, if some customer is owed $9.75 (as in the case where a newspaper costs 25¢ but the customer pays with a $10 bill), assume that your program’s input will be 9.75 and not $9.75 or 975.

However, if some customer is owed $9 exactly, assume that your program’s input will be 9.00 or just 9 but, again, not $9 or 900.

Using GetFloat alone will ensure that the user’s input is indeed a floating-point (or integral) value but not that it is non-negative. If the user fails to provide a non-negative value, your program should re-prompt the user for a valid amount again and again until the user complies.

// OUTPUT EXAMPLE:

O hai! How much change is owed?
-0.41
How much change is owed?
-0.41
How much change is owed?
foo
Retry: 0.41
4

MY THOUGHT PROCESS/STEPS:

  • Ask user for non-negative float number.
  • Reject if negative or not a number.
  • Take the float number into a variable called amount.
  • Multiply amount by 100 to turn dollars and cents into just a variable called cents.
  • Cast that number to an int (how?) -http://cs50.stackexchange.com/questions/2356/roundf-function-gives-error-in-clang-but-runs-smoothly-in-g
  • Have a loop that checks if the number > 25, then > 10, then >5, then adds in the rest as pennies.
  • Start with quarters (25)
  • Divide number by 25.
  • Take integer part of the number and add to a coin counter variable called change.
  • Take the remainder (modulo?), add it to the coint counter, and move down the loop until the end.
  • Print the final coin counter variable.
  • Math.h library can round and what else can it do?
  • How do I deal with rounding?
  • I like to test stuff with printf after a loop.
  • Actually using modulo from what I remember from the lectures/shorts I don’t actually NEED any loops to do this problem set.
  • Not sure how to overcome a rounding error for really larger numbers though (although it’s not checked for and probably out of the scope right now)

Solved!

See screenshot of my solution below.

#include <stdio.h>
#include <cs50.h>
#include <math.h>

/** Write a program that first asks the user how much change is owed
    and then spits out the minimum number of coins with which said change
    can be made. */

int main(void)
{
    float amount;
    int coins_returned;

    //get non-negative float from the user
    do
    {
        printf("How much change is owed?\n");
        amount = GetFloat();

    } while (amount < 0);

    //convert the float into cents rounded
    int cents = round(amount * 100);

    //quarters
    coins_returned = cents/25;
    cents %= 25;

    //dimes
    coins_returned += cents/10;
    cents %= 10;

    //nickels
    coins_returned += cents/5;
    cents %= 5;

    //pennies
    coins_returned += cents;

    printf("%i\n", coins_returned);
}

I wanted to add multi-line comments so I looked at Harvard’s style guide here: https://manual.cs50.net/style/

Submitted Problem Set 1.

Shawn mentioned functions. Can I write a function for greedy?

Functions: Saves time in programming

In your water program, you could have extracted the calculation to a function that would be reusable in other programs, and would be easy to put in automated unit tests. As a general rule, if I can see using something more than once, I'll extract to function. That saves copy/paste and maintenance hell later.

On the typo in the for loop, welcome to the rest of your career. Won't be the last time that you beat your head against the wall over a typo. I've had apps where my issue was a spelling error that it takes hours to see.

-Shawn

5/27/16

Had no time today. Sigh.

5/28/16

  • Week 2 - Lecture #1 (50m)
  • Declare prototype function at the very top of the program, or it’ll error out.
  • New library unlocked: string.h

5/29/16

  • Week 2 - Lecture #2 (47m)
  • Strings in memory end with /0 (null terminator)
  • Arrays must be declared (like variables) - ex: int ages[n];
  • Command-line arguments.

5/30/16

Week 2 - Short Videos:

  • Arrays - essentially a big block of memory. 10 integers would require 40 bytes of memory.
  • Off-by-1 error.
  • Caesar Cipher - had to loop back around from Z → A. Not very complicated. “26 - key” to return to the original word.
  • Command-Line arguments - argc is the argument counter. It is an integer.
    • Argv stands for argument vector.
  • Global variables - variables declared inside of a function cannot be used outside of that function!
    • Can use #define for constants like pi. #define pi 3.141592
  • Redirecting and Pipes
  • Return values
  • RSA Encryption
  • Scope
  • Vigenere Cipher

Worked on initials.c

Program Goal: Write in a file called initials.c, a program that prompts a user for their name (using GetString to obtain their name as a string) and then outputs their initials in uppercase with no spaces or periods, followed by a newline (\n) and nothing more.

You may assume that the user’s input will contain only letters (uppercase and/or lowercase) plus single spaces between words.

Folks like Joseph Gordon-Levitt, Conan O’Brien, and David J. Malan won’t be using your program. (If only!)

Output Examples:
username:~/workspace/pset2 $ ./initials
Zamyla Chan
ZC
username:~/workspace/pset2 $ ./initials
robert thomas bowden
RTB

Steps:

  • Get input from the user.
  • Put name into an array (didn't actually need to do this. String already does this).
  • Print out the first string's first char as a capital letter.
  • Scroll through the string and find the space.
  • Print the next character after the space as a capital letter.
  • Go back to 4-5 until at end of string.
  • Print new line.

Notes:

  • I can use the library ctype.h to call the function toupper to return an uppercase version of the character!
    • int toupper (int ch);
  • Need to increment through a loop to find the null terminator and print the first character after it? (nope)
  • How would I account for the last null terminator then?
  • Didn't actually need to look for the null terminator, just the spaces...duh.
  • When I did the first loop I forgot to initialize i to anything and it threw an error. for (i = 0) instead of for (int i = 0)

Solved initials.c

Solution below:

#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
/**
 * 
 * Write, in a file called initials.c, a program that prompts a user for
 * their name (using GetString to obtain their name as a string) and then
 * outputs their initials in uppercase with no spaces or periods,
 * followed by a newline (\n) and nothing more.
 * 
 * */
int main(void)
{
    string name = GetString();
    //print the first initial as an uppercase letter
    printf("%c", toupper(name[0]));
    //loop and find space characters.
    for (int i = 0; i < strlen(name); i++)
    {
        if (name[i] == ' ')
        {
            // print next character as an uppercase char
            printf("%c", toupper(name[i + 1]));
        }
    }
    //print new line
    printf("\n");
}

Update! See here: https://gist.github.com/CraigRodrigues/c79e0b778b9794112624c8bb76f45d8b

5/31/16

Solved caesar.c and discovered gist.github.com! 😀

Steps:

  • Get a single command-line argument "key" from the user that is a non-negative integer.
  • If not a single argument then yell at user.
  • Take the "key" and turn it into an int with atoi since it starts as a string.
  • Prompt user for a code they want to encrypt.
  • Need to loop through the entire code letter by letter.
  • Check if each letter is either lowercase, uppercase or neither.
  • Standardize the ASCII value of the char to 26 then add the key. Then convert back into ASCII so that the code can wrap around properly.
  • If neither a lowercase or uppercase letter then just print whatever the char is. This allows for spaces or special characters like ! or &.
  • Once all of the above is complete print a new line.
  • Return 0.

Notes:

  • Checking if the argv[1] was a non-negative integer (don't think I even needed to do this).
  • Figuring out how to standardize ASCII to the regular alphabet then converting back took a lot of time.
  • This ASCII chart was incredibly useful - http://www.kerryr.net/pioneers/ascii3.htm
  • Trying to put argv[1] into a variable before I check if there is even an argv[1] caused a segmentation fault.
  • Didn't notice that toupper/tolower already checks if the character is a letter. First I had two more checks to see if the character was a letter or not when that wasn't necessary.
  • ctype.h library is also very useful - https://cs50.harvard.edu/resources/cppreference.com/stdstring/all.html
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>

/**
 * Caesar.c
 * A program that encrypts messages using Caesar’s cipher. Your program must
 * accept a single command-line argument: a non-negative integer. Let’s call it
 * k for the sake of discussion. If your program is executed without any
 * command-line arguments or with more than one command-line argument, your
 * program should yell at the user and return a value of 1.
 * 
 * */

 int main(int argc, string argv[])
 {
    // check for 2 arguments only
    if (argc != 2)
    {
        printf("Nope\n");
        return 1;
    }

    // once I check for correct argv put key into an int k
    int k = atoi(argv[1]);

    // check if the integer is non-negative
    if (k < 0)
    {
        printf("Nope\n");
        return 1;
    }
    else
    {
        // prompt user for a code to encrypt
        string code = GetString();

        for (int i = 0, n = strlen(code); i < n; i++)
            {
                //check if the letter is uppercase or lowercase then convert
                if islower(code[i])
                    printf("%c", (((code[i] + k) - 97) % 26) + 97);
                else if isupper(code[i])
                    printf("%c", (((code[i] + k) - 65) % 26) + 65);
                //if neither then just print whatever it is
                else
                    printf("%c", code[i]);
            }
            printf("\n");
            return 0;
    }
 }
  • There is no CS50 meetup in Georgia. I feel like I should start one when I get further into the class to at least be able to help anyone that wants to join.
  • I got mother fucking Rick Rolled by CS50. Using key of 13 on this url they posted: uggc://jjj.lbhghor.pbz/jngpu?i=bUt5FWLEUN0

Solved vigenere.c

Steps:

  1. Accept command-line argument to be used as a keyword.
  2. Loop through the inputted string to make sure it is all alpha characters.
  3. If not then throw an error (return 1).
  4. Prompt user for plaintext string codeword(s).
  5. Loop through the codeword char by char.
  6. If the char is a letter then take the first char of the keyword and use that char as the key for encrypting that particular letter.
  7. The keyword loop should continue and wraparound on itself until the codeword is full encrypted.
  8. Blank spaces and special characters should be printed out as is and they keyword key should not be incremented or used for those characters.
  9. The key characters should be standardized to 26 and start at 0. So 'a' and 'A' are 0. 'b' and 'B' are 1, etc.
  10. Preserve the case for the encrypted codeword characters.
  11. Print new line at the very end.
  12. Return 1.

Notes:

  • How could I make the code formula into a function?
  • isalpha is very useful for this problem.
  • Thought I may need ispunct, but the keyword shouldn't contain any special characters anyway.
  • atoi is not needed, although I thought it was from Caesar.
  • It took a bit for me to figure out how to have the keyword continually loop, but it's the exact same method as converting the ASCII to standard 26 count alphabet.
  • Factoring out the main formulas was a PITA.
  • There are only 4 different types of outcomes available for each codeword character and keyword key configuration.
  • I didn't fully read the instructions and had A = 1 and B = 2 to start and that messed me up for a bit.
  • I am not sure how I could use a function in place of the encrypting formula. I want to come back to this later after I learn more. I feel like it could be cleaned up!

Solution: https://gist.github.com/CraigRodrigues/5b4fd2e2d29c9a2746d566fd589431ea

#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>

/**
 * Vigenere.c
 * 
 * A program that encrypts messages using Vigenère’s cipher. This program
 * must accept a single command-line argument: a keyword, k, composed entirely
 * of alphabetical characters. If your program is executed without any
 * command-line arguments, with more than one command-line argument, or with one
 * command-line argument that contains any non-alphabetical character, your
 * program should complain and exit immediately, with main returning 1
 * (thereby signifying an error that our own tests can detect). Otherwise, your
 * program must proceed to prompt the user for a string of plaintext, p, which
 * it must then encrypt according to Vigenère’s cipher with k, ultimately
 * printing the result and exiting, with main returning 0.
 * 
 * */

 int main(int argc, string argv[])
 {
    // check for 2 arguments only
    if (argc != 2)
    {
        printf("Nope\n");
        return 1;
    }

    // check if argument is all alpha char (no punct) - use loop and isalpha
    for (int i = 0; i < strlen(argv[1]); i++)
    {
        if (isalpha(argv[1][i]) == 0)
        {
            printf("Nope\n");
            return 1;
        }
    }

    // prompt user for codeword
    string codeword = GetString();
    int j = 0;

    // loop through the codeword. If not a letter than print unmodified.
    for (int i = 0, n = strlen(codeword); i < n; i++)
    {
        // to keep looping through the key continously
        j = j % strlen(argv[1]);

        // check if the char is alpha
        if (isalpha(codeword[i]))
        {
            // only 4 types of outcomes
            if (islower(codeword[i]) && islower(argv[1][j]))
                printf("%c", (((codeword[i] - 97) + (argv[1][j] - 97)) % 26) + 97);
            else if (isupper(codeword[i]) && islower(argv[1][j]))
                printf("%c", (((codeword[i] - 65) + (argv[1][j] - 97)) % 26) + 65);
            else if (islower(codeword[i]) && isupper(argv[1][j]))
                printf("%c", (((codeword[i] - 97) + (argv[1][j] - 65)) % 26) + 97);
            else if (isupper(codeword[i]) && isupper(argv[1][j]))
                printf("%c", (((codeword[i] - 65) + (argv[1][j] - 65)) % 26) + 65);
            j++;
        }
        else
        {
            printf("%c", codeword[i]);
        }
    }
    printf("\n");
 }

6/01/16

How much to comment is subjective. If I know that I'm writing something to hand off to others, I'll put in more comments than something for myself. When it is my own stuff, I'll only comment on things where there are multiple ways to skin the same cat so that I don't have to remember why I did something a certain way.

-Shawn

Learning to Code: Week 4 - Algorithms

Learning to Code: Week 2 – mario.c