Yahtzee

Source: src/yahtzee.c · 151 lines · 18 functions.

Screen captures

Views of this part running on the Mini Arcade.

Yahtzee game screen on the Mini Arcade
Yahtzee showing dice and score information.

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

random_next() · source line 31
static uint32_t random_next(void)
  1. Executerng ^= rng << 13;
  2. Executerng ^= rng >> 17;
  3. Executerng ^= rng << 5;
  4. Exit this functionreturn rng;
Read original function source
static uint32_t random_next(void) { rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5; return rng; }
roll() · source line 32
static void roll(uint8_t dice[DICE], const bool keep[DICE])

Calls into this documentation:

  1. FOR loopunsigned i=0;i<DICE;i++

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

    1. Decision!keep[i]
      True
      1. Executedice[i]=(uint8_t)(1U+random_next()%6U);
      False

      Continue after this decision.

  2. ExecuteaudioPlay(AUDIO_SFX_DICE_ROLL);
Read original function source
static void roll(uint8_t dice[DICE], const bool keep[DICE]) { for (unsigned i=0;i<DICE;i++) if (!keep[i]) dice[i]=(uint8_t)(1U+random_next()%6U); audioPlay(AUDIO_SFX_DICE_ROLL); }
total() · source line 33
static uint16_t total(const uint8_t dice[DICE])
  1. Executeuint16_t n=0;
  2. FOR loopunsigned i=0;i<DICE;i++

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

    1. Executen+=dice[i];
  3. Exit this functionreturn n;
Read original function source
static uint16_t total(const uint8_t dice[DICE]) { uint16_t n=0; for(unsigned i=0;i<DICE;i++) n+=dice[i]; return n; }
score() · source line 34
static uint16_t score(const uint8_t dice[DICE], unsigned cat)

Calls into this documentation:

  1. Executeuint8_t c[6]={0};
  2. FOR loopunsigned i=0;i<DICE;i++

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

    1. Execute++c[dice[i]-1U];
  3. Decisioncat<6
    True
    1. Exit this functionreturn c[cat]*(cat+1U);
    False

    Continue after this decision.

  4. Decisioncat==6||cat==7
    True
      1. Executeunsigned need=cat==6?3:4;
      2. FOR loopunsigned i=0;i<6;i++

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

        1. Decisionc[i]>=need
          True
          1. Exit this functionreturn total(dice);
          False

          Continue after this decision.

      3. Exit this functionreturn 0;
    False

    Continue after this decision.

  5. Decisioncat==8
    True
      1. Executebool p=false,t=false;
      2. FOR loopunsigned i=0;i<6;i++

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

          1. Executep|=c[i]==2;
          2. Executet|=c[i]==3;
      3. Exit this functionreturn p&&t?25:0;
    False

    Continue after this decision.

  6. Decisioncat==9
    True
    1. Exit this functionreturn (c[0]&&c[1]&&c[2]&&c[3])||(c[1]&&c[2]&&c[3]&&c[4])||(c[2]&&c[3]&&c[4]&&c[5])?30:0;
    False

    Continue after this decision.

  7. Decisioncat==10
    True
    1. Exit this functionreturn (c[0]&&c[1]&&c[2]&&c[3]&&c[4])||(c[1]&&c[2]&&c[3]&&c[4]&&c[5])?40:0;
    False

    Continue after this decision.

  8. Decisioncat==11
    True
      1. FOR loopunsigned i=0;i<6;i++

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

        1. Decisionc[i]==5
          True
          1. Exit this functionreturn 50;
          False

          Continue after this decision.

      2. Exit this functionreturn 0;
    False

    Continue after this decision.

  9. Exit this functionreturn total(dice);
Read original function source
static uint16_t score(const uint8_t dice[DICE], unsigned cat) {
    uint8_t c[6]={0}; for(unsigned i=0;i<DICE;i++) ++c[dice[i]-1U];
    if(cat<6) return c[cat]*(cat+1U);
    if(cat==6||cat==7) { unsigned need=cat==6?3:4; for(unsigned i=0;i<6;i++) if(c[i]>=need) return total(dice); return 0; }
    if(cat==8) { bool p=false,t=false; for(unsigned i=0;i<6;i++){p|=c[i]==2;t|=c[i]==3;} return p&&t?25:0; }
    if(cat==9) return (c[0]&&c[1]&&c[2]&&c[3])||(c[1]&&c[2]&&c[3]&&c[4])||(c[2]&&c[3]&&c[4]&&c[5])?30:0;
    if(cat==10) return (c[0]&&c[1]&&c[2]&&c[3]&&c[4])||(c[1]&&c[2]&&c[3]&&c[4]&&c[5])?40:0;
    if(cat==11) { for(unsigned i=0;i<6;i++) if(c[i]==5) return 50; return 0; } return total(dice);
}
score_total() · source line 43
static uint16_t score_total(const int16_t s[CATEGORIES])
  1. Executeuint16_t n=0,u=0;
  2. FOR loopunsigned i=0;i<CATEGORIES;i++

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

    1. Decisions[i]>=0
      True
        1. Executen+=(uint16_t)s[i];
        2. Decisioni<6
          True
          1. Executeu+=(uint16_t)s[i];
          False

          Continue after this decision.

      False

      Continue after this decision.

  3. Exit this functionreturn n+(u>=63?35:0);
Read original function source
static uint16_t score_total(const int16_t s[CATEGORIES]) { uint16_t n=0,u=0; for(unsigned i=0;i<CATEGORIES;i++) if(s[i]>=0){n+=(uint16_t)s[i];if(i<6)u+=(uint16_t)s[i];} return n+(u>=63?35:0); }
clear_game() · source line 44
static void clear_game(void)
  1. FOR loopunsigned i=0;i<CATEGORIES;i++

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

    1. Executeplayer_scores[i]=cpu_scores[i]=-1;
  2. FOR loopunsigned i=0;i<DICE;i++

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

    1. Executeheld[i]=cpu_held[i]=false;
  3. Executerolls=cpu_rolls=round=selected=category=0;
  4. Executeplayer_total=cpu_total=0;
