Poker

Source: src/poker.c · 222 lines · 43 functions.

Screen captures

Views of this part running on the Mini Arcade.

Poker game screen on the Mini Arcade
Poker showing the card table and current hand.

Module entry points

These functions are exposed to the rest of the project. Open one to follow its decisions and the helpers it calls.

How to read these diagrams

Each function has its own expandable diagram using the actual expressions and statements from this file. Decisions split into True and False; loop bodies are indented. Returns end the function immediately; break and continue follow their C meanings. Arrows between statements show their order when execution reaches them.

These are structural source diagrams before preprocessing, not runtime traces. Conditional compilation and macros remain source expressions. Ternary choices remain inside their statement. Calls are linked separately; callback execution and task scheduling are not inferred.

Functions in this file

currentBet() · source line 43
static uint32_t currentBet(void)
  1. Exit this functionreturn player_bet > cpu_bet ? player_bet : cpu_bet;
Read original function source
static uint32_t currentBet(void) { return player_bet > cpu_bet ? player_bet : cpu_bet; }
compareRanks() · source line 44
static int8_t compareRanks(hand_rank_t a, hand_rank_t b)
  1. Decisiona.value != b.value
    True
    1. Exit this functionreturn a.value > b.value ? 1 : -1;
    False

    Continue after this decision.

  2. FOR loopuint8_t i = 0; i < 5U; ++i

    Repeat body while the loop condition permits; then continue below.

    1. Decisiona.tie[i] != b.tie[i]
      True
      1. Exit this functionreturn a.tie[i] > b.tie[i] ? 1 : -1;
      False

      Continue after this decision.

  3. Exit this functionreturn 0;
Read original function source
static int8_t compareRanks(hand_rank_t a, hand_rank_t b) {
    if (a.value != b.value) return a.value > b.value ? 1 : -1;
    for (uint8_t i = 0; i < 5U; ++i) if (a.tie[i] != b.tie[i]) return a.tie[i] > b.tie[i] ? 1 : -1;
    return 0;
}
straightHigh() · source line 49
static uint8_t straightHigh(const uint8_t count[15])
  1. FOR loopint high = 14; high >= 6; --high

    Repeat body while the loop condition permits; then continue below.

    1. Decisioncount[high] && count[high-1] && count[high-2] && count[high-3] && count[high-4]
      True
      1. Exit this functionreturn (uint8_t)high;
      False

      Continue after this decision.

  2. Exit this functionreturn count[14] && count[2] && count[3] && count[4] && count[5] ? 5U : 0U;
Read original function source
static uint8_t straightHigh(const uint8_t count[15]) {
    for (int high = 14; high >= 6; --high) if (count[high] && count[high-1] && count[high-2] && count[high-3] && count[high-4]) return (uint8_t)high;
    return count[14] && count[2] && count[3] && count[4] && count[5] ? 5U : 0U;
}
evaluateFive() · source line 53
static hand_rank_t evaluateFive(const card_t cards[5])

Calls into this documentation:

  1. Executehand_rank_t result = { .value = HAND_HIGH, .tie = {0} };
  2. Executeuint8_t count[15] = {0};
  3. Executebool flush = true;
  4. FOR loopuint8_t i = 0; i < 5U; ++i

    Repeat body while the loop condition permits; then continue below.

      1. Execute++count[cards[i].rank];
      2. Decisioni && cards[i].suit != cards[0].suit
        True
        1. Executeflush = false;
        False

        Continue after this decision.

  5. Executeconst uint8_t straight = straightHigh(count);
  6. Decisionflush && straight
    True
      1. Executeresult.value = HAND_STRAIGHT_FLUSH;
      2. Executeresult.tie[0] = straight;
      3. Exit this functionreturn result;
    False

    Continue after this decision.

  7. FOR loopint rank = 14; rank >= 2; --rank

    Repeat body while the loop condition permits; then continue below.

    1. Decisioncount[rank] == 4U
      True
        1. Executeresult.value = HAND_QUADS;
        2. Executeresult.tie[0] = (uint8_t)rank;
        3. FOR loopint k=14;k>=2;--k

          Repeat body while the loop condition permits; then continue below.

          1. Decisioncount[k]==1U
            True
              1. Executeresult.tie[1]=(uint8_t)k;
              2. Exit enclosing loop or switchbreak;
            False

            Continue after this decision.

        4. Exit this functionreturn result;
      False

      Continue after this decision.

  8. Executeuint8_t three = 0, pair = 0;
  9. FOR loopint rank=14; rank>=2; --rank

    Repeat body while the loop condition permits; then continue below.

      1. Decision!three && count[rank] == 3U
        True
        1. Executethree=(uint8_t)rank;
        False
        1. Decision!pair && count[rank] == 2U
          True
          1. Executepair=(uint8_t)rank;
          False

          Continue after this decision.

  10. Decisionthree && pair
    True
      1. Executeresult.value=HAND_FULL_HOUSE;
      2. Executeresult.tie[0]=three;
      3. Executeresult.tie[1]=pair;
      4. Exit this functionreturn result;
    False

    Continue after this decision.

  11. Decisionflush
    True
      1. Executeresult.value=HAND_FLUSH;
      2. Executeuint8_t n=0;
      3. FOR loopint rank=14;rank>=2;--rank

        Repeat body while the loop condition permits; then continue below.

        1. FOR loopuint8_t j=0;j<count[rank];++j

          Repeat body while the loop condition permits; then continue below.

          1. Executeresult.tie[n++]=(uint8_t)rank;
      4. Exit this functionreturn result;
    False

    Continue after this decision.

  12. Decisionstraight
    True
      1. Executeresult.value=HAND_STRAIGHT;
      2. Executeresult.tie[0]=straight;
      3. Exit this functionreturn result;
    False

    Continue after this decision.

  13. Decisionthree
    True
      1. Executeresult.value=HAND_TRIPS;
      2. Executeresult.tie[0]=three;
      3. Executeuint8_t n=1;
      4. FOR loopint rank=14;rank>=2;--rank

        Repeat body while the loop condition permits; then continue below.

        1. Decisioncount[rank]==1U
          True
          1. Executeresult.tie[n++]=(uint8_t)rank;
          False

          Continue after this decision.

      5. Exit this functionreturn result;
    False

    Continue after this decision.

  14. Executeuint8_t high_pair=0, low_pair=0;
  15. FOR loopint rank=14;rank>=2;--rank

    Repeat body while the loop condition permits; then continue below.

    1. Decisioncount[rank]==2U
      True
        1. Decision!high_pair
          True
          1. Executehigh_pair=(uint8_t)rank;
          False
          1. Executelow_pair=(uint8_t)rank;
      False

      Continue after this decision.

  16. Decisionhigh_pair && low_pair
    True
      1. Executeresult.value=HAND_TWO_PAIR;
      2. Executeresult.tie[0]=high_pair;
      3. Executeresult.tie[1]=low_pair;
      4. FOR loopint rank=14;rank>=2;--rank

        Repeat body while the loop condition permits; then continue below.

        1. Decisioncount[rank]==1U
          True
            1. Executeresult.tie[2]=(uint8_t)rank;
            2. Exit enclosing loop or switchbreak;
          False

          Continue after this decision.

      5. Exit this functionreturn result;
    False

    Continue after this decision.

  17. Decisionhigh_pair
    True
      1. Executeresult.value=HAND_PAIR;
      2. Executeresult.tie[0]=high_pair;
      3. Executeuint8_t n=1;
      4. FOR loopint rank=14;rank>=2;--rank

        Repeat body while the loop condition permits; then continue below.

        1. Decisioncount[rank]==1U
          True
          1. Executeresult.tie[n++]=(uint8_t)rank;
          False

          Continue after this decision.

      5. Exit this functionreturn result;
    False

    Continue after this decision.

  18. Executeuint8_t n=0;
  19. FOR loopint rank=14;rank>=2;--rank

    Repeat body while the loop condition permits; then continue below.

    1. Decisioncount[rank]
      True
      1. Executeresult.tie[n++]=(uint8_t)rank;
      False

      Continue after this decision.

  20. Exit this functionreturn result;
Read original function source
static hand_rank_t evaluateFive(const card_t cards[5]) {
    hand_rank_t result = { .value = HAND_HIGH, .tie = {0} }; uint8_t count[15] = {0}; bool flush = true;
    for (uint8_t i = 0; i < 5U; ++i) { ++count[cards[i].rank]; if (i && cards[i].suit != cards[0].suit) flush = false; }
    const uint8_t straight = straightHigh(count);
    if (flush && straight) { result.value = HAND_STRAIGHT_FLUSH; result.tie[0] = straight; return result; }
    for (int rank = 14; rank >= 2; --rank) if (count[rank] == 4U) { result.value = HAND_QUADS; result.tie[0] = (uint8_t)rank; for (int k=14;k>=2;--k) if (count[k]==1U) { result.tie[1]=(uint8_t)k; break; } return result; }
    uint8_t three = 0, pair = 0;
    for (int rank=14; rank>=2; --rank) { if (!three && count[rank] == 3U) three=(uint8_t)rank; else if (!pair && count[rank] == 2U) pair=(uint8_t)rank; }
    if (three && pair) { result.value=HAND_FULL_HOUSE; result.tie[0]=three; result.tie[1]=pair; return result; }
    if (flush) { result.value=HAND_FLUSH; uint8_t n=0; for(int rank=14;rank>=2;--rank) for(uint8_t j=0;j<count[rank];++j) result.tie[n++]=(uint8_t)rank; return result; }
    if (straight) { result.value=HAND_STRAIGHT; result.tie[0]=straight; return result; }
    if (three) { result.value=HAND_TRIPS; result.tie[0]=three; uint8_t n=1; for(int rank=14;rank>=2;--rank) if(count[rank]==1U) result.tie[n++]=(uint8_t)rank; return result; }
    uint8_t high_pair=0, low_pair=0;
    for(int rank=14;rank>=2;--rank) if(count[rank]==2U) { if(!high_pair) high_pair=(uint8_t)rank; else low_pair=(uint8_t)rank; }
    if(high_pair && low_pair) { result.value=HAND_TWO_PAIR; result.tie[0]=high_pair; result.tie[1]=low_pair; for(int rank=14;rank>=2;--rank) if(count[rank]==1U) { result.tie[2]=(uint8_t)rank; break; } return result; }
    if(high_pair) { result.value=HAND_PAIR; result.tie[0]=high_pair; uint8_t n=1; for(int rank=14;rank>=2;--rank) if(count[rank]==1U) result.tie[n++]=(uint8_t)rank; return result; }
    uint8_t n=0; for(int rank=14;rank>=2;--rank) if(count[rank]) result.tie[n++]=(uint8_t)rank; return result;
}
evaluateBest() · source line 71
static hand_rank_t evaluateBest(const card_t *cards, uint8_t count)

Calls into this documentation:

  1. Executehand_rank_t best = {0};
  2. Executebool first = true;
  3. FOR loopuint8_t a=0;a<count-4U;++a

    Repeat body while the loop condition permits; then continue below.

    1. FOR loopuint8_t b=a+1;b<count-3U;++b

      Repeat body while the loop condition permits; then continue below.

      1. FOR loopuint8_t c=b+1;c<count-2U;++c

        Repeat body while the loop condition permits; then continue below.

        1. FOR loopuint8_t d=c+1;d<count-1U;++d

          Repeat body while the loop condition permits; then continue below.

          1. FOR loopuint8_t e=d+1;e<count;++e

            Repeat body while the loop condition permits; then continue below.

              1. Executeconst card_t five[5]={cards[a],cards[b],cards[c],cards[d],cards[e]};
              2. Executehand_rank_t candidate=evaluateFive(five);
              3. Decisionfirst || compareRanks(candidate,best)>0
                True
                  1. Executebest=candidate;
                  2. Executefirst=false;
                False

                Continue after this decision.

  4. Exit this functionreturn best;
