Eine im Handel erhältliche "Vier Gewinnt" - Ausgabe.
Die selbstgemachte meine-schaltung.de-Version.
Das Spiel „Vier Gewinnt“, das in den 70er-Jahren entstand, ist ein Strategiespiel für zwei Personen. Man
könnte sagen, „Vier Gewinnt“ ist ein Zwillingsbruder von „Vier in eine Reihe“ oder „Fünf in eine Reihe“. Die Grundregeln aller
dieser Spiele sind nahezu
identisch. Der Unterschied bezieht sich lediglich auf den Zugang zu dem Spielfeld. Ist die Frage bei „Vier in eine Reihe“ eher
irrelevant, trifft man bei „Vier Gewinnt“ gleich auf eine Einschränkung. Das Spielfeld steht hier normalerweise senkrecht und
die Steine, die das Kreuz und Kreis ersetzen, können nur von oben auf das Spielfeld gebracht werden. Auch hier geht es darum, vier
eigene Steine in eine Linie zu bringen. Sobald man dies geschafft hat, ist der Gegner geschlagen und das Spiel
zu Ende.
Arduino Nano
In unserem Versuch wird ein der Spieler von dem Arduino ersetzt. Es kommt der kleine Arduino Nano zum Einsatz.
Arduino Nano ist eine der kleinsten Arduino-Platinen und wird überwiegend bei kleinen Projekten eingesetzt. Bei dem Versuch kann
man beobachten, dass der Kleine ganz flott mit seinen Aufgaben fertig wird und zusätzlich noch über Reserven
verfügt. Die Betriebsspannung von Arduino beträgt 5V, ein Ausgang kann mit bis zu 40 mA belastet werden. Empfohlene
Versorgungsspannung beträgt 7-12 VDC.
8x8 RGB-Matrix CJMCU
Arduino steuert mit seinem Programm die RGB-Matrix CJMCU an. Die Matrix besteht aus 64 RGB-Dioden, die in
acht Reihen und acht Spalten angeordnet sind. Dabei handelt es sich um die WS2812 LEDs, die intern aus drei Leuchtdioden (Rot, Grün,
Blau) und einem Controller-Chip bestehen. Damit ist für die Matrix kein externer Treiber notwendig. Die gesamte Matrix wird mit
nur einer Datenleitung angesteuert. Auf Empfehlung des Herstellers wird die einfache Schaltung mit einem Kondensator von 1000 µF und
einem Widerstand im Wert von 300 Ohm erweitert. Als weitere Komponenten werden an die Eingänge des Arduino fünf Taster angeschlossen. Sie
ermöglichen während des Spiels die Kommunikation mit dem Mikrocontroller. Eine separate Leuchtdiode dient als Signalleuchte,
die das Ende des Spiels markiert.
Rückseite der Testplatine.
Schaltplan
Schaltplan (Arduino ohne USB-Anschluss: Vin > 7 V)
Das Programm
Mit den Tasten „ROT“ und „GRÜN“ kann gewählt werden, wer von den Spielern den ersten Zug tätigt. Die Farben sind
fest zugeordnet. Arduino spielt mit „ROT“, sein Gegenüber mit „Grün“. Mit den Tasten „Links“ und „Rechts“ kann der Spieler den
Cursor auf die gewünschte Spalte bewegen, mit der Taste „Enter“ seinen Zug-Wunsch bestätigen. Nachdem der Spieler seinen Zug
absolviert hatte, ist Arduino an der Reihe.
Das Programm (Sketch) untersucht die Felder, die für den nächsten Zug infrage kommen und bewertet sie. Bei jedem Zug
werden dem entsprechenden Feld Punkte zugewiesen. Ein Arduino-Zug bringt einem Feld 1 Punkt. Die vom Gegner besetzten Felder
werden mit 5 Punkten bewertet. Bei der Untersuchung werden diese Punkte für alle 4er-Kombinationen summiert und verglichen.
Grundsätzlich untersucht das Programm nur eigene Züge. Nur in einem Fall wird Bezug auf die Züge des Gegners genommen (3er
Kombination). Somit besteht noch genügend Raum für Erweiterungen und Verbesserungen des Programms. In dem folgenden Kurzvideo wird
diese einfache Version des Programms vorgestellt. Das Programm benötigt lediglich 22% des Programmierspeicherplatzes des
Mikrocontrollers.
Dank der Kommentare im Programm kann man die Vorgehensweise bei Berechnungen leicht nachvollziehen. Es wird dabei schnell
sichtbar, dass das Programm keinen ausgeklügelten Spiel-Strategien folgt. Es wird bei der Feldbewertung lediglich Stellung zu
der aktuellen Situation genommen. Und dennoch, diese einfache Vorgehensweise bei der Vergabe der Punkte bewirkt, dass jeder
Gelegenheitsspieler beim Spiel mit Sicherheit in Schwierigkeiten gerät.
Vor der Nutzung des Programms muss die Bibliothek "Adafruit NeoPixel" installiert werden.
// ************************************************************************
// Vier Gewinnt
// Mit RGB-Matrix CJMCU und Arduino Nano
// IDE 1.8.12
// ************************************************************************
#include<Adafruit_NeoPixel.h>
#ifdef __AVR__
#include<avr/power.h>
#endif#define LED_PIN 2
#define LED_COUNT 64
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Eingänge + Ausgänge ....................................................
int Taste_Links = 7;
int Taste_Rechts = 6;
int Taste_Gegner = 3;
int Taste_Ich = 4;
int Taste_Enter = 5;
int Spielende = 8;
// Variablen ..............................................................
int Spielbrett [15][15];
int Spieler = 0; // Eröffnung: 1 - Spieler, 2 - Arduino
int Reihe = 0;
int Spalte = 0;
int Feld_Nummer = 0;
int Cursor = 0;
int Game_is_over = 0;
int Zufallszahl;
// SETUP ******************************************************************
void setup() {
strip.begin();
strip.show();
strip.setBrightness(20);
pinMode(Taste_Links, INPUT_PULLUP);
pinMode(Taste_Rechts, INPUT_PULLUP);
pinMode(Taste_Gegner, INPUT_PULLUP);
pinMode(Taste_Ich, INPUT_PULLUP);
pinMode(Taste_Enter, INPUT_PULLUP);
pinMode(Spielende, OUTPUT);
Serial.begin(9600);
}
// HAUPTPROGRAMM **********************************************************
void loop() {
// Zufallszahlen via Schlaufe .............................
Zufallszahl++;
if (Zufallszahl > 11) { Zufallszahl = 4; }
delay (10);
// Rand vorbelegen ........................................
if (Spielbrett [1][1] == 0) { Rand_vorbelegen(); }
// Eröffnungsentscheidung .................................
if ( Spieler == 0) {
if (digitalRead(Taste_Gegner) == LOW) {
delay (300);
Spieler = 1; }
if (digitalRead(Taste_Ich) == LOW) {
delay (300);
Mein_Zufallszug(); } }
// Spiel ..................................................
if (Game_is_over == 0) {
switch (Spieler) {
case 1: { Spieler_am_Zug(); break; }
case 2: { Mein_Zug(); break;
} } }
// Spiel zu Ende ..........................................
if (Game_is_over == 1) {
do {
digitalWrite (Spielende, HIGH);
delay(500);
digitalWrite (Spielende, LOW);
delay(500);
} while (Game_is_over == 0); }
}
// HAUPTPROGRAMM ENDE *****************************************************
// Rand vorbelegen --------------------------------------------------------
void Rand_vorbelegen() {
int i,j;
for (i=1; i<15; i++) {
for (j=1; j<15; j++) {
Spielbrett [i][j] = 0;
if ((i < 5) or (i > 11)) {
Spielbrett [i][j] = -50; }
if ((j < 4) or (j > 11)) {
Spielbrett [i][j] = -50; } } }
}
// Programm Mein_Zufallszug -----------------------------------------------
void Mein_Zufallszug() {
Spalte = Zufallszahl;
for (int i=5; i<12; i++) {
if ((Spielbrett [i][Spalte] == 0) and (Spielbrett [i+1][Spalte] != 0)) {
Reihe = i; } }
Spielbrett [Reihe][Spalte] = 1;
Zug_ausfueren ();
Spieler = 1;
}
// Spieler am Zug ---------------------------------------------------------
void Spieler_am_Zug() {
if (Cursor == 0) {
Cursor = 1;
Reihe = 4;
Spalte = 4;
Cursor_Zeigen(1); }
if (Cursor == 1) {
if (digitalRead(Taste_Links) == LOW) {
delay (300);
Cursor_Zeigen(0);
Spalte--;
if (Spalte < 4) { Spalte = 11; }
Cursor_Zeigen(1); }
if (digitalRead(Taste_Rechts) == LOW) {
delay (300);
Cursor_Zeigen(0);
Spalte++;
if (Spalte > 11) { Spalte = 4; }
Cursor_Zeigen(1); }
if (digitalRead(Taste_Enter) == LOW) {
delay (300);
for (int i=5; i<12; i++) {
if ((Spielbrett [i][Spalte] == 0) and (Spielbrett [i+1][Spalte] != 0)) {
Reihe = i; } }
Spielbrett [Reihe][Spalte] = 5;
Cursor = 0;
Spieler = 2;
Zug_ausfueren ();
Vier_in_Reihe (20); } }
}
// Cursor Zeigen ----------------------------------------------------------
void Cursor_Zeigen (int EinAus) {
Feld_Nummer = (Reihe-4)*8 + (Spalte-3) - 1;
if (EinAus == 0) { strip.setPixelColor(Feld_Nummer, 0, 0, 0); }
if (EinAus == 1) { strip.setPixelColor(Feld_Nummer, 0, 0, 255); }
strip.show();
}
// Computer am Zug --------------------------------------------------------
void Mein_Zug() {
int Punkte_horizontal[2]; // 1 Arduino, 2 Gegenspieler
int Punkte_vertikal[2];
int Punkte_diagonal_L[2];
int Punkte_diagonal_R[2];
int Punkte_Zahl = 0;
int Best_Punkte = 0;
int Best_Reihe = 0;
int Best_Spalte = 0;
int Punkte_addiert;
// Alle Felder durchsuchen ..............................
for (Spalte = 4; Spalte < 12; Spalte++) {
for (Reihe = 5; Reihe < 12; Reihe++) {
if ((Spielbrett [Reihe][Spalte] == 0) and (Spielbrett [Reihe+1][Spalte] != 0)) {
// Feldbewertung ..................................
Punkte_horizontal[1] = 0; Punkte_horizontal[2] = 0;
for (int i=-3; i<1; i++) {
Punkte_addiert = Summe_Punkte_horizontal (Reihe, Spalte+i);
if ((Punkte_addiert > Punkte_horizontal[1]) and (Punkte_addiert < 5)) {
Punkte_horizontal[1] = Punkte_addiert; }
if ((Punkte_addiert > Punkte_horizontal[2]) and (Punkte_addiert > 4)) {
Punkte_horizontal[2] = Punkte_addiert; } }
Punkte_vertikal[1] = 0; Punkte_vertikal[2] = 0;
for (int i=-3; i<1; i++) {
Punkte_addiert = Summe_Punkte_vertikal (Reihe+i, Spalte);
if ((Punkte_addiert > Punkte_vertikal[1]) and (Punkte_addiert < 5)) {
Punkte_vertikal[1] = Punkte_addiert; }
if ((Punkte_addiert > Punkte_vertikal[2]) and (Punkte_addiert > 4)) {
Punkte_vertikal[2] = Punkte_addiert; } }
Punkte_diagonal_L[1] = 0; Punkte_diagonal_L[2] = 0;
for (int i=-3; i<1; i++) {
Punkte_addiert = Summe_Punkte_diagonal_L (Reihe+i, Spalte+i);
if ((Punkte_addiert > Punkte_diagonal_L[1]) and (Punkte_addiert < 5)) {
Punkte_diagonal_L[1] = Punkte_addiert; }
if ((Punkte_addiert > Punkte_diagonal_L[2]) and (Punkte_addiert > 4)) {
Punkte_diagonal_L[2] = Punkte_addiert; } }
Punkte_diagonal_R[1] = 0; Punkte_diagonal_R[2] = 0;
for (int i=-3; i<1; i++) {
Punkte_addiert = Summe_Punkte_diagonal_R (Reihe-i, Spalte+i);
if ((Punkte_addiert > Punkte_diagonal_R[1]) and (Punkte_addiert < 5)) {
Punkte_diagonal_R[1] = Punkte_addiert; }
if ((Punkte_addiert > Punkte_diagonal_R[2]) and (Punkte_addiert > 4)) {
Punkte_diagonal_R[2] = Punkte_addiert; } }
// Beste Position ermitteln .......................
Punkte_Zahl = 0;
// 1 Pixel = 1 Punkt ..............................
if (Punkte_horizontal[1] == 1) { Punkte_Zahl = 1; }
if (Punkte_vertikal[1] == 1) { Punkte_Zahl = 1; }
if (Punkte_diagonal_L[1] == 1) { Punkte_Zahl = 1; }
if (Punkte_diagonal_R[1] == 1) { Punkte_Zahl = 1; }
// Kreuzung 1 x 1 Pixel = 2 Punkte ................
if (Punkte_horizontal[1] == 1 and Punkte_vertikal[1] == 1) { Punkte_Zahl = 2; }
if (Punkte_horizontal[1] == 1 and Punkte_diagonal_L[1] == 1) { Punkte_Zahl = 2; }
if (Punkte_horizontal[1] == 1 and Punkte_diagonal_R[1] == 1) { Punkte_Zahl = 2; }
if (Punkte_vertikal[1] == 1 and Punkte_diagonal_L[1] == 1) { Punkte_Zahl = 2; }
if (Punkte_vertikal[1] == 1 and Punkte_diagonal_R[1] == 1) { Punkte_Zahl = 2; }
if (Punkte_diagonal_L[1] == 1 and Punkte_diagonal_R[1] == 1) { Punkte_Zahl = 2; }
// 2 Pixel - 3 Punkte .............................
if (Punkte_horizontal[1] == 2) { Punkte_Zahl = 3; }
if (Punkte_vertikal[1] == 2) { Punkte_Zahl = 3; }
if (Punkte_diagonal_L[1] == 2) { Punkte_Zahl = 3; }
if (Punkte_diagonal_R[1] == 2) { Punkte_Zahl = 3; }
// Kreuzung 2 x 1 Pixel = 4 Punkte ................
if (Punkte_horizontal[1] == 2 and Punkte_vertikal[1] == 1) { Punkte_Zahl = 4; }
if (Punkte_horizontal[1] == 1 and Punkte_vertikal[1] == 2) { Punkte_Zahl = 4; }
if (Punkte_horizontal[1] == 2 and Punkte_diagonal_L[1] == 1) { Punkte_Zahl = 4; }
if (Punkte_horizontal[1] == 1 and Punkte_diagonal_L[1] == 2) { Punkte_Zahl = 4; }
if (Punkte_horizontal[1] == 2 and Punkte_diagonal_R[1] == 1) { Punkte_Zahl = 4; }
if (Punkte_horizontal[1] == 1 and Punkte_diagonal_R[1] == 2) { Punkte_Zahl = 4; }
if (Punkte_vertikal[1] == 2 and Punkte_diagonal_L[1] == 1) { Punkte_Zahl = 4; }
if (Punkte_vertikal[1] == 1 and Punkte_diagonal_L[1] == 2) { Punkte_Zahl = 4; }
if (Punkte_vertikal[1] == 2 and Punkte_diagonal_R[1] == 1) { Punkte_Zahl = 4; }
if (Punkte_vertikal[1] == 1 and Punkte_diagonal_R[1] == 2) { Punkte_Zahl = 4; }
if (Punkte_diagonal_L[1] == 2 and Punkte_diagonal_R[1] == 1) { Punkte_Zahl = 4; }
if (Punkte_diagonal_L[1] == 1 and Punkte_diagonal_R[1] == 2) { Punkte_Zahl = 4; }
// Kreuzung 2 x 2 Pixel = 5 Punkte ................
if (Punkte_horizontal[1] == 2 and Punkte_vertikal[1] == 2) { Punkte_Zahl = 5; }
if (Punkte_horizontal[1] == 2 and Punkte_diagonal_L[1] == 2) { Punkte_Zahl = 5; }
if (Punkte_horizontal[1] == 2 and Punkte_diagonal_R[1] == 2) { Punkte_Zahl = 5; }
if (Punkte_vertikal[1] == 2 and Punkte_diagonal_L[1] == 2) { Punkte_Zahl = 5; }
if (Punkte_vertikal[1] == 2 and Punkte_diagonal_R[1] == 2) { Punkte_Zahl = 5; }
if (Punkte_diagonal_L[1] == 2 and Punkte_diagonal_R[1] == 2) { Punkte_Zahl = 5; }
// 3 Pixel beim Gegner = 6 Punkte ..................
if (Punkte_horizontal[2] == 15) { Punkte_Zahl = 6; }
if (Punkte_vertikal[2] == 15) { Punkte_Zahl = 6; }
if (Punkte_diagonal_L[2] == 15) { Punkte_Zahl = 6; }
if (Punkte_diagonal_R[2] == 15) { Punkte_Zahl = 6; }
// 3 Pixel = 7 Punkte ..............................
if (Punkte_horizontal[1] == 3) { Punkte_Zahl = 7; }
if (Punkte_vertikal[1] == 3) { Punkte_Zahl = 7; }
if (Punkte_diagonal_L[1] == 3) { Punkte_Zahl = 7; }
if (Punkte_diagonal_R[1] == 3) { Punkte_Zahl = 7; }
// Entscheidung ....................................
if (Punkte_Zahl > Best_Punkte) {
Best_Punkte = Punkte_Zahl; Best_Reihe = Reihe; Best_Spalte = Spalte; }
// Nichts gefunden .................................
if (Best_Punkte == 0) { Best_Reihe = Reihe; Best_Spalte = Zufallszahl; } } } }
Reihe = Best_Reihe;
Spalte = Best_Spalte;
Spielbrett [Reihe][Spalte] = 1;
Zug_ausfueren();
Spieler = 1;
Cursor = 0;
// gewonnen ? .............................................
Vier_in_Reihe (4);
}
// Nach 4er-Kombination suchen --------------------------------------------
void Vier_in_Reihe (int Wert) {
int Summe = 0;
for (int i=-3; i<1; i++) {
Summe = Summe_Punkte_horizontal (Reihe, Spalte+i);
if (Summe == Wert) { Game_is_over = 1; } }
for (int i=-3; i<1; i++) {
Summe = Summe_Punkte_vertikal (Reihe+i, Spalte);
if (Summe == Wert) { Game_is_over = 1; } }
for (int i=-3; i<1; i++) {
Summe = Summe_Punkte_diagonal_L (Reihe+i, Spalte+i);
if (Summe == Wert) { Game_is_over = 1; } }
for (int i=-3; i<1; i++) {
Summe = Summe_Punkte_diagonal_R (Reihe-i, Spalte+i);
if (Summe == Wert) { Game_is_over = 1; } }
}
// ------------------------------------------------------------------------
int Summe_Punkte_horizontal (int R, int S) {
int Summe = 0;
for (int i=S; i<S+4; i++) { Summe = Summe + Spielbrett [R][i]; }
return Summe;
}
// ------------------------------------------------------------------------
int Summe_Punkte_vertikal (int R, int S) {
int Summe = 0;
for (int i=R; i<R+4; i++) { Summe = Summe + Spielbrett [i][S]; }
return Summe;
}
// ------------------------------------------------------------------------
int Summe_Punkte_diagonal_L (int R, int S) {
int Summe = Spielbrett [R][S] + Spielbrett [R+1][S+1] + Spielbrett [R+2][S+2] + Spielbrett [R+3][S+3];
return Summe;
}
// ------------------------------------------------------------------------
int Summe_Punkte_diagonal_R (int R, int S) {
int Summe = Spielbrett [R][S] + Spielbrett [R-1][S+1] + Spielbrett [R-2][S+2] + Spielbrett [R-3][S+3];
return Summe;
}
// Zug ausführen **********************************************************
void Zug_ausfueren () {
for (int i = 4; i < Reihe+1; i++) {
Feld_Nummer = (i-4)*8 + (Spalte-3) - 1;
for (int j=1; j<4; j++) {
strip.setPixelColor(Feld_Nummer, 0, 0, 255);
strip.show(); delay (50);
strip.setPixelColor(Feld_Nummer, 0, 0, 0);
strip.show(); delay(50); } }
// Rot Arduino .............................................
if (Spielbrett [Reihe][Spalte] == 1) { strip.setPixelColor(Feld_Nummer, 255, 0, 0); }
// Grün Spieler ............................................
if (Spielbrett [Reihe][Spalte] == 5) { strip.setPixelColor(Feld_Nummer, 0, 255, 0); }
strip.show();
}