Getting “Invalid header file” error on Tinkercad – can’t use Arduino IDE

Hi everyone,

I’m trying to run my Arduino code on Tinkercad Circuits, but I keep getting the error message:

Invalid header file

I’ve double-checked my includes and syntax, but I can’t figure out what’s wrong.
Unfortunately, I can’t use the Arduino IDE right now, so I have to do everything in Tinkercad.

Could someone please help me understand what this error means and how to fix it?
Any hints or examples would be really appreciated!

Thanks in advance :slight_smile:

#include <Wire.h>

#include <LiquidCrystal_I2C.h>


// ====== PIN KONFIGURATION ======
const int PIN_CALL_BUTTON = 2;
const int PIN_FOLD_BUTTON = 3;
const int PIN_ENTER_BUTTON = 4;
const int PIN_POTENTIOMETER = A0;

// ====== DISPLAY KONFIGURATION ======
const int LCD_COLS = 26;
const int LCD_ROWS = 2;

// ====== I2C ADRESSEN FÜR LCD DISPLAYS ======
const int LCD_POT_ADDR    = 0x27;
const int LCD_PLAYER_ADDR = 0x26;
const int LCD_CENTER_ADDR = 0x25;

// ====== LCD OBJEKTE (26x2) ======
LiquidCrystal_I2C lcdPot(LCD_POT_ADDR, LCD_COLS, LCD_ROWS);
LiquidCrystal_I2C lcdPlayer(LCD_PLAYER_ADDR, LCD_COLS, LCD_ROWS);
LiquidCrystal_I2C lcdCenter(LCD_CENTER_ADDR, LCD_COLS, LCD_ROWS);

// ====== SPIEL KONFIGURATION ======
const int START_CHIPS = 100;
const int MIN_RAISE = 5;
const int RAISE_STEP = 5;
const long ENTER_HOLD_TIME = 3000;

// ====== SPIELVARIABLEN ======
int spielerChips = START_CHIPS;
int botChips = START_CHIPS;
int pot = 0;
int spielerEinsatz = 0;
int botEinsatz = 0;
int aktuellerRaiseBetrag = 0; 
int min_call_amount = 0;
String aktionsStatus = "";

// ====== KARTENDECK & PHASEN ======
struct Karte {
  byte wert; // 2-14 
  byte farbe; // 0-3
};

Karte deck[52];
int deckIndex = 0;

Karte spielerHand[2];
Karte botHand[2];
Karte gemeinschaftsKarten[5];
int anzahlGemeinschaftsKarten = 0;

unsigned long enterPressedTime = 0;
bool enterPressed = false;

enum GamePhase {
  PHASE_WAIT_START,
  PHASE_PRE_FLOP,
  PHASE_FLOP,
  PHASE_TURN,
  PHASE_RIVER,
  PHASE_SHOWDOWN
};

GamePhase currentPhase = PHASE_WAIT_START;

// ====== HAND RANKINGS ======
enum HandRank {
  HIGH_CARD = 0,
  ONE_PAIR = 1,
  TWO_PAIR = 2,
  THREE_KIND = 3,
  STRAIGHT = 4,
  FLUSH = 5,
  FULL_HOUSE = 6,
  FOUR_KIND = 7,
  STRAIGHT_FLUSH = 8,
  ROYAL_FLUSH = 9
};

// ====== HAND EVALUATION STRUKTUR ======
struct HandEvaluation {
  HandRank rank;
  int primaryValue;   // Hauptwert (z.B. Drilling-Wert)
  int secondaryValue; // Nebenwert (z.B. Paar bei Full House)
  int kickers[5];     // Kicker-Karten
  int score;          // Gesamtscore für Vergleich
};

// ====== VORWÄRTSDEKLARATIONEN ======
String wertZuChar(byte wert);
String farbeZuChar(byte farbe);
String karteZuString(Karte k);
void spielerCall();
void spielerRaise(int totalBet);
void botCall();
void botRaise(int totalBet);
void aktualisiereAlleDisplays(); 
void fehlerPiep(String meldung);
void spielerGewinntHand();
void botGewinntHand();
bool pruefeGameOver();
void ermittleGewinner();
void spielAbbrechen();
void spielerGewinntShowdown();
void botGewinntShowdown();
void splitPot();
bool frageWeiterspielen();
void warteAufStart();
void startCountdown();
void neueHandStarten();
void mischeDeck();
Karte zieheKarte();
void teileHoleCardsAus();
bool bettingRunde();
void zeigeFlop();
void zeigeTurn();
void zeigeRiver();
void showdown();
void handPause();
int spielerAktion();

// ====== NEUE BOT KI FUNKTIONEN ======
HandEvaluation evaluiereHand(Karte hand[2], Karte community[], int commCount);
int berechneHandScore(HandEvaluation eval);
bool hatPaar(Karte karten[], int count, int &paarWert);
bool hatZweiPaar(Karte karten[], int count, int &hoeheresPaar, int &niedrigeresPaar);
bool hatDrilling(Karte karten[], int count, int &drillingWert);
bool hatStraight(Karte karten[], int count, int &hoechsteKarte);
bool hatFlush(Karte karten[], int count, int &hoechsteKarte);
bool hatFullHouse(Karte karten[], int count, int &drilling, int &paar);
bool hatVierling(Karte karten[], int count, int &vierlingWert);
bool hatStraightFlush(Karte karten[], int count, int &hoechsteKarte);
int botEntscheidungIntelligent();
float berechnePotOdds(int callAmount);
float berechneHandEquity(HandEvaluation eval, int phase);
bool sollBotBluffen(HandEvaluation eval);
int berechneOptimalenEinsatz(HandEvaluation eval, float equity);