Read original function source
static void clear_game(void) { for(unsigned i=0;i<CATEGORIES;i++)player_scores[i]=cpu_scores[i]=-1; for(unsigned i=0;i<DICE;i++)held[i]=cpu_held[i]=false; rolls=cpu_rolls=round=selected=category=0; player_total=cpu_total=0; }
best_category() · source line 45
static unsigned best_category(const uint8_t dice[DICE],const int16_t s[CATEGORIES])

Calls into this documentation:

  1. Executeunsigned best=0;
  2. Executeint v=-1000;
  3. FOR loopunsigned i=0;i<CATEGORIES;i++

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

    1. Decisions[i]<0
      True
        1. Executeint n=(int)score(dice,i)-(i==12?8:0);
        2. Decisionn>v
          True
            1. Executev=n;
            2. Executebest=i;
          False

          Continue after this decision.

      False

      Continue after this decision.

  4. Exit this functionreturn best;
Read original function source
static unsigned best_category(const uint8_t dice[DICE],const int16_t s[CATEGORIES]) { unsigned best=0; int v=-1000; for(unsigned i=0;i<CATEGORIES;i++)if(s[i]<0){int n=(int)score(dice,i)-(i==12?8:0);if(n>v){v=n;best=i;}}return best; }
ai_holds() · source line 46
static void ai_holds(const uint8_t dice[DICE],bool keep[DICE])
  1. Executeuint8_t c[6]={0},best=1;
  2. FOR loopunsigned i=0;i<DICE;i++

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

    1. Execute++c[dice[i]-1U];
  3. FOR loopunsigned i=2;i<=6;i++

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

    1. Decisionc[i-1]>c[best-1]
      True
      1. Executebest=i;
      False

      Continue after this decision.

  4. FOR loopunsigned i=0;i<DICE;i++

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

    1. Executekeep[i]=dice[i]==best;
Read original function source
static void ai_holds(const uint8_t dice[DICE],bool keep[DICE]) { uint8_t c[6]={0},best=1; for(unsigned i=0;i<DICE;i++)++c[dice[i]-1U]; for(unsigned i=2;i<=6;i++)if(c[i-1]>c[best-1])best=i; for(unsigned i=0;i<DICE;i++)keep[i]=dice[i]==best; }
begin_player() · source line 47
static void begin_player(uint32_t now)

Calls into this documentation:

  1. FOR loopunsigned i=0;i<DICE;i++

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

    1. Executeheld[i]=false;
  2. Executerolls=1;
  3. Executeroll(player,held);
  4. Decisionauto_mode
    True
    1. Executeai_holds(player,held);
    False

    Continue after this decision.

  5. Executestate=Y_PLAYER;
  6. Executestarted=now;
Read original function source
static void begin_player(uint32_t now) { for(unsigned i=0;i<DICE;i++)held[i]=false; rolls=1; roll(player,held); if(auto_mode)ai_holds(player,held); state=Y_PLAYER;started=now; }
begin_cpu() · source line 48
static void begin_cpu(uint32_t now)

Calls into this documentation:

  1. FOR loopunsigned i=0;i<DICE;i++

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

    1. Executecpu_held[i]=false;
  2. Executecpu_rolls=1;
  3. Executeroll(cpu,cpu_held);
  4. Executeai_holds(cpu,cpu_held);
  5. Executestate=Y_CPU_ROLL;
  6. Executestarted=now;
Read original function source
static void begin_cpu(uint32_t now) { for(unsigned i=0;i<DICE;i++)cpu_held[i]=false;cpu_rolls=1;roll(cpu,cpu_held);ai_holds(cpu,cpu_held);state=Y_CPU_ROLL;started=now; }
finish() · source line 49
static void finish(uint32_t now)

Calls into this documentation:

  1. Executeplayer_total=score_total(player_scores);
  2. Executecpu_total=score_total(cpu_scores);
  3. ExecuteaudioPlay(player_total>cpu_total ? AUDIO_SFX_WAVE_CLEAR : player_total<cpu_total ? AUDIO_SFX_GAME_OVER : AUDIO_SFX_MENU_SELECT);
  4. Decision!auto_mode
    True
      1. Decisionplayer_total>cpu_total
        True
        1. Execute++results.wins;
        False
        1. Decisionplayer_total<cpu_total
          True
          1. Execute++results.losses;
          False
          1. Execute++results.draws;
      2. Executegame_scores_save(GAME_YAHTZEE,&results);
      3. Decisionplayer_total>cpu_total
        True
        1. ExecuteaddUserGoldWon(WIN_GOLD);
        False

        Continue after this decision.

      4. ExecutestartHighScoreEntry(HIGH_SCORE_YAHTZEE,player_total,now);
      5. Executestate=Y_HIGH;
    False
    1. Executestate=Y_FINISHED;
  5. Executestarted=now;
Read original function source
static void finish(uint32_t now) { player_total=score_total(player_scores);cpu_total=score_total(cpu_scores); audioPlay(player_total>cpu_total ? AUDIO_SFX_WAVE_CLEAR : player_total<cpu_total ? AUDIO_SFX_GAME_OVER : AUDIO_SFX_MENU_SELECT); if(!auto_mode){if(player_total>cpu_total)++results.wins;else if(player_total<cpu_total)++results.losses;else ++results.draws;game_scores_save(GAME_YAHTZEE,&results);if(player_total>cpu_total)addUserGoldWon(WIN_GOLD);startHighScoreEntry(HIGH_SCORE_YAHTZEE,player_total,now);state=Y_HIGH;}else state=Y_FINISHED;started=now; }
startYahtzee() · source line 50
void startYahtzee(uint32_t now)

Calls into this documentation:

  1. Executerng=esp_random();
  2. Decision!rng
    True
    1. Executerng=1;
    False

    Continue after this decision.

  3. Executegame_scores_load(GAME_YAHTZEE,&results);
  4. Executeauto_mode=false;
  5. Executeai_speed=AI_SPEED_DEFAULT_INDEX;
  6. ExecutemodeSelectionReset(&mode_input);
  7. Executestate=Y_SCORES;
  8. Executestarted=now;
  9. Executerunning=true;
