Table of Contents

Aufgabe 3

Gegeben ist eine Aufteilung in Regionen und die Wörter in den Regionen.

Es soll jetzt überprüft werden, ob das Puzzle eine eindeutige Lösung hat.

Dazu wird ein Algorithmus gesucht, der systematisch das Puzzle versucht zu lösen, indem dieser sämtliche Möglichkeiten durchprobiert. Sobald eine zweite Lösung gefunden wurde, kann der Algorithmus abgebrochen werden. Ansonsten muss er fertig laufen.

«Alles durchprobieren» ist je nach Definition von «alles» praktisch schlicht unmöglich. Die Idee ist, möglichst früh sagen zu können, dass mit der bereits getroffenen Wahl keine Lösung möglich ist.

Dazu verwenden wir folgende zwei Eigenschaften:

Navigieren Sie in das Verzeichnis funkmast und «checken» Sie den «tag» a3 aus:

git fetch --all --tags
git checkout tags/a3
git switch -c dev

Vorschlag zur Implementation

Folgende Feststellung ist zentral für den Algorithmus:

Wortbaum

Die Idee ist, alle Wörter der Wortliste möglichst schnell und effizient durchprobieren zu können. Da jeweils der, bzw. die ersten Buchstaben gegeben sind, brauchen wir schnellen Zugriff auf alle Wörter die mit gegebenen Buchstaben beginnent. Dazu wird die Liste in einem Baum gespeichert, modelliert mit JavaScript Objects (key-value pairs).

Jeder Knoten hat maximal 27 Einträge, 'a'-'z' und '_', um ein Wortende zu markieren.

Die Liste [“aber”, “apfel”, “abend”, “bla”] sieht als Baum wie folgt aus:

{'a': {'b': {'e': {'n': {'d': {'_'}}}, 'r':{'_'}}}}, 'p':{'f':{'e':{'l':{'_'}}}}}, 'b':{'l':{'a':{'_'}}}}

Der Clue ist, dass einfach bei dem Unterbaum weitergearbeitet werden kann, der durch die bereits gegebenen Buchstaben erreicht werden kann. Dazu reicht es, die Referenz auf den Unterbaum zu haben, es brauchen keine weiteren Daten kopiert oder gespeichert zu werden.

Dieser Baum muss nur einmal aus der Wortliste erstellt werden.

tryNextLetter(used, curUsed, pft)

Diese Funktion liefert ein Array von möglichen Wörtern, die bei der obersten, am meisten links liegenden freien Zellen starten. Ein Wort ist wie die Variable curUsed (siehe unten) codiert. Ist das Array leer, heisst das, dass die vorhergehende Platzierung nicht möglich ist und diese schrittweise rückgängig gemacht werden muss.

Beim ersten Aufruf wird die Funktion mit curUsed = [[x,y]] und pft = wortbaum[buchstabe] aufgerufen, wobei x,y die Koordinaten der ersten Zelle und buchstabe der entsprechende Buchstabe ist.

Die Funktion initialisiert das Array found=[] und geht dann alle möglichen Buchstaben letter in pft durch:

connected(coords)

Diese Funktion überprüft, ob die Koordinaten im Array coords eine zusammenhängende Region bilden.

Dabei wird wie folgt vorgegangen:

placeNextWord(used, pft, curRegion=0)

Diese Funktion ermittelt die aktuelle Startposition für das nächste mögliche Wort und platziert alle möglichen Wörter und ruft sich dabei wieder auf. Als Resultat liefert die Funktion die Anzahl Lösungen (0,1 oder 2, mehr werden nicht gesucht).

Zuerst wird die Startzelle gesucht. Gibt es keine solche, ist das Puzzle voll und die Funktion gibt als Resultat 1 Lösung zurück.

Ab der Startzelle wird für alle möglichen Wörter gesucht und in words gespeichert.

Ist words leer, hat das Puzzle so keine Lösung und die Funktion gibt als Resultat 0 zurück.

numSol=0, die Anzahl möglicher Lösungen bis jetzt.

Sonst wird für jedes word in words folgendes gemacht:

solve()

Die Funktion erstellt den Wortbaum pft, initialisiert alle used[x][y] auf -1 und ruft placeNextWord(used, pft) auf und meldet das Resultat (1 oder 2) zurück. Das Puzzle hat einen eindeutige Lösung wenn 1 gemolden wird. (0 ist nicht möglich, weil das Puzzle ja mit einer Lösung generiert wurde).

Weitere Betrachtungen

Es gibt erstaunlich wenige Wörter, die jeweils überhaupt platziert werden können (durchschnittlich unter zwei, wenn man alle Fälle weglässt, wo kein Wort platziert werden kann). Der Algorithmus funktioniert auch noch einigermassen gut bei 4x so grossen Feldern (14×24) mit einer Wortliste von 23k Wörtern.

Darum funktioniert der hier vorgestellte Algorithmus auch. Es ist nicht klar, ob sich eine frühe Feststellung von nicht-zusammenhängenden Regionen überhaupt lohnen würde.

Wenn es mehrere Lösungen gibt, sind es fast immer Fälle der folgenden Art:

+---+---+ 
| A   B |
+   +   +
| E   R |
+---+---+
| R   A |
+   +   +
| B   E |
+---+---+
 
oder
 
+---+---+ 
| A   B |
+   +---+
| E | R |
+   +   +
| R | A |
+---+   +
| B   E |
+---+---+

Vor allem bei grösseren Felder würde es sich wohl lohnen, solche Fälle zum vornherein zu finden und neue Wörter zu wählen.