Skip to content

Technik Blog

Programmieren | Arduino | ESP32 | MicroPython | Python | Raspberry Pi | Raspberry Pi Pico

Menu
  • Smarthome
  • Gartenautomation
  • Arduino
  • ESP32 & Co.
  • Raspberry Pi & Pico
  • Solo Mining
  • Deutsch
  • English
Menu

Neues Spiel, neuer Spaß: Vier gewinnt auf dem ESP32 TFT-Display

Posted on 29. April 202426. April 2024 by Stefan Draeger

In diesem neuen Beitrag möchte ich dir gerne ein weiteres Spiel vorstellen, welches du recht einfach am ESP32 Development Board mit 2,8″ TFT-Display nachprogrammieren kannst. Es ist „Vier gewinnt!“, das Spiel gibt es in diversen offline Varianten und wirst du bestimmt bereits kennen und gespielt haben.

Neues Spiel, neuer Spaß: Vier gewinnt auf dem ESP32 TFT-Display
Dieses Video auf YouTube ansehen.

Inhaltsverzeichnis

  • Ziel des Spieles „Vier gewinnt!“
  • Benötigte Ressourcen für die Programmierung am ESP32 Development Board
  • Schritt-für-Schritt-Anleitung zum Programmieren von Vier gewinnt am ESP32 mit TFT-Display
    • Besonderheiten & Vorwort
    • Schritt 1 – zeichnen der Spielfläche
    • Schritt 2 – setzen eines Spielsteines
    • Schritt 4 – ermitteln ob ein Spieler gewonnen hat
  • Das fertige Spiel – Vier gewinnt für das ESP32 mit TFT-Display

Ziel des Spieles „Vier gewinnt!“

Das Ziel des Spiels „Vier gewinnt!“ ist es, als erster Spieler vier seiner Spielsteine in einer horizontalen, vertikalen oder diagonalen Linie zu platzieren. Die Spieler wechseln sich ab, indem sie abwechselnd einen Spielstein in eine der vertikalen Spalten des Spielbretts fallen lassen. Der Spieler, der zuerst eine ununterbrochene Linie aus vier seiner Spielsteine bildet, gewinnt das Spiel. Es erfordert strategisches Denken, um die Spielsteine so zu platzieren, dass man entweder eine eigene Linie vervollständigt oder gleichzeitig die des Gegners blockiert.

Spielfläche - Vier gewinnt am ESP32 Development Board
Spielfläche – Vier gewinnt am ESP32 Development Board
gesetzter Spielstein bei Vier gewinnt am ESP32 TFT Display
gesetzter Spielstein bei Vier gewinnt am ESP32 TFT Display
Spieler Gelb hat beim Spiel Vier gewinnt gewonnen!
Spieler Gelb hat beim Spiel Vier gewinnt gewonnen!
Spieler Rot hat beim Spiel Vier gewinnt gewonnen!
Spieler Rot hat beim Spiel Vier gewinnt gewonnen!

Benötigte Ressourcen für die Programmierung am ESP32 Development Board

Wenn du dieses kleine Spiel nachprogrammieren möchtest, dann benötigst du:

  • ein ESP32 Development Board (LVGL, ESP32-2432S028)*,
    • ein Datenkabel, Eingabestift,
  • die Arduino IDE

Hinweis von mir: Die mit einem Sternchen (*) markierten Links sind Affiliate-Links. Wenn du über diese Links einkaufst, erhalte ich eine kleine Provision, die dazu beiträgt, diesen Blog zu unterstützen. Der Preis für dich bleibt dabei unverändert. Vielen Dank für deine Unterstützung!

Schritt-für-Schritt-Anleitung zum Programmieren von Vier gewinnt am ESP32 mit TFT-Display

Nachfolgend nun die Schritt-für-Schritt-Anleitung zum programmieren von Vier gewinnt am ESP32 Development Board.

Wenn du dich jedoch nur für das fertige Spiel interessierst, dann kannst du dir dieses auch als ZIP-Datei herunterladen.