// ====== HILFSFUNKTIONEN FÜR KARTEN-DARSTELLUNG ======
String wertZuChar(byte wert) {
  if (wert == 14) return "A";
  if (wert == 13) return "K";
  if (wert == 12) return "Q";
  if (wert == 11) return "J";
  if (wert == 10) return "T";
  return String(wert);
}

String farbeZuChar(byte farbe) {
  if (farbe == 0) return "K";
  if (farbe == 1) return "C";
  if (farbe == 2) return "P";
  return "H";
}

String karteZuString(Karte k) {
  return wertZuChar(k.wert) + farbeZuChar(k.farbe);
}

// ====== SETUP ======
void setup() {
  Serial.begin(9600);

  pinMode(PIN_CALL_BUTTON, INPUT_PULLUP);
  pinMode(PIN_FOLD_BUTTON, INPUT_PULLUP);
  pinMode(PIN_ENTER_BUTTON, INPUT_PULLUP);

  randomSeed(analogRead(A1));

  systemInitialisierung();
  warteAufStart();
}

// ====== MAIN LOOP ======
void loop() {
  if (pruefeGameOver()) {
    return;
  }

  neueHandStarten();
  teileHoleCardsAus();
  aktualisiereAlleDisplays();

  currentPhase = PHASE_PRE_FLOP;
  if (!bettingRunde()) {
    goto hand_ende; 
  }

  zeigeFlop();
  currentPhase = PHASE_FLOP;
  if (!bettingRunde()) {
    goto hand_ende;
  }

  zeigeTurn();
  currentPhase = PHASE_TURN;
  if (!bettingRunde()) {
    goto hand_ende;
  }

  zeigeRiver();
  currentPhase = PHASE_RIVER;
  if (!bettingRunde()) {
    goto hand_ende;
  }

  showdown();

  hand_ende:
    handPause();
    if (!frageWeiterspielen()) { 
        spielerChips = START_CHIPS;
        botChips = START_CHIPS;
        warteAufStart();
    }
}

// ====== DISPLAYS AKTUALISIEREN ======
void aktualisiereAlleDisplays() {
    int call_betrag_live = max(0, botEinsatz - spielerEinsatz);
    int anzuzeigender_betrag_live = max(call_betrag_live, aktuellerRaiseBetrag);
    
    Serial.println("==================================================");
    Serial.print("PHASE: "); Serial.println(currentPhase);
    Serial.print("POT: "); Serial.println(pot); 
    Serial.print("Spieler Chips: "); Serial.println(spielerChips);
    Serial.print("Bot Chips: "); Serial.println(botChips);
    Serial.print("Spieler Einsatz: "); Serial.println(spielerEinsatz);
    Serial.print("Bot Einsatz: "); Serial.println(botEinsatz);
    Serial.println("==================================================");

    lcdPot.clear();
    lcdPot.setCursor(0, 0);
    if (currentPhase != PHASE_WAIT_START && currentPhase != PHASE_SHOWDOWN) {
        lcdPot.print("POT: ");
        lcdPot.print(pot);
        lcdPot.setCursor(0, 1);
        lcdPot.print(aktionsStatus);
    } else {
        lcdPot.print("POKER TIME");
        lcdPot.setCursor(0, 1);
        lcdPot.print(aktionsStatus);
    }

    lcdPlayer.clear();
    if (currentPhase >= PHASE_PRE_FLOP) {
        lcdPlayer.setCursor(0, 0); lcdPlayer.print(farbeZuChar(spielerHand[0].farbe));
        lcdPlayer.setCursor(0, 1); lcdPlayer.print(wertZuChar(spielerHand[0].wert));
        lcdPlayer.setCursor(2, 0); lcdPlayer.print(farbeZuChar(spielerHand[1].farbe));
        lcdPlayer.setCursor(2, 1); lcdPlayer.print(wertZuChar(spielerHand[1].wert));
        
        lcdPlayer.setCursor(5, 0); 
        lcdPlayer.print("R:");
        if (min_call_amount == 0 && aktuellerRaiseBetrag == 0) {
             lcdPlayer.print("CHECK");
        } else {
             int anzuzeigender_betrag = max(min_call_amount, aktuellerRaiseBetrag);
             lcdPlayer.print(anzuzeigender_betrag);
        }

        lcdPlayer.setCursor(5, 1);
        lcdPlayer.print("G:");
        lcdPlayer.print(spielerChips);
    } else {
        lcdPlayer.setCursor(0, 0);
        lcdPlayer.print("Warte auf Karten");
        lcdPlayer.setCursor(0, 1);
        lcdPlayer.print("G:");
        lcdPlayer.print(spielerChips);
    }

    lcdCenter.clear();
    int cardPositions[] = {1, 4, 7, 10, 13}; 
    for (int i = 0; i < 5; i++) {
        if (i < anzahlGemeinschaftsKarten) {
            lcdCenter.setCursor(cardPositions[i], 0);
            lcdCenter.print(farbeZuChar(gemeinschaftsKarten[i].farbe));
            lcdCenter.setCursor(cardPositions[i], 1); 
            lcdCenter.print(wertZuChar(gemeinschaftsKarten[i].wert));
        } else {
            lcdCenter.setCursor(cardPositions[i], 0);
            lcdCenter.print("-"); 
            lcdCenter.setCursor(cardPositions[i], 1);
            lcdCenter.print("-");
        }
    }
}

