Scrabble

Scrabble Brett

Aufgabe

Bei Scrabble bilden die Spieler Wörter und erhalten dafür Punkte. Die Anzahl der Punkte ergibt sich aus der Summe der Punktwerte der einzelnen Buchstaben des Wortes.

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

Wenn wir zum Beispiel das Wort “CODE” bewerten wollen, würden wir der Tabelle entnehmen, dass das “C” 3 Punkte, das “O” 1 Punkt, das “D” 2 Punkte und das “E” 1 Punkt wert sind. Zusammengerechnet ergibt dies 7 Punkte für “CODE”.

In dieser Aufgabe erstellen wir ein Programm, das den Gewinner eines kurzen Scrabble-ähnlichen Spiels ermittelt. Ihr Programm sollte zweimal zur Eingabe auffordern: einmal zur Eingabe des Wortes für “Spieler 1” und ein zweites Mal zur Eingabe des Wortes für “Spieler 2”. Je nachdem, welcher Spieler die meisten Punkte erzielt, sollte Ihr Programm dann entweder “Spieler 1 gewinnt!”, “Spieler 2 gewinnt!” oder “Gleichstand!” (falls beide Spieler gleich viele Punkte haben) ausgeben.

Implementieren Sie das Programm in C in einer Datei namens scrabble.c in einem Ordner namens scrabble.

ℹ️
Beachten Sie die Hinweise zur Erstellung von Ordnern und Dateien unter Schritt für Schritt.

Demo

Schritt für Schritt

Öffnen Sie VS Code entsprechend Ihrem Setup.

ℹ️
Eine einfache Installation von VS Code ist nicht ausreichend.

Führen Sie cd in Ihrem Terminalfenster aus. Die Eingabeaufforderung Ihres Terminalfensters sollte wie folgt aussehen:

$

Nun führen Sie folgenden Befehl aus,

mkdir scrabble

um einen Ordner scrabble in Ihrem aktuellen Projektordner zu erstellen.

Dann führen Sie

cd scrabble

aus, um in diesen Ordner zu wechseln. In der Eingabeaufforderung Ihres Terminals sollte nun scrabble/ $ erscheinen.

ℹ️
Um nach der Bearbeitung dieser Aufgabe in der Verzeichnisstruktur wieder nach oben zu gelangen, können Sie cd ../ eingeben. Anschließend können Sie alle Schritte für die nächste Aufgabe wiederholen.

Sie können nun Folgendes ausführen,

code scrabble.c

um eine Datei namens scrabble.c zu erstellen, in die Sie Ihren Code schreiben können. Im Endeffekt passiert hier aber nichts anderes, als wenn man eine Datei mit dem Namen scrabble.c auf die übliche Art und Weise erstellt. Nur schneller und effizienter.

Hilfestellung

Klicken Sie auf die folgenden Tipps, um einige Ratschläge zu erhalten. Versuchen Sie aber zunächst, selbst so weit wie möglich zu kommen.

Beginnen Sie mit Code, der kompilierbar ist
#include <ctype.h>
#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{

}

Beachten Sie, dass in diesem Grundgerüst mehrere Header-Dateien eingebunden sind, die Ihnen Zugriff auf Funktionen geben, die Ihnen bei der Lösung dieses Problems helfen könnten.

Versuchen Sie das Problem in Pseudocode zu beschreiben

Wenn Sie unsicher sind, wie Sie das eigentliche Problem lösen können, unterteilen Sie es in kleinere Probleme, die Sie wahrscheinlich einfacher lösen können. Das Problem dieser Aufgabe besteht eigentlich nur aus einer Handvoll kleinerer Probleme:

  1. Die Aufforderung an den Benutzer zur Eingabe von zwei Wörtern.
  2. Das Berechnen der Punktzahl für jedes Wort.
  3. Das Ausgeben des Gewinners.

Fügen Sie diese kleineren Probleme nun in Form von Pseudocode als Kommentare ein, um Sie dann nacheinander bearbeiten zu können:

#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
}
⚠️
Der letzte Tipp zeigt Ihnen Schritt für Schritt den Code einer möglichen Lösung. Idealerweise schauen Sie sich diesen erst an, nachdem Sie die Aufgabe bearbeitet haben - oder zumindest ernsthaft versucht haben, die Aufgabe zu bearbeiten. Nicht jede Aufgabe enthält einen derart umfangreichen Lösungsweg, und normalerweise ist die Aufgabe, für die in der Hilfestellung bereits eine (fast) vollständige Lösung angegeben ist, nur eine Aufwärmübung für eine schwierigere Aufgabe, die Sie später lösen sollen.
Wandeln Sie den Pseudocode in Code um

Überlegen Sie zunächst, wie Sie den Benutzer nach zwei Wörtern fragen könnten. Erinnern Sie sich, dass get_string, eine Funktion der CS50-Bibliothek, den Benutzer nach einer Zeichenkette fragt.

#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
}

Überlegen Sie nun, wie Sie die Punktzahl für jedes Wort berechnen können. Da für beide Wörter derselbe Bewertungsalgorithmus gilt, bietet sich eine gute Gelegenheit zur Abstraktion. Daher werden wir eine Funktion namens compute_score definieren, die einen string als Argument entgegennimmt, auf den innerhalb der Funktion über word zugegriffen werden kann. Die Punktzahl von word wird dann als int zurückgegeben. Wie immer gilt: Den Funktionsprototypen oberhalb der main-Funktion nicht vergessen!

#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
}

Wenden wir uns nun der Implementierung von compute_score zu. Um die Punktzahl eines Wortes zu berechnen, müssen wir den Punktwert jedes Buchstabens im Wort kennen. Man kann Buchstaben und ihre Punktwerte mit einem Array verknüpfen. Stellen Sie sich ein Array mit 26 int vor, genannt POINTS, in dem die erste Zahl der Punktwert für ‘A’ ist, die zweite Zahl der Punktwert für ‘B’, und so weiter. Wenn Sie ein solches Array außerhalb aller Funktionen deklarieren und initialisieren, kann jede Funktion auf dieses Array zugreifen, einschließlich 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
}

Damit die Implementation von compute_score die Punktzahl des Wortes richtig berechnet, versuchen Sie zunächst, den Punktwert eines einzelnen Buchstabens in word zu finden.

  • Erinnern Sie sich, dass Sie s[n] schreiben können, um das Zeichen am n-ten Index einer Zeichenkette s, zu finden. So liefert word[0] zum Beispiel das erste Zeichen von word.
  • Erinnern Sie sich nun daran, dass Computer Zeichen mit ASCII darstellen, einem Standard, der jedes Zeichen als eine Zahl repräsentiert.
  • Erinnern Sie sich auch daran, dass der 0. Index von POINTS, POINTS[0], Ihnen den Punktwert von ‘A’ liefert. Überlegen Sie, wie Sie die numerische Darstellung von ‘A’ in den Index seines Punktwerts umwandeln können. Und was ist dann mit ‘a’? Auf Groß- und Kleinbuchstaben müssen Sie jeweils unterschiedliche Transformationen anwenden. Dabei sind vermutlich die Funktionen isupper und islower recht hilfreich.
  • Beachten Sie auch, dass Zeichen, die keine Buchstaben sind, mit null Punkten bewertet werden sollten. Zum Beispiel ist ! 0 Punkte wert.

Wenn Sie den Wert von einem Zeichen in word richtig berechnen können, können Sie eine Schleife verwenden, um die Punkte für die restlichen Zeichen zu addieren. Wenn Sie die obigen Hinweise selbst ausprobiert haben, vergleichen Sie Ihren Ansatz mit dem folgenden Code-Snippet (Achtung: Spoiler!).

#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;
}