Quellcode – Vier gewinnt am ESP32 Development BoardHerunterladen

Besonderheiten & Vorwort

Bevor wir mit der Programmierung beginnen möchte ich dir zunächst einpaar Hinweise an die Hand geben.

Zum einen habe ich hier einen ansatz gewählt welcher deutlich weniger Speicher benötigt. Das musste ich zum einen weil die Bibliothek für das TFT-Display schon alleine recht groß ist und zum anderen wenn die Daten des Spielfeldes abgelegt werden diese dann unnötig Platz verbrauchen.

Zusätzlich habe ich hier ChatGPT benutzt um den Quellcode zu erstellen. Mit den richtigen Prompts kann die künstliche Intelligenz eine sehr gute Unterstützung sein.

Schritt 1 – zeichnen der Spielfläche

Zunächst zeichnen wir die Spielfläche. Dazu gehört die Überschrift „Vier gewinnt“ sowie die blaue Fläche mit den Pfeilen und schwarzen Kreisen.

Spielfläche - Vier gewinnt am ESP32 mit TFT-Display
Spielfläche – Vier gewinnt am ESP32 mit TFT-Display

Die Pfeile zeichne ich in einer Funktion und speichere die Informationen (Index / Spalte, X & Y Koordinate) in einem Array.

//Feld für die Daten eines Pfeiles
struct Arrow {
  int col;
  int x;  //X Koordinate
  int y;  //Y Koordinate
};

//Array für die Pfeile
//das Array wird beim zeichnen befüllt
Arrow arrowFields[COLS] = {};

//Funktion zum zeichnen der Spielfläche
void drawPlayground() {
  //leeren des Displays
  lcd.clear();

  //Hintergrundfarbe Schwarz
  lcd.fillScreen(TFT_BLACK);
  //Schriftgröße 4
  lcd.setFont(&fonts::Font4);
  //Schriftfarbe Gelb,
  //Hintergrundfarbe Schwarz
  lcd.setTextColor(TFT_YELLOW, TFT_BLACK);
  //Text an die Position x & y schreiben
  lcd.drawString("Vier gewinnt!", 105, 5);
  //ein blaues rechteck zeichnen
  lcd.fillRect(40, 35, 255, 205, TFT_BLUE);

  // For-Schleife zum Zeichnen von sieben Dreiecken
  for (int i = 0; i < 7; i++) {
    // Koordinaten für die Eckpunkte des Dreiecks berechnen
    int  x1 = startX + (i * (triangleWidth + spacing));
    int  y1 = startY;
    int  x2 = x1 + triangleWidth / 2;
    int  y2 = startY + (triangleWidth / 2);
    int  x3 = x1 + triangleWidth;
    int  y3 = startY;

    // fillTriangle-Funktion aufrufen, um das Dreieck zu zeichnen
    lcd.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREEN);

    Arrow arrowField = {i, x1, y1};
    arrowFields[i] = arrowField;
  }
  //zeichnen des Arrays
  drawHoles();
  //anzeigen der Daten auf dem Display
  lcd.display();
}

Die schwarzen Kreise werden später mit den gesetzten Spielsteinen befüllt. Hier speichere ich keine Daten ab denn die Berechnung der X & Y Koordinate für einen Kreis erfolgt anhand eines der Pfeile. (Wenn der Spieler eines der grünen Pfeile betätigt dann wird die Spalte und die X & Y Koordinate ermittelt.)

//Anzahl der Spalten
const int COLS = 7;
//Anzahl der Zeilen
const int ROWS = 5;

//Das Array mit dem Spielfeld
//initial sind alle Felder leer
//bzw. mit einem Strich markiert
char playground[ROWS][COLS] = {
  {DASH, DASH, DASH, DASH, DASH, DASH, DASH},
  {DASH, DASH, DASH, DASH, DASH, DASH, DASH},
  {DASH, DASH, DASH, DASH, DASH, DASH, DASH},
  {DASH, DASH, DASH, DASH, DASH, DASH, DASH},
  {DASH, DASH, DASH, DASH, DASH, DASH, DASH}
};