// ====== SPIELER AKTION ======
int spielerAktion() {
  min_call_amount = max(0, botEinsatz - spielerEinsatz);
  aktuellerRaiseBetrag = min_call_amount; 
  aktualisiereAlleDisplays();
  
  int lastPotiValue = analogRead(PIN_POTENTIOMETER);
  bool poti_moved = false;

  while (true) {
    if (digitalRead(PIN_FOLD_BUTTON) == LOW) {
      delay(200); 
      while (digitalRead(PIN_FOLD_BUTTON) == LOW) delay(10);
      aktuellerRaiseBetrag = 0; 
      return 0;
    }

    if (digitalRead(PIN_CALL_BUTTON) == LOW) {
      delay(200);
      while (digitalRead(PIN_CALL_BUTTON) == LOW) delay(10);
      spielerCall();
      aktuellerRaiseBetrag = 0; 
      return 1;
    }

    int potiValue = analogRead(PIN_POTENTIOMETER);
    if (abs(potiValue - lastPotiValue) > 5) { 
        poti_moved = true;
    }
    
    if (poti_moved) {
        int max_setzbarer_gesamtbetrag = spielerChips + spielerEinsatz;
        int raise_range = max(0, max_setzbarer_gesamtbetrag - min_call_amount); 
        int poti_raise_diff = map(potiValue, 0, 1023, 0, raise_range / RAISE_STEP) * RAISE_STEP;
        int poti_total_bet = min_call_amount + poti_raise_diff;
        poti_total_bet = constrain(poti_total_bet, min_call_amount, max_setzbarer_gesamtbetrag);
        aktuellerRaiseBetrag = poti_total_bet;

        if (abs(poti_total_bet - lastPotiValue) >= RAISE_STEP || lastPotiValue == -1) {
          aktualisiereAlleDisplays(); 
          lastPotiValue = poti_total_bet;
        }
    }

    if (digitalRead(PIN_ENTER_BUTTON) == LOW && !enterPressed) {
      delay(200);
      int max_chips = spielerChips + spielerEinsatz;
      int min_raise_to = min_call_amount + MIN_RAISE; 

      if (aktuellerRaiseBetrag == min_call_amount) {
          spielerCall();
          aktuellerRaiseBetrag = 0; 
          return 1;
      }
      
      if (aktuellerRaiseBetrag < min_raise_to && aktuellerRaiseBetrag != max_chips) {
        fehlerPiep("MIN R:");
        lcdPot.setCursor(0, 1); 
        lcdPot.print("MIN RAISE ERROR");
        delay(1000);
        aktualisiereAlleDisplays(); 
        continue;
      }
      
      spielerRaise(aktuellerRaiseBetrag);
      aktuellerRaiseBetrag = 0;
      return 2;
    }
    
    if (digitalRead(PIN_ENTER_BUTTON) == LOW) {
        if (!enterPressed) {
            enterPressed = true;
            enterPressedTime = millis();
        } else if (millis() - enterPressedTime >= ENTER_HOLD_TIME) {
            aktuellerRaiseBetrag = 0;
            return -1;
        }
    } else {
        enterPressed = false;
    }
    delay(50);
  }
}

// ====== SYSTEM INITIALISIERUNG ======
void systemInitialisierung() {
  lcdPot.init(); lcdPot.backlight();
  lcdPlayer.init(); lcdPlayer.backlight();
  lcdCenter.init(); lcdCenter.backlight();

  lcdPot.clear(); lcdPot.setCursor(0, 0); lcdPot.print("POKER GAME");
  lcdPlayer.clear(); lcdPlayer.setCursor(0, 0); lcdPlayer.print("Initializing...");
  lcdCenter.clear(); lcdCenter.setCursor(0, 0); lcdCenter.print("Texas Hold'em");
  lcdCenter.setCursor(0, 1); lcdCenter.print("Smart Bot AI");
  delay(2000);
}

// ====== WARTE AUF START ======
void warteAufStart() {
  currentPhase = PHASE_WAIT_START;
  aktionsStatus = "Press ENTER";
  aktualisiereAlleDisplays(); 

  lcdCenter.clear();
  lcdCenter.setCursor(0, 0);
  lcdCenter.print("Press ENTER");
  lcdCenter.setCursor(0, 1);
  lcdCenter.print("to Start Game");

  while (digitalRead(PIN_ENTER_BUTTON) == HIGH) {
    delay(50);
  }
  startCountdown();
}

// ====== START COUNTDOWN ======
void startCountdown() {
  for (int i = 3; i > 0; i--) {
    aktionsStatus = "Start in " + String(i);
    aktualisiereAlleDisplays();
    lcdCenter.clear();
    lcdCenter.setCursor(7, 0);
    lcdCenter.print(i);
    delay(1000);
  }
  aktionsStatus = "LET'S PLAY!";
  aktualisiereAlleDisplays();
  lcdCenter.clear();
  lcdCenter.print("LET'S PLAY!");
  delay(1000);
}

// ====== NEUE HAND STARTEN ======
void neueHandStarten() {
  mischeDeck();
  pot = 0;
  spielerEinsatz = 0;
  botEinsatz = 0;
  anzahlGemeinschaftsKarten = 0;
  aktuellerRaiseBetrag = 0;
  aktionsStatus = "NEW HAND";
}

// ====== DECK MISCHEN / KARTE ZIEHEN / HOLE CARDS AUSTEILEN ======
void mischeDeck() {
  int index = 0;
  for (int farbe = 0; farbe < 4; farbe++) {
    for (int wert = 2; wert <= 14; wert++) {
      deck[index].wert = wert;
      deck[index].farbe = farbe;
      index++;
    }
  }

  for (int i = 51; i > 0; i--) {
    int j = random(0, i + 1);
    Karte temp = deck[i];
    deck[i] = deck[j];
    deck[j] = temp;
  }
  deckIndex = 0;
}

Karte zieheKarte() {
  return deck[deckIndex++];
}

void teileHoleCardsAus() {
  spielerHand[0] = zieheKarte();
  spielerHand[1] = zieheKarte();
  botHand[0] = zieheKarte();
  botHand[1] = zieheKarte();
}