Read original function source
static hand_rank_t evaluateBest(const card_t *cards, uint8_t count) {
    hand_rank_t best = {0}; bool first = true;
    for(uint8_t a=0;a<count-4U;++a) for(uint8_t b=a+1;b<count-3U;++b) for(uint8_t c=b+1;c<count-2U;++c) for(uint8_t d=c+1;d<count-1U;++d) for(uint8_t e=d+1;e<count;++e) {
        const card_t five[5]={cards[a],cards[b],cards[c],cards[d],cards[e]}; hand_rank_t candidate=evaluateFive(five);
        if(first || compareRanks(candidate,best)>0) { best=candidate; first=false; }
    }
    return best;
}
handName() · source line 79
static const char *handName(hand_value_t value)
  1. Executestatic const char *names[] = { "HIGH CARD", "ONE PAIR", "TWO PAIR", "THREE KIND", "STRAIGHT", "FLUSH", "FULL HOUSE", "FOUR KIND", "STRAIGHT FLUSH" };
  2. Exit this functionreturn names[value];
Read original function source
static const char *handName(hand_value_t value) {
    static const char *names[] = { "HIGH CARD", "ONE PAIR", "TWO PAIR", "THREE KIND", "STRAIGHT", "FLUSH", "FULL HOUSE", "FOUR KIND", "STRAIGHT FLUSH" };
    return names[value];
}
shuffleDeck() · source line 83
static void shuffleDeck(void)
  1. Executestatic const char suits[] = { 'C', 'D', 'H', 'S' };
  2. Executeuint8_t n=0;
  3. FOR loopuint8_t s=0;s<4U;++s

    Repeat body while the loop condition permits; then continue below.

    1. FOR loopuint8_t rank=2U;rank<=14U;++rank

      Repeat body while the loop condition permits; then continue below.

      1. Executedeck[n++]=(card_t){rank,suits[s]};
  4. FOR loopint i=DECK_SIZE-1;i>0;--i

    Repeat body while the loop condition permits; then continue below.

      1. Executeconst uint8_t j=esp_random()%(uint32_t)(i+1);
      2. Executeconst card_t card=deck[i];
      3. Executedeck[i]=deck[j];
      4. Executedeck[j]=card;
  5. Executedeck_position=0;
Read original function source
static void shuffleDeck(void) {
    static const char suits[] = { 'C', 'D', 'H', 'S' }; uint8_t n=0;
    for(uint8_t s=0;s<4U;++s) for(uint8_t rank=2U;rank<=14U;++rank) deck[n++]=(card_t){rank,suits[s]};
    for(int i=DECK_SIZE-1;i>0;--i) { const uint8_t j=esp_random()%(uint32_t)(i+1); const card_t card=deck[i]; deck[i]=deck[j]; deck[j]=card; }
    deck_position=0;
}
deal() · source line 89
static bool deal(card_t *card)
  1. Decisiondeck_position>=DECK_SIZE
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Execute*card=deck[deck_position++];
  3. Exit this functionreturn true;
Read original function source
static bool deal(card_t *card) { if(deck_position>=DECK_SIZE) return false; *card=deck[deck_position++]; return true; }
dealHoleCards() · source line 90
static void dealHoleCards(void)

Calls into this documentation:

  1. Decisionplayer_dealer
    True
      1. Executedeal(&player_cards[0]);
      2. Executedeal(&cpu_cards[0]);
      3. Executedeal(&player_cards[1]);
      4. Executedeal(&cpu_cards[1]);
    False
      1. Executedeal(&cpu_cards[0]);
      2. Executedeal(&player_cards[0]);
      3. Executedeal(&cpu_cards[1]);
      4. Executedeal(&player_cards[1]);
  2. ExecuteaudioPlay(AUDIO_SFX_CARD_DEAL);
Read original function source
static void dealHoleCards(void) { if(player_dealer) { deal(&player_cards[0]); deal(&cpu_cards[0]); deal(&player_cards[1]); deal(&cpu_cards[1]); } else { deal(&cpu_cards[0]); deal(&player_cards[0]); deal(&cpu_cards[1]); deal(&player_cards[1]); } audioPlay(AUDIO_SFX_CARD_DEAL); }
remainingRaisePotRoom() · source line 91
static uint32_t remainingRaisePotRoom(void)
  1. Exit this functionreturn pot < POKER_RAISE_POT_LIMIT ? POKER_RAISE_POT_LIMIT-pot : 0U;
Read original function source
static uint32_t remainingRaisePotRoom(void) { return pot < POKER_RAISE_POT_LIMIT ? POKER_RAISE_POT_LIMIT-pot : 0U; }
commitPlayer() · source line 92
static bool commitPlayer(uint32_t amount)

Calls into this documentation:

  1. Decisionamount > getGold() || !spendGold(amount)
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Executeplayer_bet+=amount;
  3. Executeplayer_committed+=amount;
  4. Executepot+=amount;
  5. Exit this functionreturn true;
Read original function source
static bool commitPlayer(uint32_t amount) { if(amount > getGold() || !spendGold(amount)) return false; player_bet+=amount; player_committed+=amount; pot+=amount; return true; }
commitCpu() · source line 93
static void commitCpu(uint32_t amount)
  1. Decisionamount>cpu_stack
    True
    1. Executeamount=cpu_stack;
    False

    Continue after this decision.

  2. Executecpu_stack-=amount;
  3. Executecpu_bet+=amount;
  4. Executepot+=amount;
Read original function source
static void commitCpu(uint32_t amount) { if(amount>cpu_stack) amount=cpu_stack; cpu_stack-=amount; cpu_bet+=amount; pot+=amount; }
minimumRaise() · source line 94
static uint32_t minimumRaise(void)

Calls into this documentation:

  1. Executeconst uint32_t maximum=player_bet+getGold(), full=currentBet()+last_raise;
  2. Decisionmaximum<=currentBet()
    True
    1. Exit this functionreturn 0U;
    False

    Continue after this decision.

  3. Executeconst uint32_t target=maximum<full?maximum:full;
  4. Exit this functionreturn target-player_bet>remainingRaisePotRoom()?0U:target;
Read original function source
static uint32_t minimumRaise(void) { const uint32_t maximum=player_bet+getGold(), full=currentBet()+last_raise; if(maximum<=currentBet()) return 0U; const uint32_t target=maximum<full?maximum:full; return target-player_bet>remainingRaisePotRoom()?0U:target; }
holeStrength() · source line 95
static uint8_t holeStrength(const card_t cards[2])
  1. Executeconst uint8_t high=cards[0].rank>cards[1].rank?cards[0].rank:cards[1].rank, low=cards[0].rank<cards[1].rank?cards[0].rank:cards[1].rank;
  2. Decisionhigh==low
    True
    1. Exit this functionreturn (uint8_t)(55U+high*3U);
    False

    Continue after this decision.

  3. Executeuint8_t strength=(uint8_t)(high*3U+low);
  4. Decisioncards[0].suit==cards[1].suit
    True
    1. Executestrength+=8U;
    False

    Continue after this decision.

  5. Executeconst uint8_t gap=high-low;
  6. Decisiongap==1U
    True
    1. Executestrength+=6U;
    False
    1. Decisiongap==2U
      True
      1. Executestrength+=3U;
      False

      Continue after this decision.

  7. Decisionhigh==14U
    True
    1. Executestrength+=8U;
    False

    Continue after this decision.

  8. Exit this functionreturn strength>100U?100U:strength;
Read original function source
static uint8_t holeStrength(const card_t cards[2]) { const uint8_t high=cards[0].rank>cards[1].rank?cards[0].rank:cards[1].rank, low=cards[0].rank<cards[1].rank?cards[0].rank:cards[1].rank; if(high==low) return (uint8_t)(55U+high*3U); uint8_t strength=(uint8_t)(high*3U+low); if(cards[0].suit==cards[1].suit) strength+=8U; const uint8_t gap=high-low; if(gap==1U) strength+=6U; else if(gap==2U) strength+=3U; if(high==14U) strength+=8U; return strength>100U?100U:strength; }
postFlopStrength() · source line 96
static uint8_t postFlopStrength(const card_t cards[2])

Calls into this documentation:

  1. Executecard_t seven[7]={cards[0],cards[1]};
  2. FOR loopuint8_t i=0;i<community_count;++i

    Repeat body while the loop condition permits; then continue below.

    1. Executeseven[i+2U]=community_cards[i];
  3. Executeconst hand_rank_t rank=evaluateBest(seven,(uint8_t)(community_count+2U));
  4. Executestatic const uint8_t values[]={15,40,62,72,80,84,90,96,100};
  5. Exit this functionreturn (uint8_t)(values[rank.value]+(rank.value==HAND_PAIR||rank.value==HAND_HIGH?rank.tie[0]:0U));
Read original function source
static uint8_t postFlopStrength(const card_t cards[2]) { card_t seven[7]={cards[0],cards[1]}; for(uint8_t i=0;i<community_count;++i) seven[i+2U]=community_cards[i]; const hand_rank_t rank=evaluateBest(seven,(uint8_t)(community_count+2U)); static const uint8_t values[]={15,40,62,72,80,84,90,96,100}; return (uint8_t)(values[rank.value]+(rank.value==HAND_PAIR||rank.value==HAND_HIGH?rank.tie[0]:0U)); }
strength() · source line 97
static uint8_t strength(const card_t cards[2])

Calls into this documentation:

  1. Exit this functionreturn street==STREET_PREFLOP?holeStrength(cards):postFlopStrength(cards);
Read original function source
static uint8_t strength(const card_t cards[2]) { return street==STREET_PREFLOP?holeStrength(cards):postFlopStrength(cards); }
setStatus() · source line 98
static void setStatus(const char *text)
  1. Executesnprintf(status_text,sizeof(status_text),"%s",text);
  2. Executeredraw_needed=true;
Read original function source
static void setStatus(const char *text) { snprintf(status_text,sizeof(status_text),"%s",text); redraw_needed=true; }
dealFlop() · source line 99
static void dealFlop(void)

Calls into this documentation:

  1. Executecard_t burn;
  2. Executedeal(&burn);
  3. FOR loopuint8_t i=0;i<3U;++i

    Repeat body while the loop condition permits; then continue below.

    1. Executedeal(&community_cards[i]);
  4. Executecommunity_count=3U;
  5. Executestreet=STREET_FLOP;
  6. ExecuteaudioPlay(AUDIO_SFX_CARD_DEAL);
Read original function source
static void dealFlop(void) { card_t burn; deal(&burn); for(uint8_t i=0;i<3U;++i) deal(&community_cards[i]); community_count=3U; street=STREET_FLOP; audioPlay(AUDIO_SFX_CARD_DEAL); }
dealTurn() · source line 100
static void dealTurn(void)

Calls into this documentation:

  1. Executecard_t burn;
  2. Executedeal(&burn);
  3. Executedeal(&community_cards[3]);
  4. Executecommunity_count=4U;
  5. Executestreet=STREET_TURN;
  6. ExecuteaudioPlay(AUDIO_SFX_CARD_DEAL);
Read original function source
static void dealTurn(void) { card_t burn; deal(&burn); deal(&community_cards[3]); community_count=4U; street=STREET_TURN; audioPlay(AUDIO_SFX_CARD_DEAL); }
dealRiver() · source line 101
static void dealRiver(void)

