Scrabble

Scrabble 棋盘

要解决的问题

Scrabble 游戏中,玩家通过拼出单词获得分数。一个单词的总分等于该单词中每个字母分值之和。

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

例如,如果我们要计算单词 “CODE” 的分数,可以看到 ‘C’ 值 3 分,‘O’ 值 1 分,‘D’ 值 2 分,‘E’ 值 1 分。相加后,“CODE” 总共值 7 分。

请在名为 scrabble 的文件夹中创建 scrabble.c 文件,并用 C 实现一个程序,判断一局简短的类 Scrabble 游戏谁获胜。程序应提示输入两次:一次让 “Player 1” 输入单词,一次让 “Player 2” 输入单词。然后根据哪位玩家得分更高,输出 “Player 1 wins!”、“Player 2 wins!” 或者在双方得分相同时输出 “Tie!”。

演示

建议和提示

先写一些你确定能编译的代码
#include <ctype.h>
#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{

}

注意,你现在已经包含了一些头文件,这些头文件会让你能使用一些可能帮助你解决本题的函数。

在继续写更多代码前,先写一些伪代码

如果你还不确定如何解决这个问题,可以先把它拆成几个你大概能解决的小问题。例如,这个问题其实只包含少数几个步骤:

  1. 提示用户输入两个单词
  2. 计算每个单词的分数
  3. 输出获胜者

我们可以把这些步骤写成注释形式的伪代码,提醒自己要完成什么:

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

int main(void)
{
    // Prompt the user for two words

    // Compute the score of each word

    // Print the winner
}
把伪代码转换成代码

首先,思考如何提示用户输入两个单词。回忆一下,CS50 库中的 get_string 函数可以提示用户输入字符串。

#include <ctype.h>
#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
}

接下来,思考如何计算每个单词的分数。由于两个单词使用同样的计分算法,这正是进行抽象的好机会。这里我们定义一个名为 compute_score 的函数,它接受一个名为 word 的字符串作为输入,并以 int 形式返回该单词的分数。

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

int compute_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 = compute_score(word1);
    int score2 = compute_score(word2);

    // Print the winner   
}

int compute_score(string word)
{
    // Compute and return score for word
}

现在转向实现 compute_score。要计算一个单词的分数,你需要知道单词中每个字母对应的分值。可以用一个数组把字母和分值关联起来。想象一个包含 26 个 int 的数组,名为 POINTS,其中第一个数字是 ‘A’ 的分值,第二个数字是 ‘B’ 的分值,依此类推。把这个数组声明并初始化在任意单个函数之外,就可以确保包括 compute_score 在内的所有函数都能访问它。

#include <ctype.h>
#include <cs50.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 compute_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 = compute_score(word1);
    int score2 = compute_score(word2);

    // Print the winner   
}

int compute_score(string word)
{
    // Compute and return score for word
}

实现 compute_score 时,可以先尝试找出 word 中单个字母的分值。

  • 记住,要找出字符串 s 中第 n 个索引处的字符,可以写 s[n]。例如,word[0] 会给出 word 的第一个字符。
  • 另外,记住计算机会使用 ASCII 表示字符;ASCII 是一种把每个字符表示为数字的标准。
  • 还要记住,POINTS 的第 0 个索引 POINTS[0] 给出的是 ‘A’ 的分值。思考一下,如何把 ‘A’ 的数字表示转换成它对应分值的索引。那么 ‘a’ 呢?你需要对大写和小写字母应用不同的转换,所以 isupperislower 这些函数可能会有帮助。
  • 请记住,不是字母的字符应计为 0 分。例如,! 值 0 分。

如果你能正确计算 word一个字符的分值,那么很可能就能用循环把其他字符的分值加起来。自己尝试过上面的思路后,可以看看下面这个(相当直接的!)提示。

#include <ctype.h>
#include <cs50.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 compute_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 = compute_score(word1);
    int score2 = compute_score(word2);

    // Print the winner   
}

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

    // Compute 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;
}

最后,完成伪代码的最后一步:打印获胜者。记住,if 语句可以用来检查某个条件是否为真,而继续使用 else ifelse 可以检查其他互斥的条件。

if (/* Player 1 wins */)
{
    // ...
}
else if (/* Player 2 wins */)
{
    // ...
}
else
{
    // ...
}

尝试过上面的内容后,可以看看下面的提示(或者说,完整解法!)。

#include <ctype.h>
#include <cs50.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 compute_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 = compute_score(word1);
    int score2 = compute_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 compute_score(string word)
{
    // Keep track of score
    int score = 0;

    // Compute 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;
}

如何测试

你的程序应当表现得像下面的示例一样。

$ ./scrabble
Player 1: Question?
Player 2: Question!
Tie!
$ ./scrabble
Player 1: red
Player 2: wheelbarrow
Player 2 wins!
$ ./scrabble
Player 1: COMPUTER
Player 2: science
Player 1 wins!
$ ./scrabble
Player 1: Scrabble
Player 2: wiNNeR
Player 1 wins!

正确性

check50 cs50/problems/2026/x/scrabble

代码风格

style50 scrabble.c

如何提交

在终端中执行以下命令提交你的作业,并按提示完成操作。

submit50 cs50/problems/2026/x/scrabble