//Funktion zum zeichnen der Daten des Arrays
void drawHoles() {
  //ein gefülltes, blaues rechteck über die Spielfläche zeichnen
  //damit werden alle zuvor gezeichneten Daten überschrieben
  lcd.fillRect(40, 60, 255, 185, TFT_BLUE);
  //Schleife über das Array
  for (int col = 0; col < COLS; col++) {
    Arrow arrowField = arrowFields[col];
    for (int  row = 0; row < ROWS; row++) {
      //Berechnen der X & Y Koordinate für das Loch
      int circleY = (startY + triangleWidth + circleSpacing + (2 * circleRadius + circleYspacing) * row) - 10;  // y-Koordinate des Kreises
      int circleX = arrowField.x + triangleWidth / 2;                                                                        // x-Koordinate des Kreises
      lcd.fillCircle(circleX, circleY, circleRadius, TFT_BLACK);

      //Wenn an der Koordinate im Array kein Strich ist, dann ist dort ein Spielstein abgelegt
      if (playground[row][col] != DASH) {
        //den Spielstein zeichnen
        drawStone(circleX, circleY, playground[row][col] == PLAYER1_CHAR);
      }
    }
  }
}

Zusätzlich prüfe ich in der Funktion drawHoles auch ob in dem Array playground ein Spielstein an der Position gesetzt ist. Wenn dieses so ist dann wird an die Position zusätzlich der entsprechende Spielstein gezeichnet.

//Zeichnet ein Spielstein an die Position X & Y
//der boolsche Parameter player1 gibt an ob Gelb oder Rot am zug ist
void drawStone(int x, int y, bool player1) {
  //initial ist Gelb gesetzt
  uint8_t colorRingOuter1 = lcd.color332(137, 121, 5);;
  uint8_t colorRingOuter2 = lcd.color332(238, 226, 146);
  uint8_t colorRingInner = lcd.color332(243, 212, 0);
  //bei Spieler 2 soll ein Stein in der Farbe Rot gezeichnet werden.
  if (!player1) {
    colorRingOuter1 = lcd.color332(106, 5, 13);
    colorRingOuter2 = lcd.color332(239, 109, 119);
    colorRingInner = lcd.color332(255, 0, 0);
  }

  //zeichnen des Spielsteins
  lcd.fillCircle(x, y, 15, colorRingOuter1);
  lcd.fillCircle(x, y, 12, colorRingOuter2);
  lcd.fillCircle(x, y, 8, colorRingInner);
}

Das macht die Funktion multifunktional denn wir können mit dieser eine die komplette Spielfläche neu zeichnen und sparen somit Speicherplatz und der Code wird auch reduziert.

Schritt 2 – setzen eines Spielsteines

Das Spiel ist für zwei Spieler ausgelegt und diese setzen nacheinander jeweils einen Spielstein. Zum setzen eines Spielsteins in eine Zeile wird eines der grünen Pfeile betätigt. Es wird dann der letzte freie Platz der Spalte ermittelt und in diese eines der Steine gesetzt.

gesetzter Spielstein im Spiel Vier gewinnt
gesetzter Spielstein im Spiel Vier gewinnt

Bevor wir jedoch einen Spielstein setzen können, müssen wir den klick auf eines der Pfeile erkennen. Dieses machen wir wie beim Spiel Tic-Tac-Toe in der Funktion Loop.