// ====== PRÜFE GAME OVER / GEWINNER ERMITTELN ======
bool pruefeGameOver() {
  if (spielerChips <= 0 || botChips <= 0) {
    ermittleGewinner();
    return true;
  }
  return false;
}

void ermittleGewinner() {
  lcdCenter.clear();
  lcdCenter.setCursor(0, 0);
  aktionsStatus = "";
  
  if (spielerChips > botChips) {
    lcdCenter.print("PLAYER WINS!");
    aktionsStatus = "PLAYER WINS GAME!";
  } else if (botChips > spielerChips) {
    lcdCenter.print("BOT WINS!");
    aktionsStatus = "BOT WINS GAME!";
  } else {
    lcdCenter.print("TIE!");
    aktionsStatus = "GAME TIE!";
  }
  aktualisiereAlleDisplays();

  lcdCenter.setCursor(0, 1);
  lcdCenter.print("P:");
  lcdCenter.print(spielerChips);
  lcdCenter.print(" B:");
  lcdCenter.print(botChips);

  while (true) {
    delay(1000); 
  }
}

void spielAbbrechen() {
  aktionsStatus = "GAME ABORTED";
  aktualisiereAlleDisplays();
  lcdCenter.clear();
  lcdCenter.print("GAME ABORTED");
  delay(2000);
  ermittleGewinner();
}

// ====== WEITERSPIELEN ABFRAGE ======
bool frageWeiterspielen() {
    aktionsStatus = "Next? CALL/FOLD";
    aktualisiereAlleDisplays();

    lcdCenter.clear();
    lcdCenter.setCursor(0, 0);
    lcdCenter.print("CALL: Next Hand"); 
    lcdCenter.setCursor(0, 1);
    lcdCenter.print("FOLD: Quit Game"); 

    while (true) {
        if (digitalRead(PIN_CALL_BUTTON) == LOW) {
            delay(200);
            while (digitalRead(PIN_CALL_BUTTON) == LOW) delay(10);
            return true; 
        }
        if (digitalRead(PIN_FOLD_BUTTON) == LOW) {
            delay(200);
            while (digitalRead(PIN_FOLD_BUTTON) == LOW) delay(10);
            return false; 
        }
        delay(50);
    }
}

// ====== BETTING RUNDE ======
bool bettingRunde() {
  spielerEinsatz = 0;
  botEinsatz = 0;
  
  if (spielerEinsatz == 0 && botEinsatz == 0) {
    aktionsStatus = "BOT ACTION";
    aktualisiereAlleDisplays();
    int botAction = botEntscheidungIntelligent();
    if (botAction == 0) {
      spielerGewinntHand(); 
      return false;
    }
  }
  
  while (true) {
    aktionsStatus = "PLAYER ACTION";
    aktualisiereAlleDisplays();
    int playerAction = spielerAktion();

    if (playerAction == -1) {
      spielAbbrechen();
      return false;
    }
    if (playerAction == 0) {
      botGewinntHand(); 
      return false;
    }
    if (spielerEinsatz == botEinsatz) {
      return true;
    }
    
    aktionsStatus = "BOT ACTION";
    aktualisiereAlleDisplays();
    int botAction = botEntscheidungIntelligent();

    if (botAction == 0) {
      spielerGewinntHand(); 
      return false;
    }
    if (spielerEinsatz == botEinsatz) {
      return true;
    }
  }
}

// ====== SPIELER CALL / RAISE ======
void spielerCall() {
  int differenz = botEinsatz - spielerEinsatz;
  differenz = max(0, differenz); 
  int tatsaechlicherEinsatz = min(differenz, spielerChips); 

  spielerEinsatz += tatsaechlicherEinsatz;
  spielerChips -= tatsaechlicherEinsatz;
  pot += tatsaechlicherEinsatz;
  
  if (tatsaechlicherEinsatz == 0) {
     aktionsStatus = "P CHECK";
  } else if (tatsaechlicherEinsatz < differenz) {
     aktionsStatus = "P ALL-IN";
  } else {
     aktionsStatus = "P CALL";
  }
  aktualisiereAlleDisplays(); 
  delay(1500);
}

void spielerRaise(int totalBet) {
  int einsatzDifferenz = totalBet - spielerEinsatz; 
  spielerEinsatz = totalBet;
  spielerChips -= einsatzDifferenz;
  pot += einsatzDifferenz;
  aktionsStatus = "P RAISE";
  aktualisiereAlleDisplays(); 
  delay(1500);
}

void fehlerPiep(String meldung) {
  delay(500);
}

// ====== BOT CALL / RAISE ======
void botCall() {
  int differenz = spielerEinsatz - botEinsatz;
  differenz = max(0, differenz);
  int tatsaechlicherEinsatz = min(differenz, botChips);

  botEinsatz += tatsaechlicherEinsatz;
  botChips -= tatsaechlicherEinsatz;
  pot += tatsaechlicherEinsatz;
  
  if (tatsaechlicherEinsatz == 0) {
    aktionsStatus = "B CHECK";
  } else if (tatsaechlicherEinsatz < differenz) {
    aktionsStatus = "B ALLIN";
  } else {
    aktionsStatus = "B CALL";
  }
  aktualisiereAlleDisplays(); 
  delay(1500);
}

void botRaise(int totalBet) {
  int einsatzDifferenz = totalBet - botEinsatz;
  einsatzDifferenz = min(einsatzDifferenz, botChips); 
  totalBet = botEinsatz + einsatzDifferenz;

  botEinsatz = totalBet;
  botChips -= einsatzDifferenz;
  pot += einsatzDifferenz;
  
  aktionsStatus = "B RAISE " + String(einsatzDifferenz);
  aktualisiereAlleDisplays(); 
  delay(1500);
}