Calls into this documentation:

  1. Executecard_t burn;
  2. Executedeal(&burn);
  3. Executedeal(&community_cards[4]);
  4. Executecommunity_count=5U;
  5. Executestreet=STREET_RIVER;
  6. ExecuteaudioPlay(AUDIO_SFX_CARD_DEAL);
Read original function source
static void dealRiver(void) { card_t burn; deal(&burn); deal(&community_cards[4]); community_count=5U; street=STREET_RIVER; audioPlay(AUDIO_SFX_CARD_DEAL); }
startAutoRestart() · source line 102
static void startAutoRestart(uint32_t now, uint32_t delay)
  1. Decisionauto_mode
    True
      1. Executeauto_restart_started=now;
      2. Executeauto_restart_delay=delay;
      3. Executeauto_seconds=(uint8_t)((delay+999U)/1000U);
    False

    Continue after this decision.

Read original function source
static void startAutoRestart(uint32_t now, uint32_t delay) { if(auto_mode) { auto_restart_started=now; auto_restart_delay=delay; auto_seconds=(uint8_t)((delay+999U)/1000U); } }
awardPot() · source line 103
static bool awardPot(void)

Calls into this documentation:

  1. Decisionplayer_committed && !addGold(player_committed)
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Executeconst uint32_t profit=pot-player_committed;
  3. Exit this functionreturn !profit || (auto_mode?addAIGold(profit):addUserGoldWon(profit));
Read original function source
static bool awardPot(void) { if(player_committed && !addGold(player_committed)) return false; const uint32_t profit=pot-player_committed; return !profit || (auto_mode?addAIGold(profit):addUserGoldWon(profit)); }
recordLoss() · source line 104
static bool recordLoss(void)

Calls into this documentation:

  1. Exit this functionreturn auto_mode?recordAIGoldLost(player_committed):recordUserGoldLost(player_committed);
Read original function source
static bool recordLoss(void) { return auto_mode?recordAIGoldLost(player_committed):recordUserGoldLost(player_committed); }
finishFold() · source line 105
static void finishFold(bool player_won, uint32_t now)

Calls into this documentation:

  1. ExecuteaudioPlay(player_won ? AUDIO_SFX_WAVE_CLEAR : AUDIO_SFX_GAME_OVER);
  2. Executehand_active=false;
  3. Executestate=POKER_FINISHED;
  4. Executereveal_cpu=false;
  5. ExecutesetStatus(player_won?(awardPot()?"CPU FOLDS":"GOLD ERROR"):(recordLoss()?"YOU FOLD":"GOLD ERROR"));
  6. ExecutestartAutoRestart(now,auto_mode&&!player_won&&street==STREET_PREFLOP?AUTO_PREFLOP_FOLD_DELAY_MS:aiRestartDelay());
Read original function source
static void finishFold(bool player_won, uint32_t now) { audioPlay(player_won ? AUDIO_SFX_WAVE_CLEAR : AUDIO_SFX_GAME_OVER); hand_active=false; state=POKER_FINISHED; reveal_cpu=false; setStatus(player_won?(awardPot()?"CPU FOLDS":"GOLD ERROR"):(recordLoss()?"YOU FOLD":"GOLD ERROR")); startAutoRestart(now,auto_mode&&!player_won&&street==STREET_PREFLOP?AUTO_PREFLOP_FOLD_DELAY_MS:aiRestartDelay()); }
finishShowdown() · source line 106
static void finishShowdown(uint32_t now)

Calls into this documentation:

  1. Executecard_t player[7]={player_cards[0],player_cards[1],community_cards[0],community_cards[1],community_cards[2],community_cards[3],community_cards[4]};
  2. Executecard_t cpu[7]={cpu_cards[0],cpu_cards[1],community_cards[0],community_cards[1],community_cards[2],community_cards[3],community_cards[4]};
  3. Executeconst hand_rank_t a=evaluateBest(player,7), b=evaluateBest(cpu,7);
  4. Executesnprintf(player_hand_text,sizeof(player_hand_text),"%s",handName(a.value));
  5. Executesnprintf(cpu_hand_text,sizeof(cpu_hand_text),"%s",handName(b.value));
  6. Executereveal_cpu=true;
  7. Executehand_active=false;
  8. Executestate=POKER_FINISHED;
  9. Executeconst int8_t result=compareRanks(a,b);
  10. ExecuteaudioPlay(result>0 ? AUDIO_SFX_WAVE_CLEAR : result<0 ? AUDIO_SFX_GAME_OVER : AUDIO_SFX_MENU_SELECT);
  11. Decisionresult>0
    True
    1. ExecutesetStatus(awardPot()?"YOU WIN":"GOLD ERROR");
    False
    1. Decisionresult<0
      True
      1. ExecutesetStatus(recordLoss()?"CPU WINS":"GOLD ERROR");
      False
        1. ExecuteaddGold(player_committed);
        2. ExecutesetStatus("DRAW");
  12. ExecutestartAutoRestart(now,aiRestartDelay());
Read original function source
static void finishShowdown(uint32_t now) { card_t player[7]={player_cards[0],player_cards[1],community_cards[0],community_cards[1],community_cards[2],community_cards[3],community_cards[4]}; card_t cpu[7]={cpu_cards[0],cpu_cards[1],community_cards[0],community_cards[1],community_cards[2],community_cards[3],community_cards[4]}; const hand_rank_t a=evaluateBest(player,7), b=evaluateBest(cpu,7); snprintf(player_hand_text,sizeof(player_hand_text),"%s",handName(a.value)); snprintf(cpu_hand_text,sizeof(cpu_hand_text),"%s",handName(b.value)); reveal_cpu=true; hand_active=false; state=POKER_FINISHED; const int8_t result=compareRanks(a,b); audioPlay(result>0 ? AUDIO_SFX_WAVE_CLEAR : result<0 ? AUDIO_SFX_GAME_OVER : AUDIO_SFX_MENU_SELECT); if(result>0) setStatus(awardPot()?"YOU WIN":"GOLD ERROR"); else if(result<0) setStatus(recordLoss()?"CPU WINS":"GOLD ERROR"); else { addGold(player_committed); setStatus("DRAW"); } startAutoRestart(now,aiRestartDelay()); }
moveToPlayer() · source line 107
static void moveToPlayer(const char *message, uint32_t now)

Calls into this documentation:

  1. Executestate=POKER_PLAYER;
  2. Executeselected_action=ACTION_CALL_CHECK;
  3. ExecutesetStatus(message);
  4. Decisionauto_mode
    True
    1. Executestate_started=now;
    False

    Continue after this decision.

Read original function source
static void moveToPlayer(const char *message, uint32_t now) { state=POKER_PLAYER; selected_action=ACTION_CALL_CHECK; setStatus(message); if(auto_mode) state_started=now; }
moveToCpu() · source line 108
static void moveToCpu(uint32_t now)

Calls into this documentation:

  1. Executestate=POKER_CPU;
  2. ExecutesetStatus("CPU THINK");
  3. Executestate_started=now;
Read original function source
static void moveToCpu(uint32_t now) { state=POKER_CPU; setStatus("CPU THINK"); state_started=now; }
startPostFlop() · source line 109
static void startPostFlop(const char *message, uint32_t now)

Calls into this documentation:

  1. Executeplayer_bet=cpu_bet=0;
  2. Executelast_raise=BIG_BLIND;
  3. Executeplayer_acted=cpu_acted=false;
  4. Decisionplayer_dealer
    True
    1. ExecutemoveToCpu(now);
    False
    1. ExecutemoveToPlayer(message,now);
Read original function source
static void startPostFlop(const char *message, uint32_t now) { player_bet=cpu_bet=0; last_raise=BIG_BLIND; player_acted=cpu_acted=false; if(player_dealer) moveToCpu(now); else moveToPlayer(message,now); }
startAllIn() · source line 110
static void startAllIn(uint32_t now)

Calls into this documentation:

  1. Executestate=POKER_ALL_IN;
  2. ExecutesetStatus("ALL IN");
  3. Executestate_started=now;
Read original function source
static void startAllIn(uint32_t now) { state=POKER_ALL_IN; setStatus("ALL IN"); state_started=now; }
completeRound() · source line 111
static void completeRound(uint32_t now)

Calls into this documentation:

  1. Decision!getGold()||!cpu_stack
    True
      1. Decisioncommunity_count<5U
        True
        1. ExecutestartAllIn(now);
        False
        1. ExecutefinishShowdown(now);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decisionstreet==STREET_PREFLOP
    True
      1. ExecutedealFlop();
      2. ExecutestartPostFlop("FLOP",now);
    False
    1. Decisionstreet==STREET_FLOP
      True
        1. ExecutedealTurn();
        2. ExecutestartPostFlop("TURN",now);
      False
      1. Decisionstreet==STREET_TURN
        True
          1. ExecutedealRiver();
          2. ExecutestartPostFlop("RIVER",now);
        False
        1. ExecutefinishShowdown(now);
Read original function source
static void completeRound(uint32_t now) { if(!getGold()||!cpu_stack) { if(community_count<5U) startAllIn(now); else finishShowdown(now); return; } if(street==STREET_PREFLOP) { dealFlop(); startPostFlop("FLOP",now); } else if(street==STREET_FLOP) { dealTurn(); startPostFlop("TURN",now); } else if(street==STREET_TURN) { dealRiver(); startPostFlop("RIVER",now); } else finishShowdown(now); }
processAllIn() · source line 112
static void processAllIn(uint32_t now)

Calls into this documentation:

  1. Decisioncommunity_count==0U
    True
      1. ExecutedealFlop();
      2. ExecutesetStatus("ALL IN FLOP");
    False
    1. Decisioncommunity_count==3U
      True
        1. ExecutedealTurn();
        2. ExecutesetStatus("ALL IN TURN");
      False
      1. Decisioncommunity_count==4U
        True
          1. ExecutedealRiver();
          2. ExecutesetStatus("ALL IN RIVER");
        False
          1. ExecutefinishShowdown(now);
          2. Exit this functionreturn;
  2. Executestate_started=now;
Read original function source
static void processAllIn(uint32_t now) { if(community_count==0U) { dealFlop(); setStatus("ALL IN FLOP"); } else if(community_count==3U) { dealTurn(); setStatus("ALL IN TURN"); } else if(community_count==4U) { dealRiver(); setStatus("ALL IN RIVER"); } else { finishShowdown(now); return; } state_started=now; }
shouldFold() · source line 113
static bool shouldFold(uint32_t call, uint8_t hand_strength)
  1. Exit this functionreturn call && hand_strength<35U && esp_random()%100U>=15U;
Read original function source
static bool shouldFold(uint32_t call, uint8_t hand_strength) { return call && hand_strength<35U && esp_random()%100U>=15U; }
processCpu() · source line 114
static void processCpu(uint32_t now)

