Scrabble in C

In the second week of CS50, I was introduced to the notion of arrays and then given the following problem to solve as a practice:

In the game of Scrabble, players create words to score points, and the number of points is the sum of the point values of each letter in the word. For example, if we wanted to score the word “CODE”, we would note that the ‘C’ is worth 3 points, the ‘O’ is worth 1 point, the ‘D’ is worth 2 points, and the ‘E’ is worth 1 point. Summing these, we get that “CODE” is worth 7 points.

Implement a program in C that determines the winner of a short Scrabble-like game. Your program should prompt for input twice: once for “Player 1” to input their word and once for “Player 2” to input their word. Then, depending on which player scores the most points, your program should either print “Player 1 wins!”, “Player 2 wins!”, or “Tie!” (in the event the two players score equal points).

First, I outlined the general steps needed to solve this problem:

  1. Get text inputs from the user;
  2. Calculate the score of each input;
  3. Show the winner.

For the first step, I’ll create two variables named word1 and word2 respectively, and assign each to the value of the respective user input using the get_string function from the CS50 library:

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

int main(void)
{
    // Prompt the user for two words
    string word1 = get_string("Player 1: ");
    string word2 = get_string("Player 2: ");

    // Compute the score of each word

    // Print the winner
}

That was easy. The trickiest part here, of course, is the second step, which is to assign each letter of the alphabet its unique score points value.

Here are the values:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
1 3 3 2 1 4 2 4 1 8 5 1 3 1 1 3 10 1 1 1 1 4 4 8 4 10

Now that I know about arrays, I can create an array called points with a size of 26 and the type of integer, and assign values as follows:

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

int main(void)
{
    // Prompt the user for two words
    string word1 = get_string("Player 1: ");
    string word2 = get_string("Player 2: ");

    // Compute the score of each word
    int points[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10};

    // Print the winner
}

I don’t quite like the length of that like, but it seems there is not much to do about it since all values are set by the rules of the game.

Now that I know a little bit about functions too, I’ll create a separate function for computing the score. This should make the code more efficient and better designed, especially since I need to use it twice for calculating the score for each word.

I’ll call this function calculate_score, with one input parameter of the type of string, and output of integer. I can also straight away create a variable called score with the type of integer, as this is ultimately what I want to get from this function:

int calculate_score(string word)
{
    int score = 0;

    return score;
}

Now, the big question is how to assign each letter a corresponding score value. Well, turns out that every string is basically just an array of characters. Let’s take the word CODE as an example. We can imagine it as an array as follows, keeping in mind the zero-based indexing of arrays, i.e. starting counting from 0:

c word[i]
word[0] = C
word[1] = O
word[2] = D
word[3] = E

Knowing this, I can create a loop to ‘scan’ through the word character by character. For this loop to work, I would to know the length of the word, and luckily such a function already exists in the string.h library and is called strlen:

for (int i = 0; i < strlen(word); i++)
{
}

What I don’t quite like about this loop though, is that every time it works, it makes the function repeat that many times. To optimise this, let me create another variable called len with its value equal to the length of a word, and then use this variable as the condition of the loop:

for (int i = 0, len = strlen(word); i < len; i++)
{
}

Now, how to make the loop ‘know’ which score value corresponds to each letter? There is actually a trick that still blows my mind even after completing this task. Since every character has its numeric value in the ASCII code, we can use this information to ‘offset’ characters from one array to another and this way map them together.

Let’s take the capital letter ‘C’ from the word CODE as I used the example. Its numeric value in ASCII is 67, and the trick is to subtract the value of the letter A, which equals 65. Why A? Because it’s the first letter of the alphabet, and if we subtract it from another letter, we’ll know its place in the array. So 67 – 65 = 2, and that is exactly the index number we need from the points array.

To make it more visual, here is a similar exercise for all letters of the word CODE:

Letter ASCII Index Points
C 67 67 – 65 = [2] 3
O 79 79 – 65 = [14] 1
D 68 68 – 65 = [3] 2
E 69 69 – 65 = [4] 1

... which means the word CODE gives the Scrabble score of 7 (as 3 + 1 + 2 +1), and that is indeed correct. It works like a charm!

To make array indexing a little more obvious, I’ll add it to the score table too:

Letter: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Score: 1 3 3 2 1 4 2 4 1 8 5 1 3 1 1 3 10 1 1 1 1 4 4 8 4 10
Index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

This system doesn’t quite work for the lowercase letters though, because the ASCII values of the lowercase letters are different from the capital letters. However, it simply means that we need to subtract the value of the lowercase letter ‘a’ instead of the capital ‘A’.

So it seems I need an if statement within the loop to calculate the score depending on the case of the letters using isupper and islower functions from the ctype.h library, which I’ll include in the header too:

int calculate_score(string word)
{
    // Keep track of the score
    int score = 0;

    // Compute the score for each character
    for (int i = 0, len = strlen(word); i < len; i++)
    {
        if (isupper(word[i]))
        {
            score += points[word[i] - 'A'];
        }
        else if (islower(word[i]))
        {
            score += points[word[i] - 'a'];
        }
    }
    return score;
}

Since I’m using the points array in this function, I need to move it out from the main’s local scope to the global scope. The rest is easy: all I need to do is to call the function calculate_score (twice, for each word) and then compare players’ scores to determine the winner.

And here is my final code:

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

// points assigned to each letter of the alphabet
int points[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10};

int calculate_score(string word);

int main(void)
{
    // Prompt the user for two words
    string word1 = get_string("Player 1: ");
    string word2 = get_string("Player 2: ");

    // Compute the score of each word
    int score1 = calculate_score(word1);
    int score2 = calculate_score(word2);

    // Print the winner
    if (score1 > score2)
    {
        printf("Player 1 wins!\n");
    }
    else if (score1 < score2)
    {
        printf("Player 2 wins!\n");
    }
    else
    {
        printf("Tie!\n");
    }
}

int calculate_score(string word)
{
    // Keep track of the score
    int score = 0;

    // Compute the score for each character
    for (int i = 0, len = strlen(word); i < len; i++)
    {
        if (isupper(word[i]))
        {
            score += points[word[i] - 'A'];
        }
        else if (islower(word[i]))
        {
            score += points[word[i] - 'a'];
        }
    }
    return score;
}
 109   2 mo   C   CS50   Programming
Next
© Daniel Sokolovskiy, 2024
Powered by Aegea