void loop() {
  //Wenn ein Touchaktion ausgeführt / erkannt wurde,
  //dann liefert die Funktion getTouch den Wert 1 und
  //befüllt die übergebenen Parametern mit den Koordinaten
  if (lcd.getTouch(&x, &y) == 1) {

    //Wenn die ermittelte X oder Y Positon außerhalb der Range ist,
    //dann soll die Funktion hier verlassen werden.
    if (x > MAX_X || y > MAX_Y) {
      return;
    }

    //Wenn das Spiel beendet ist, dann...
    if (gameOver) {
      //initialisiern der Spielfläche
      initPlayground();
      drawPlayground();
      //Feld wieder auf false setzen
      gameOver = false;
    } else {
      //Wenn das Spiel noch läuft, dann...

      //Variable zum speichern ob ein Pfeil geklickt wurde
      bool playerChooseArrow = false;
      //Variable zum speichern ob ein leeres Feld in der Spalte gefunden wurde
      bool foundEmptyPlace = false;
      //Schleife über alle Pfeile
      for (int arrow = 0; arrow < COLS; arrow++) {
        Arrow arrowField = arrowFields[arrow];
        //prüfen ob der Pfeil an der Position aus dem Array geklickt wurde
        if (checkCoord(arrowField, x, y)) {
          //Variable auf true setzen
          playerChooseArrow = true;
          for (int row = ROWS - 1; row >= 0; row--) {
            //prüfen ob das Feld leer / mit einem Strich belegt ist
            if (playground[row][arrow] == DASH) {
              //Umkehren des Wertes für den Spieler
              currentPlayer1 = !currentPlayer1;
              //bei Spieler 1 soll ein X und bei Spieler 2 ein O an die Stelle im
              //Array gespeichert werden
              playground[row][arrow] = currentPlayer1 ? PLAYER1_CHAR : PLAYER2_CHAR;
              //Es wurde ein leeres Feld gefunden und somit Variable auf true setzen
              foundEmptyPlace = true;
              //Schleife abbrechen
              break;
            }
          }
        }
      }

      //Wenn ein leeres Feld gefunden wurde,
      //und ein Pfeil gewählt wurde, dann...
      if (foundEmptyPlace && playerChooseArrow) {


        //zeichnen des Arrays
        drawHoles();

        //prüfen ob der aktuelle Spieler gewonnen hat
        char player = currentPlayer1 ? PLAYER1_CHAR : PLAYER2_CHAR;
        if (hasPlayerWon(player)) {
          String playerColor = currentPlayer1 ? "Gelb" : "Rot";
          displayMessage("Der Spieler " + playerColor + " hat gewonnen!", 0);
          gameOver = true;
        } else if (isGameDraw()) {
          displayMessage("Das Spiel ist unentschieden!", 40);
          gameOver = true;
        }
      }
      //eine kleine Pause von 300 ms.
      //Damit wird dem Spieler die möglichkeit gegeben
      //den Stift vom Display zu nehmen.
      delay(300);
    }
  }
  //eine Pause von 50ms.
  delay(50);
}

Schritt 4 – ermitteln ob ein Spieler gewonnen hat

Wenn ein Spieler einen Spielstein gesetzt hat, dann wird zusätzlich auch geprüft ob dieser Spieler gewonnen hat oder es sogar ein unentschieden gibt.

// Funktion, die prüft, ob ein Spieler gewonnen hat
bool hasPlayerWon(char player) {
  // Überprüfen von horizontalen Linien
  for (int row = 0; row < ROWS; row++) {
    for (int col = 0; col <= COLS - 4; col++) {
      if (playground[row][col] == player &&
          playground[row][col + 1] == player &&
          playground[row][col + 2] == player &&
          playground[row][col + 3] == player) {
        return true;
      }
    }
  }

  // Überprüfen von vertikalen Linien
  for (int col = 0; col < COLS; col++) {
    for (int row = 0; row <= ROWS - 4; row++) {
      if (playground[row][col] == player &&
          playground[row + 1][col] == player &&
          playground[row + 2][col] == player &&
          playground[row + 3][col] == player) {
        return true;
      }
    }
  }

  // Überprüfen von diagonalen Linien (von links oben nach rechts unten)
  for (int row = 0; row <= ROWS - 4; row++) {
    for (int col = 0; col <= COLS - 4; col++) {
      if (playground[row][col] == player &&
          playground[row + 1][col + 1] == player &&
          playground[row + 2][col + 2] == player &&
          playground[row + 3][col + 3] == player) {
        return true;
      }
    }
  }

  // Überprüfen von diagonalen Linien (von rechts oben nach links unten)
  for (int row = 0; row <= ROWS - 4; row++) {
    for (int col = 3; col < COLS; col++) {
      if (playground[row][col] == player &&
          playground[row + 1][col - 1] == player &&
          playground[row + 2][col - 2] == player &&
          playground[row + 3][col - 3] == player) {
        return true;
      }
    }
  }

  // Falls kein Spieler gewonnen hat, return false
  return false;
}