// **********************************************
// ******** INTELLIGENTE BOT KI - NEU **********
// **********************************************

// ====== HAND EVALUIERUNG ======
HandEvaluation evaluiereHand(Karte hand[2], Karte community[], int commCount) {
  HandEvaluation eval;
  Karte alleKarten[7];
  
  for(int i = 0; i < 2; i++) alleKarten[i] = hand[i];
  for(int i = 0; i < commCount; i++) alleKarten[i+2] = community[i];
  int totalCards = 2 + commCount;
  
  // Sortiere Karten nach Wert (absteigend)
  for(int i = 0; i < totalCards-1; i++) {
    for(int j = i+1; j < totalCards; j++) {
      if(alleKarten[j].wert > alleKarten[i].wert) {
        Karte temp = alleKarten[i];
        alleKarten[i] = alleKarten[j];
        alleKarten[j] = temp;
      }
    }
  }
  
  // Prüfe Hände von stärkster bis schwächster
  int hoechste;
  
  if(hatStraightFlush(alleKarten, totalCards, hoechste)) {
    eval.rank = (hoechste == 14) ? ROYAL_FLUSH : STRAIGHT_FLUSH;
    eval.primaryValue = hoechste;
    eval.score = berechneHandScore(eval);
    return eval;
  }
  
  int vierling;
  if(hatVierling(alleKarten, totalCards, vierling)) {
    eval.rank = FOUR_KIND;
    eval.primaryValue = vierling;
    eval.score = berechneHandScore(eval);
    return eval;
  }
  
  int drilling, paar;
  if(hatFullHouse(alleKarten, totalCards, drilling, paar)) {
    eval.rank = FULL_HOUSE;
    eval.primaryValue = drilling;
    eval.secondaryValue = paar;
    eval.score = berechneHandScore(eval);
    return eval;
  }
  
  if(hatFlush(alleKarten, totalCards, hoechste)) {
    eval.rank = FLUSH;
    eval.primaryValue = hoechste;
    eval.score = berechneHandScore(eval);
    return eval;
  }
  
  if(hatStraight(alleKarten, totalCards, hoechste)) {
    eval.rank = STRAIGHT;
    eval.primaryValue = hoechste;
    eval.score = berechneHandScore(eval);
    return eval;
  }
  
  if(hatDrilling(alleKarten, totalCards, drilling)) {
    eval.rank = THREE_KIND;
    eval.primaryValue = drilling;
    eval.score = berechneHandScore(eval);
    return eval;
  }
  
  int paar1, paar2;
  if(hatZweiPaar(alleKarten, totalCards, paar1, paar2)) {
    eval.rank = TWO_PAIR;
    eval.primaryValue = paar1;
    eval.secondaryValue = paar2;
    eval.score = berechneHandScore(eval);
    return eval;
  }
  
  if(hatPaar(alleKarten, totalCards, paar)) {
    eval.rank = ONE_PAIR;
    eval.primaryValue = paar;
    eval.score = berechneHandScore(eval);
    return eval;
  }
  
  // High Card
  eval.rank = HIGH_CARD;
  eval.primaryValue = alleKarten[0].wert;
  eval.score = berechneHandScore(eval);
  return eval;
}

// ====== HAND SCORE BERECHNUNG ======
int berechneHandScore(HandEvaluation eval) {
  int baseScore = eval.rank * 1000;
  baseScore += eval.primaryValue * 10;
  baseScore += eval.secondaryValue;
  return baseScore;
}

// ====== HAND ERKENNUNGS-FUNKTIONEN ======
bool hatPaar(Karte karten[], int count, int &paarWert) {
  for(int i = 0; i < count-1; i++) {
    for(int j = i+1; j < count; j++) {
      if(karten[i].wert == karten[j].wert) {
        paarWert = karten[i].wert;
        return true;
      }
    }
  }
  return false;
}

bool hatZweiPaar(Karte karten[], int count, int &hoeheresPaar, int &niedrigeresPaar) {
  int gefundenePaare = 0;
  int paare[2] = {0, 0};
  
  for(int i = 0; i < count-1; i++) {
    for(int j = i+1; j < count; j++) {
      if(karten[i].wert == karten[j].wert) {
        bool schonGefunden = false;
        for(int k = 0; k < gefundenePaare; k++) {
          if(paare[k] == karten[i].wert) schonGefunden = true;
        }
        if(!schonGefunden && gefundenePaare < 2) {
          paare[gefundenePaare++] = karten[i].wert;
        }
      }
    }
  }
  
  if(gefundenePaare >= 2) {
    hoeheresPaar = max(paare[0], paare[1]);
    niedrigeresPaar = min(paare[0], paare[1]);
    return true;
  }
  return false;
}

bool hatDrilling(Karte karten[], int count, int &drillingWert) {
  for(int i = 0; i < count-2; i++) {
    int gleiche = 1;
    for(int j = i+1; j < count; j++) {
      if(karten[i].wert == karten[j].wert) gleiche++;
    }
    if(gleiche >= 3) {
      drillingWert = karten[i].wert;
      return true;
    }
  }
  return false;
}