Calls into this documentation:

  1. Executeconst uint32_t call=player_bet>cpu_bet?player_bet-cpu_bet:0U;
  2. Executeconst uint8_t value=strength(cpu_cards);
  3. DecisionshouldFold(call,value)
    True
      1. ExecutefinishFold(true,now);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  4. Executeconst uint32_t target=currentBet()+last_raise, maximum=cpu_bet+cpu_stack;
  5. Executeconst bool can_raise=value>=GOLD_HAND_STRENGTH && target>currentBet() && maximum>=target && target-cpu_bet<=remainingRaisePotRoom();
  6. Decisioncan_raise
    True
      1. Executeconst uint32_t previous=currentBet();
      2. ExecutecommitCpu(target-cpu_bet);
      3. Executelast_raise=target-previous;
      4. Executecpu_acted=true;
      5. Executeplayer_acted=false;
      6. ExecutemoveToPlayer("CPU RAISE",now);
      7. Exit this functionreturn;
    False

    Continue after this decision.

  7. ExecutecommitCpu(call);
  8. Executecpu_acted=true;
  9. Decisionplayer_acted && player_bet==cpu_bet
    True
    1. ExecutecompleteRound(now);
    False
    1. ExecutemoveToPlayer(call?"CPU CALL":"CPU CHECK",now);
Read original function source
static void processCpu(uint32_t now) { const uint32_t call=player_bet>cpu_bet?player_bet-cpu_bet:0U; const uint8_t value=strength(cpu_cards); if(shouldFold(call,value)) { finishFold(true,now); return; } const uint32_t target=currentBet()+last_raise, maximum=cpu_bet+cpu_stack; const bool can_raise=value>=GOLD_HAND_STRENGTH && target>currentBet() && maximum>=target && target-cpu_bet<=remainingRaisePotRoom(); if(can_raise) { const uint32_t previous=currentBet(); commitCpu(target-cpu_bet); last_raise=target-previous; cpu_acted=true; player_acted=false; moveToPlayer("CPU RAISE",now); return; } commitCpu(call); cpu_acted=true; if(player_acted && player_bet==cpu_bet) completeRound(now); else moveToPlayer(call?"CPU CALL":"CPU CHECK",now); }
processAuto() · source line 115
static void processAuto(uint32_t now)

Calls into this documentation:

  1. Executeconst uint32_t call=cpu_bet>player_bet?cpu_bet-player_bet:0U;
  2. Executeconst uint8_t value=strength(player_cards);
  3. Decisioncall>getGold()||shouldFold(call,value)
    True
      1. ExecutefinishFold(false,now);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  4. Executeconst uint32_t target=minimumRaise();
  5. Decisionvalue>=GOLD_HAND_STRENGTH && target>currentBet()
    True
      1. Executeconst uint32_t previous=currentBet();
      2. Decision!commitPlayer(target-player_bet)
        True
          1. ExecutefinishFold(false,now);
          2. Exit this functionreturn;
        False

        Continue after this decision.

      3. Executelast_raise=target-previous;
      4. Executeplayer_acted=true;
      5. Executecpu_acted=false;
      6. ExecutemoveToCpu(now);
      7. Exit this functionreturn;
    False

    Continue after this decision.

  6. Decision!commitPlayer(call)
    True
      1. ExecutefinishFold(false,now);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  7. Executeplayer_acted=true;
  8. Decisioncpu_acted&&player_bet==cpu_bet
    True
    1. ExecutecompleteRound(now);
    False
    1. ExecutemoveToCpu(now);
Read original function source
static void processAuto(uint32_t now) { const uint32_t call=cpu_bet>player_bet?cpu_bet-player_bet:0U; const uint8_t value=strength(player_cards); if(call>getGold()||shouldFold(call,value)) { finishFold(false,now); return; } const uint32_t target=minimumRaise(); if(value>=GOLD_HAND_STRENGTH && target>currentBet()) { const uint32_t previous=currentBet(); if(!commitPlayer(target-player_bet)) { finishFold(false,now); return; } last_raise=target-previous; player_acted=true; cpu_acted=false; moveToCpu(now); return; } if(!commitPlayer(call)) { finishFold(false,now); return; } player_acted=true; if(cpu_acted&&player_bet==cpu_bet) completeRound(now); else moveToCpu(now); }
beginHand() · source line 116
static void beginHand(uint32_t now)

Calls into this documentation:

  1. Executepot=player_bet=cpu_bet=player_committed=community_count=0;
  2. Executestreet=STREET_PREFLOP;
  3. Executelast_raise=BIG_BLIND;
  4. Executeselected_action=ACTION_CALL_CHECK;
  5. Executeplayer_acted=cpu_acted=reveal_cpu=false;
  6. Executehand_active=false;
  7. Executeconst uint32_t starting=getGold();
  8. Decision!starting
    True
      1. Executestate=POKER_NO_GOLD;
      2. ExecutesetStatus("NEED GOLD");
      3. Exit this functionreturn;
    False

    Continue after this decision.

  9. Executecpu_stack=starting;
  10. ExecuteshuffleDeck();
  11. ExecutedealHoleCards();
  12. Decisionplayer_dealer
    True
      1. ExecutecommitPlayer(starting<SMALL_BLIND?starting:SMALL_BLIND);
      2. ExecutecommitCpu(cpu_stack<BIG_BLIND?cpu_stack:BIG_BLIND);
      3. Executestate=POKER_PLAYER;
      4. ExecutesetStatus("PREFLOP");
      5. Decisionauto_mode
        True
        1. Executestate_started=now;
        False

        Continue after this decision.

    False
      1. ExecutecommitCpu(cpu_stack<SMALL_BLIND?cpu_stack:SMALL_BLIND);
      2. ExecutecommitPlayer(starting<BIG_BLIND?starting:BIG_BLIND);
      3. ExecutemoveToCpu(now);
  13. Executehand_active=true;
  14. Executeredraw_needed=true;
Read original function source
static void beginHand(uint32_t now) { pot=player_bet=cpu_bet=player_committed=community_count=0; street=STREET_PREFLOP; last_raise=BIG_BLIND; selected_action=ACTION_CALL_CHECK; player_acted=cpu_acted=reveal_cpu=false; hand_active=false; const uint32_t starting=getGold(); if(!starting) { state=POKER_NO_GOLD; setStatus("NEED GOLD"); return; } cpu_stack=starting; shuffleDeck(); dealHoleCards(); if(player_dealer) { commitPlayer(starting<SMALL_BLIND?starting:SMALL_BLIND); commitCpu(cpu_stack<BIG_BLIND?cpu_stack:BIG_BLIND); state=POKER_PLAYER; setStatus("PREFLOP"); if(auto_mode) state_started=now; } else { commitCpu(cpu_stack<SMALL_BLIND?cpu_stack:SMALL_BLIND); commitPlayer(starting<BIG_BLIND?starting:BIG_BLIND); moveToCpu(now); } hand_active=true; redraw_needed=true; }
startPoker() · source line 117
void startPoker(uint32_t now_ms)

Calls into this documentation:

  1. Executerunning=true;
  2. Executeauto_mode=false;
  3. Executeai_speed=AI_SPEED_DEFAULT_INDEX;
  4. ExecutemodeSelectionReset(&mode_input);
  5. Executeplayer_dealer=true;
  6. Executestate=POKER_MODE;
  7. ExecutesetStatus("SELECT MODE");
  8. Executestate_started=now_ms;
  9. Executeredraw_needed=true;
Read original function source
void startPoker(uint32_t now_ms) { running=true; auto_mode=false; ai_speed=AI_SPEED_DEFAULT_INDEX; modeSelectionReset(&mode_input); player_dealer=true; state=POKER_MODE; setStatus("SELECT MODE"); state_started=now_ms; redraw_needed=true; }
stopPoker() · source line 118
void stopPoker(void)

Calls into this documentation:

  1. Decisionhand_active
    True
      1. ExecuterecordLoss();
      2. Executehand_active=false;
      3. Executepot=0;
      4. Executeplayer_dealer=!player_dealer;
    False
    1. Decisionstate==POKER_FINISHED
      True
      1. Executeplayer_dealer=!player_dealer;
      False

      Continue after this decision.

  2. Executerunning=false;
Read original function source
void stopPoker(void) { if(hand_active) { recordLoss(); hand_active=false; pot=0; player_dealer=!player_dealer; } else if(state==POKER_FINISHED) player_dealer=!player_dealer; running=false; }
pokerAction() · source line 119
void pokerAction(bool action, bool cycle, bool speed, uint32_t now)

Calls into this documentation:

  1. Decision!running
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decisionstate==POKER_MODE
    True
      1. Decisionaction
        True
        1. ExecutebeginHand(now);
        False

        Continue after this decision.

      2. Exit this functionreturn;
    False

    Continue after this decision.

  3. Decisionauto_mode
    True
      1. Decisionspeed
        True
          1. ExecutecycleAISpeed(&ai_speed);
          2. Executestate_started=now;
          3. Executeredraw_needed=true;
        False

        Continue after this decision.

      2. Decisionaction
        True
          1. Decisionhand_active
            True
              1. ExecuterecordLoss();
              2. Executehand_active=false;
              3. Executepot=0;
            False

            Continue after this decision.

          2. Executeauto_mode=false;
          3. ExecutemodeSelectionReset(&mode_input);
          4. Executestate=POKER_MODE;
          5. Executeredraw_needed=true;
        False

        Continue after this decision.

      3. Exit this functionreturn;
    False

    Continue after this decision.

  4. Decisionstate==POKER_FINISHED && action
    True
      1. Executeplayer_dealer=!player_dealer;
      2. ExecutebeginHand(now);
      3. Exit this functionreturn;
    False

    Continue after this decision.

  5. Decisionstate==POKER_RAISE
    True
      1. Decisioncycle
        True
          1. Executeconst uint32_t maximum=player_bet+getGold(), minimum=minimumRaise();
          2. Executeselected_raise=selected_raise<maximum?selected_raise+1U:minimum;
          3. Executeredraw_needed=true;
        False

        Continue after this decision.

      2. Decisionaction
        True
          1. Executeconst uint32_t previous=currentBet(), amount=selected_raise-player_bet;
          2. Decisionamount<=remainingRaisePotRoom()&&commitPlayer(amount)
            True
              1. Executelast_raise=selected_raise-previous;
              2. Executeplayer_acted=true;
              3. Executecpu_acted=false;
              4. ExecutemoveToCpu(now);
            False

            Continue after this decision.

        False

        Continue after this decision.

      3. Exit this functionreturn;
    False

    Continue after this decision.

  6. Decisionstate!=POKER_PLAYER
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  7. Decisioncycle
    True
      1. Executeselected_action=(poker_action_t)((selected_action+1U)%ACTION_COUNT);
      2. Executeredraw_needed=true;
    False

    Continue after this decision.

  8. Decision!action
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  9. Decisionselected_action==ACTION_FOLD
    True
      1. ExecutefinishFold(false,now);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  10. Decisionselected_action==ACTION_RAISE
    True
      1. Executeselected_raise=minimumRaise();
      2. Decisionselected_raise
        True
          1. Executestate=POKER_RAISE;
          2. ExecutesetStatus("RAISE TO");
        False
        1. ExecutesetStatus("NO RAISE");
      3. Exit this functionreturn;
    False

    Continue after this decision.

  11. Executeconst uint32_t call=cpu_bet>player_bet?cpu_bet-player_bet:0U;
  12. Decision!commitPlayer(call)
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  13. Executeplayer_acted=true;
  14. Decisioncpu_acted&&player_bet==cpu_bet
    True
    1. ExecutecompleteRound(now);
    False
    1. ExecutemoveToCpu(now);