Um zu prüfen ob das Spiel untentschieden ist, muss zunächst geprüft werden ob keines der Spielfelder mit einem Strich belegt ist.

bool isGameDraw() {
  // Überprüfen, ob noch leere Felder vorhanden sind
  for (int row = 0; row < ROWS; row++) {
    for (int col = 0; col < COLS; col++) {
      if (playground[row][col] == '-') {
        // Es gibt noch mindestens ein leeres Feld, das Spiel ist nicht unentschieden
        return false;
      }
    }
  }

  // Das Spielfeld ist vollständig belegt
  // Überprüfen, ob einer der Spieler gewonnen hat
  return !hasPlayerWon(PLAYER1_CHAR) && !hasPlayerWon(PLAYER2_CHAR);
}

Wenn dieses nicht so ist, dann wird zusätzlich geprüft ob Spieler 1 oder Spieler 2 gewonnen hat. Dieses ist theoretisch obsolete da dieses zuvor ebenso geprüft wurde, dient jedoch der Sicherheit.

Das fertige Spiel – Vier gewinnt für das ESP32 mit TFT-Display

Nachfolgend der Quellcode zum fertigen Spiel mit allen Kommentaren.

/*
  Einbinden der Bibliothek zum kommunizieren
  mit dem Display.
*/
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include "lgfx_ESP32_2432S028.h"

//Größe der Zeichenfläche definieren
#define MAX_X 319
#define MAX_Y 239

//Felder für die ermittelte Position
//bei einem klick
uint16_t x = 0, y = 0;

//Instanz des Displays
static LGFX lcd;


//Feld für die Daten eines Pfeiles
struct Arrow {
  int col;
  int x;  //X Koordinate
  int y;  //Y Koordinate
};

//die beiden Spieler erhalten jeweils die Farbe Gelb&Rot
//im Array jedoch werden diese durch die beiden nachfolgenden
//Symbole dargestellt.
const char PLAYER1_CHAR = 'X';
const char PLAYER2_CHAR = '0';
//Der Strich ist der default Wert
const char DASH = '-';

int triangleWidth = 25;   // Breite der Dreiecke
int startX = 50;          // Start-X-Koordinate
int startY = 45;          // Start-Y-Koordinate
int spacing = 10;          // Abstand zwischen den Dreiecken
int circleRadius = 15;    // Radius der Kreise
int circleSpacing = 20;   // Abstand zwischen den Kreisen (korrigiert)
int circleYspacing = 5;  // Abstand zwischen den Kreisen untereinander

//Anzahl der Spalten
const int COLS = 7;
//Anzahl der Zeilen
const int ROWS = 5;

//Array für die Pfeile
//das Array wird beim zeichnen befüllt
Arrow arrowFields[COLS] = {};

//Das Array mit dem Spielfeld
//initial sind alle Felder leer
//bzw. mit einem Strich markiert
char playground[ROWS][COLS] = {
  {DASH, DASH, DASH, DASH, DASH, DASH, DASH},
  {DASH, DASH, DASH, DASH, DASH, DASH, DASH},
  {DASH, DASH, DASH, DASH, DASH, DASH, DASH},
  {DASH, DASH, DASH, DASH, DASH, DASH, DASH},
  {DASH, DASH, DASH, DASH, DASH, DASH, DASH}
};

//Feld zum speichern ob das Spiel beendet ist
bool gameOver = false;

void setup(void) {
  //beginn der seriellen Kommunikation mit
  //115200 Baud
  Serial.begin(115200);

  //beginn der Kommunikation mit dem Display
  lcd.init();
  //drehen des Displays
  lcd.setRotation(1);
  //füllen des Displays mit der Farbe Schwarz
  lcd.fillScreen(TFT_BLACK);
  //eine kleine Pause von 100ms.
  delay(100);

  //zeichnen der Spielfläche
  drawPlayground();
}