bool hatStraight(Karte karten[], int count, int &hoechsteKarte) {
  if(count < 5) return false;
  
  // Entferne Duplikate
  int einzigartigeWerte[7];
  int einzigartigeCount = 0;
  
  for(int i = 0; i < count; i++) {
    bool schonVorhanden = false;
    for(int j = 0; j < einzigartigeCount; j++) {
      if(einzigartigeWerte[j] == karten[i].wert) {
        schonVorhanden = true;
        break;
      }
    }
    if(!schonVorhanden) {
      einzigartigeWerte[einzigartigeCount++] = karten[i].wert;
    }
  }
  
  // Sortiere absteigend
  for(int i = 0; i < einzigartigeCount-1; i++) {
    for(int j = i+1; j < einzigartigeCount; j++) {
      if(einzigartigeWerte[j] > einzigartigeWerte[i]) {
        int temp = einzigartigeWerte[i];
        einzigartigeWerte[i] = einzigartigeWerte[j];
        einzigartigeWerte[j] = temp;
      }
    }
  }
  
  // Prüfe auf 5 aufeinanderfolgende Karten
  for(int i = 0; i <= einzigartigeCount-5; i++) {
    bool straight = true;
    for(int j = 0; j < 4; j++) {
      if(einzigartigeWerte[i+j] - einzigartigeWerte[i+j+1] != 1) {
        straight = false;
        break;
      }
    }
    if(straight) {
      hoechsteKarte = einzigartigeWerte[i];
      return true;
    }
  }
  
  // Prüfe A-2-3-4-5 (Wheel)
  if(einzigartigeCount >= 5) {
    bool hasAce = false, hasTwo = false, hasThree = false, hasFour = false, hasFive = false;
    for(int i = 0; i < einzigartigeCount; i++) {
      if(einzigartigeWerte[i] == 14) hasAce = true;
      if(einzigartigeWerte[i] == 2) hasTwo = true;
      if(einzigartigeWerte[i] == 3) hasThree = true;
      if(einzigartigeWerte[i] == 4) hasFour = true;
      if(einzigartigeWerte[i] == 5) hasFive = true;
    }
    if(hasAce && hasTwo && hasThree && hasFour && hasFive) {
      hoechsteKarte = 5;
      return true;
    }
  }
  
  return false;
}

bool hatFlush(Karte karten[], int count, int &hoechsteKarte) {
  if(count < 5) return false;
  
  for(int farbe = 0; farbe < 4; farbe++) {
    int anzahl = 0;
    int hoechste = 0;
    for(int i = 0; i < count; i++) {
      if(karten[i].farbe == farbe) {
        anzahl++;
        if(karten[i].wert > hoechste) hoechste = karten[i].wert;
      }
    }
    if(anzahl >= 5) {
      hoechsteKarte = hoechste;
      return true;
    }
  }
  return false;
}

bool hatFullHouse(Karte karten[], int count, int &drilling, int &paar) {
  if(count < 5) return false;
  
  // Finde Drilling
  bool hatDrill = false;
  for(int i = 0; i < count-2; i++) {
    int gleiche = 1;
    for(int j = i+1; j < count; j++) {
      if(karten[i].wert == karten[j].wert) gleiche++;
    }
    if(gleiche >= 3) {
      drilling = karten[i].wert;
      hatDrill = true;
      break;
    }
  }
  
  if(!hatDrill) return false;
  
  // Finde Paar (nicht gleicher Wert wie Drilling)
  for(int i = 0; i < count-1; i++) {
    if(karten[i].wert == drilling) continue;
    for(int j = i+1; j < count; j++) {
      if(karten[j].wert == drilling) continue;
      if(karten[i].wert == karten[j].wert) {
        paar = karten[i].wert;
        return true;
      }
    }
  }
  return false;
}

bool hatVierling(Karte karten[], int count, int &vierlingWert) {
  for(int i = 0; i < count-3; i++) {
    int gleiche = 1;
    for(int j = i+1; j < count; j++) {
      if(karten[i].wert == karten[j].wert) gleiche++;
    }
    if(gleiche >= 4) {
      vierlingWert = karten[i].wert;
      return true;
    }
  }
  return false;
}

bool hatStraightFlush(Karte karten[], int count, int &hoechsteKarte) {
  if(count < 5) return false;
  
  // Prüfe jede Farbe separat
  for(int farbe = 0; farbe < 4; farbe++) {
    Karte gleicheFarbe[7];
    int farbCount = 0;
    
    for(int i = 0; i < count; i++) {
      if(karten[i].farbe == farbe) {
        gleicheFarbe[farbCount++] = karten[i];
      }
    }
    
    if(farbCount >= 5) {
      if(hatStraight(gleicheFarbe, farbCount, hoechsteKarte)) {
        return true;
      }
    }
  }
  return false;
}

// ====== POT ODDS BERECHNUNG ======
float berechnePotOdds(int callAmount) {
  if(callAmount <= 0) return 999.0;
  if(pot == 0) return 0.0;
  return (float)pot / (float)callAmount;
}

// ====== HAND EQUITY BERECHNUNG (Vereinfacht) ======
float berechneHandEquity(HandEvaluation eval, int phase) {
  float baseEquity = 0.0;
  
  // Basis-Equity basierend auf Hand-Rank
  switch(eval.rank) {
    case ROYAL_FLUSH:
    case STRAIGHT_FLUSH: baseEquity = 0.99; break;
    case FOUR_KIND: baseEquity = 0.95; break;
    case FULL_HOUSE: baseEquity = 0.90; break;
    case FLUSH: baseEquity = 0.80; break;
    case STRAIGHT: baseEquity = 0.70; break;
    case THREE_KIND: baseEquity = 0.60; break;
    case TWO_PAIR: baseEquity = 0.50; break;
    case ONE_PAIR:
      if(eval.primaryValue >= 10) baseEquity = 0.40;
      else baseEquity = 0.30;
      break;
    case HIGH_CARD:
      if(eval.primaryValue == 14) baseEquity = 0.25;
      else if(eval.primaryValue >= 12) baseEquity = 0.20;
      else baseEquity = 0.15;
      break;
  }
  
  // Anpassung basierend auf Phase (je früher, desto unsicherer)
  switch(phase) {
    case PHASE_PRE_FLOP: baseEquity *= 0.7; break;
    case PHASE_FLOP: baseEquity *= 0.85; break;
    case PHASE_TURN: baseEquity *= 0.95; break;
    case PHASE_RIVER: break; // Volle Equity
  }
  
  return constrain(baseEquity, 0.0, 1.0);
}

