Yahtzee
Source: src/yahtzee.c · 151 lines · 18 functions.
Screen captures
Views of this part running on the Mini Arcade.

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)
- Execute
rng ^= rng << 13; - Execute
rng ^= rng >> 17; - Execute
rng ^= rng << 5; - Exit this function
return 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:
- FOR loop
unsigned i=0;i<DICE;i++Repeat body while the loop condition permits; then continue below.
- Decision
!keep[i]True- Execute
dice[i]=(uint8_t)(1U+random_next()%6U);
FalseContinue after this decision.
- Execute
audioPlay(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])
- Execute
uint16_t n=0; - FOR loop
unsigned i=0;i<DICE;i++Repeat body while the loop condition permits; then continue below.
- Execute
n+=dice[i];
- Exit this function
return 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:
- Execute
uint8_t c[6]={0}; - FOR loop
unsigned i=0;i<DICE;i++Repeat body while the loop condition permits; then continue below.
- Execute
++c[dice[i]-1U];
- Decision
cat<6True- Exit this function
return c[cat]*(cat+1U);
FalseContinue after this decision.
- Decision
cat==6||cat==7True- Execute
unsigned need=cat==6?3:4; - FOR loop
unsigned i=0;i<6;i++Repeat body while the loop condition permits; then continue below.
- Decision
c[i]>=needTrue- Exit this function
return total(dice);
FalseContinue after this decision.
- Exit this function
return 0;
FalseContinue after this decision.
- Decision
cat==8True- Execute
bool p=false,t=false; - FOR loop
unsigned i=0;i<6;i++Repeat body while the loop condition permits; then continue below.
- Execute
p|=c[i]==2; - Execute
t|=c[i]==3;
- Exit this function
return p&&t?25:0;
FalseContinue after this decision.
- Decision
cat==9True- Exit this function
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;
FalseContinue after this decision.
- Decision
cat==10True- Exit this function
return (c[0]&&c[1]&&c[2]&&c[3]&&c[4])||(c[1]&&c[2]&&c[3]&&c[4]&&c[5])?40:0;
FalseContinue after this decision.
- Decision
cat==11True- FOR loop
unsigned i=0;i<6;i++Repeat body while the loop condition permits; then continue below.
- Decision
c[i]==5True- Exit this function
return 50;
FalseContinue after this decision.
- Exit this function
return 0;
FalseContinue after this decision.
- Exit this function
return 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])
- Execute
uint16_t n=0,u=0; - FOR loop
unsigned i=0;i<CATEGORIES;i++Repeat body while the loop condition permits; then continue below.
- Decision
s[i]>=0True- Execute
n+=(uint16_t)s[i]; - Decision
i<6True- Execute
u+=(uint16_t)s[i];
FalseContinue after this decision.
FalseContinue after this decision.
- Exit this function
return 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)
- FOR loop
unsigned i=0;i<CATEGORIES;i++Repeat body while the loop condition permits; then continue below.
- Execute
player_scores[i]=cpu_scores[i]=-1;
- FOR loop
unsigned i=0;i<DICE;i++Repeat body while the loop condition permits; then continue below.
- Execute
held[i]=cpu_held[i]=false;
- Execute
rolls=cpu_rolls=round=selected=category=0; - Execute
player_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:
- Execute
unsigned best=0; - Execute
int v=-1000; - FOR loop
unsigned i=0;i<CATEGORIES;i++Repeat body while the loop condition permits; then continue below.
- Decision
s[i]<0True- Execute
int n=(int)score(dice,i)-(i==12?8:0); - Decision
n>vTrue- Execute
v=n; - Execute
best=i;
FalseContinue after this decision.
FalseContinue after this decision.
- Exit this function
return 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])
- Execute
uint8_t c[6]={0},best=1; - FOR loop
unsigned i=0;i<DICE;i++Repeat body while the loop condition permits; then continue below.
- Execute
++c[dice[i]-1U];
- FOR loop
unsigned i=2;i<=6;i++Repeat body while the loop condition permits; then continue below.
- Decision
c[i-1]>c[best-1]True- Execute
best=i;
FalseContinue after this decision.
- FOR loop
unsigned i=0;i<DICE;i++Repeat body while the loop condition permits; then continue below.
- Execute
keep[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:
- FOR loop
unsigned i=0;i<DICE;i++Repeat body while the loop condition permits; then continue below.
- Execute
held[i]=false;
- Execute
rolls=1; - Execute
roll(player,held); - Decision
auto_modeTrue- Execute
ai_holds(player,held);
FalseContinue after this decision.
- Execute
state=Y_PLAYER; - Execute
started=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:
- FOR loop
unsigned i=0;i<DICE;i++Repeat body while the loop condition permits; then continue below.
- Execute
cpu_held[i]=false;
- Execute
cpu_rolls=1; - Execute
roll(cpu,cpu_held); - Execute
ai_holds(cpu,cpu_held); - Execute
state=Y_CPU_ROLL; - Execute
started=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:
- Execute
player_total=score_total(player_scores); - Execute
cpu_total=score_total(cpu_scores); - Execute
audioPlay(player_total>cpu_total ? AUDIO_SFX_WAVE_CLEAR : player_total<cpu_total ? AUDIO_SFX_GAME_OVER : AUDIO_SFX_MENU_SELECT); - Decision
!auto_modeTrue- Decision
player_total>cpu_totalTrue- Execute
++results.wins;
False- Decision
player_total<cpu_totalTrue- Execute
++results.losses;
False- Execute
++results.draws;
- Execute
game_scores_save(GAME_YAHTZEE,&results); - Decision
player_total>cpu_totalTrue- Execute
addUserGoldWon(WIN_GOLD);
FalseContinue after this decision.
- Execute
startHighScoreEntry(HIGH_SCORE_YAHTZEE,player_total,now); - Execute
state=Y_HIGH;
False- Execute
state=Y_FINISHED;
- Execute
started=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:
- Execute
rng=esp_random(); - Decision
!rngTrue- Execute
rng=1;
FalseContinue after this decision.
- Execute
game_scores_load(GAME_YAHTZEE,&results); - Execute
auto_mode=false; - Execute
ai_speed=AI_SPEED_DEFAULT_INDEX; - Execute
modeSelectionReset(&mode_input); - Execute
state=Y_SCORES; - Execute
started=now; - Execute
running=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:
- Execute
cancelHighScoreEntry(); - Execute
running=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:
- Decision
!runningTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
state==Y_MODETrue- Decision
nextTrue- Execute
clear_game(); - Execute
begin_player(now);
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
auto_modeTrue- Decision
speedTrue- Execute
cycleAISpeed(&ai_speed); - Execute
started=now;
FalseContinue after this decision.
- Decision
selectTrue- Execute
auto_mode=false; - Execute
modeSelectionReset(&mode_input); - Execute
state=Y_MODE;
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
state==Y_PLAYERTrue- Decision
nextTrue- Execute
selected=(selected+1U)%7U;
FalseContinue after this decision.
- Decision
selectTrue- Decision
selected<DICETrue- Execute
held[selected]=!held[selected]; - Execute
audioPlay(AUDIO_SFX_DICE_HOLD);
False- Decision
selected==5&&rolls<MAX_ROLLSTrue- Execute
roll(player,held); - Decision
++rolls==MAX_ROLLSTrue- FOR loop
unsigned i=0;i<DICE;i++Repeat body while the loop condition permits; then continue below.
- Execute
held[i]=false;
- Execute
state=Y_CATEGORY; - Execute
category=0; - WHILE loop
player_scores[category]>=0Repeat body while the loop condition permits; then continue below.
- Execute
++category;
FalseContinue after this decision.
False- Decision
selected==6True- Execute
state=Y_CATEGORY; - Execute
category=0; - WHILE loop
player_scores[category]>=0Repeat body while the loop condition permits; then continue below.
- Execute
++category;
FalseContinue after this decision.
FalseContinue after this decision.
False- Decision
state==Y_CATEGORYTrue- Decision
nextTrue- DO / WHILE loop
player_scores[category]>=0Run body, then test condition; repeat while true.
- Execute
category=(category+1U)%CATEGORIES;
FalseContinue after this decision.
- Decision
selectTrue- Execute
audioPlay(AUDIO_SFX_MENU_SELECT); - Execute
player_scores[category]=(int16_t)score(player,category); - Execute
player_total=score_total(player_scores); - Execute
begin_cpu(now);
FalseContinue after this decision.
False- Decision
state==Y_FINISHED&&selectTrue- Execute
clear_game(); - Execute
begin_player(now);
FalseContinue 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:
- Decision
!runningTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
state==Y_SCORES&&now-started>=SHOW_SCORES_MSTrue- Execute
state=Y_MODE; - Exit this function
return;
FalseContinue after this decision.
- Decision
state==Y_MODETrue- Decision
modeSelectionNext(&mode_input,x,y,valid,left,right,now)True- Execute
auto_mode=!auto_mode;
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
state==Y_HIGHTrue- Decision
updateHighScoreEntry(x,y,valid,now)True- Execute
state=Y_MODE;
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
state==Y_FINISHED&&auto_mode&&now-started>=aiRestartDelay()True- Execute
clear_game(); - Execute
begin_player(now); - Exit this function
return;
FalseContinue after this decision.
- Execute
uint32_t wait=auto_mode?aiSpeedDelay(ai_speed):CPU_ROLL_MS; - Decision
state==Y_PLAYER&&auto_mode&&now-started>=waitTrue- Decision
rolls<MAX_ROLLSTrue- Execute
roll(player,held); - Execute
++rolls; - Decision
rolls<MAX_ROLLSTrue- Execute
ai_holds(player,held);
False- FOR loop
unsigned i=0;i<DICE;i++Repeat body while the loop condition permits; then continue below.
- Execute
held[i]=false;
- Execute
started=now;
False- Execute
unsigned c=best_category(player,player_scores); - Execute
player_scores[c]=(int16_t)score(player,c); - Execute
player_total=score_total(player_scores); - Execute
begin_cpu(now);
False- Decision
state==Y_CPU_ROLL&&now-started>=waitTrue- Decision
cpu_rolls<MAX_ROLLSTrue- Execute
roll(cpu,cpu_held); - Execute
++cpu_rolls; - Decision
cpu_rolls<MAX_ROLLSTrue- Execute
ai_holds(cpu,cpu_held);
FalseContinue after this decision.
- Execute
started=now;
False- Execute
unsigned c=best_category(cpu,cpu_scores); - Execute
cpu_scores[c]=(int16_t)score(cpu,c); - Execute
cpu_total=score_total(cpu_scores); - Execute
state=Y_CPU_RESULT; - Execute
started=now;
False- Decision
state==Y_CPU_RESULT&&now-started>=(auto_mode?aiSpeedDelay(ai_speed):CPU_RESULT_MS)True- Decision
++round>=CATEGORIESTrue- Execute
finish(now);
False- Execute
begin_player(now);
FalseContinue 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:
- Execute
fill_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:
- Execute
const uint16_t border = selected ? COLOR_YELLOW : held_die ? COLOR_CYAN : COLOR_DARKGREY; - Execute
const int low = 12, middle = 25, high = 38; - Execute
fill_rect(frame, x, y, 50, 50, COLOR_BLACK); - Execute
fill_rect(frame, x, y, 50, 1, border); - Execute
fill_rect(frame, x, y + 49, 50, 1, border); - Execute
fill_rect(frame, x, y, 1, 50, border); - Execute
fill_rect(frame, x + 49, y, 1, 50, border); - Decision
held_dieTrue- Execute
draw_text_centred(frame, x + 25, y - 10, "H", 1, COLOR_CYAN);
FalseContinue after this decision.
- Decision
value < 1U || value > 6UTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
value == 1U || value == 3U || value == 5UTrue- Execute
pip(frame, x + middle, y + middle);
FalseContinue after this decision.
- Decision
value >= 2UTrue- Execute
pip(frame, x + low, y + low); - Execute
pip(frame, x + high, y + high);
FalseContinue after this decision.
- Decision
value >= 4UTrue- Execute
pip(frame, x + high, y + low); - Execute
pip(frame, x + low, y + high);
FalseContinue after this decision.
- Decision
value == 6UTrue- Execute
pip(frame, x + low, y + middle); - Execute
pip(frame, x + high, y + middle);
FalseContinue 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:
- Decision
!runningTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
state == Y_HIGHTrue- Execute
drawHighScoreEntry(f); - Exit this function
return;
FalseContinue after this decision.
- Execute
clear(f, COLOR_BLACK); - Execute
char t[40]; - Decision
state == Y_SCORESTrue- Execute
draw_text_centred(f, 160, 8, "YAHTZEE", 4, COLOR_CYAN); - Execute
draw_text_centred(f, 160, 49, "GAME RECORD", 2, COLOR_LIGHTGRAY); - Execute
draw_text_centred(f, 55, 78, "WINS", 1, COLOR_GREEN); - Execute
draw_text_centred(f, 160, 78, "LOSSES", 1, COLOR_RED); - Execute
draw_text_centred(f, 265, 78, "DRAWS", 1, COLOR_YELLOW); - Execute
snprintf(t, sizeof t, "%u", (unsigned)results.wins); - Execute
draw_text_centred(f, 55, 96, t, 3, COLOR_GREEN); - Execute
snprintf(t, sizeof t, "%u", (unsigned)results.losses); - Execute
draw_text_centred(f, 160, 96, t, 3, COLOR_RED); - Execute
snprintf(t, sizeof t, "%u", (unsigned)results.draws); - Execute
draw_text_centred(f, 265, 96, t, 3, COLOR_YELLOW); - Execute
draw_text_centred(f, 160, 145, "CHOOSE A MODE", 1, COLOR_LIGHTGRAY); - Exit this function
return;
FalseContinue after this decision.
- Decision
state == Y_MODETrue- Execute
draw_text_centred(f, 160, 8, "YAHTZEE", 4, COLOR_CYAN); - Execute
draw_text_centred(f, 160, 55, "SELECT MODE", 2, COLOR_LIGHTGRAY); - Execute
draw_text_centred(f, 100, 84, "PLAY", 2, auto_mode ? COLOR_LIGHTGRAY : COLOR_YELLOW); - Execute
draw_text_centred(f, 220, 84, "AUTO", 2, auto_mode ? COLOR_YELLOW : COLOR_LIGHTGRAY); - Execute
draw_text_centred(f, 160, 142, "D/B/STICK CHANGE A/K START", 1, COLOR_LIGHTGRAY); - Exit this function
return;
FalseContinue after this decision.
- Execute
const uint8_t *d = (state == Y_CPU_ROLL || state == Y_CPU_RESULT) ? cpu : player; - Execute
const bool *h = (state == Y_CPU_ROLL || state == Y_CPU_RESULT) ? cpu_held : held; - FOR loop
unsigned i = 0; i < DICE; i++Repeat body while the loop condition permits; then continue below.
- Execute
die(f, d[i], 7 + (int)i * 62, 28, state == Y_PLAYER && selected == i, h[i]);
- Execute
snprintf(t, sizeof t, "ROUND %u/13 YOU %u CPU %u", round + 1U, player_total, cpu_total); - Execute
draw_text_centred(f, 160, 5, t, 1, COLOR_WHITE); - Decision
state == Y_CATEGORYTrue- Execute
snprintf(t, sizeof t, "%s %u", names[category], score(player, category)); - Execute
draw_text_centred(f, 160, 95, t, 2, COLOR_YELLOW); - Execute
draw_text_centred(f, 160, 130, "A NEXT C SCORE", 1, COLOR_LIGHTGRAY);
False- Decision
state == Y_PLAYERTrue- Execute
snprintf(t, sizeof t, "ROLL %u/3", rolls); - Execute
draw_text_centred(f, 160, 90, t, 2, COLOR_WHITE); - Execute
draw_text_centred(f, 160, 120, "A NEXT C HOLD/ROLL/SCORE", 1, COLOR_LIGHTGRAY);
False- Decision
state == Y_FINISHEDTrue- Execute
snprintf(t, sizeof t, player_total > cpu_total ? "YOU WIN %u" : player_total < cpu_total ? "CPU WINS %u" : "DRAW %u", player_total); - Execute
draw_text_centred(f, 160, 100, t, 3, player_total >= cpu_total ? COLOR_GREEN : COLOR_RED); - Execute
draw_text_centred(f, 160, 140, "C NEW GAME", 1, COLOR_LIGHTGRAY);
False- Execute
snprintf(t, sizeof t, "CPU %s", auto_mode ? aiSpeedShortText(ai_speed) : "PLAYING"); - Execute
draw_text_centred(f, 160, 100, t, 2, COLOR_YELLOW); - Execute
draw_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);
}
}