//Feld zum speichern welcher Spieler am zug ist
//beginnen tut der Spieler 1 / Gelb
bool currentPlayer1 = false;

//Funktion zum initialisieren der Spielfläche
void initPlayground() {
  //Schleife über die Spalten und Zeilen
  for (int col = 0; col < COLS; col++) {
    for (int  row = 0; row < ROWS; row++) {
      //Zuweisen eines Striches an die Position im Array
      playground[row][col] = DASH;
    }
  }
}

//Funktion zum zeichnen der Spielfläche
void drawPlayground() {
  //leeren des Displays
  lcd.clear();

  //Hintergrundfarbe Schwarz
  lcd.fillScreen(TFT_BLACK);
  //Schriftgröße 4
  lcd.setFont(&fonts::Font4);
  //Schriftfarbe Gelb,
  //Hintergrundfarbe Schwarz
  lcd.setTextColor(TFT_YELLOW, TFT_BLACK);
  //Text an die Position x & y schreiben
  lcd.drawString("Vier gewinnt!", 105, 5);
  //ein blaues rechteck zeichnen
  lcd.fillRect(40, 35, 255, 205, TFT_BLUE);

  // For-Schleife zum Zeichnen von sieben Dreiecken
  for (int i = 0; i < 7; i++) {
    // Koordinaten für die Eckpunkte des Dreiecks berechnen
    int  x1 = startX + (i * (triangleWidth + spacing));
    int  y1 = startY;
    int  x2 = x1 + triangleWidth / 2;
    int  y2 = startY + (triangleWidth / 2);
    int  x3 = x1 + triangleWidth;
    int  y3 = startY;

    // fillTriangle-Funktion aufrufen, um das Dreieck zu zeichnen
    lcd.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREEN);

    Arrow arrowField = {i, x1, y1};
    arrowFields[i] = arrowField;
  }
  //zeichnen des Arrays
  drawHoles();
  //anzeigen der Daten auf dem Display
  lcd.display();
}

//Funktion zum zeichnen der Daten des Arrays
void drawHoles() {
  //ein gefülltes, blaues rechteck über die Spielfläche zeichnen
  //damit werden alle zuvor gezeichneten Daten überschrieben
  lcd.fillRect(40, 60, 255, 185, TFT_BLUE);
  //Schleife über das Array
  for (int col = 0; col < COLS; col++) {
    Arrow arrowField = arrowFields[col];
    for (int  row = 0; row < ROWS; row++) {
      //Berechnen der X & Y Koordinate für das Loch
      int circleY = (startY + triangleWidth + circleSpacing + (2 * circleRadius + circleYspacing) * row) - 10;  // y-Koordinate des Kreises
      int circleX = arrowField.x + triangleWidth / 2;                                                                        // x-Koordinate des Kreises
      lcd.fillCircle(circleX, circleY, circleRadius, TFT_BLACK);

      //Wenn an der Koordinate im Array kein Strich ist, dann ist dort ein Spielstein abgelegt
      if (playground[row][col] != DASH) {
        //den Spielstein zeichnen
        drawStone(circleX, circleY, playground[row][col] == PLAYER1_CHAR);
      }
    }
  }
}

//Zeichnet ein Spielstein an die Position X & Y
//der boolsche Parameter player1 gibt an ob Gelb oder Rot am zug ist
void drawStone(int x, int y, bool player1) {
  //initial ist Gelb gesetzt
  uint8_t colorRingOuter1 = lcd.color332(137, 121, 5);;
  uint8_t colorRingOuter2 = lcd.color332(238, 226, 146);
  uint8_t colorRingInner = lcd.color332(243, 212, 0);
  //bei Spieler 2 soll ein Stein in der Farbe Rot gezeichnet werden.
  if (!player1) {
    colorRingOuter1 = lcd.color332(106, 5, 13);
    colorRingOuter2 = lcd.color332(239, 109, 119);
    colorRingInner = lcd.color332(255, 0, 0);
  }

  //zeichnen des Spielsteins
  lcd.fillCircle(x, y, 15, colorRingOuter1);
  lcd.fillCircle(x, y, 12, colorRingOuter2);
  lcd.fillCircle(x, y, 8, colorRingInner);
}