Beachten Sie, dass die Prüfung mit isupper und islower in den if-Bedingungen implizit auch prüft, ob es sich überhaupt um einen Buchstaben handelt. Ist ein Zeichen im Wort z.B. ein !, so wird keine der beiden if-Bedingungen als true ausgewertet und das Zeichen wird gewissermaßen einfach übersprungen, was einer Bewertung mit 0 Punkten gleichkommt. Das Weglassen der zweiten if-Bedingung - unter der naiven Annahme, dass jeder Buchstabe, der nicht großgeschrieben ist, kleingeschrieben sein muss - würde also problematisch werden, sobald ein Benutzer entgegen der Regeln z.B. ein ! in eines der Wörter einfügt.

Fehlt nur noch der letzte Schritt des Pseudocodes: das Ausgeben des Gewinners. Wie bereits im vorherigen Code-Snippet verwendet, kann mit einer if-Bedingung geprüft werden, ob eine Bedingung wahr ist, und mit der zusätzlichen Verwendung von else if oder else können weitere (exklusive) Bedingungen geprüft werden.

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

Wenn Sie die obigen Schritte ausprobiert haben, können Sie sich den letzten Code-Snippet (oder besser gesagt die vollständige Lösung!) ansehen und Ihren Code vergleichen:

#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;
}

Testen

Kompilieren und Ausführen

Wenn Sie mit dem Programm fertig sind, können Sie scrabble.c mit folgendem Befehl kompilieren:

make scrabble
ℹ️
Prüfen Sie beim Auftreten eines Fehlers auch, ob Sie sich im richtigen Verzeichnis befinden, d.h. dem Verzeichnis, in dem auch die zu kompilierende Datei liegt. Wenn Sie den Ordner und die Datei wie in Schritt für Schritt beschrieben erstellt haben, sollten Sie sich in scrabble befinden. Sie können sich Ihr aktuelles Verzeichnis mit dem Befehl pwd ausgeben lassen.

Wenn Sie keine Fehlermeldung sehen, wurde das Programm erfolgreich kompiliert! Sie können dies mit

ls

überprüfen. Es sollte nun nicht nur scrabble.c (das ist der Quellcode), sondern auch scrabble (darin steht der Maschinencode) aufgelistet werden.

Wenn Sie eine Fehlermeldung sehen, versuchen Sie, Ihren Code zu korrigieren und ihn erneut zu kompilieren. Wenn Sie die Fehlermeldung jedoch nicht verstehen, versuchen Sie

help50 make scrabble

auszuführen, um Hilfe zu erhalten.

Sobald Ihr Code erfolgreich kompiliert wurde, können Sie Ihr Programm mit folgendem Befehl ausführen:

./scrabble

Testeingaben

Ihr Programm sollte sich wie in den folgenden Beispielen verhalten:

$ ./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!

Korrektheit

Führen Sie in Ihrem Terminal den folgenden Befehl aus, um die Korrektheit Ihrer Arbeit zu überprüfen:

check50 -l cs50/problems/2024/x/scrabble

Dieses Kommandozeilenprogramm gibt fröhliche Gesichter aus, wenn Ihr Code die automatisierten Tests von CS50 besteht, und traurige Gesichter, wenn er es nicht tut!

ℹ️
Damit das automatische Testen über check50 funktioniert, muss Ihr Programm den in der Aufgabenstellung vorgegebenen Namen scrabble.c haben. Bei der Ausführung des obigen Befehls müssen Sie sich zudem in dem Ordner befinden, in dem auch die Dateien zu scrabble liegen.

Style

Führen Sie den folgenden Befehl aus, um den Stil Ihres Codes mit style50 zu analysieren:

style50 scrabble.c

Dieses Kommandozeilenprogramm gibt Ergänzungen (in grün) und Löschungen (in rot) aus, die Sie an Ihrem Programm vornehmen sollten, um seinen Stil zu verbessern. Wenn Sie Probleme haben, diese Farben zu sehen, unterstützt style50 auch andere Modi!