Scrabble
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
.
Demo
Schritt für Schritt
Öffnen Sie VS Code entsprechend Ihrem Setup.
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.
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:
- Die Aufforderung an den Benutzer zur Eingabe von zwei Wörtern.
- Das Berechnen der Punktzahl für jedes Wort.
- 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
}
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 Zeichenkettes
, zu finden. So liefertword[0]
zum Beispiel das erste Zeichen vonword
. - 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 Funktionenisupper
undislower
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
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!
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!