// Funktion, die prüft, ob ein Spieler gewonnen hat
bool hasPlayerWon(char player) {
  // Überprüfen von horizontalen Linien
  for (int row = 0; row < ROWS; row++) {
    for (int col = 0; col <= COLS - 4; col++) {
      if (playground[row][col] == player &&
          playground[row][col + 1] == player &&
          playground[row][col + 2] == player &&
          playground[row][col + 3] == player) {
        return true;
      }
    }
  }

  // Überprüfen von vertikalen Linien
  for (int col = 0; col < COLS; col++) {
    for (int row = 0; row <= ROWS - 4; row++) {
      if (playground[row][col] == player &&
          playground[row + 1][col] == player &&
          playground[row + 2][col] == player &&
          playground[row + 3][col] == player) {
        return true;
      }
    }
  }

  // Überprüfen von diagonalen Linien (von links oben nach rechts unten)
  for (int row = 0; row <= ROWS - 4; row++) {
    for (int col = 0; col <= COLS - 4; col++) {
      if (playground[row][col] == player &&
          playground[row + 1][col + 1] == player &&
          playground[row + 2][col + 2] == player &&
          playground[row + 3][col + 3] == player) {
        return true;
      }
    }
  }

  // Überprüfen von diagonalen Linien (von rechts oben nach links unten)
  for (int row = 0; row <= ROWS - 4; row++) {
    for (int col = 3; col < COLS; col++) {
      if (playground[row][col] == player &&
          playground[row + 1][col - 1] == player &&
          playground[row + 2][col - 2] == player &&
          playground[row + 3][col - 3] == player) {
        return true;
      }
    }
  }

  // Falls kein Spieler gewonnen hat, return false
  return false;
}


void loop() {
  //Wenn ein Touchaktion ausgeführt / erkannt wurde,
  //dann liefert die Funktion getTouch den Wert 1 und
  //befüllt die übergebenen Parametern mit den Koordinaten
  if (lcd.getTouch(&x, &y) == 1) {

    //Wenn die ermittelte X oder Y Positon außerhalb der Range ist,
    //dann soll die Funktion hier verlassen werden.
    if (x > MAX_X || y > MAX_Y) {
      return;
    }

    //Wenn das Spiel beendet ist, dann...
    if (gameOver) {
      //initialisiern der Spielfläche
      initPlayground();
      drawPlayground();
      //Feld wieder auf false setzen
      gameOver = false;
    } else {
      //Wenn das Spiel noch läuft, dann...

      //Variable zum speichern ob ein Pfeil geklickt wurde
      bool playerChooseArrow = false;
      //Variable zum speichern ob ein leeres Feld in der Spalte gefunden wurde
      bool foundEmptyPlace = false;
      //Schleife über alle Pfeile
      for (int arrow = 0; arrow < COLS; arrow++) {
        Arrow arrowField = arrowFields[arrow];
        //prüfen ob der Pfeil an der Position aus dem Array geklickt wurde
        if (checkCoord(arrowField, x, y)) {
          //Variable auf true setzen
          playerChooseArrow = true;
          //Umkehren des Wertes für den Spieler
          currentPlayer1 = !currentPlayer1;
          for (int row = ROWS - 1; row >= 0; row--) {
            //prüfen ob das Feld leer / mit einem Strich belegt ist
            if (playground[row][arrow] == DASH) {
              //bei Spieler 1 soll ein X und bei Spieler 2 ein O an die Stelle im
              //Array gespeichert werden
              playground[row][arrow] = currentPlayer1 ? PLAYER1_CHAR : PLAYER2_CHAR;
              //Es wurde ein leeres Feld gefunden und somit Variable auf true setzen
              foundEmptyPlace = true;
              //Schleife abbrechen
              break;
            }
          }
        }
      }
      //Wenn kein leeres Feld gefunden wurde, dann soll der Spieler wieder
      //umgekehrt werden, somit ist der gleiche Spieler wieder dran
      if (!foundEmptyPlace) {
        currentPlayer1 = !currentPlayer1;
      } else {
        //Wenn ein leeres Feld gefunden wurde, dann...

        //zeichnen des Arrays
        drawHoles();

        //prüfen ob der aktuelle Spieler gewonnen hat
        char player = currentPlayer1 ? PLAYER1_CHAR : PLAYER2_CHAR;
        if (playerChooseArrow && hasPlayerWon(player)) {
          lcd.fillRect(15, 120, 295, 30, TFT_RED);
          lcd.setCursor(20, 125);
          lcd.setTextSize(0.85);
          lcd.setTextColor(TFT_GREEN, TFT_RED);
          String playerColor = currentPlayer1 ? "Gelb" : "Rot";
          lcd.print("Der Spieler " + playerColor + " hat gewonnen!");
          gameOver = true;
        }
      }
      //eine kleine Pause von 300 ms.
      //Damit wird dem Spieler die möglichkeit gegeben 
      //den Stift vom Display zu nehmen.
      delay(300);
    }
  }
  //eine Pause von 50ms.
  delay(50);
}