Read original function source
void pokerAction(bool action, bool cycle, bool speed, uint32_t now) {
    if(!running) return;
    if(state==POKER_MODE) { if(action) beginHand(now); return; }
    if(auto_mode) { if(speed) { cycleAISpeed(&ai_speed); state_started=now; redraw_needed=true; } if(action) { if(hand_active) { recordLoss(); hand_active=false; pot=0; } auto_mode=false; modeSelectionReset(&mode_input); state=POKER_MODE; redraw_needed=true; } return; }
    if(state==POKER_FINISHED && action) { player_dealer=!player_dealer; beginHand(now); return; }
    if(state==POKER_RAISE) { if(cycle) { const uint32_t maximum=player_bet+getGold(), minimum=minimumRaise(); selected_raise=selected_raise<maximum?selected_raise+1U:minimum; redraw_needed=true; } if(action) { const uint32_t previous=currentBet(), amount=selected_raise-player_bet; if(amount<=remainingRaisePotRoom()&&commitPlayer(amount)) { last_raise=selected_raise-previous; player_acted=true; cpu_acted=false; moveToCpu(now); } } return; }
    if(state!=POKER_PLAYER) return;
    if(cycle) { selected_action=(poker_action_t)((selected_action+1U)%ACTION_COUNT); redraw_needed=true; }
    if(!action) return;
    if(selected_action==ACTION_FOLD) { finishFold(false,now); return; }
    if(selected_action==ACTION_RAISE) { selected_raise=minimumRaise(); if(selected_raise) { state=POKER_RAISE; setStatus("RAISE TO"); } else setStatus("NO RAISE"); return; }
    const uint32_t call=cpu_bet>player_bet?cpu_bet-player_bet:0U; if(!commitPlayer(call)) return; player_acted=true; if(cpu_acted&&player_bet==cpu_bet) completeRound(now); else moveToCpu(now);
}
updatePoker() · source line 132
void updatePoker(uint16_t *frame, int x_offset, int y_offset, bool joystick_valid, bool left, bool right, uint32_t now)

Calls into this documentation:

  1. Execute(void)frame;
  2. Decision!running
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  3. Decisionstate==POKER_MODE
    True
      1. DecisionmodeSelectionNext(&mode_input,x_offset,y_offset,joystick_valid,left,right,now)
        True
          1. Executeauto_mode=!auto_mode;
          2. Executeredraw_needed=true;
        False

        Continue after this decision.

      2. Exit this functionreturn;
    False

    Continue after this decision.

  4. Decisionstate==POKER_FINISHED&&auto_mode
    True
      1. Executeconst uint32_t elapsed=now-auto_restart_started;
      2. Decisionelapsed>=auto_restart_delay
        True
          1. Executeplayer_dealer=!player_dealer;
          2. ExecutebeginHand(now);
          3. Exit this functionreturn;
        False

        Continue after this decision.

      3. Executeconst uint8_t seconds=(uint8_t)((auto_restart_delay-elapsed+999U)/1000U);
      4. Decisionseconds!=auto_seconds
        True
          1. Executeauto_seconds=seconds;
          2. Executeredraw_needed=true;
        False

        Continue after this decision.

      5. Exit this functionreturn;
    False

    Continue after this decision.

  5. Decisionstate==POKER_PLAYER&&auto_mode&&now-state_started>=aiSpeedDelay(ai_speed)
    True
      1. ExecuteprocessAuto(now);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  6. Decisionstate==POKER_CPU&&now-state_started>=(auto_mode?aiSpeedDelay(ai_speed):CPU_ACTION_DELAY_MS)
    True
      1. ExecuteprocessCpu(now);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  7. Decisionstate==POKER_ALL_IN&&now-state_started>=(auto_mode?aiSpeedDelay(ai_speed):ALL_IN_REVEAL_MS)
    True
    1. ExecuteprocessAllIn(now);
    False

    Continue after this decision.

Read original function source
void updatePoker(uint16_t *frame, int x_offset, int y_offset, bool joystick_valid, bool left, bool right, uint32_t now) { (void)frame; if(!running) return; if(state==POKER_MODE) { if(modeSelectionNext(&mode_input,x_offset,y_offset,joystick_valid,left,right,now)) { auto_mode=!auto_mode; redraw_needed=true; } return; } if(state==POKER_FINISHED&&auto_mode) { const uint32_t elapsed=now-auto_restart_started; if(elapsed>=auto_restart_delay) { player_dealer=!player_dealer; beginHand(now); return; } const uint8_t seconds=(uint8_t)((auto_restart_delay-elapsed+999U)/1000U); if(seconds!=auto_seconds) { auto_seconds=seconds; redraw_needed=true; } return; } if(state==POKER_PLAYER&&auto_mode&&now-state_started>=aiSpeedDelay(ai_speed)) { processAuto(now); return; } if(state==POKER_CPU&&now-state_started>=(auto_mode?aiSpeedDelay(ai_speed):CPU_ACTION_DELAY_MS)) { processCpu(now); return; } if(state==POKER_ALL_IN&&now-state_started>=(auto_mode?aiSpeedDelay(ai_speed):ALL_IN_REVEAL_MS)) processAllIn(now); }
cardPath() · source line 133
static void cardPath(const card_t *card, char *path, size_t size)
  1. Executeconst char *rank=card->rank==11U?"J":card->rank==12U?"Q":card->rank==13U?"K":card->rank==14U?"A":NULL;
  2. Decisionrank
    True
    1. Executesnprintf(path,size,"/spiffs/cards/%s%c.jpg",rank,card->suit);
    False
    1. Executesnprintf(path,size,"/spiffs/cards/%u%c.jpg",(unsigned)card->rank,card->suit);
Read original function source
static void cardPath(const card_t *card, char *path, size_t size) { const char *rank=card->rank==11U?"J":card->rank==12U?"Q":card->rank==13U?"K":card->rank==14U?"A":NULL; if(rank) snprintf(path,size,"/spiffs/cards/%s%c.jpg",rank,card->suit); else snprintf(path,size,"/spiffs/cards/%u%c.jpg",(unsigned)card->rank,card->suit); }
drawCard() · source line 134
static void drawCard(uint16_t *frame,const card_t *card,int x,int y,unsigned scale)

Calls into this documentation:

  1. Executechar path[32];
  2. ExecutecardPath(card,path,sizeof(path));
  3. Executeconst bool decoded=scale?imageDecodeJpegScaledAt(path,frame,(unsigned)x,(unsigned)y,scale):imageDecodeJpegAt(path,frame,(unsigned)x,(unsigned)y);
  4. Decision!decoded
    True
      1. Executeconst int width=48>>scale,height=64>>scale;
      2. Executefill_rect(frame,x,y,width,height,COLOR_WHITE);
      3. Executedraw_rect(frame,x,y,width,height,COLOR_RED);
      4. Executedraw_text_centred(frame,x+width/2,y+height/2-4,path+14,1,COLOR_BLACK);
    False

    Continue after this decision.

Read original function source
static void drawCard(uint16_t *frame,const card_t *card,int x,int y,unsigned scale) { char path[32]; cardPath(card,path,sizeof(path)); const bool decoded=scale?imageDecodeJpegScaledAt(path,frame,(unsigned)x,(unsigned)y,scale):imageDecodeJpegAt(path,frame,(unsigned)x,(unsigned)y); if(!decoded) { const int width=48>>scale,height=64>>scale; fill_rect(frame,x,y,width,height,COLOR_WHITE); draw_rect(frame,x,y,width,height,COLOR_RED); draw_text_centred(frame,x+width/2,y+height/2-4,path+14,1,COLOR_BLACK); } }
drawBack() · source line 135
static void drawBack(uint16_t *frame,int x,int y)

Calls into this documentation:

  1. Decision!imageDecodeJpegAt("/spiffs/cards/Back.jpg",frame,(unsigned)x,(unsigned)y)
    True
      1. Executefill_rect(frame,x,y,48,64,COLOR_BLUE);
      2. Executedraw_rect(frame,x,y,48,64,COLOR_WHITE);
    False

    Continue after this decision.

Read original function source
static void drawBack(uint16_t *frame,int x,int y) { if(!imageDecodeJpegAt("/spiffs/cards/Back.jpg",frame,(unsigned)x,(unsigned)y)) { fill_rect(frame,x,y,48,64,COLOR_BLUE); draw_rect(frame,x,y,48,64,COLOR_WHITE); } }
drawPoker() · source line 136
void drawPoker(uint16_t *frame)

Calls into this documentation:

  1. Decision!running || !redraw_needed
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Executeredraw_needed = false;
  3. Executeclear(frame, COLOR_BLACK);
  4. Executechar text[42];
  5. Decisionstate == POKER_MODE
    True
      1. Executedraw_text_centred(frame, 160, 12, "POKER", 4, COLOR_CYAN);
      2. Executedraw_text_centred(frame, 160, 42, "SELECT MODE", 1, COLOR_LIGHTGRAY);
      3. Executedraw_text_centred(frame, 100, 60, "PLAY", 2, auto_mode ? COLOR_LIGHTGRAY : COLOR_YELLOW);
      4. Executedraw_text_centred(frame, 220, 60, "AUTO", 2, auto_mode ? COLOR_YELLOW : COLOR_LIGHTGRAY);
      5. Executedraw_text_centred(frame, 160, 105, "D/B/STICK CHANGE A/K START", 1, COLOR_LIGHTGRAY);
      6. Executedraw_text_centred(frame, 160, 130, "BOOT EXIT", 1, COLOR_LIGHTGRAY);
      7. Exit this functionreturn;
    False

    Continue after this decision.

  6. Decisionstate == POKER_NO_GOLD
    True
      1. Executedraw_text_centred(frame, 160, 18, "POKER", 4, COLOR_CYAN);
      2. Executedraw_text_centred(frame, 160, 72, "NO GOLD", 3, COLOR_RED);
      3. Exit this functionreturn;
    False

    Continue after this decision.

  7. Decisionreveal_cpu
    True
      1. ExecutedrawCard(frame, &cpu_cards[0], 112, 2, 0);
      2. ExecutedrawCard(frame, &cpu_cards[1], 164, 2, 0);
    False
      1. ExecutedrawBack(frame, 112, 2);
      2. ExecutedrawBack(frame, 164, 2);
  8. FOR loopuint8_t i = 0; i < 5U; ++i

    Repeat body while the loop condition permits; then continue below.

      1. Executeconst int x = 94 + (int)i * 27;
      2. Decisioni < community_count
        True
        1. ExecutedrawCard(frame, &community_cards[i], x, 69, 1);
        False
          1. Executefill_rect(frame, x, 69, 24, 32, COLOR_BLACK);
          2. Executedraw_rect(frame, x, 69, 24, 32, COLOR_DARKGREY);
  9. ExecutedrawCard(frame, &player_cards[0], 112, 104, 0);
  10. ExecutedrawCard(frame, &player_cards[1], 164, 104, 0);
  11. Executedraw_text(frame, 3, 4, "CPU", 1, COLOR_CYAN);
  12. Decisionreveal_cpu
    True
    1. Executedraw_text(frame, 3, 18, cpu_hand_text, 1, COLOR_WHITE);
    False
      1. Executesnprintf(text, sizeof(text), "B %u", (unsigned)cpu_bet);
      2. Executedraw_text(frame, 3, 18, text, 1, COLOR_WHITE);
  13. Executedraw_text(frame, 3, 106, "YOU", 1, COLOR_GREEN);
  14. Decisionreveal_cpu
    True
    1. Executedraw_text(frame, 3, 120, player_hand_text, 1, COLOR_WHITE);
    False
      1. Executesnprintf(text, sizeof(text), "B %u", (unsigned)player_bet);
      2. Executedraw_text(frame, 3, 120, text, 1, COLOR_WHITE);
  15. Executedraw_text(frame, 3, 48, status_text, 1, COLOR_CYAN);
  16. Executeconst char *choice = state == POKER_RAISE ? "TO" : state == POKER_CPU ? "WAIT" : state == POKER_ALL_IN ? "ALL IN" : state == POKER_FINISHED ? (auto_mode ? "NEXT" : "DEAL") : (selected_action == ACTION_RAISE ? "RAISE" : selected_action == ACTION_FOLD ? "FOLD" : player_bet < currentBet() ? "CALL" : "CHECK");
  17. Decisionstate == POKER_RAISE
    True
    1. Executesnprintf(text, sizeof(text), "TO %u", (unsigned)selected_raise);
    False
    1. Decisionstate == POKER_FINISHED && auto_mode
      True
      1. Executesnprintf(text, sizeof(text), "NEXT %u", (unsigned)auto_seconds);
      False
      1. Executesnprintf(text, sizeof(text), "%s", choice);
  18. Executedraw_text(frame, 3, 65, text, 1, COLOR_YELLOW);
  19. ExecutedrawGameGold(frame, 260, 4, 56, true);
  20. Executesnprintf(text, sizeof(text), "POT %u", (unsigned)pot);
  21. Executedraw_text(frame, 260, 46, text, 1, COLOR_ORANGE);
  22. Executedraw_text(frame, 260, 106, player_dealer ? "D YOU" : "D CPU", 1, COLOR_CYAN);
  23. Executedraw_text(frame, 260, 120, "BL 1/2", 1, COLOR_DARKGREY);
  24. Decisionstate == POKER_PLAYER
    True
      1. Decisionauto_mode
        True
          1. Executesnprintf(text, sizeof(text), "AUTO E SPEED %s A STOP", aiSpeedShortText(ai_speed));
          2. Executedraw_text_centred(frame, 160, 158, text, 1, COLOR_LIGHTGRAY);
        False
          1. Executedraw_text_centred(frame, 160, 158, "C CHOOSE A/K SELECT", 1, COLOR_LIGHTGRAY);
    False
    1. Decisionstate == POKER_FINISHED
      True
      1. Executedraw_text_centred(frame, 160, 158, auto_mode ? "E SPEED A STOP" : "A/K DEAL", 1, COLOR_LIGHTGRAY);
      False

      Continue after this decision.

