Bildbox 1 (klick hier)
Den vollständigen Quellcode für dieses Projekt finden Sie auf meinem Github-Repository: https://github.com/OttoReuter/Picopad/Sudoku. Erschrecken Sie nicht bei der Anzahl der vielen Dateien. In der readme.md habe ich beschrieben, welches Ziel damit verfolgt wird. Ich habe mich nämlich bewusst für einen monolithischen Arduino-Sketch mit modularer Architektur entschieden. D.h. jede Datei besitzt eine klar definierte Aufgabe. Trotzdem wird die Anwendung von der IDE als ein gemeinsamer Sketch verwaltet. Dadurch bleibt der Code:
übersichtlich
wartbar
leicht testbar
einfach erweiterbar
Was wird nun im Einzelnen bei diesem Sudoku-Spiel umgesetzt?
Los gehts:
Benutzt wird ein 9x9 Spielfeld mit den allgemein üblichen Sudokuregeln (werden hier vorausgesetzt und sind ansonsten überall im Netz wiedergegeben).
Damit ein gültiges Spielfeld entsteht, gebe ich ein Anfangsfeld 'baseArray[81]' vor und definiere auch gleich ein Arbeitsarray 'array[81]' und ein Array für das Ergebnis 'result[81]'.
45 // Ausgangsfeld, welches den Regeln entspricht
46 int baseArray[81] = {1,2,3,4,5,6,7,8,9,4,5,6,7,8,9,1,2,3,7,8,9,1,2,3,4,5,6,
47 2,3,4,5,6,7,8,9,1,5,6,7,8,9,1,2,3,4,8,9,1,2,3,4,5,6,7,
48 3,4,5,6,7,8,9,1,2,6,7,8,9,1,2,3,4,5,9,1,2,3,4,5,6,7,8};
49 //const uint8_t array[81];
50 int array[81];
51 int result[81];
Auf dem Display würde das 'baseArray' so angezeigt. Es bildet den Ausgangspunkt für eine lange Reihe von
Veränderungen.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 4 | 5 | 6 | 7 | 8 | 9 | 1 | 2 | 3 |
| 7 | 8 | 9 | 1 | 2 | 3 | 4 | 5 | 6 |
| 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 |
| 5 | 6 | 7 | 8 | 9 | 1 | 2 | 3 | 4 |
| 8 | 9 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 | 2 |
| 6 | 7 | 8 | 9 | 1 | 2 | 3 | 4 | 5 |
| 9 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
Wie man sieht, stehen in jeder Zeile und jeder Spalte die Ziffer 1 bis 9 genau einmal. Ebenso in jedem der neun 3x3 Blöcke. Als Summe in jeder Zeile, jeder Spalte und jedem 3x3 Block ergibt sich 1+2+3+4+5+6+7+8+9 = 45. Nun wäre es natürlich langweilig, wenn jedesmal das gleiche Muster vorkommt. Deshalb werden jetzt eine Vielzahl von Vertauschungen vorgenommen, ohne dass sich die Gültigkeit der Anordnung ändert. Und zwar von jeweils zwei Ziffern, von zwei Zeilen im Block, von zwei Spalten im Block, von Zeilenblöcken, von Spaltenblöcken und einem Transponieren der Matrix (d.h. an den Diaginalen spiegeln). Sehen Sie exemplarisch zwei Beispiele aus dem Quellcode:
12 // ==================
13 // Zahlen vertauschen
14 // ==================
15 void swapNumbers() {
16 int zahl1 = random(1, 10);
17 int zahl2;
18
19 do {
20 zahl2 = random(1, 10);
21 } while (zahl1 == zahl2);
22
23 for (i = 0; i < 81; i++) {
24 if (array[i] == zahl1)
25 array[i] = zahl2;
26 else if (array[i] == zahl2)
27 array[i] = zahl1;
28 }
29 }
30
31 // =============================
32 // zwei Zeilen im Block tauschen
33 // =============================
34 void swapRows() {
35 int block = random(3);
36 int r1 = block * 3 + random(3);
37 int r2;
38
39 do {
40 r2 = block * 3 + random(3);
41 } while (r1 == r2);
42
43 for (int c = 0; c < 9; c++) {
44 int t = array[r1 * 9 + c];
45 array[r1 * 9 + c] = array[r2 * 9 + c];
46 array[r2 * 9 + c] = t;
47 }
48 }
Wenn diese Vertauschungen in zufälliger Reihenfolge und zufälliger Anzahl von Wiederholungen durchgeführt
werden, entsteht ein
nicht vorhersehbares Spielfeld aus den ca. 5,5 Milliarden Möglichkeiten. Das sollte Zufall genug sein.
Nun folgt der wichtigste Teil.
Legt man nämlich aus drei Schierigkeitsstufen (hier gibt es leicht, mittel und schwer)
eine per 'random(10)' ausgewählte Maske, von denen es z.Z. jeweils 10 verschiedene Masken gibt, auf das Feld und verdeckt so eine gewisse
Anzahl von Zellen, ist die eindeutige Lösbarkeit des Rätsels nicht automatisch gegeben. Die
untere Tabelle zeigt ein Beispiel mit einer Maske. Die Anordnung mit
dieser Maske ist eindeutig lösbar. Da angebotene Sudokurätsel immer eindeutig sein sollten, wird dies mit der Datei 'Solver.cpp'
überprüft. Falls das nicht zutrifft, wird so lange ein neues Spielfeld generiert, bis die Bedingung erfüllt ist. Das erledigt der
Microkontroller sehr schnell, so dass der Nutzer nur sehr kurze Wartezeiten angezeigt bekommt.
| 3 | 5 | 4 | 2 | |||||
| 5 | 8 | 7 | 6 | |||||
| 2 | 3 | 1 | ||||||
| 4 | 8 | 6 | 3 | |||||
| 8 | 9 | 1 | ||||||
| 6 | 9 | 5 | 8 | |||||
| 3 | 5 | 2 | ||||||
| 4 | 7 | 2 | 6 | |||||
| 8 | 6 | 3 | 5 |
Nun kann der Nutzer das Sudoku durch entsprechende Eingaben lösen. Mit den Steuertasten navigiert er dazu an die gewünschte Stelle und bestätigt mit 'Y'. Das 'OK' auf dem rechten Sideboard zeigt an, dass eine Eingabe möglich ist. Ansonsten reagiert 'Y' nicht und die Position muss geändert werden.
Stimmt die ausgewählte Ziffer nicht, wird sie auf dem Display rot angezeigt und nach einer Sekunde wieder gelöscht. Eine 'Bestrafung' oder Punktabzug ist z.Z. nicht im Programm enthalten. Momentan darf also auch noch ein wenig geschummelt werden, indem man einfach mal eine Ziffer probiert.
In der Zwischenzeit wurde ein Update vorgenommen. Damit werden jetzt für jede gelöste Ziffer 15 Punkte gutgeschrieben. Bei einem Fehlversuch werden 15 Punkte abgezogen und die Anzahl der Fehlversuche angegeben. Probieren ist also noch möglich, wird aber bei falschen Ziffern mit Punktabzug geahndet.
Noch ein Hinweis für alle Download-Interessenten.
Wenn Sie das Picopad von Pajenicko als Fertiggerät bestellt oder als Selbtbau mit dem mitgelieferten RP2040 Pico W besitzen, dann finden Sie hier die 'uf2-Datei' zum downloaden des Sudoku-Spiels.
Hier bekommen Sie die 'uf2-Datei für den Pico 2W:
Ansonsten kopieren Sie alle Dateien zum Sudoku-Spiel aus meinem Github-Repository, öffnen die Datei 'sudoku.ino', wählen Ihren MC aus und kompilieren den Sketch.
Viel Spass und Erfolg beim Ausprobieren.