Search code examples
cloopsasciics50caesar-cipher

Logic for looping back to start of alphabet after last letter in Caesar's Cipher


I've been writing the below program as an implementation of Caesar's Cipher and it works unless the key provided is larger than 26. The problem is that I don't know how to loop back once the value exceeds 'z' or 'Z'.

What I've tried so far is to nest if statements and while loops, which caused a memory leak (LOL), so suffice it to say, that I'm pretty stuck.

How to use the program: gcc -o caesar caesar.c -lcs50 -lm && ./caesar key(number) where caesar.c is the filename.

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

bool input_valid(int count, string arg);

int main(int argc, string argv[])
{
    int key;
    char final_val;
    string string;
    char cipher_string[80];

    if (!input_valid(argc, argv[1]))
    {
        printf("Invalid input!\nUSAGE: ./caesar key\n");
        return 1;
    }

    string = get_string("plaintext: ");
    key = strtol(argv[1], NULL, 10);

    for (int i = 0; i < strlen(string); i++)
    {
        int ascii_val = (int)string[i];

        bool valid_lower_case = (ascii_val + key) >= 'a' && (ascii_val + key) < 'z';
        bool valid_upper_case = (ascii_val + key) >= 'A' && (ascii_val + key) < 'Z';

        if (isalpha(string[i]))
        {
            if (valid_lower_case || valid_upper_case)
            {
                final_val = ascii_val + key;
            }
            else
            {
                // loop back to 'a' if ascii_val reaches 'z'
                final_val = 'a' + (key - ('z' - (ascii_val - 1)));
            }
        }
        else
        {
            final_val = ascii_val;
        }

        cipher_string[i] = final_val;
    }

    printf("ciphertext: %s\n", cipher_string);
}

bool input_valid(int count, string arg)
{
    // input has more args than just the file name
    // input is an integer
    return count > 1 && isdigit(arg[0]);
}

Solution

  • Are you familiar with the MOD operator, %?

    It's useful for implementing "ring-oriented" data, e.g.

    array = [0, 1, 2] // "ring"
    
    array[0 % 3] == 0
    array[1 % 3] == 1
    array[2 % 3] == 2
    array[3 % 3] == 0 // "ring" loops back to 0
    array[4 % 3] == 1
    array[5 % 3] == 2
    

    Mathematically, it's the remainder of division.

    10 / 4    # (4 * 2) + 2
    10 % 4             == 2
    
    17 / 2    # (2 * 8) + 1
    17 % 2             == 1