// ====== BLUFF ENTSCHEIDUNG ======
bool sollBotBluffen(HandEvaluation eval) {
  // Bluffe nur mit schwachen Händen
  if(eval.rank > TWO_PAIR) return false;
  
  // 15% Bluff-Chance bei schwacher Hand
  if(random(100) < 15) {
    Serial.println("Bot beschließt zu bluffen!");
    return true;
  }
  return false;
}

// ====== OPTIMALEN EINSATZ BERECHNEN ======
int berechneOptimalenEinsatz(HandEvaluation eval, float equity) {
  int maxEinsatz = botChips + botEinsatz;
  int potGroesse = pot;
  
  // Starke Hand: Aggressives Value Betting
  if(eval.rank >= STRAIGHT) {
    int betSize = potGroesse * 0.75;
    return min(betSize, maxEinsatz);
  }
  
  // Mittelstarke Hand: Moderates Betting
  if(eval.rank >= TWO_PAIR) {
    int betSize = potGroesse * 0.5;
    return min(betSize, maxEinsatz);
  }
  
  // Schwache Hand mit Potential: Kleine Bets
  if(equity > 0.3) {
    int betSize = potGroesse * 0.33;
    return min(betSize, maxEinsatz);
  }
  
  // Sehr schwache Hand: Minimum Bet
  return MIN_RAISE;
}

// **********************************************
// ******* INTELLIGENTE BOT ENTSCHEIDUNG *******
// **********************************************

int botEntscheidungIntelligent() {
  delay(500);
  
  // Evaluiere Bot's Hand
  HandEvaluation botEval = evaluiereHand(botHand, gemeinschaftsKarten, anzahlGemeinschaftsKarten);
  float equity = berechneHandEquity(botEval, currentPhase);
  
  Serial.print("Bot Hand Rank: "); Serial.println(botEval.rank);
  Serial.print("Bot Equity: "); Serial.println(equity);
  
  int differenz = spielerEinsatz - botEinsatz;
  differenz = max(0, differenz);
  
  // ***** 1. BOT ERÖFFNET DIE RUNDE *****
  if(spielerEinsatz == 0 && botEinsatz == 0) {
    
    // Sehr starke Hand: Fast immer raise
    if(botEval.rank >= STRAIGHT || equity > 0.70) {
      int raiseAmount = berechneOptimalenEinsatz(botEval, equity);
      raiseAmount = max(raiseAmount, MIN_RAISE);
      raiseAmount = min(raiseAmount, botChips);
      
      if(raiseAmount > 0) {
        botRaise(raiseAmount);
        return 2;
      }
    }
    
    // Gute Hand: Häufig raise
    if(botEval.rank >= TWO_PAIR || equity > 0.50) {
      if(random(100) < 70) { // 70% raise
        int raiseAmount = berechneOptimalenEinsatz(botEval, equity);
        raiseAmount = max(raiseAmount, MIN_RAISE);
        raiseAmount = min(raiseAmount, botChips);
        
        if(raiseAmount > 0) {
          botRaise(raiseAmount);
          return 2;
        }
      }
    }
    
    // Mittelmäßige Hand: Manchmal raise
    if(botEval.rank == ONE_PAIR || equity > 0.35) {
      if(random(100) < 40) { // 40% raise
        int raiseAmount = min(MIN_RAISE * 2, botChips);
        if(raiseAmount > 0) {
          botRaise(raiseAmount);
          return 2;
        }
      }
    }
    
    // Bluff-Möglichkeit
    if(sollBotBluffen(botEval) && botChips >= MIN_RAISE * 2) {
      botRaise(MIN_RAISE * 2);
      return 2;
    }
    
    // Ansonsten: Check
    botCall();
    return 1;
  }
  
  // ***** 2. SPIELER HAT GESETZT (DIFFERENZ > 0) *****
  if(differenz > 0) {
    
    float potOdds = berechnePotOdds(differenz);
    Serial.print("Pot Odds: "); Serial.println(potOdds);
    
    // Sehr schwache Hand und teurer Call -> FOLD
    if(equity < 0.25 && differenz > pot * 0.5) {
      aktionsStatus = "B FOLD";
      aktualisiereAlleDisplays();
      delay(1500);
      return 0;
    }
    
    // Pot Odds Check: Lohnt sich der Call?
    float benoetigteEquity = 1.0 / (potOdds + 1.0);
    
    if(equity < benoetigteEquity && differenz > MIN_RAISE * 2) {
      // Call lohnt sich nicht -> FOLD
      aktionsStatus = "B FOLD";
      aktualisiereAlleDisplays();
      delay(1500);
      return 0;
    }
    
    // Sehr starke Hand: Re-Raise
    if(botEval.rank >= FLUSH || equity > 0.80) {
      if(random(100) < 80 && botChips >= differenz + MIN_RAISE) {
        int raiseAmount = botEinsatz + differenz + berechneOptimalenEinsatz(botEval, equity);
        raiseAmount = min(raiseAmount, botChips + botEinsatz);
        
        if(raiseAmount > botEinsatz + differenz) {
          botRaise(raiseAmount);
          return 2;
        }
      }
    }
    
    // Starke Hand: Manchmal Re-Raise
    if(botEval.rank >= THREE_KIND || equity > 0.65) {
      if(random(100) < 50 && botChips >= differenz + MIN_RAISE) {
        int raiseAmount = botEinsatz + differenz + (pot * 0.5);
        raiseAmount = min(raiseAmount, botChips + botEinsatz);
        
        if(raiseAmount > botEinsatz + differenz) {
          botRaise(raiseAmount);
          return 2;
        }
      }
    }
    
    // Standard: CALL
    botCall();
    return 1;
  }
  
  // ***** 3. SPIELER HAT GECHECKT (DIFFERENZ == 0) *****
  else {
    
    // Sehr starke Hand: Value Bet
    if(botEval.rank >= STRAIGHT || equity > 0.75) {
      int betSize = berechneOptimalenEinsatz(botEval, equity);
      betSize = max(betSize, MIN_RAISE);
      betSize = min(betSize, botChips + botEinsatz);
      
      if(betSize > botEinsatz) {
        botRaise(betSize);
        return 2;
      }
    }
    
    // Gute Hand: Oft Value Bet
    if(botEval.rank >= TWO_PAIR || equity > 0.55) {
      if(random(100) < 60) {
        int betSize = pot * 0.5;
        betSize = max(betSize, MIN_RAISE);
        betSize = min(betSize, botChips + botEinsatz);
        
        if(betSize > botEinsatz) {
          botRaise(betSize);
          return 2;
        }
      }
    }
    
    // Bluff-Versuch
    if(sollBotBluffen(botEval) && random(100) < 20) {
      int bluffSize = pot * 0.4;
      bluffSize = max(bluffSize, MIN_RAISE);
      bluffSize = min(bluffSize, botChips + botEinsatz);
      
      if(bluffSize > botEinsatz) {
        botRaise(bluffSize);
        return 2;
      }
    }
    
    // Ansonsten: CHECK
    botCall();
    return 1;
  }
}

