Codeknacker ist ein Logikspiel, das sich mit einer 64x64-Matrix und einem Mikrocontroller leicht realisieren lässt. Es geht darum, einen Code,
der via Zufallsgenerator von dem Mikrocontroller erstellt wird, zu entschlüsseln. Um ans Ziel zu kommen, stehen dem Spieler mehrere Entschlüsselungsversuche zur
Verfügung. Nach jedem Versuch bekommt er Hinweise, die ihm verraten, inwieweit sein Versuch gelungen (oder nicht) ist. Durch logisches Denken, Kombinieren und
Schlussfolgern ist es möglich, anhand der Hinweise den geheimen Code zu knacken. Die vier einzelnen Segmente des geheimen Codes werden mithilfe von Spielwürfeln
dargestellt. Es geht also schließlich darum, die Augenzahlen der einzelnen Würfel zu erraten. Demnach kann der gesuchte Code wie folgt
aussehen:
In diesem Beispiel wiederholen sich die Augenzahlen einzelner Würfel nicht. Im Spiel kann dies jedoch vorkommen. Eine solche Kombination ist
demnach auch zulässig:
Dem Spieler steht ein Spielfeld mit vier Spalten und sechs Reihen, wo er seine Entschlüsselungsversuche tätigen kann, zur Verfügung. Unter dem
Feld befinden sich vier Taster (S1-S4), die dazu dienen, jedem Versuchswürfel die gewünschte Augenzahl zu vergeben. Die aktuelle Versuchsreihe wird mit einem
Pfeil gekennzeichnet:
Durch Betätigen eines Tasters kann der Spiele beim Raten die Augenzahl des betreffenden Würfels verändern. Nachdem der Spieler die gewünschte
Kombination zusammengestellt hat, bekommt er durch Betätigen des Tasters „Auswertung“ (S5) die Hinweise, die ihm bei weiteren Rateversuchen helfen sollen.
Das Feld mit den Hinweisen besteht aus zwei Spalten. In der linken Spalte wird Anzahl der Halbtreffer angegeben. Bei einem Halbtreffer handelt es sich um
einen Würfel, bei dem man die richtige Augenzahl erraten hat, der jedoch am falschen Platz positioniert wurde. In der rechten Spalte wird dagegen die
Anzahl der Volltreffer angegeben. Volltreffer sich die Versuche, wo man so die Augenzahl als auch die Position richtig erraten hat. Ein Beispiel soll
dies veranschaulichen:
Im oberen Bereich der Abbildung befindet sich der gesuchte Code, der während des Spiels unsichtbar bleibt. Im unteren Bereich sehen wir zwei
Entschlüsselungsversuche (links) und die anschließend ausgegeben Hinweise (rechts). In dem ersten Versuch hat der Spieler drei Halbtreffer erzielt. Die Würfel
mit Augenzahlen 4, 5 und 3 sind in dem gesuchten Code enthalten. Hier kann man sehen, dass es dem Spieler nicht gelungen ist, die richtige Würfel-Position zu
erraten. So zum Beispiel Würfel mit Augenzahl vier steht bei seinem Versuch auf der ersten Position. In dem gesuchten Code steht der entsprechende Würfel auf
dem vierten Platz. Als Ergebnis bekommt der Spieler drei Punkte in der linken Spalte der Hinweisfelder.
Im zweiten Versuch hat sich die Situation geändert. Der Spieler hat in zwei Fällen so die richtige Augenzahl der Würfel als auch ihre richtige Position
erraten. Es sind die Würfel mit Augenzahl 5 und 3. Dafür gibt es einen Hinweis für zwei Volltreffer. Der Hinweis erscheint in der rechten Spalte der
Hinweisfelder.
Bei dem Würfel mit der Augenzahl 4 hat er die richtige Position verfehlt. Dafür bekommt der Spieler einen Punkt, der in der linken Spalte der
Hinweisfelder erscheint.
Basierend auf den gesammelten Ergebnissen kann der Spieler seine Versuche fortsetzen, bis er schließlich den versteckten Code erfolgreich
herausgefunden hat.
Matrix
Das Spielfeld des Codeknackers wird auf einer Matrix, die aus 64x64 RGB-LEDs besteht, dargestellt. In diesem Fall wird nach einem 4-stelligen geheimen
Würfel-Code gesucht. Die Matrix mit 4096 Pixeln macht es jedoch möglich, auch andere Variationen des Spiels zu realisieren. Ein Codeknacker mit 5-stelligem
geheimem Code für große Logik-Denker wäre kein großes Problem.
Das Programm wird von dem Mikrocontroller Arduino Mega verwaltet. Seine Aufgabe ist nicht besonders kompliziert. Grundsätzlich besteht sie aus zwei
Teilen. Mit vorgefertigten Anweisungen, die hier die Bibliothek "RGBmatrixPanel.h" zur Verfügung stellt, werden Schriften, Rechtecke oder Punkte auf der Matrix platziert, um
die Spielgeschehnisse sichtbar zu machen. Erst nachdem der Spieler den „Auswertung“-Taster betätigt hat, muss der Mikrocontroller ein wenig nachdenken, um als
Antwort die richtigen Hinweise zu liefern. Hat der Spieler etwas getroffen, sind das Halbtreffer oder möglicherweise Volltreffer?
Um die Schaltung klein zu halten, kommt hier kein Spannungsregler vor. Der Mikrocontroller wird direkt vom PC aus über den USB-Anschluss mit
Strom versorgt.
Die Aktivitäten des Programms beschränken sich zur Laufzeit lediglich auf die Abfrage der sechs Taster, die vom Spieler betätigt werden können. Mit dem Taster
S0 wird das Spiel gestartet. Hier wird u.a. via Zufall der geheime Code (random() Funktion) generiert. Die Variable „Spiel_laeuft“ wird auf „1“ gesetzt und der
Taster S0 wird bis zum Spielende nicht mehr benutzt.
Mit den Tastern S1 bis S4 werden während des Ratens die Augenzahlen der einzelnen Würfel bestimmt. Jeder Taster bezieht sich auf eine bestimmte Spalte. Die
aktuelle Versuchsreihe wird mit einem Pfeil gekennzeichnet. Die Position des Pfeils bestimmt die Variable „Versuch_Nr“.
Mit dem Taster S5 werden die Hinweise angefordert, die dem Spieler verraten, wie viel Halb- bzw. Volltreffer er in dem jeweiligen Versuch erzielt hat.
Das Spiel ist beendet, wenn der Code erfolgreich entschlüsselt wurde oder der sechste Entschlüsselungsversuch misslungen ist.
Vor dem Start des Programms muss die Bibliothek "RGBmatrixPanel.h" installiert werden.
// *****************************************************************************************
// Codeknacker mit RGB Matrix 64x64
// Eine Schaltung mit Arduino Mega
// Arduino IDE 2.0.4
// ************************************************************************
#include "RGBmatrixPanel.h"
#define CLK 11 // Pins für Matrix-Anschlüsse
#define OE 9
#define LAT 10
#define A A0
#define B A1
#define C A2
#define D A3
#define E A4
RGBmatrixPanel matrix(A, B, C, D, E, CLK, LAT, OE, false, 64);
int Taster_Start = 2; // Digitaler Eingang Spiel Start
int Taster_Spalte_1 = 3; // Digitaler Eingang Spalte 1
int Taster_Spalte_2 = 4; // Digitaler Eingang Spalte 2
int Taster_Spalte_3 = 5; // Digitaler Eingang Spalte 3
int Taster_Spalte_4 = 6; // Digitaler Eingang Spalte 4
int Taster_Auswertung = 7; // Digitaler Eingang Antwort holen
int Wuerfel_Wert; // Würfel Augenzahl
bool Spiel_laeuft; // Spiel gestartet
int Versuch_Nr; // Maximal 6 Versuche
int CodeZahl[4]; // Geheimer Code via Zufall erstellt
int Versuchszahl[4]; // Augenzahl pro Spalte
// *****************************************************************************************
void setup() {
matrix.begin();
delay(500);
pinMode(Taster_Start, INPUT_PULLUP); // Alle Taster PullUp
pinMode(Taster_Spalte_1, INPUT_PULLUP);
pinMode(Taster_Spalte_2, INPUT_PULLUP);
pinMode(Taster_Spalte_3, INPUT_PULLUP);
pinMode(Taster_Spalte_4, INPUT_PULLUP);
pinMode(Taster_Auswertung, INPUT_PULLUP);
// Start-Text 3 Sek.
matrix.setTextSize(2); // Textgröße
matrix.setCursor(3, 5); // Cursor Position
matrix.setTextColor(matrix.Color333(7, 7, 0)); // Textfarbe
matrix.println("Code");
matrix.setTextSize(1);
matrix.setCursor(17, 25);
matrix.println("Knacker");
delay(3000);
matrix.fillRect(0, 0, matrix.width(), matrix.height(), matrix.Color333(0, 0, 0));
Feld_aufbauen(); // Spielfeld aufbauen
}
// *****************************************************************************************
void loop() {
if (digitalRead(Taster_Start) == LOW & Spiel_laeuft == 0) { // Spiel Start
int Zufall_Startwert;
while (digitalRead(Taster_Start) == LOW) {
Zufall_Startwert++; // Startwert für randomSize
if (Zufall_Startwert > 10) {
Zufall_Startwert = 0;
}
}
randomSeed(Zufall_Startwert); // Startwert vorgeben
CodeZahl[0] = random(1, 7); // Zufalsszahl Spalte 1
CodeZahl[1] = random(1, 7); // Zufalsszahl Spalte 2
CodeZahl[2] = random(1, 7); // Zufalsszahl Spalte 3
CodeZahl[3] = random(1, 7); // Zufalsszahl Spalte 4
Spiel_laeuft = 1; // Spiel gestartet
Smiles(46, 9, 1); // Smiles neutral
Versuch_Nr = 1; // Versuch_Nr = aktuelle Reihe
Pfeil(Versuch_Nr); // Reihe-Markierung
}
if (digitalRead(Taster_Spalte_1) == LOW) { // Versuch Spalte 1
Mein_Versuch(1);
}
if (digitalRead(Taster_Spalte_2) == LOW) { // Versuch Spalte 2
Mein_Versuch(2);
}
if (digitalRead(Taster_Spalte_3) == LOW) { // Versuch Spalte 3
Mein_Versuch(3);
}
if (digitalRead(Taster_Spalte_4) == LOW) { // Versuch Spalte 4
Mein_Versuch(4);
}
if (digitalRead(Taster_Auswertung) == LOW) { // Antwort holen
Auswertung();
}
}
// Auswertung ------------------------------------------------------------------------------
void Auswertung() {
int Platz_Halter[4][2];
int Volltreffer = 0;
int Halbtreffer = 0;
for (int i = 0; i < 4; i++) {
Platz_Halter[i][0] = false;
Platz_Halter[i][1] = false;
}
// Voltreffer Untersuchung
for (int j = 0; j < 4; j++) { // Richtige Zahl am richtigen Platz
if (Versuchszahl[j] == CodeZahl[j]) {
Volltreffer++;
Platz_Halter[j][0] = true;
Platz_Halter[j][1] = true;
}
}
if (Volltreffer > 0) { // Anzahl der Volltreffer anzeigen
Wuerfel(2 * 7 + 33, 60 - Versuch_Nr * 7, Volltreffer, 7);
}
// Halbtreffer Untersuchung
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 4; k++) {
if (Versuchszahl[j] == CodeZahl[k]) {
if (Platz_Halter[j][0] == false) {
if (Platz_Halter[k][1] == false) {
Halbtreffer++;
Platz_Halter[j][0] = true;
Platz_Halter[k][1] = true;
}
}
}
}
}
if (Halbtreffer > 0) { // Anzahl der Halbtreffer anzeigen
Wuerfel(1 * 7 + 33, 60 - Versuch_Nr * 7, Halbtreffer, 7);
}
// Gewonnen
if (Volltreffer == 4) {
Smiles(46, 9, 2); // Smiles lächelt
for (int i = 0; i < 4; i++) {
Wuerfel(i * 7 + 9, 5, CodeZahl[i], 0); // Code anzeigen
}
}
// Verloren
if ((Versuch_Nr == 6) & (Volltreffer < 4)) {
Smiles(46, 9, 3); // Smiles trauert
for (int i = 0; i < 4; i++) {
Wuerfel(i * 7 + 9, 5, CodeZahl[i], 0); // Code anzeigen
}
}
// Weitermachen
if ((Versuch_Nr < 6) & (Volltreffer < 4)) {
Versuch_Nr++; // Sprung in die nächste Reihe
Pfeil(Versuch_Nr); // Aktuelle Reihe markieren
}
delay(300); // Taster Prellung abwarten
}
// Mein Versuch ----------------------------------------------------------------------------
void Mein_Versuch(int Spalte) {
Wuerfel_Wert++; // Augenzahl erhöhen
if (Wuerfel_Wert > 6) { Wuerfel_Wert = 1; }
Wuerfel(Spalte * 7 + 2, 60 - Versuch_Nr * 7, Wuerfel_Wert, 0); // anzeigen
Versuchszahl[Spalte - 1] = Wuerfel_Wert;
delay(300);
}
// Feld aufbauen ---------------------------------------------------------------------------
void Feld_aufbauen() {
matrix.drawRect(0, 0, 64, 64, matrix.Color333(0, 7, 0));
for (int x = 1; x < 5; x++) {
for (int y = 1; y < 7; y++) {
Wuerfel(x * 7 + 2, y * 7 + 11, 0, 0);
}
}
for (int x = 1; x < 3; x++) {
for (int y = 1; y < 7; y++) {
Wuerfel(x * 7 + 33, y * 7 + 11, 0, 7);
}
}
}
// Pfeil -----------------------------------------------------------------------------------
void Pfeil(int X) {
// Aktuelle Reihe markieren
// Zuerst den alten Pfeil löschen
if (Versuch_Nr > 1) {
matrix.fillRect(3, 62 - (Versuch_Nr - 1) * 7, 5, 3, matrix.Color333(0, 0, 0));
}
// Neuen Pfeil aufbauen
matrix.drawLine(3, 63 - Versuch_Nr * 7, 7, 63 - Versuch_Nr * 7, matrix.Color333(7, 7, 0));
matrix.drawPixel(6, 62 - Versuch_Nr * 7, matrix.Color333(7, 7, 0));
matrix.drawPixel(6, 64 - Versuch_Nr * 7, matrix.Color333(7, 7, 0));
}
// Smiles ----------------------------------------------------------------------------------
void Smiles(int X, int Y, int Form) {
// 1 - Smiles neutral: Spiel läuft
// 2 - Gewonnen
// 3 - Verloren
matrix.drawCircle(X, Y, 5, matrix.Color333(7, 7, 0));
matrix.fillRect(X - 2, Y - 2, 2, 2, matrix.Color333(7, 7, 0));
matrix.fillRect(X + 1, Y - 2, 2, 2, matrix.Color333(7, 7, 0));
matrix.fillRect(X - 2, Y + 1, 5, 3, matrix.Color333(0, 0, 0));
switch (Form) {
case 1: {
matrix.drawLine(X - 2, Y + 2, X + 2, Y + 2, matrix.Color333(7, 7, 0));
break;
}
case 2: {
matrix.drawLine(X - 1, Y + 3, X + 1, Y + 3, matrix.Color333(7, 7, 0));
matrix.drawPixel(X - 2, Y + 2, matrix.Color333(7, 7, 0));
matrix.drawPixel(X + 2, Y + 2, matrix.Color333(7, 7, 0));
break;
}
case 3: {
matrix.drawLine(X - 1, Y + 1, X + 1, Y + 1, matrix.Color333(7, 7, 0));
matrix.drawPixel(X - 2, Y + 2, matrix.Color333(7, 7, 0));
matrix.drawPixel(X + 2, Y + 2, matrix.Color333(7, 7, 0));
break;
}
}
}
// Würfel ----------------------------------------------------------------------------------
void Wuerfel(int X, int Y, int Wert, int Farbe) {
matrix.drawRect(X, Y, 7, 7, matrix.Color333(Farbe, 0, 7)); // Würfel Rahmen
matrix.fillRect(X + 1, Y + 1, 5, 5, matrix.Color333(0, 0, 0)); // Inhalt löschen
switch (Wert) {
case 0: {
matrix.drawPixel(X + 2, Y + 2, matrix.Color333(0, 0, 0));
matrix.drawPixel(X + 2, Y + 3, matrix.Color333(0, 0, 0));
matrix.drawPixel(X + 2, Y + 4, matrix.Color333(0, 0, 0));
matrix.drawPixel(X + 4, Y + 2, matrix.Color333(0, 0, 0));
matrix.drawPixel(X + 4, Y + 3, matrix.Color333(0, 0, 0));
matrix.drawPixel(X + 4, Y + 4, matrix.Color333(0, 0, 0));
matrix.drawPixel(X + 3, Y + 3, matrix.Color333(0, 0, 0));
break;
}
case 1: {
matrix.drawPixel(X + 3, Y + 3, matrix.Color333(Farbe, 0, 7));
break;
}
case 2: {
matrix.drawPixel(X + 3, Y + 2, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 3, Y + 4, matrix.Color333(Farbe, 0, 7));
break;
}
case 3: {
matrix.drawPixel(X + 2, Y + 2, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 3, Y + 3, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 4, Y + 4, matrix.Color333(Farbe, 0, 7));
break;
}
case 4: {
matrix.drawPixel(X + 2, Y + 2, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 2, Y + 4, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 4, Y + 2, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 4, Y + 4, matrix.Color333(Farbe, 0, 7));
break;
}
case 5: {
matrix.drawPixel(X + 2, Y + 2, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 2, Y + 4, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 4, Y + 2, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 4, Y + 4, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 3, Y + 3, matrix.Color333(Farbe, 0, 7));
break;
}
case 6: {
matrix.drawPixel(X + 2, Y + 2, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 2, Y + 3, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 2, Y + 4, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 4, Y + 2, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 4, Y + 3, matrix.Color333(Farbe, 0, 7));
matrix.drawPixel(X + 4, Y + 4, matrix.Color333(Farbe, 0, 7));
break;
}
}
}