Read original function source
void drawPoker(uint16_t *frame) {
  if (!running || !redraw_needed)
    return;
  redraw_needed = false;
  clear(frame, COLOR_BLACK);
  char text[42];
  if (state == POKER_MODE) {
    draw_text_centred(frame, 160, 12, "POKER", 4, COLOR_CYAN);
    draw_text_centred(frame, 160, 42, "SELECT MODE", 1, COLOR_LIGHTGRAY);
    draw_text_centred(frame, 100, 60, "PLAY", 2, auto_mode ? COLOR_LIGHTGRAY : COLOR_YELLOW);
    draw_text_centred(frame, 220, 60, "AUTO", 2, auto_mode ? COLOR_YELLOW : COLOR_LIGHTGRAY);
    draw_text_centred(frame, 160, 105, "D/B/STICK CHANGE  A/K START", 1,
                      COLOR_LIGHTGRAY);
    draw_text_centred(frame, 160, 130, "BOOT EXIT", 1, COLOR_LIGHTGRAY);
    return;
  }
  if (state == POKER_NO_GOLD) {
    draw_text_centred(frame, 160, 18, "POKER", 4, COLOR_CYAN);
    draw_text_centred(frame, 160, 72, "NO GOLD", 3, COLOR_RED);
    return;
  }
  if (reveal_cpu) {
    drawCard(frame, &cpu_cards[0], 112, 2, 0);
    drawCard(frame, &cpu_cards[1], 164, 2, 0);
  } else {
    drawBack(frame, 112, 2);
    drawBack(frame, 164, 2);
  }
  for (uint8_t i = 0; i < 5U; ++i) {
    const int x = 94 + (int)i * 27;
    if (i < community_count)
      drawCard(frame, &community_cards[i], x, 69, 1);
    else {
      fill_rect(frame, x, 69, 24, 32, COLOR_BLACK);
      draw_rect(frame, x, 69, 24, 32, COLOR_DARKGREY);
    }
  }
  drawCard(frame, &player_cards[0], 112, 104, 0);
  drawCard(frame, &player_cards[1], 164, 104, 0);
  draw_text(frame, 3, 4, "CPU", 1, COLOR_CYAN);
  if (reveal_cpu)
    draw_text(frame, 3, 18, cpu_hand_text, 1, COLOR_WHITE);
  else {
    snprintf(text, sizeof(text), "B %u", (unsigned)cpu_bet);
    draw_text(frame, 3, 18, text, 1, COLOR_WHITE);
  }
  draw_text(frame, 3, 106, "YOU", 1, COLOR_GREEN);
  if (reveal_cpu)
    draw_text(frame, 3, 120, player_hand_text, 1, COLOR_WHITE);
  else {
    snprintf(text, sizeof(text), "B %u", (unsigned)player_bet);
    draw_text(frame, 3, 120, text, 1, COLOR_WHITE);
  }
  draw_text(frame, 3, 48, status_text, 1, COLOR_CYAN);
  const char *choice = state == POKER_RAISE    ? "TO"
                       : state == POKER_CPU    ? "WAIT"
                       : state == POKER_ALL_IN ? "ALL IN"
                       : state == POKER_FINISHED
                           ? (auto_mode ? "NEXT" : "DEAL")
                           : (selected_action == ACTION_RAISE  ? "RAISE"
                              : selected_action == ACTION_FOLD ? "FOLD"
                              : player_bet < currentBet()      ? "CALL"
                                                               : "CHECK");
  if (state == POKER_RAISE)
    snprintf(text, sizeof(text), "TO %u", (unsigned)selected_raise);
  else if (state == POKER_FINISHED && auto_mode)
    snprintf(text, sizeof(text), "NEXT %u", (unsigned)auto_seconds);
  else
    snprintf(text, sizeof(text), "%s", choice);
  draw_text(frame, 3, 65, text, 1, COLOR_YELLOW);
  drawGameGold(frame, 260, 4, 56, true);
  snprintf(text, sizeof(text), "POT %u", (unsigned)pot);
  draw_text(frame, 260, 46, text, 1, COLOR_ORANGE);
  draw_text(frame, 260, 106, player_dealer ? "D YOU" : "D CPU", 1, COLOR_CYAN);
  draw_text(frame, 260, 120, "BL 1/2", 1, COLOR_DARKGREY);
  if (state == POKER_PLAYER) {
    if (auto_mode) {
      snprintf(text, sizeof(text), "AUTO  E SPEED %s  A STOP",
               aiSpeedShortText(ai_speed));
      draw_text_centred(frame, 160, 158, text, 1, COLOR_LIGHTGRAY);
    } else {
      draw_text_centred(frame, 160, 158, "C CHOOSE  A/K SELECT", 1,
                        COLOR_LIGHTGRAY);
    }
  } else if (state == POKER_FINISHED)
    draw_text_centred(frame, 160, 158,
                      auto_mode ? "E SPEED  A STOP" : "A/K DEAL", 1,
                      COLOR_LIGHTGRAY);
}
Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
// Heads-up Texas Hold'em.  This module is intentionally unregistered until
// its menu and main-loop lifecycle routing are added in the next step.
#include "poker.h"

#include "ai_speed.h"
#include "audio.h"
#include "esp_random.h"
#include "game_graphics.h"
#include "PET2001_font.h"
#include "gold.h"
#include "image_decoder.h"
#include "mode_selection.h"
#include <stdio.h>
#include <string.h>

#define DECK_SIZE 52U
#define SMALL_BLIND 1U
#define BIG_BLIND 2U
#define CPU_ACTION_DELAY_MS 700U
#define ALL_IN_REVEAL_MS 700U
#define AUTO_PREFLOP_FOLD_DELAY_MS 1000U
#define GOLD_HAND_STRENGTH 75U
#define BLUFF_GOLD_PERCENT 25U
#define POKER_RAISE_POT_LIMIT 1000U

typedef enum { POKER_MODE, POKER_NO_GOLD, POKER_PLAYER, POKER_RAISE, POKER_CPU, POKER_ALL_IN, POKER_FINISHED } poker_state_t;
typedef enum { STREET_PREFLOP, STREET_FLOP, STREET_TURN, STREET_RIVER } poker_street_t;
typedef enum { ACTION_CALL_CHECK, ACTION_RAISE, ACTION_FOLD, ACTION_COUNT } poker_action_t;
typedef enum { HAND_HIGH, HAND_PAIR, HAND_TWO_PAIR, HAND_TRIPS, HAND_STRAIGHT, HAND_FLUSH, HAND_FULL_HOUSE, HAND_QUADS, HAND_STRAIGHT_FLUSH } hand_value_t;
typedef struct { uint8_t rank; char suit; } card_t;
typedef struct { hand_value_t value; uint8_t tie[5]; } hand_rank_t;

static card_t deck[DECK_SIZE], player_cards[2], cpu_cards[2], community_cards[5];
static uint8_t deck_position, community_count, ai_speed, auto_seconds;
static uint32_t pot, player_bet, cpu_bet, player_committed, cpu_stack, last_raise, selected_raise;
static uint32_t state_started, auto_restart_started, auto_restart_delay;
static poker_state_t state;
static poker_street_t street;
static poker_action_t selected_action;
static bool player_acted, cpu_acted, reveal_cpu, hand_active, player_dealer, auto_mode, running, redraw_needed;
static mode_selection_input_t mode_input;
static char status_text[20], player_hand_text[20], cpu_hand_text[20];

