You can display gltf models in lvgl?
They must have added this since I last looked.
I'll see if I can get this working...
seen from Canada
seen from Russia
seen from Argentina
seen from Germany
seen from United States

seen from United Kingdom

seen from United States

seen from United States
seen from United States
seen from Germany
seen from Norway

seen from Latvia

seen from United Kingdom
seen from United States
seen from Russia
seen from United States
seen from China

seen from United States
seen from United Kingdom
seen from Germany
You can display gltf models in lvgl?
They must have added this since I last looked.
I'll see if I can get this working...
2.8" TFT touchscreen revision with TSC2007 🛠️🖥️🚀
We're finally redoing our 2.8" resistive touch screen shield https://www.adafruit.com/product/1651, which has been out of stock for a looooong time due to parts shortages and then TFT pricing nuttiness. This time, we're revising it to use the TSC2007 https://www.adafruit.com/product/5423 and testing it against the new Metro ESP32-S3 prototypes. We've wired the IRQ pin-up, and the touchscreen responsiveness is excellent. One nice thing about using I2C is that we don't have to share the SPI port, so if you're using something like LVGL, which does TFT drawing or touch screen reading in an interrupt, there's less risk of contention.
ESP32-S3 RLCD Display ansteuern: So klappt die Programmierung mit Arduino IDE (Teil 2)
Im ersten Teil habe ich mir das Board im Detail angeschaut und einen ersten Eindruck gesammelt. Jetzt wird es praktisch.
In diesem Beitrag zeige ich dir, wie du das ESP32-S3 RLCD Display von Waveshare ganz bequem mit der Arduino IDE programmierst und erste Inhalte auf dem Display darstellst.
Dank der Unterstützung von LVGL (Light and Versatile Graphics Library) ist der Einstieg überraschend einfach. Wenn du bereits Erfahrungen mit dem bekannten CYD (Cheap Yellow Display) gesammelt hast, wirst du dich hier sehr schnell zurechtfinden.
👉 Ziel ist es, nicht nur das Demo-Programm zu nutzen, sondern eigene Inhalte wie Text, Werte oder später sogar kleine Dashboards auf dem RLCD darzustellen.
https://youtu.be/-ohhYlIscEo
Transparenzhinweis: Das Gerät wurde mir von Waveshare für dieses Review kostenfrei zur Verfügung gestellt. Ich freue mich immer, wenn ich in dieser Form unterstützt werde und neue Hardware frühzeitig testen kann.
Selbstverständlich hat dies keinen Einfluss auf meine Bewertung – ich teile wie gewohnt meine ehrliche Meinung und praktische Erfahrungen mit dem Board.
Arduino IDE für das ESP32-S3 RLCD einrichten
Damit wir das Display programmieren können, müssen wir zunächst die Arduino IDE vorbereiten.
1. ESP32 Boardpaket installieren - Arduino IDE öffnen - Datei → Einstellungen - folgende URL hinzufügen: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json 2. Board installieren - Werkzeuge → Board → Boardverwalter - nach ESP32 suchen - „esp32 by Espressif Systems“ installieren
Arduino IDE - einrichten der Boardtreiber für ESP32 von Espressif - Step1
Arduino IDE - einrichten der Boardtreiber für ESP32 von Espressif - Step2
Arduino IDE - einrichten der Boardtreiber für ESP32 von Espressif - Step3
Arduino IDE - einrichten der Boardtreiber für ESP32 von Espressif - Step4 3. Richtiges Board auswählen
Auf dem Board ist ein ESP32-S3 verbaut, somit wählen wir unter Werkzeuge > Boards > esp32 das Modul ESP32S3 Dev Module aus.
Arduino IDE - Auswahl des ESP32-S3
Arduino IDE - Konfiguration des ESP32-S3
Für serielle Ausgaben muss zusätzlich noch die Auswahl bei der Konfiguration "USB CDC On Boot" auf Enabled gesetzt werden.
4. Boardkonfiguration
Damit das Board korrekt angesteuert werden kann, müssen zunächst die richtigen Einstellungen in der Boardkonfiguration vorgenommen werden.
- USB CDC On Boot > Enabled - Flash Mode > "QIO 80 MHz" - Flash Size > "16MB (128Mb)" - Partition Scheme > "16M Falsh (3MB APP/9.9 FATS)" - PSRAM >"OPI PSRAM"
Wird diese Konfiguration nicht korrekt gesetzt, kommt es zu einem Assertion Error. Im seriellen Monitor der Arduino IDE erscheint dann folgende Fehlermeldung:
E (285) esp_core_dump_flash: Core dump flash config is corrupted! CRC=0x7bd5c66f instead of 0x0 E (294) esp_core_dump_elf: Elf write init failed! E (298) esp_core_dump_common: Core dump write failed with error=-1 Rebooting... ESP-ROM:esp32s3-20210327 Build:Mar 27 2021 rst:0xc (RTC_SW_CPU_RST),boot:0x8 (SPI_FAST_FLASH_BOOT) Saved PC:0x4037f489 SPIWP:0xee mode:DIO, clock div:1 load:0x3fce2820,len:0x10cc load:0x403c8700,len:0xc2c load:0x403cb700,len:0x30c0 entry 0x403c88b8 assert failed: DisplayPort::DisplayPort(int, int, int, int, int, int, int, spi_host_device_t) C:UsersstefaDownloadsESP32-S3-RLCD-4.2-mainESP32-S3-RLCD-4.2-main�2_ExampleArduino�9_LVGL_V9_Test
Erstes Beispiel: „Hello World!“ auf dem RLCD anzeigen
Nachdem die Arduino IDE korrekt eingerichtet ist und das Board erkannt wird, starten wir mit einem einfachen ersten Beispiel.
👉 Ziel: Eine erste Ausgabe auf dem Display – unser klassisches „Hello World!“.
Als Grundlage nutze ich das von Waveshare bereitgestellte Beispielprojekt: 👉 09_LVGL_V9_Test
Dieses findest du im offiziellen Repository unter: ESP32-S3-RLCD-4.2/02_Example/Arduino/09_LVGL_V9_Test/
#include "display_bsp.h" #include "src/app_bsp/lvgl_bsp.h" #include "src/ui_src/generated/gui_guider.h" static lv_ui init_ui; DisplayPort RlcdPort(12, 11, 5, 40, 41, 400, 300); static void Lvgl_FlushCallback(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map) { uint16_t *buffer = (uint16_t *)color_map; for (int y = area->y1; y y2; y++) { for (int x = area->x1; x x2; x++) { uint8_t color = (*buffer < 0x7fff) ? ColorBlack : ColorWhite; RlcdPort.RLCD_SetPixel(x, y, color); buffer++; } } RlcdPort.RLCD_Display(); lv_disp_flush_ready(drv); } void setup() { RlcdPort.RLCD_Init(); Lvgl_PortInit(400, 300, Lvgl_FlushCallback); if (Lvgl_lock(-1)) { create_hello_world_pyramid(); Lvgl_unlock(); } } void loop() { vTaskDelay(pdMS_TO_TICKS(1000)); } void create_hello_world_pyramid() { lv_obj_t *screen = lv_scr_act(); lv_obj_set_style_bg_color(screen, lv_color_white(), LV_PART_MAIN); lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, LV_PART_MAIN); const lv_font_t *fonts = { &lv_font_montserrat_48, &lv_font_montserrat_40, &lv_font_montserrat_32, &lv_font_montserrat_24, &lv_font_montserrat_18, &lv_font_montserrat_12, }; int yPositions = { 25, 85, 135, 180, 220, 250 }; for (int i = 0; i < 6; i++) { lv_obj_t *label = lv_label_create(screen); lv_label_set_text(label, "Hello World!"); lv_obj_set_style_text_font(label, fonts, LV_PART_MAIN); lv_obj_set_style_text_color(label, lv_color_black(), LV_PART_MAIN); lv_obj_align(label, LV_ALIGN_TOP_MID, 0, yPositions); } } Zusätzliche Schriftgrößen in LVGL aktivieren
Für unser Beispiel möchten wir verschiedene Schriftgrößen verwenden. Standardmäßig sind in LVGL jedoch nicht alle Fonts aktiviert, um Speicher zu sparen.
👉 Deshalb müssen wir einige Schriftgrößen manuell freischalten.
Datei anpassen
Öffne die Datei:
C:UsersDocumentsArduinolibrarieslvgllv_conf.h
👉 Scrolle dort zum Abschnitt „FONT USAGE“.
Schriftgrößen aktivieren
Setze die folgenden Einträge von 0 auf 1:
#define LV_FONT_MONTSERRAT_12 1 #define LV_FONT_MONTSERRAT_14 1 #define LV_FONT_MONTSERRAT_16 1 #define LV_FONT_MONTSERRAT_18 1 #define LV_FONT_MONTSERRAT_24 1 #define LV_FONT_MONTSERRAT_32 1 #define LV_FONT_MONTSERRAT_40 1 #define LV_FONT_MONTSERRAT_48 1
👉 Dadurch werden die jeweiligen Schriftgrößen im Projekt verfügbar.
Hintergrund
LVGL ist so aufgebaut, dass nur die tatsächlich benötigten Ressourcen eingebunden werden. 👉 Das spart Speicher – ist aber am Anfang etwas ungewohnt.
Fazit: Einstieg geschafft – jetzt wird es spannend
Mit der richtigen Konfiguration in der Arduino IDE und der Einrichtung von LVGL ist der Einstieg in das Waveshare ESP32-S3 RLCD Display deutlich einfacher, als man zunächst vermuten würde.
👉 Bereits mit wenigen Zeilen Code lässt sich eine eigene Ausgabe auf dem Display realisieren.
Besonders positiv fällt dabei auf:
- LVGL ermöglicht eine sehr flexible Darstellung von Inhalten - das RLCD-Display reagiert schnell und ohne störendes Flackern - die Kombination aus ESP32-S3 und Display bietet eine starke Basis für eigene Projekte
Natürlich gibt es auch ein paar Stolpersteine – insbesondere bei der Boardkonfiguration und den benötigten Fonts. Hat man diese Hürden jedoch einmal genommen, steht eigenen Projekten nichts mehr im Weg.
👉 Genau hier wird es jetzt interessant.
Ausblick: Vom „Hello World“ zum echten Projekt
Im nächsten Schritt reicht ein statischer Text natürlich nicht mehr aus.
👉 Deshalb zeige ich dir im nächsten Beitrag ein konkretes Praxisprojekt:
- Daten aus einer öffentlichen API abrufen - Darstellung als Newsticker - Kombination aus Text und Bildern mit LVGL - Dynamische Aktualisierung der Inhalte
👉 Ziel: Ein echtes, praxisnahes Dashboard auf dem RLCD-Display.
Quellen & weiterführende Links
Für diesen Beitrag habe ich auf die offiziellen Dokumentationen und Beispiele von Waveshare zurückgegriffen. Wenn du tiefer in das Thema einsteigen möchtest, findest du hier die wichtigsten Ressourcen:
- Offizielle Dokumentation & Ressourcen https://docs.waveshare.com/ESP32-S3-RLCD-4.2/Resources-And-Documents - Produktseite mit technischen Details https://www.waveshare.com/esp32-s3-rlcd-4.2.htm - Weitere Beispielprojekte & Tutorials (Arduino) https://docs.waveshare.com/ESP32-Arduino-Tutorials/Fun-Project - GitHub Repository mit allen Beispielen https://github.com/waveshareteam/ESP32-S3-RLCD-4.2/tree/main
Step-by-step guide to port LVGL 8.2 to OK3568-C running Linux 4.19 with Buildroot. Includes setup, touchscreen, framebuffer, and demo execut
Porting LVGL 8.2 on OK3568-C Under Linux 4.19 (Buildroot) We've published a detailed, hands-on guide for bringing LVGL 8.2 to life on the Forlinx OK3568-C platform, built on Linux 4.19 and powered by Buildroot.
🛠️ The guide walks through:
Source setup and framebuffer integration
Touchscreen calibration via evdev
Display and resolution configuration
Cross-compilation tips with aarch64 toolchain
Full demo testing and optimization
This is ideal for developers building UI solutions in embedded HMI, smart control panels, and industrial displays using open-source technologies.
Learn about deploying LVGL on i.MX93 series dev board (OK-MX9352-C) for efficient GUI. It has 3.1s startup, rich features shown in Ebike Scr
🚀 3.1s Fast Startup! 🚀
We're excited to announce that LVGL (Light and Versatile Graphics Library) has been successfully deployed on the i.MX93 Series Development Board, achieving an incredible startup time of just 3.1 seconds!
This lightweight and easy-to-use UI enhances user efficiency and satisfaction, making it perfect for embedded systems. Check out our Ebike Screen Demo showcasing a beautiful and functional interface that displays essential information like speed and battery level.
Stay tuned for more updates as we innovate and adapt LVGL for more products! For inquiries, contact our sales engineers today!
Forlinx OK-MX9352-C based on NXP i.MX93 Family i.MX9352 is a reliable single board computer with FET-MX9352-C SoM separated from carrier boa
Neues Spiel, neuer Spaß: Vier gewinnt auf dem ESP32 TFT-Display
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. https://youtu.be/fFLct1JabOk
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
gesetzter Spielstein bei Vier gewinnt am ESP32 TFT Display
Spieler Gelb 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 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 = {}; //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 = 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 = { {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; 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 != DASH) { //den Spielstein zeichnen drawStone(circleX, circleY, playground == 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 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; //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 == 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 = 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 Read the full article