// ====== FLOP / TURN / RIVER ZEIGEN ======
void zeigeFlop() {
  gemeinschaftsKarten[0] = zieheKarte();
  gemeinschaftsKarten[1] = zieheKarte();
  gemeinschaftsKarten[2] = zieheKarte();
  anzahlGemeinschaftsKarten = 3;
  aktionsStatus = "FLOP";
  aktualisiereAlleDisplays();
  delay(1500);
}

void zeigeTurn() {
  gemeinschaftsKarten[3] = zieheKarte();
  anzahlGemeinschaftsKarten = 4;
  aktionsStatus = "TURN";
  aktualisiereAlleDisplays();
  delay(1500);
}

void zeigeRiver() {
  gemeinschaftsKarten[4] = zieheKarte();
  anzahlGemeinschaftsKarten = 5;
  aktionsStatus = "RIVER";
  aktualisiereAlleDisplays();
  delay(1500);
}

// ====== SHOWDOWN MIT VERBESSERTER EVALUIERUNG ======
void showdown() {
  currentPhase = PHASE_SHOWDOWN;

  HandEvaluation spielerEval = evaluiereHand(spielerHand, gemeinschaftsKarten, anzahlGemeinschaftsKarten);
  HandEvaluation botEval = evaluiereHand(botHand, gemeinschaftsKarten, anzahlGemeinschaftsKarten);

  Serial.print("Spieler Score: "); Serial.println(spielerEval.score);
  Serial.print("Bot Score: "); Serial.println(botEval.score);

  if(spielerEval.score > botEval.score) {
    spielerGewinntShowdown();
  } else if(botEval.score > spielerEval.score) {
    botGewinntShowdown();
  } else {
    splitPot();
  }
}

// ====== GEWINN-FUNKTIONEN ======
void spielerGewinntHand() {
  spielerChips += pot;
  pot = 0; 
  aktionsStatus = "P WINS (FOLD)";
  aktualisiereAlleDisplays();
  delay(2000);
}

void botGewinntHand() {
  botChips += pot;
  pot = 0;
  aktionsStatus = "B WINS (FOLD)";
  aktualisiereAlleDisplays();
  delay(2000);
}

void spielerGewinntShowdown() {
  spielerChips += pot;
  pot = 0;
  aktionsStatus = "P WINS SHOWDOWN!";
  aktualisiereAlleDisplays();
  delay(2000);
}

void botGewinntShowdown() {
  botChips += pot;
  pot = 0;
  aktionsStatus = "B WINS SHOWDOWN!";
  aktualisiereAlleDisplays();
  delay(2000);
}

void splitPot() {
  int halberPot = pot / 2;
  spielerChips += halberPot;
  botChips += halberPot;
  pot = 0;
  aktionsStatus = "SPLIT POT";
  aktualisiereAlleDisplays();
  delay(2000);
}

void handPause() {
  aktualisiereAlleDisplays();
  delay(2000);
}

Welcome to the forum

I know little or nothing about Tinkercad but it sounds like it does not support the LiquidCrystal_I2C library

Does it include a an I2C liquid crystal display in its components

Have you considered using Wokwi ?

Agree with @UKHeliBob

Tinkercad supports Wire.h, but it likely does not support the LiquidCrystal_I2C library. That’s why you get the “Invalid header file” error.

To make it work in Tinkercad, you might try to use the standard LiquidCrystal.h library with pins connected directly (not via I2C).

Search results on the forum: Search results for 'tinkercad invalid header file order:latest_topic' - Arduino Forum

I'll leave the digging to you.

1 Like

Hi, ticercad does support the LiquidCrystal_I2C. Because in another projekt it works.

Are you sure your display has 26 columns?

// ====== DISPLAY KONFIGURATION ======
const int LCD_COLS = 26;
const int LCD_ROWS = 2;

@mr_creeper789
What the board did you selected in Tinkercad?

I selected the arduino uno.

Are you really use a three LCD screens in your code?

Can we ask why can't you use the Arduino IDE?
The IDE can be used even without a hardware, only for testing and compile the code.

My PC is broken, but i can use tincercad in my ipad, and i need this project until end of october.