static uint32_t currentBet(void) { return player_bet > cpu_bet ? player_bet : cpu_bet; }
static int8_t compareRanks(hand_rank_t a, hand_rank_t b) {
    if (a.value != b.value) return a.value > b.value ? 1 : -1;
    for (uint8_t i = 0; i < 5U; ++i) if (a.tie[i] != b.tie[i]) return a.tie[i] > b.tie[i] ? 1 : -1;
    return 0;
}
static uint8_t straightHigh(const uint8_t count[15]) {
    for (int high = 14; high >= 6; --high) if (count[high] && count[high-1] && count[high-2] && count[high-3] && count[high-4]) return (uint8_t)high;
    return count[14] && count[2] && count[3] && count[4] && count[5] ? 5U : 0U;
}
static hand_rank_t evaluateFive(const card_t cards[5]) {
    hand_rank_t result = { .value = HAND_HIGH, .tie = {0} }; uint8_t count[15] = {0}; bool flush = true;
    for (uint8_t i = 0; i < 5U; ++i) { ++count[cards[i].rank]; if (i && cards[i].suit != cards[0].suit) flush = false; }
    const uint8_t straight = straightHigh(count);
    if (flush && straight) { result.value = HAND_STRAIGHT_FLUSH; result.tie[0] = straight; return result; }
    for (int rank = 14; rank >= 2; --rank) if (count[rank] == 4U) { result.value = HAND_QUADS; result.tie[0] = (uint8_t)rank; for (int k=14;k>=2;--k) if (count[k]==1U) { result.tie[1]=(uint8_t)k; break; } return result; }
    uint8_t three = 0, pair = 0;
    for (int rank=14; rank>=2; --rank) { if (!three && count[rank] == 3U) three=(uint8_t)rank; else if (!pair && count[rank] == 2U) pair=(uint8_t)rank; }
    if (three && pair) { result.value=HAND_FULL_HOUSE; result.tie[0]=three; result.tie[1]=pair; return result; }
    if (flush) { result.value=HAND_FLUSH; uint8_t n=0; for(int rank=14;rank>=2;--rank) for(uint8_t j=0;j<count[rank];++j) result.tie[n++]=(uint8_t)rank; return result; }
    if (straight) { result.value=HAND_STRAIGHT; result.tie[0]=straight; return result; }
    if (three) { result.value=HAND_TRIPS; result.tie[0]=three; uint8_t n=1; for(int rank=14;rank>=2;--rank) if(count[rank]==1U) result.tie[n++]=(uint8_t)rank; return result; }
    uint8_t high_pair=0, low_pair=0;
    for(int rank=14;rank>=2;--rank) if(count[rank]==2U) { if(!high_pair) high_pair=(uint8_t)rank; else low_pair=(uint8_t)rank; }
    if(high_pair && low_pair) { result.value=HAND_TWO_PAIR; result.tie[0]=high_pair; result.tie[1]=low_pair; for(int rank=14;rank>=2;--rank) if(count[rank]==1U) { result.tie[2]=(uint8_t)rank; break; } return result; }
    if(high_pair) { result.value=HAND_PAIR; result.tie[0]=high_pair; uint8_t n=1; for(int rank=14;rank>=2;--rank) if(count[rank]==1U) result.tie[n++]=(uint8_t)rank; return result; }
    uint8_t n=0; for(int rank=14;rank>=2;--rank) if(count[rank]) result.tie[n++]=(uint8_t)rank; return result;
}
static hand_rank_t evaluateBest(const card_t *cards, uint8_t count) {
    hand_rank_t best = {0}; bool first = true;
    for(uint8_t a=0;a<count-4U;++a) for(uint8_t b=a+1;b<count-3U;++b) for(uint8_t c=b+1;c<count-2U;++c) for(uint8_t d=c+1;d<count-1U;++d) for(uint8_t e=d+1;e<count;++e) {
        const card_t five[5]={cards[a],cards[b],cards[c],cards[d],cards[e]}; hand_rank_t candidate=evaluateFive(five);
        if(first || compareRanks(candidate,best)>0) { best=candidate; first=false; }
    }
    return best;
}
static const char *handName(hand_value_t value) {
    static const char *names[] = { "HIGH CARD", "ONE PAIR", "TWO PAIR", "THREE KIND", "STRAIGHT", "FLUSH", "FULL HOUSE", "FOUR KIND", "STRAIGHT FLUSH" };
    return names[value];
}
static void shuffleDeck(void) {
    static const char suits[] = { 'C', 'D', 'H', 'S' }; uint8_t n=0;
    for(uint8_t s=0;s<4U;++s) for(uint8_t rank=2U;rank<=14U;++rank) deck[n++]=(card_t){rank,suits[s]};
    for(int i=DECK_SIZE-1;i>0;--i) { const uint8_t j=esp_random()%(uint32_t)(i+1); const card_t card=deck[i]; deck[i]=deck[j]; deck[j]=card; }
    deck_position=0;
}
static bool deal(card_t *card) { if(deck_position>=DECK_SIZE) return false; *card=deck[deck_position++]; return true; }
static void dealHoleCards(void) { if(player_dealer) { deal(&player_cards[0]); deal(&cpu_cards[0]); deal(&player_cards[1]); deal(&cpu_cards[1]); } else { deal(&cpu_cards[0]); deal(&player_cards[0]); deal(&cpu_cards[1]); deal(&player_cards[1]); } audioPlay(AUDIO_SFX_CARD_DEAL); }
static uint32_t remainingRaisePotRoom(void) { return pot < POKER_RAISE_POT_LIMIT ? POKER_RAISE_POT_LIMIT-pot : 0U; }
static bool commitPlayer(uint32_t amount) { if(amount > getGold() || !spendGold(amount)) return false; player_bet+=amount; player_committed+=amount; pot+=amount; return true; }
static void commitCpu(uint32_t amount) { if(amount>cpu_stack) amount=cpu_stack; cpu_stack-=amount; cpu_bet+=amount; pot+=amount; }
static uint32_t minimumRaise(void) { const uint32_t maximum=player_bet+getGold(), full=currentBet()+last_raise; if(maximum<=currentBet()) return 0U; const uint32_t target=maximum<full?maximum:full; return target-player_bet>remainingRaisePotRoom()?0U:target; }
static uint8_t holeStrength(const card_t cards[2]) { const uint8_t high=cards[0].rank>cards[1].rank?cards[0].rank:cards[1].rank, low=cards[0].rank<cards[1].rank?cards[0].rank:cards[1].rank; if(high==low) return (uint8_t)(55U+high*3U); uint8_t strength=(uint8_t)(high*3U+low); if(cards[0].suit==cards[1].suit) strength+=8U; const uint8_t gap=high-low; if(gap==1U) strength+=6U; else if(gap==2U) strength+=3U; if(high==14U) strength+=8U; return strength>100U?100U:strength; }
static uint8_t postFlopStrength(const card_t cards[2]) { card_t seven[7]={cards[0],cards[1]}; for(uint8_t i=0;i<community_count;++i) seven[i+2U]=community_cards[i]; const hand_rank_t rank=evaluateBest(seven,(uint8_t)(community_count+2U)); static const uint8_t values[]={15,40,62,72,80,84,90,96,100}; return (uint8_t)(values[rank.value]+(rank.value==HAND_PAIR||rank.value==HAND_HIGH?rank.tie[0]:0U)); }
static uint8_t strength(const card_t cards[2]) { return street==STREET_PREFLOP?holeStrength(cards):postFlopStrength(cards); }
static void setStatus(const char *text) { snprintf(status_text,sizeof(status_text),"%s",text); redraw_needed=true; }
static void dealFlop(void) { card_t burn; deal(&burn); for(uint8_t i=0;i<3U;++i) deal(&community_cards[i]); community_count=3U; street=STREET_FLOP; audioPlay(AUDIO_SFX_CARD_DEAL); }
static void dealTurn(void) { card_t burn; deal(&burn); deal(&community_cards[3]); community_count=4U; street=STREET_TURN; audioPlay(AUDIO_SFX_CARD_DEAL); }
static void dealRiver(void) { card_t burn; deal(&burn); deal(&community_cards[4]); community_count=5U; street=STREET_RIVER; audioPlay(AUDIO_SFX_CARD_DEAL); }
static void startAutoRestart(uint32_t now, uint32_t delay) { if(auto_mode) { auto_restart_started=now; auto_restart_delay=delay; auto_seconds=(uint8_t)((delay+999U)/1000U); } }
static bool awardPot(void) { if(player_committed && !addGold(player_committed)) return false; const uint32_t profit=pot-player_committed; return !profit || (auto_mode?addAIGold(profit):addUserGoldWon(profit)); }
static bool recordLoss(void) { return auto_mode?recordAIGoldLost(player_committed):recordUserGoldLost(player_committed); }
static void finishFold(bool player_won, uint32_t now) { audioPlay(player_won ? AUDIO_SFX_WAVE_CLEAR : AUDIO_SFX_GAME_OVER); hand_active=false; state=POKER_FINISHED; reveal_cpu=false; setStatus(player_won?(awardPot()?"CPU FOLDS":"GOLD ERROR"):(recordLoss()?"YOU FOLD":"GOLD ERROR")); startAutoRestart(now,auto_mode&&!player_won&&street==STREET_PREFLOP?AUTO_PREFLOP_FOLD_DELAY_MS:aiRestartDelay()); }
static void finishShowdown(uint32_t now) { card_t player[7]={player_cards[0],player_cards[1],community_cards[0],community_cards[1],community_cards[2],community_cards[3],community_cards[4]}; card_t cpu[7]={cpu_cards[0],cpu_cards[1],community_cards[0],community_cards[1],community_cards[2],community_cards[3],community_cards[4]}; const hand_rank_t a=evaluateBest(player,7), b=evaluateBest(cpu,7); snprintf(player_hand_text,sizeof(player_hand_text),"%s",handName(a.value)); snprintf(cpu_hand_text,sizeof(cpu_hand_text),"%s",handName(b.value)); reveal_cpu=true; hand_active=false; state=POKER_FINISHED; const int8_t result=compareRanks(a,b); audioPlay(result>0 ? AUDIO_SFX_WAVE_CLEAR : result<0 ? AUDIO_SFX_GAME_OVER : AUDIO_SFX_MENU_SELECT); if(result>0) setStatus(awardPot()?"YOU WIN":"GOLD ERROR"); else if(result<0) setStatus(recordLoss()?"CPU WINS":"GOLD ERROR"); else { addGold(player_committed); setStatus("DRAW"); } startAutoRestart(now,aiRestartDelay()); }
static void moveToPlayer(const char *message, uint32_t now) { state=POKER_PLAYER; selected_action=ACTION_CALL_CHECK; setStatus(message); if(auto_mode) state_started=now; }
static void moveToCpu(uint32_t now) { state=POKER_CPU; setStatus("CPU THINK"); state_started=now; }
static void startPostFlop(const char *message, uint32_t now) { player_bet=cpu_bet=0; last_raise=BIG_BLIND; player_acted=cpu_acted=false; if(player_dealer) moveToCpu(now); else moveToPlayer(message,now); }
static void startAllIn(uint32_t now) { state=POKER_ALL_IN; setStatus("ALL IN"); state_started=now; }
static void completeRound(uint32_t now) { if(!getGold()||!cpu_stack) { if(community_count<5U) startAllIn(now); else finishShowdown(now); return; } if(street==STREET_PREFLOP) { dealFlop(); startPostFlop("FLOP",now); } else if(street==STREET_FLOP) { dealTurn(); startPostFlop("TURN",now); } else if(street==STREET_TURN) { dealRiver(); startPostFlop("RIVER",now); } else finishShowdown(now); }
static void processAllIn(uint32_t now) { if(community_count==0U) { dealFlop(); setStatus("ALL IN FLOP"); } else if(community_count==3U) { dealTurn(); setStatus("ALL IN TURN"); } else if(community_count==4U) { dealRiver(); setStatus("ALL IN RIVER"); } else { finishShowdown(now); return; } state_started=now; }
static bool shouldFold(uint32_t call, uint8_t hand_strength) { return call && hand_strength<35U && esp_random()%100U>=15U; }
static void processCpu(uint32_t now) { const uint32_t call=player_bet>cpu_bet?player_bet-cpu_bet:0U; const uint8_t value=strength(cpu_cards); if(shouldFold(call,value)) { finishFold(true,now); return; } const uint32_t target=currentBet()+last_raise, maximum=cpu_bet+cpu_stack; const bool can_raise=value>=GOLD_HAND_STRENGTH && target>currentBet() && maximum>=target && target-cpu_bet<=remainingRaisePotRoom(); if(can_raise) { const uint32_t previous=currentBet(); commitCpu(target-cpu_bet); last_raise=target-previous; cpu_acted=true; player_acted=false; moveToPlayer("CPU RAISE",now); return; } commitCpu(call); cpu_acted=true; if(player_acted && player_bet==cpu_bet) completeRound(now); else moveToPlayer(call?"CPU CALL":"CPU CHECK",now); }
static void processAuto(uint32_t now) { const uint32_t call=cpu_bet>player_bet?cpu_bet-player_bet:0U; const uint8_t value=strength(player_cards); if(call>getGold()||shouldFold(call,value)) { finishFold(false,now); return; } const uint32_t target=minimumRaise(); if(value>=GOLD_HAND_STRENGTH && target>currentBet()) { const uint32_t previous=currentBet(); if(!commitPlayer(target-player_bet)) { finishFold(false,now); return; } last_raise=target-previous; player_acted=true; cpu_acted=false; moveToCpu(now); return; } if(!commitPlayer(call)) { finishFold(false,now); return; } player_acted=true; if(cpu_acted&&player_bet==cpu_bet) completeRound(now); else moveToCpu(now); }
static void beginHand(uint32_t now) { pot=player_bet=cpu_bet=player_committed=community_count=0; street=STREET_PREFLOP; last_raise=BIG_BLIND; selected_action=ACTION_CALL_CHECK; player_acted=cpu_acted=reveal_cpu=false; hand_active=false; const uint32_t starting=getGold(); if(!starting) { state=POKER_NO_GOLD; setStatus("NEED GOLD"); return; } cpu_stack=starting; shuffleDeck(); dealHoleCards(); if(player_dealer) { commitPlayer(starting<SMALL_BLIND?starting:SMALL_BLIND); commitCpu(cpu_stack<BIG_BLIND?cpu_stack:BIG_BLIND); state=POKER_PLAYER; setStatus("PREFLOP"); if(auto_mode) state_started=now; } else { commitCpu(cpu_stack<SMALL_BLIND?cpu_stack:SMALL_BLIND); commitPlayer(starting<BIG_BLIND?starting:BIG_BLIND); moveToCpu(now); } hand_active=true; redraw_needed=true; }
void startPoker(uint32_t now_ms) { running=true; auto_mode=false; ai_speed=AI_SPEED_DEFAULT_INDEX; modeSelectionReset(&mode_input); player_dealer=true; state=POKER_MODE; setStatus("SELECT MODE"); state_started=now_ms; redraw_needed=true; }
void stopPoker(void) { if(hand_active) { recordLoss(); hand_active=false; pot=0; player_dealer=!player_dealer; } else if(state==POKER_FINISHED) player_dealer=!player_dealer; running=false; }
void pokerAction(bool action, bool cycle, bool speed, uint32_t now) {
    if(!running) return;
    if(state==POKER_MODE) { if(action) beginHand(now); return; }
    if(auto_mode) { if(speed) { cycleAISpeed(&ai_speed); state_started=now; redraw_needed=true; } if(action) { if(hand_active) { recordLoss(); hand_active=false; pot=0; } auto_mode=false; modeSelectionReset(&mode_input); state=POKER_MODE; redraw_needed=true; } return; }
    if(state==POKER_FINISHED && action) { player_dealer=!player_dealer; beginHand(now); return; }
    if(state==POKER_RAISE) { if(cycle) { const uint32_t maximum=player_bet+getGold(), minimum=minimumRaise(); selected_raise=selected_raise<maximum?selected_raise+1U:minimum; redraw_needed=true; } if(action) { const uint32_t previous=currentBet(), amount=selected_raise-player_bet; if(amount<=remainingRaisePotRoom()&&commitPlayer(amount)) { last_raise=selected_raise-previous; player_acted=true; cpu_acted=false; moveToCpu(now); } } return; }
    if(state!=POKER_PLAYER) return;
    if(cycle) { selected_action=(poker_action_t)((selected_action+1U)%ACTION_COUNT); redraw_needed=true; }
    if(!action) return;
    if(selected_action==ACTION_FOLD) { finishFold(false,now); return; }
    if(selected_action==ACTION_RAISE) { selected_raise=minimumRaise(); if(selected_raise) { state=POKER_RAISE; setStatus("RAISE TO"); } else setStatus("NO RAISE"); return; }
    const uint32_t call=cpu_bet>player_bet?cpu_bet-player_bet:0U; if(!commitPlayer(call)) return; player_acted=true; if(cpu_acted&&player_bet==cpu_bet) completeRound(now); else moveToCpu(now);
}
void updatePoker(uint16_t *frame, int x_offset, int y_offset, bool joystick_valid, bool left, bool right, uint32_t now) { (void)frame; if(!running) return; if(state==POKER_MODE) { if(modeSelectionNext(&mode_input,x_offset,y_offset,joystick_valid,left,right,now)) { auto_mode=!auto_mode; redraw_needed=true; } return; } if(state==POKER_FINISHED&&auto_mode) { const uint32_t elapsed=now-auto_restart_started; if(elapsed>=auto_restart_delay) { player_dealer=!player_dealer; beginHand(now); return; } const uint8_t seconds=(uint8_t)((auto_restart_delay-elapsed+999U)/1000U); if(seconds!=auto_seconds) { auto_seconds=seconds; redraw_needed=true; } return; } if(state==POKER_PLAYER&&auto_mode&&now-state_started>=aiSpeedDelay(ai_speed)) { processAuto(now); return; } if(state==POKER_CPU&&now-state_started>=(auto_mode?aiSpeedDelay(ai_speed):CPU_ACTION_DELAY_MS)) { processCpu(now); return; } if(state==POKER_ALL_IN&&now-state_started>=(auto_mode?aiSpeedDelay(ai_speed):ALL_IN_REVEAL_MS)) processAllIn(now); }
static void cardPath(const card_t *card, char *path, size_t size) { const char *rank=card->rank==11U?"J":card->rank==12U?"Q":card->rank==13U?"K":card->rank==14U?"A":NULL; if(rank) snprintf(path,size,"/spiffs/cards/%s%c.jpg",rank,card->suit); else snprintf(path,size,"/spiffs/cards/%u%c.jpg",(unsigned)card->rank,card->suit); }
static void drawCard(uint16_t *frame,const card_t *card,int x,int y,unsigned scale) { char path[32]; cardPath(card,path,sizeof(path)); const bool decoded=scale?imageDecodeJpegScaledAt(path,frame,(unsigned)x,(unsigned)y,scale):imageDecodeJpegAt(path,frame,(unsigned)x,(unsigned)y); if(!decoded) { const int width=48>>scale,height=64>>scale; fill_rect(frame,x,y,width,height,COLOR_WHITE); draw_rect(frame,x,y,width,height,COLOR_RED); drawPet2001TextCentredTransparent(frame,x+width/2,y+height/2-4,path+14,COLOR_BLACK,1); } }
static void drawBack(uint16_t *frame,int x,int y) { if(!imageDecodeJpegAt("/spiffs/cards/Back.jpg",frame,(unsigned)x,(unsigned)y)) { fill_rect(frame,x,y,48,64,COLOR_BLUE); draw_rect(frame,x,y,48,64,COLOR_WHITE); } }
void drawPoker(uint16_t *frame) {
  if (!running || !redraw_needed)
    return;
  redraw_needed = false;
  clear(frame, COLOR_BLACK);
  char text[42];
  if (state == POKER_MODE) {
    drawPet2001TextCentredTransparent(frame, 160, 12, "POKER", COLOR_CYAN, 4);
    drawPet2001TextCentredTransparent(frame, 160, 42, "SELECT MODE", COLOR_LIGHTGRAY, 1);
    drawPet2001TextCentredTransparent(frame, 100, 60, "PLAY", auto_mode ? COLOR_LIGHTGRAY : COLOR_YELLOW, 2);
    drawPet2001TextCentredTransparent(frame, 220, 60, "AUTO", auto_mode ? COLOR_YELLOW : COLOR_LIGHTGRAY, 2);
    drawPet2001TextCentredTransparent(frame, 160, 105, "D/B/STICK CHANGE  A/K START", COLOR_LIGHTGRAY, 1);
    drawPet2001TextCentredTransparent(frame, 160, 130, "BOOT EXIT", COLOR_LIGHTGRAY, 1);
    return;
  }
  if (state == POKER_NO_GOLD) {
    drawPet2001TextCentredTransparent(frame, 160, 18, "POKER", COLOR_CYAN, 4);
    drawPet2001TextCentredTransparent(frame, 160, 72, "NO GOLD", COLOR_RED, 3);
    return;
  }
  if (reveal_cpu) {
    drawCard(frame, &cpu_cards[0], 112, 2, 0);
    drawCard(frame, &cpu_cards[1], 164, 2, 0);
  } else {
    drawBack(frame, 112, 2);
    drawBack(frame, 164, 2);
  }
  for (uint8_t i = 0; i < 5U; ++i) {
    const int x = 94 + (int)i * 27;
    if (i < community_count)
      drawCard(frame, &community_cards[i], x, 69, 1);
    else {
      fill_rect(frame, x, 69, 24, 32, COLOR_BLACK);
      draw_rect(frame, x, 69, 24, 32, COLOR_DARKGREY);
    }
  }
  drawCard(frame, &player_cards[0], 112, 104, 0);
  drawCard(frame, &player_cards[1], 164, 104, 0);
  drawPet2001TextTransparent(frame, 3, 4, "CPU", COLOR_CYAN, 1);
  if (reveal_cpu)
    drawPet2001TextTransparent(frame, 0, 18, cpu_hand_text, COLOR_WHITE, 1);
  else {
    snprintf(text, sizeof(text), "B %u", (unsigned)cpu_bet);
    drawPet2001TextTransparent(frame, 3, 18, text, COLOR_WHITE, 1);
  }
  drawPet2001TextTransparent(frame, 3, 106, "YOU", COLOR_GREEN, 1);
  if (reveal_cpu)
    drawPet2001TextTransparent(frame, 0, 120, player_hand_text, COLOR_WHITE, 1);
  else {
    snprintf(text, sizeof(text), "B %u", (unsigned)player_bet);
    drawPet2001TextTransparent(frame, 3, 120, text, COLOR_WHITE, 1);
  }
  drawPet2001TextTransparent(frame, 3, 48, status_text, COLOR_CYAN, 1);
  const char *choice = state == POKER_RAISE    ? "TO"
                       : state == POKER_CPU    ? "WAIT"
                       : state == POKER_ALL_IN ? "ALL IN"
                       : state == POKER_FINISHED
                           ? (auto_mode ? "NEXT" : "DEAL")
                           : (selected_action == ACTION_RAISE  ? "RAISE"
                              : selected_action == ACTION_FOLD ? "FOLD"
                              : player_bet < currentBet()      ? "CALL"
                                                               : "CHECK");
  if (state == POKER_RAISE)
    snprintf(text, sizeof(text), "TO %u", (unsigned)selected_raise);
  else if (state == POKER_FINISHED && auto_mode)
    snprintf(text, sizeof(text), "NEXT %u", (unsigned)auto_seconds);
  else
    snprintf(text, sizeof(text), "%s", choice);
  drawPet2001TextTransparent(frame, 3, 65, text, COLOR_YELLOW, 1);
  drawGameGold(frame, 260, 4, 56, true);
  snprintf(text, sizeof(text), "POT %u", (unsigned)pot);
  drawPet2001TextTransparent(frame, 256, 46, text, COLOR_ORANGE, 1);
  drawPet2001TextTransparent(frame, 260, 106, player_dealer ? "D YOU" : "D CPU", COLOR_CYAN, 1);
  drawPet2001TextTransparent(frame, 260, 120, "BL 1/2", COLOR_DARKGREY, 1);
  if (state == POKER_PLAYER) {
    if (auto_mode) {
      snprintf(text, sizeof(text), "AUTO  E SPEED %s  A STOP",
               aiSpeedShortText(ai_speed));
      drawPet2001TextCentredTransparent(frame, 160, 158, text, COLOR_LIGHTGRAY, 1);
    } else {
      drawPet2001TextCentredTransparent(frame, 160, 158, "C CHOOSE  A/K SELECT", COLOR_LIGHTGRAY, 1);
    }
  } else if (state == POKER_FINISHED)
    drawPet2001TextCentredTransparent(frame, 160, 158,
                                       auto_mode ? "E SPEED  A STOP" : "A/K DEAL", COLOR_LIGHTGRAY, 1);
}