Read original function source
void startYahtzee(uint32_t now) { rng=esp_random();if(!rng)rng=1;game_scores_load(GAME_YAHTZEE,&results);auto_mode=false;ai_speed=AI_SPEED_DEFAULT_INDEX;modeSelectionReset(&mode_input);state=Y_SCORES;started=now;running=true; }
stopYahtzee() · source line 51
void stopYahtzee(void)

Calls into this documentation:

  1. ExecutecancelHighScoreEntry();
  2. Executerunning=false;
Read original function source
void stopYahtzee(void) { cancelHighScoreEntry();running=false; }
yahtzeeAction() · source line 52
void yahtzeeAction(bool next,bool speed,bool select,uint32_t now)

Calls into this documentation:

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

    Continue after this decision.

  2. Decisionstate==Y_MODE
    True
      1. Decisionnext
        True
          1. Executeclear_game();
          2. Executebegin_player(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. Executestarted=now;
        False

        Continue after this decision.

      2. Decisionselect
        True
          1. Executeauto_mode=false;
          2. ExecutemodeSelectionReset(&mode_input);
          3. Executestate=Y_MODE;
        False

        Continue after this decision.

      3. Exit this functionreturn;
    False

    Continue after this decision.

  4. Decisionstate==Y_PLAYER
    True
      1. Decisionnext
        True
        1. Executeselected=(selected+1U)%7U;
        False

        Continue after this decision.

      2. Decisionselect
        True
          1. Decisionselected<DICE
            True
              1. Executeheld[selected]=!held[selected];
              2. ExecuteaudioPlay(AUDIO_SFX_DICE_HOLD);
            False
            1. Decisionselected==5&&rolls<MAX_ROLLS
              True
                1. Executeroll(player,held);
                2. Decision++rolls==MAX_ROLLS
                  True
                    1. FOR loopunsigned i=0;i<DICE;i++

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

                      1. Executeheld[i]=false;
                    2. Executestate=Y_CATEGORY;
                    3. Executecategory=0;
                    4. WHILE loopplayer_scores[category]>=0

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

                      1. Execute++category;
                  False

                  Continue after this decision.

              False
              1. Decisionselected==6
                True
                  1. Executestate=Y_CATEGORY;
                  2. Executecategory=0;
                  3. WHILE loopplayer_scores[category]>=0

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

                    1. Execute++category;
                False

                Continue after this decision.

        False

        Continue after this decision.

    False
    1. Decisionstate==Y_CATEGORY
      True
        1. Decisionnext
          True
            1. DO / WHILE loopplayer_scores[category]>=0

              Run body, then test condition; repeat while true.

                1. Executecategory=(category+1U)%CATEGORIES;
          False

          Continue after this decision.

        2. Decisionselect
          True
            1. ExecuteaudioPlay(AUDIO_SFX_MENU_SELECT);
            2. Executeplayer_scores[category]=(int16_t)score(player,category);
            3. Executeplayer_total=score_total(player_scores);
            4. Executebegin_cpu(now);
          False

          Continue after this decision.

      False
      1. Decisionstate==Y_FINISHED&&select
        True
          1. Executeclear_game();
          2. Executebegin_player(now);
        False

        Continue after this decision.

Read original function source
void yahtzeeAction(bool next,bool speed,bool select,uint32_t now) { if(!running)return; if(state==Y_MODE){if(next){clear_game();begin_player(now);}return;} if(auto_mode){if(speed){cycleAISpeed(&ai_speed);started=now;}if(select){auto_mode=false;modeSelectionReset(&mode_input);state=Y_MODE;}return;} if(state==Y_PLAYER){if(next)selected=(selected+1U)%7U; if(select){if(selected<DICE){held[selected]=!held[selected];audioPlay(AUDIO_SFX_DICE_HOLD);}else if(selected==5&&rolls<MAX_ROLLS){roll(player,held);if(++rolls==MAX_ROLLS){for(unsigned i=0;i<DICE;i++)held[i]=false;state=Y_CATEGORY;category=0;while(player_scores[category]>=0)++category;}}else if(selected==6){state=Y_CATEGORY;category=0;while(player_scores[category]>=0)++category;}}} else if(state==Y_CATEGORY){if(next){do{category=(category+1U)%CATEGORIES;}while(player_scores[category]>=0);}if(select){audioPlay(AUDIO_SFX_MENU_SELECT);player_scores[category]=(int16_t)score(player,category);player_total=score_total(player_scores);begin_cpu(now);}} else if(state==Y_FINISHED&&select){clear_game();begin_player(now);} }
updateYahtzee() · source line 53
void updateYahtzee(int x,int y,bool valid,bool left,bool right,uint32_t now)

Calls into this documentation:

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

    Continue after this decision.

  2. Decisionstate==Y_SCORES&&now-started>=SHOW_SCORES_MS
    True
      1. Executestate=Y_MODE;
      2. Exit this functionreturn;
    False

    Continue after this decision.

  3. Decisionstate==Y_MODE
    True
      1. DecisionmodeSelectionNext(&mode_input,x,y,valid,left,right,now)
        True
        1. Executeauto_mode=!auto_mode;
        False

        Continue after this decision.

      2. Exit this functionreturn;
    False

    Continue after this decision.

  4. Decisionstate==Y_HIGH
    True
      1. DecisionupdateHighScoreEntry(x,y,valid,now)
        True
        1. Executestate=Y_MODE;
        False

        Continue after this decision.

      2. Exit this functionreturn;
    False

    Continue after this decision.

  5. Decisionstate==Y_FINISHED&&auto_mode&&now-started>=aiRestartDelay()
    True
      1. Executeclear_game();
      2. Executebegin_player(now);
      3. Exit this functionreturn;
    False

    Continue after this decision.

  6. Executeuint32_t wait=auto_mode?aiSpeedDelay(ai_speed):CPU_ROLL_MS;
  7. Decisionstate==Y_PLAYER&&auto_mode&&now-started>=wait
    True
      1. Decisionrolls<MAX_ROLLS
        True
          1. Executeroll(player,held);
          2. Execute++rolls;
          3. Decisionrolls<MAX_ROLLS
            True
            1. Executeai_holds(player,held);
            False
              1. FOR loopunsigned i=0;i<DICE;i++

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

                1. Executeheld[i]=false;
          4. Executestarted=now;
        False
          1. Executeunsigned c=best_category(player,player_scores);
          2. Executeplayer_scores[c]=(int16_t)score(player,c);
          3. Executeplayer_total=score_total(player_scores);
          4. Executebegin_cpu(now);
    False
    1. Decisionstate==Y_CPU_ROLL&&now-started>=wait
      True
        1. Decisioncpu_rolls<MAX_ROLLS
          True
            1. Executeroll(cpu,cpu_held);
            2. Execute++cpu_rolls;
            3. Decisioncpu_rolls<MAX_ROLLS
              True
              1. Executeai_holds(cpu,cpu_held);
              False

              Continue after this decision.

            4. Executestarted=now;
          False
            1. Executeunsigned c=best_category(cpu,cpu_scores);
            2. Executecpu_scores[c]=(int16_t)score(cpu,c);
            3. Executecpu_total=score_total(cpu_scores);
            4. Executestate=Y_CPU_RESULT;
            5. Executestarted=now;
      False
      1. Decisionstate==Y_CPU_RESULT&&now-started>=(auto_mode?aiSpeedDelay(ai_speed):CPU_RESULT_MS)
        True
          1. Decision++round>=CATEGORIES
            True
            1. Executefinish(now);
            False
            1. Executebegin_player(now);
        False

        Continue after this decision.

Read original function source
void updateYahtzee(int x,int y,bool valid,bool left,bool right,uint32_t now) { if(!running)return;if(state==Y_SCORES&&now-started>=SHOW_SCORES_MS){state=Y_MODE;return;}if(state==Y_MODE){if(modeSelectionNext(&mode_input,x,y,valid,left,right,now))auto_mode=!auto_mode;return;}if(state==Y_HIGH){if(updateHighScoreEntry(x,y,valid,now))state=Y_MODE;return;}if(state==Y_FINISHED&&auto_mode&&now-started>=aiRestartDelay()){clear_game();begin_player(now);return;}uint32_t wait=auto_mode?aiSpeedDelay(ai_speed):CPU_ROLL_MS;if(state==Y_PLAYER&&auto_mode&&now-started>=wait){if(rolls<MAX_ROLLS){roll(player,held);++rolls;if(rolls<MAX_ROLLS)ai_holds(player,held);else {for(unsigned i=0;i<DICE;i++)held[i]=false;}started=now;}else{unsigned c=best_category(player,player_scores);player_scores[c]=(int16_t)score(player,c);player_total=score_total(player_scores);begin_cpu(now);}}else if(state==Y_CPU_ROLL&&now-started>=wait){if(cpu_rolls<MAX_ROLLS){roll(cpu,cpu_held);++cpu_rolls;if(cpu_rolls<MAX_ROLLS)ai_holds(cpu,cpu_held);started=now;}else{unsigned c=best_category(cpu,cpu_scores);cpu_scores[c]=(int16_t)score(cpu,c);cpu_total=score_total(cpu_scores);state=Y_CPU_RESULT;started=now;}}else if(state==Y_CPU_RESULT&&now-started>=(auto_mode?aiSpeedDelay(ai_speed):CPU_RESULT_MS)){if(++round>=CATEGORIES)finish(now);else begin_player(now);} }
pip() · source line 54
static void pip(uint16_t *frame, int x, int y)

Calls into this documentation:

  1. Executefill_circle(frame, x, y, 3, COLOR_WHITE);
Read original function source
static void pip(uint16_t *frame, int x, int y)
{
    fill_circle(frame, x, y, 3, COLOR_WHITE);
}
die() · source line 59
static void die(uint16_t *frame, unsigned value, int x, int y, bool selected, bool held_die)

Calls into this documentation:

  1. Executeconst uint16_t border = selected ? COLOR_YELLOW : held_die ? COLOR_CYAN : COLOR_DARKGREY;
  2. Executeconst int low = 12, middle = 25, high = 38;
  3. Executefill_rect(frame, x, y, 50, 50, COLOR_BLACK);
  4. Executefill_rect(frame, x, y, 50, 1, border);
  5. Executefill_rect(frame, x, y + 49, 50, 1, border);
  6. Executefill_rect(frame, x, y, 1, 50, border);
  7. Executefill_rect(frame, x + 49, y, 1, 50, border);
  8. Decisionheld_die
    True
    1. Executedraw_text_centred(frame, x + 25, y - 10, "H", 1, COLOR_CYAN);
    False

    Continue after this decision.

  9. Decisionvalue < 1U || value > 6U
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  10. Decisionvalue == 1U || value == 3U || value == 5U
    True
    1. Executepip(frame, x + middle, y + middle);
    False

    Continue after this decision.

  11. Decisionvalue >= 2U
    True
      1. Executepip(frame, x + low, y + low);
      2. Executepip(frame, x + high, y + high);
    False

    Continue after this decision.

  12. Decisionvalue >= 4U
    True
      1. Executepip(frame, x + high, y + low);
      2. Executepip(frame, x + low, y + high);
    False

    Continue after this decision.

  13. Decisionvalue == 6U
    True
      1. Executepip(frame, x + low, y + middle);
      2. Executepip(frame, x + high, y + middle);
    False

    Continue after this decision.

Read original function source
static void die(uint16_t *frame, unsigned value, int x, int y, bool selected, bool held_die)
{
    const uint16_t border = selected ? COLOR_YELLOW :
                            held_die ? COLOR_CYAN : COLOR_DARKGREY;
    const int low = 12, middle = 25, high = 38;

    fill_rect(frame, x, y, 50, 50, COLOR_BLACK);
    fill_rect(frame, x, y, 50, 1, border);
    fill_rect(frame, x, y + 49, 50, 1, border);
    fill_rect(frame, x, y, 1, 50, border);
    fill_rect(frame, x + 49, y, 1, 50, border);
    if (held_die) draw_text_centred(frame, x + 25, y - 10, "H", 1, COLOR_CYAN);
    if (value < 1U || value > 6U) return;

    if (value == 1U || value == 3U || value == 5U) pip(frame, x + middle, y + middle);
    if (value >= 2U) {
        pip(frame, x + low, y + low);
        pip(frame, x + high, y + high);
    }
    if (value >= 4U) {
        pip(frame, x + high, y + low);
        pip(frame, x + low, y + high);
    }
    if (value == 6U) {
        pip(frame, x + low, y + middle);
        pip(frame, x + high, y + middle);
    }
}
drawYahtzee() · source line 87
void drawYahtzee(uint16_t *f)

Calls into this documentation:

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

    Continue after this decision.

  2. Decisionstate == Y_HIGH
    True
      1. ExecutedrawHighScoreEntry(f);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  3. Executeclear(f, COLOR_BLACK);
  4. Executechar t[40];
  5. Decisionstate == Y_SCORES
    True
      1. Executedraw_text_centred(f, 160, 8, "YAHTZEE", 4, COLOR_CYAN);
      2. Executedraw_text_centred(f, 160, 49, "GAME RECORD", 2, COLOR_LIGHTGRAY);
      3. Executedraw_text_centred(f, 55, 78, "WINS", 1, COLOR_GREEN);
      4. Executedraw_text_centred(f, 160, 78, "LOSSES", 1, COLOR_RED);
      5. Executedraw_text_centred(f, 265, 78, "DRAWS", 1, COLOR_YELLOW);
      6. Executesnprintf(t, sizeof t, "%u", (unsigned)results.wins);
      7. Executedraw_text_centred(f, 55, 96, t, 3, COLOR_GREEN);
      8. Executesnprintf(t, sizeof t, "%u", (unsigned)results.losses);
      9. Executedraw_text_centred(f, 160, 96, t, 3, COLOR_RED);
      10. Executesnprintf(t, sizeof t, "%u", (unsigned)results.draws);
      11. Executedraw_text_centred(f, 265, 96, t, 3, COLOR_YELLOW);
      12. Executedraw_text_centred(f, 160, 145, "CHOOSE A MODE", 1, COLOR_LIGHTGRAY);
      13. Exit this functionreturn;
    False

    Continue after this decision.

  6. Decisionstate == Y_MODE
    True
      1. Executedraw_text_centred(f, 160, 8, "YAHTZEE", 4, COLOR_CYAN);
      2. Executedraw_text_centred(f, 160, 55, "SELECT MODE", 2, COLOR_LIGHTGRAY);
      3. Executedraw_text_centred(f, 100, 84, "PLAY", 2, auto_mode ? COLOR_LIGHTGRAY : COLOR_YELLOW);
      4. Executedraw_text_centred(f, 220, 84, "AUTO", 2, auto_mode ? COLOR_YELLOW : COLOR_LIGHTGRAY);
      5. Executedraw_text_centred(f, 160, 142, "D/B/STICK CHANGE A/K START", 1, COLOR_LIGHTGRAY);
      6. Exit this functionreturn;
    False

    Continue after this decision.

  7. Executeconst uint8_t *d = (state == Y_CPU_ROLL || state == Y_CPU_RESULT) ? cpu : player;
  8. Executeconst bool *h = (state == Y_CPU_ROLL || state == Y_CPU_RESULT) ? cpu_held : held;
  9. FOR loopunsigned i = 0; i < DICE; i++

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

    1. Executedie(f, d[i], 7 + (int)i * 62, 28, state == Y_PLAYER && selected == i, h[i]);
  10. Executesnprintf(t, sizeof t, "ROUND %u/13 YOU %u CPU %u", round + 1U, player_total, cpu_total);
  11. Executedraw_text_centred(f, 160, 5, t, 1, COLOR_WHITE);
  12. Decisionstate == Y_CATEGORY
    True
      1. Executesnprintf(t, sizeof t, "%s %u", names[category], score(player, category));
      2. Executedraw_text_centred(f, 160, 95, t, 2, COLOR_YELLOW);
      3. Executedraw_text_centred(f, 160, 130, "A NEXT C SCORE", 1, COLOR_LIGHTGRAY);
    False
    1. Decisionstate == Y_PLAYER
      True
        1. Executesnprintf(t, sizeof t, "ROLL %u/3", rolls);
        2. Executedraw_text_centred(f, 160, 90, t, 2, COLOR_WHITE);
        3. Executedraw_text_centred(f, 160, 120, "A NEXT C HOLD/ROLL/SCORE", 1, COLOR_LIGHTGRAY);
      False
      1. Decisionstate == Y_FINISHED
        True
          1. Executesnprintf(t, sizeof t, player_total > cpu_total ? "YOU WIN %u" : player_total < cpu_total ? "CPU WINS %u" : "DRAW %u", player_total);
          2. Executedraw_text_centred(f, 160, 100, t, 3, player_total >= cpu_total ? COLOR_GREEN : COLOR_RED);
          3. Executedraw_text_centred(f, 160, 140, "C NEW GAME", 1, COLOR_LIGHTGRAY);
        False
          1. Executesnprintf(t, sizeof t, "CPU %s", auto_mode ? aiSpeedShortText(ai_speed) : "PLAYING");
          2. Executedraw_text_centred(f, 160, 100, t, 2, COLOR_YELLOW);
          3. Executedraw_text_centred(f, 160, 140, auto_mode ? "E SPEED C STOP" : "PLEASE WAIT", 1, COLOR_LIGHTGRAY);
Read original function source
void drawYahtzee(uint16_t *f) {
  if (!running)
    return;
  if (state == Y_HIGH) {
    drawHighScoreEntry(f);
    return;
  }
  clear(f, COLOR_BLACK);
  char t[40];
  if (state == Y_SCORES) {
    draw_text_centred(f, 160, 8, "YAHTZEE", 4, COLOR_CYAN);
    draw_text_centred(f, 160, 49, "GAME RECORD", 2, COLOR_LIGHTGRAY);
    draw_text_centred(f, 55, 78, "WINS", 1, COLOR_GREEN);
    draw_text_centred(f, 160, 78, "LOSSES", 1, COLOR_RED);
    draw_text_centred(f, 265, 78, "DRAWS", 1, COLOR_YELLOW);
    snprintf(t, sizeof t, "%u", (unsigned)results.wins);
    draw_text_centred(f, 55, 96, t, 3, COLOR_GREEN);
    snprintf(t, sizeof t, "%u", (unsigned)results.losses);
    draw_text_centred(f, 160, 96, t, 3, COLOR_RED);
    snprintf(t, sizeof t, "%u", (unsigned)results.draws);
    draw_text_centred(f, 265, 96, t, 3, COLOR_YELLOW);
    draw_text_centred(f, 160, 145, "CHOOSE A MODE", 1, COLOR_LIGHTGRAY);
    return;
  }
  if (state == Y_MODE) {
    draw_text_centred(f, 160, 8, "YAHTZEE", 4, COLOR_CYAN);
    draw_text_centred(f, 160, 55, "SELECT MODE", 2, COLOR_LIGHTGRAY);
    draw_text_centred(f, 100, 84, "PLAY", 2, auto_mode ? COLOR_LIGHTGRAY : COLOR_YELLOW);
    draw_text_centred(f, 220, 84, "AUTO", 2, auto_mode ? COLOR_YELLOW : COLOR_LIGHTGRAY);
    draw_text_centred(f, 160, 142, "D/B/STICK CHANGE  A/K START", 1,
                      COLOR_LIGHTGRAY);
    return;
  }
  const uint8_t *d =
      (state == Y_CPU_ROLL || state == Y_CPU_RESULT) ? cpu : player;
  const bool *h =
      (state == Y_CPU_ROLL || state == Y_CPU_RESULT) ? cpu_held : held;
  for (unsigned i = 0; i < DICE; i++)
    die(f, d[i], 7 + (int)i * 62, 28, state == Y_PLAYER && selected == i, h[i]);
  snprintf(t, sizeof t, "ROUND %u/13   YOU %u CPU %u", round + 1U, player_total,
           cpu_total);
  draw_text_centred(f, 160, 5, t, 1, COLOR_WHITE);
  if (state == Y_CATEGORY) {
    snprintf(t, sizeof t, "%s  %u", names[category], score(player, category));
    draw_text_centred(f, 160, 95, t, 2, COLOR_YELLOW);
    draw_text_centred(f, 160, 130, "A NEXT  C SCORE", 1, COLOR_LIGHTGRAY);
  } else if (state == Y_PLAYER) {
    snprintf(t, sizeof t, "ROLL %u/3", rolls);
    draw_text_centred(f, 160, 90, t, 2, COLOR_WHITE);
    draw_text_centred(f, 160, 120, "A NEXT  C HOLD/ROLL/SCORE", 1,
                      COLOR_LIGHTGRAY);
  } else if (state == Y_FINISHED) {
    snprintf(t, sizeof t,
             player_total > cpu_total   ? "YOU WIN %u"
             : player_total < cpu_total ? "CPU WINS %u"
                                        : "DRAW %u",
             player_total);
    draw_text_centred(f, 160, 100, t, 3,
                      player_total >= cpu_total ? COLOR_GREEN : COLOR_RED);
    draw_text_centred(f, 160, 140, "C NEW GAME", 1, COLOR_LIGHTGRAY);
  } else {
    snprintf(t, sizeof t, "CPU %s",
             auto_mode ? aiSpeedShortText(ai_speed) : "PLAYING");
    draw_text_centred(f, 160, 100, t, 2, COLOR_YELLOW);
    draw_text_centred(f, 160, 140,
                      auto_mode ? "E SPEED  C STOP" : "PLEASE WAIT", 1,
                      COLOR_LIGHTGRAY);
  }
}
Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "yahtzee.h"
#include "audio.h"
#include "ai_speed.h"
#include "mode_selection.h"
#include "game_graphics.h"
#include "game_scores.h"
#include "gold.h"
#include "high_scores.h"
#include "PET2001_font.h"
#include "esp_random.h"
#include <stdio.h>

#define DICE 5U
#define CATEGORIES 13U
#define MAX_ROLLS 3U
#define SHOW_SCORES_MS 2000U
#define CPU_ROLL_MS 850U
#define CPU_RESULT_MS 1400U
#define WIN_GOLD 10U
typedef enum { Y_SCORES, Y_MODE, Y_PLAYER, Y_CATEGORY, Y_CPU_ROLL, Y_CPU_RESULT, Y_FINISHED, Y_HIGH } y_state_t;
static const char *const names[CATEGORIES] = {"ONES","TWOS","THREES","FOURS","FIVES","SIXES","3 KIND","4 KIND","FULL HOUSE","SMALL STR","LARGE STR","YAHTZEE","CHANCE"};
static uint8_t player[DICE], cpu[DICE], rolls, cpu_rolls, selected, category, round, ai_speed;
static bool held[DICE], cpu_held[DICE], auto_mode, running;
static int16_t player_scores[CATEGORIES], cpu_scores[CATEGORIES];
static uint16_t player_total, cpu_total;
static uint32_t started, due;
static mode_selection_input_t mode_input;
static y_state_t state;
static game_scores_t results;
static uint32_t rng;

static uint32_t random_next(void) { rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5; return rng; }
static void roll(uint8_t dice[DICE], const bool keep[DICE]) { for (unsigned i=0;i<DICE;i++) if (!keep[i]) dice[i]=(uint8_t)(1U+random_next()%6U); audioPlay(AUDIO_SFX_DICE_ROLL); }
static uint16_t total(const uint8_t dice[DICE]) { uint16_t n=0; for(unsigned i=0;i<DICE;i++) n+=dice[i]; return n; }
static uint16_t score(const uint8_t dice[DICE], unsigned cat) {
    uint8_t c[6]={0}; for(unsigned i=0;i<DICE;i++) ++c[dice[i]-1U];
    if(cat<6) return c[cat]*(cat+1U);
    if(cat==6||cat==7) { unsigned need=cat==6?3:4; for(unsigned i=0;i<6;i++) if(c[i]>=need) return total(dice); return 0; }
    if(cat==8) { bool p=false,t=false; for(unsigned i=0;i<6;i++){p|=c[i]==2;t|=c[i]==3;} return p&&t?25:0; }
    if(cat==9) return (c[0]&&c[1]&&c[2]&&c[3])||(c[1]&&c[2]&&c[3]&&c[4])||(c[2]&&c[3]&&c[4]&&c[5])?30:0;
    if(cat==10) return (c[0]&&c[1]&&c[2]&&c[3]&&c[4])||(c[1]&&c[2]&&c[3]&&c[4]&&c[5])?40:0;
    if(cat==11) { for(unsigned i=0;i<6;i++) if(c[i]==5) return 50; return 0; } return total(dice);
}
static uint16_t score_total(const int16_t s[CATEGORIES]) { uint16_t n=0,u=0; for(unsigned i=0;i<CATEGORIES;i++) if(s[i]>=0){n+=(uint16_t)s[i];if(i<6)u+=(uint16_t)s[i];} return n+(u>=63?35:0); }
static void clear_game(void) { for(unsigned i=0;i<CATEGORIES;i++)player_scores[i]=cpu_scores[i]=-1; for(unsigned i=0;i<DICE;i++)held[i]=cpu_held[i]=false; rolls=cpu_rolls=round=selected=category=0; player_total=cpu_total=0; }
static unsigned best_category(const uint8_t dice[DICE],const int16_t s[CATEGORIES]) { unsigned best=0; int v=-1000; for(unsigned i=0;i<CATEGORIES;i++)if(s[i]<0){int n=(int)score(dice,i)-(i==12?8:0);if(n>v){v=n;best=i;}}return best; }
static void ai_holds(const uint8_t dice[DICE],bool keep[DICE]) { uint8_t c[6]={0},best=1; for(unsigned i=0;i<DICE;i++)++c[dice[i]-1U]; for(unsigned i=2;i<=6;i++)if(c[i-1]>c[best-1])best=i; for(unsigned i=0;i<DICE;i++)keep[i]=dice[i]==best; }
static void begin_player(uint32_t now) { for(unsigned i=0;i<DICE;i++)held[i]=false; rolls=1; roll(player,held); if(auto_mode)ai_holds(player,held); state=Y_PLAYER;started=now; }
static void begin_cpu(uint32_t now) { for(unsigned i=0;i<DICE;i++)cpu_held[i]=false;cpu_rolls=1;roll(cpu,cpu_held);ai_holds(cpu,cpu_held);state=Y_CPU_ROLL;started=now; }
static void finish(uint32_t now) { player_total=score_total(player_scores);cpu_total=score_total(cpu_scores); audioPlay(player_total>cpu_total ? AUDIO_SFX_WAVE_CLEAR : player_total<cpu_total ? AUDIO_SFX_GAME_OVER : AUDIO_SFX_MENU_SELECT); if(!auto_mode){if(player_total>cpu_total)++results.wins;else if(player_total<cpu_total)++results.losses;else ++results.draws;game_scores_save(GAME_YAHTZEE,&results);if(player_total>cpu_total)addUserGoldWon(WIN_GOLD);startHighScoreEntry(HIGH_SCORE_YAHTZEE,player_total,now);state=Y_HIGH;}else state=Y_FINISHED;started=now; }
void startYahtzee(uint32_t now) { rng=esp_random();if(!rng)rng=1;game_scores_load(GAME_YAHTZEE,&results);auto_mode=false;ai_speed=AI_SPEED_DEFAULT_INDEX;modeSelectionReset(&mode_input);state=Y_SCORES;started=now;running=true; }
void stopYahtzee(void) { cancelHighScoreEntry();running=false; }
void yahtzeeAction(bool next,bool speed,bool select,uint32_t now) { if(!running)return; if(state==Y_MODE){if(next){clear_game();begin_player(now);}return;} if(auto_mode){if(speed){cycleAISpeed(&ai_speed);started=now;}if(select){auto_mode=false;modeSelectionReset(&mode_input);state=Y_MODE;}return;} if(state==Y_PLAYER){if(next)selected=(selected+1U)%7U; if(select){if(selected<DICE){held[selected]=!held[selected];audioPlay(AUDIO_SFX_DICE_HOLD);}else if(selected==5&&rolls<MAX_ROLLS){roll(player,held);if(++rolls==MAX_ROLLS){for(unsigned i=0;i<DICE;i++)held[i]=false;state=Y_CATEGORY;category=0;while(player_scores[category]>=0)++category;}}else if(selected==6){state=Y_CATEGORY;category=0;while(player_scores[category]>=0)++category;}}} else if(state==Y_CATEGORY){if(next){do{category=(category+1U)%CATEGORIES;}while(player_scores[category]>=0);}if(select){audioPlay(AUDIO_SFX_MENU_SELECT);player_scores[category]=(int16_t)score(player,category);player_total=score_total(player_scores);begin_cpu(now);}} else if(state==Y_FINISHED&&select){clear_game();begin_player(now);} }
void updateYahtzee(int x,int y,bool valid,bool left,bool right,uint32_t now) { if(!running)return;if(state==Y_SCORES&&now-started>=SHOW_SCORES_MS){state=Y_MODE;return;}if(state==Y_MODE){if(modeSelectionNext(&mode_input,x,y,valid,left,right,now))auto_mode=!auto_mode;return;}if(state==Y_HIGH){if(updateHighScoreEntry(x,y,valid,now))state=Y_MODE;return;}if(state==Y_FINISHED&&auto_mode&&now-started>=aiRestartDelay()){clear_game();begin_player(now);return;}uint32_t wait=auto_mode?aiSpeedDelay(ai_speed):CPU_ROLL_MS;if(state==Y_PLAYER&&auto_mode&&now-started>=wait){if(rolls<MAX_ROLLS){roll(player,held);++rolls;if(rolls<MAX_ROLLS)ai_holds(player,held);else {for(unsigned i=0;i<DICE;i++)held[i]=false;}started=now;}else{unsigned c=best_category(player,player_scores);player_scores[c]=(int16_t)score(player,c);player_total=score_total(player_scores);begin_cpu(now);}}else if(state==Y_CPU_ROLL&&now-started>=wait){if(cpu_rolls<MAX_ROLLS){roll(cpu,cpu_held);++cpu_rolls;if(cpu_rolls<MAX_ROLLS)ai_holds(cpu,cpu_held);started=now;}else{unsigned c=best_category(cpu,cpu_scores);cpu_scores[c]=(int16_t)score(cpu,c);cpu_total=score_total(cpu_scores);state=Y_CPU_RESULT;started=now;}}else if(state==Y_CPU_RESULT&&now-started>=(auto_mode?aiSpeedDelay(ai_speed):CPU_RESULT_MS)){if(++round>=CATEGORIES)finish(now);else begin_player(now);} }
static void pip(uint16_t *frame, int x, int y)
{
    fill_circle(frame, x, y, 3, COLOR_WHITE);
}

static void die(uint16_t *frame, unsigned value, int x, int y, bool selected, bool held_die)
{
    const uint16_t border = selected ? COLOR_YELLOW :
                            held_die ? COLOR_CYAN : COLOR_DARKGREY;
    const int low = 12, middle = 25, high = 38;

    fill_rect(frame, x, y, 50, 50, COLOR_BLACK);
    fill_rect(frame, x, y, 50, 1, border);
    fill_rect(frame, x, y + 49, 50, 1, border);
    fill_rect(frame, x, y, 1, 50, border);
    fill_rect(frame, x + 49, y, 1, 50, border);
    if (held_die) drawPet2001TextCentredTransparent(frame, x + 25, y - 10, "H", COLOR_CYAN, 1);
    if (value < 1U || value > 6U) return;

    if (value == 1U || value == 3U || value == 5U) pip(frame, x + middle, y + middle);
    if (value >= 2U) {
        pip(frame, x + low, y + low);
        pip(frame, x + high, y + high);
    }
    if (value >= 4U) {
        pip(frame, x + high, y + low);
        pip(frame, x + low, y + high);
    }
    if (value == 6U) {
        pip(frame, x + low, y + middle);
        pip(frame, x + high, y + middle);
    }
}
void drawYahtzee(uint16_t *f) {
  if (!running)
    return;
  if (state == Y_HIGH) {
    drawHighScoreEntry(f);
    return;
  }
  clear(f, COLOR_BLACK);
  char t[40];
  if (state == Y_SCORES) {
    drawPet2001TextCentredTransparent(f, 160, 8, "YAHTZEE", COLOR_CYAN, 4);
    drawPet2001TextCentredTransparent(f, 160, 49, "GAME RECORD", COLOR_LIGHTGRAY, 2);
    drawPet2001TextCentredTransparent(f, 55, 78, "WINS", COLOR_GREEN, 1);
    drawPet2001TextCentredTransparent(f, 160, 78, "LOSSES", COLOR_RED, 1);
    drawPet2001TextCentredTransparent(f, 265, 78, "DRAWS", COLOR_YELLOW, 1);
    snprintf(t, sizeof t, "%u", (unsigned)results.wins);
    drawPet2001TextCentredTransparent(f, 55, 96, t, COLOR_GREEN, 1);
    snprintf(t, sizeof t, "%u", (unsigned)results.losses);
    drawPet2001TextCentredTransparent(f, 160, 96, t, COLOR_RED, 1);
    snprintf(t, sizeof t, "%u", (unsigned)results.draws);
    drawPet2001TextCentredTransparent(f, 265, 96, t, COLOR_YELLOW, 1);
    drawPet2001TextCentredTransparent(f, 160, 145, "CHOOSE A MODE", COLOR_LIGHTGRAY, 1);
    return;
  }
  if (state == Y_MODE) {
    drawPet2001TextCentredTransparent(f, 160, 8, "YAHTZEE", COLOR_CYAN, 4);
    drawPet2001TextCentredTransparent(f, 160, 55, "SELECT MODE", COLOR_LIGHTGRAY, 2);
    drawPet2001TextCentredTransparent(f, 100, 84, "PLAY", auto_mode ? COLOR_LIGHTGRAY : COLOR_YELLOW, 2);
    drawPet2001TextCentredTransparent(f, 220, 84, "AUTO", auto_mode ? COLOR_YELLOW : COLOR_LIGHTGRAY, 2);
    drawPet2001TextCentredTransparent(f, 160, 142, "D/B/STICK CHANGE  A/K START", COLOR_LIGHTGRAY, 1);
    return;
  }
  const uint8_t *d =
      (state == Y_CPU_ROLL || state == Y_CPU_RESULT) ? cpu : player;
  const bool *h =
      (state == Y_CPU_ROLL || state == Y_CPU_RESULT) ? cpu_held : held;
  for (unsigned i = 0; i < DICE; i++)
    die(f, d[i], 7 + (int)i * 62, 28, state == Y_PLAYER && selected == i, h[i]);
  snprintf(t, sizeof t, "ROUND %u/13   YOU %u CPU %u", round + 1U, player_total,
           cpu_total);
  drawPet2001TextCentredTransparent(f, 160, 5, t, COLOR_WHITE, 1);
  if (state == Y_CATEGORY) {
    snprintf(t, sizeof t, "%s  %u", names[category], score(player, category));
    drawPet2001TextCentredTransparent(f, 160, 95, t, COLOR_YELLOW, 2);
    drawPet2001TextCentredTransparent(f, 160, 130, "A NEXT  C SCORE", COLOR_LIGHTGRAY, 1);
  } else if (state == Y_PLAYER) {
    snprintf(t, sizeof t, "ROLL %u/3", rolls);
    drawPet2001TextCentredTransparent(f, 160, 90, t, COLOR_WHITE, 2);
    drawPet2001TextCentredTransparent(f, 160, 120, "A NEXT  C HOLD/ROLL/SCORE", COLOR_LIGHTGRAY, 1);
  } else if (state == Y_FINISHED) {
    snprintf(t, sizeof t,
             player_total > cpu_total   ? "YOU WIN %u"
             : player_total < cpu_total ? "CPU WINS %u"
                                        : "DRAW %u",
             player_total);
    drawPet2001TextCentredTransparent(f, 160, 100, t, player_total >= cpu_total ? COLOR_GREEN : COLOR_RED, 3);
    drawPet2001TextCentredTransparent(f, 160, 140, "C NEW GAME", COLOR_LIGHTGRAY, 1);
  } else {
    snprintf(t, sizeof t, "CPU %s",
             auto_mode ? aiSpeedShortText(ai_speed) : "PLAYING");
    drawPet2001TextCentredTransparent(f, 160, 100, t, COLOR_YELLOW, 2);
    drawPet2001TextCentredTransparent(f, 160, 140, auto_mode ? "E SPEED  C STOP" : "PLEASE WAIT", COLOR_LIGHTGRAY, 1);
  }
}