# Scrabble

Quelle: https://dev.inf.zone/exercises/03/scrabble/

![Scrabble Brett](https://dev.inf.zone/exercises/03/scrabble/scrabble.png)

## Aufgabe

**Auf einen Blick**

- **Was:** Ein Programm, das die Wörter zweier Spieler nach den Punktwerten von [Scrabble](https://de.wikipedia.org/wiki/Scrabble) bewertet und ausgibt, wer gewonnen hat.
- **Datei:** `scrabble.c` in einem Ordner `scrabble`.
- **Eingabe:** zwei Wörter, abgefragt mit `Player 1: ` und `Player 2: `.
- **Ausgabe:** `Player 1 wins!`, `Player 2 wins!` oder bei gleicher Punktzahl `Tie!` – genau so, auf Englisch.

### So werden Wörter bewertet

1. Jeder Buchstabe hat einen festen Punktwert, siehe Tabelle (Werte der englischen Ausgabe von Scrabble).
2. Groß- und Kleinschreibung spielt keine Rolle: `c` zählt so viel wie `C`.
3. Zeichen, die keine Buchstaben sind (Satzzeichen, Ziffern, Leerzeichen), zählen 0 Punkte.
4. Die Punktzahl eines Wortes ist die Summe der Punktwerte seiner Zeichen. Wer mehr Punkte hat, gewinnt.

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

**Beispiel** `Code!`:

| Zeichen | `C` | `o` | `d` | `e` | `!` | Summe |
| ------- | --- | --- | --- | --- | --- | ----- |
| Punkte  | 3   | 1   | 2   | 1   | 0   | **7** |

## Demo

[Terminal-Aufzeichnung ansehen](https://asciinema.org/a/74B4kq3ftleKe6AdN0NxFV8CN)

## Spezifikation

-   Implementieren Sie Ihr Programm in C in einer Datei namens `scrabble.c` in einem Ordner namens `scrabble`.
-   Ihr Programm fragt mit `get_string` zuerst mit der Aufforderung `Player 1: `, dann mit `Player 2: ` nach je einem Wort.
-   Es berechnet die Punktzahl beider Wörter nach den Regeln 1 bis 4 oben.
-   Es gibt danach genau eine Zeile aus: `Player 1 wins!`, `Player 2 wins!` oder `Tie!`, jeweils mit Zeilenumbruch, und sonst nichts (keine Punktzahlen).

## Ordner und Datei anlegen

Öffnen Sie VS Code entsprechend Ihrem [Setup](https://dev.inf.zone/extras/setup/); eine einfache Installation von VS Code reicht nicht. Führen Sie im Terminalfenster `cd` aus; die Eingabeaufforderung zeigt dann nur `$`. Legen Sie nun Ordner und Datei an:

```bash
mkdir scrabble
cd scrabble
code scrabble.c
```

Die Eingabeaufforderung zeigt jetzt `scrabble/ $`. Nach der Aufgabe kommen Sie mit `cd ../` wieder eine Ebene nach oben.

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

```c
#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:

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

> [!WARNING]
> Der letzte Tipp zeigt Ihnen Schritt für Schritt eine vollständige Musterlösung. Diesen sollten Sie idealerweise erst nach der Bearbeitung der Aufgabe ansehen - oder nachdem Sie zumindest ernsthaft versucht haben, die Aufgabe zu bearbeiten.
> Versuchen Sie auch nach jeder neuen Information oder Lösungsteil wieder selbst weiterzumachen.

**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](https://manual.cs50.io/3/get_string), den Benutzer nach einer Zeichenkette fragt.

```c
#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!

```c
#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`.

```c
#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](http://asciitable.com/) 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'? Entweder behandeln Sie Groß- und Kleinbuchstaben getrennt, oder Sie wandeln jeden Buchstaben vorher mit `toupper` um. Für den ersten Weg sind vermutlich die Funktionen [`isupper`](https://manual.cs50.io/3/isupper) und [`islower`](https://manual.cs50.io/3/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!).

```c
#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 jedes Zeichen, das nicht großgeschrieben ist, ein Kleinbuchstabe sein muss – würde also problematisch werden, sobald ein Wort z. B. ein `!` enthält.

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.

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

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

```c
#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 Sie Ihr Programm mit `make scrabble` und starten Sie es mit `./scrabble`. Wenn Sie eine Fehlermeldung beim Kompilieren nicht verstehen, erklärt `help50 make scrabble` sie Ihnen. Das Programm sollte sich so verhalten:

```
$ ./scrabble
Player 1: Question?
Player 2: Question!
Tie!
```

Weitere Testeingaben mit den Punktzahlen, die Ihr Programm berechnen sollte:

| Player 1    | Player 2      | Punkte  | Ausgabe          |
| ----------- | ------------- | ------- | ---------------- |
| `Question?` | `Question!`   | 17 : 17 | `Tie!`           |
| `red`       | `wheelbarrow` | 4 : 22  | `Player 2 wins!` |
| `COMPUTER`  | `science`     | 14 : 11 | `Player 1 wins!` |
| `Scrabble`  | `wiNNeR`      | 14 : 9  | `Player 1 wins!` |

### Korrektheit

Führen Sie in Ihrem Terminal den folgenden Befehl aus, um die Korrektheit Ihrer Arbeit zu überprüfen. Die Datei muss `scrabble.c` heißen, und Sie müssen sich im Ordner `scrabble` befinden.

```bash
check50 -l inf-zone/exercises/2026/scrabble
```

### Style

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

```bash
style50 scrabble.c
```

## Abgeben

Geben Sie im Ordner `scrabble` ab:

```bash
inf upload scrabble
```

Danach sehen Sie, welche Tests Ihr Programm besteht und ob die Übung für die Bonuspunkte zählt. Wie Sie `inf` installieren und sich anmelden, steht unter [Abgeben mit `inf`](https://dev.inf.zone/faq/uebung-solutions/#inf-installieren).