//Funktion zum prüfen ob die geklickte Koordinate innerhalb des Feldes liegt
bool checkCoord(Arrow arrowField, int x, int y) {
  return (x >= arrowField.x && x <= (arrowField.x + triangleWidth) && y >= arrowField.y && y <= (arrowField.y + triangleWidth));
}

Schreibe einen Kommentar Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Fragen oder Feedback?

Du hast eine Idee, brauchst Hilfe oder möchtest Feedback loswerden?
Support-Ticket erstellen

Newsletter abonnieren

Bleib auf dem Laufenden: Erhalte regelmäßig Updates zu neuen Projekten, Tutorials und Tipps rund um Arduino, ESP32 und mehr – direkt in dein Postfach.

Jetzt Newsletter abonnieren

Unterstütze meinen Blog

Wenn dir meine Inhalte gefallen, freue ich mich über deine Unterstützung auf Tipeee.
So hilfst du mit, den Blog am Leben zu halten und neue Beiträge zu ermöglichen.

draeger-it.blog auf Tipeee unterstützen

Vielen Dank für deinen Support!
– Stefan Draeger

Kategorien

Tools

  • Unix-Zeitstempel-Rechner
  • ASCII Tabelle
  • Spannung, Strom, Widerstand und Leistung berechnen
  • Widerstandsrechner
  • 8×8 LED Matrix Tool
  • 8×16 LED Matrix Modul von Keyestudio
  • 16×16 LED Matrix – Generator

Links

Blogverzeichnis Bloggerei.de TopBlogs.de das Original - Blogverzeichnis | Blog Top Liste Blogverzeichnis trusted-blogs.com

Stefan Draeger
Königsberger Str. 13
38364 Schöningen
Tel.: 01778501273
E-Mail: info@draeger-it.blog

Folge mir auf

link zu Fabook
link zu LinkedIn
link zu YouTube
link zu TikTok
link zu Pinterest
link zu Instagram
  • Impressum
  • Datenschutzerklärung
  • Disclaimer
  • Cookie-Richtlinie (EU)
©2025 Technik Blog | Built using WordPress and Responsive Blogily theme by Superb
Cookie-Zustimmung verwalten
Wir verwenden Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wir tun dies, um das Surferlebnis zu verbessern und um personalisierte Werbung anzuzeigen. Wenn Sie diesen Technologien zustimmen, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn Sie Ihre Zustimmung nicht erteilen oder zurückziehen, können bestimmte Funktionen beeinträchtigt werden.
Funktional Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt. Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
Optionen verwalten Dienste verwalten Verwalten von {vendor_count}-Lieferanten Lese mehr über diese Zwecke
Einstellungen anzeigen
{title} {title} {title}