Pig
Source: src/pig.c · 276 lines · 17 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
setMessage() · source line 40
static void setMessage(const char *message)
- Execute
snprintf(game_message, sizeof(game_message), "%s", message);
Read original function source
static void setMessage(const char *message)
{
snprintf(game_message, sizeof(game_message), "%s", message);
}loadScores() · source line 45
static void loadScores(void)
Calls into this documentation:
- Execute
bool found = false; - Execute
scores = (pig_scores_t){ .version = 1U }; - Decision
!game_storage_read(PIG_SCORE_NAMESPACE, PIG_SCORE_KEY, &scores, sizeof(scores), &found) || !found || scores.version != 1UTrue- Execute
scores = (pig_scores_t){ .version = 1U };
FalseContinue after this decision.
Read original function source
static void loadScores(void)
{
bool found = false;
scores = (pig_scores_t){ .version = 1U };
if (!game_storage_read(PIG_SCORE_NAMESPACE, PIG_SCORE_KEY, &scores, sizeof(scores), &found) ||
!found || scores.version != 1U) scores = (pig_scores_t){ .version = 1U };
}saveScores() · source line 53
static void saveScores(void)
Calls into this documentation:
- Execute
(void)game_storage_write(PIG_SCORE_NAMESPACE, PIG_SCORE_KEY, &scores, sizeof(scores));
Read original function source
static void saveScores(void)
{
(void)game_storage_write(PIG_SCORE_NAMESPACE, PIG_SCORE_KEY, &scores, sizeof(scores));
}finishGame() · source line 58
static void finishGame(pig_side_t winner, uint32_t now_ms)
Calls into this documentation:
- Execute
audioPlay(winner == PIG_PLAYER ? AUDIO_SFX_WAVE_CLEAR : AUDIO_SFX_GAME_OVER); - Execute
state = PIG_FINISHED; - Execute
turn_score = 0U; - Execute
auto_restart_started = now_ms; - Execute
auto_seconds_shown = (uint8_t)((aiRestartDelay() + 999U) / 1000U); - Decision
auto_modeTrue- Execute
setMessage(winner == PIG_PLAYER ? "A WINS" : "B WINS");
False- Decision
winner == PIG_PLAYERTrue- Execute
++scores.wins; - Execute
++scores.streak; - Execute
addUserGoldWon(PIG_WIN_GOLD); - Execute
saveScores(); - Execute
setMessage("YOU WIN +10 GOLD");
False- Execute
++scores.losses; - Execute
scores.streak = 0U; - Execute
saveScores(); - Execute
setMessage("CPU WINS");
- Execute
redraw_needed = true;
Read original function source
static void finishGame(pig_side_t winner, uint32_t now_ms)
{
audioPlay(winner == PIG_PLAYER ? AUDIO_SFX_WAVE_CLEAR : AUDIO_SFX_GAME_OVER);
state = PIG_FINISHED;
turn_score = 0U;
auto_restart_started = now_ms;
auto_seconds_shown = (uint8_t)((aiRestartDelay() + 999U) / 1000U);
if (auto_mode) setMessage(winner == PIG_PLAYER ? "A WINS" : "B WINS");
else if (winner == PIG_PLAYER) {
++scores.wins;
++scores.streak;
addUserGoldWon(PIG_WIN_GOLD);
saveScores();
setMessage("YOU WIN +10 GOLD");
} else {
++scores.losses;
scores.streak = 0U;
saveScores();
setMessage("CPU WINS");
}
redraw_needed = true;
}scheduleComputer() · source line 82
static void scheduleComputer(uint32_t now_ms)
- Execute
state_started = now_ms;
Read original function source
static void scheduleComputer(uint32_t now_ms) { state_started = now_ms; }changeTurn() · source line 84
static void changeTurn(uint32_t now_ms)
Calls into this documentation:
- Execute
turn_score = 0U; - Execute
current_side = current_side == PIG_PLAYER ? PIG_CPU : PIG_PLAYER; - Execute
selected_action = PIG_ROLL; - Execute
setMessage(auto_mode ? (current_side == PIG_PLAYER ? "A TURN" : "B TURN") : (current_side == PIG_PLAYER ? "YOUR TURN" : "CPU TURN")); - Decision
auto_mode || current_side == PIG_CPUTrue- Execute
scheduleComputer(now_ms);
FalseContinue after this decision.
Read original function source
static void changeTurn(uint32_t now_ms)
{
turn_score = 0U;
current_side = current_side == PIG_PLAYER ? PIG_CPU : PIG_PLAYER;
selected_action = PIG_ROLL;
setMessage(auto_mode ? (current_side == PIG_PLAYER ? "A TURN" : "B TURN") :
(current_side == PIG_PLAYER ? "YOUR TURN" : "CPU TURN"));
if (auto_mode || current_side == PIG_CPU) scheduleComputer(now_ms);
}beginGame() · source line 94
static void beginGame(uint32_t now_ms)
Calls into this documentation:
- Execute
player_score = 0U; - Execute
cpu_score = 0U; - Execute
turn_score = 0U; - Execute
die_value = 0U; - Execute
current_side = PIG_PLAYER; - Execute
selected_action = PIG_ROLL; - Execute
state = PIG_PLAYING; - Execute
setMessage(auto_mode ? "A STARTS" : "YOUR TURN"); - Decision
auto_modeTrue- Execute
scheduleComputer(now_ms);
FalseContinue after this decision.
- Execute
redraw_needed = true;
Read original function source
static void beginGame(uint32_t now_ms)
{
player_score = 0U;
cpu_score = 0U;
turn_score = 0U;
die_value = 0U;
current_side = PIG_PLAYER;
selected_action = PIG_ROLL;
state = PIG_PLAYING;
setMessage(auto_mode ? "A STARTS" : "YOUR TURN");
if (auto_mode) scheduleComputer(now_ms);
redraw_needed = true;
}rollCurrentTurn() · source line 108
static void rollCurrentTurn(uint32_t now_ms)
Calls into this documentation:
- Execute
die_value = (uint8_t)(esp_random() % 6U) + 1U; - Execute
audioPlay(AUDIO_SFX_DICE_ROLL); - Decision
die_value == 1UTrue- Execute
changeTurn(now_ms); - Execute
setMessage("PIG! TURN LOST");
False- Execute
turn_score += die_value; - Execute
snprintf(game_message, sizeof(game_message), "ROLLED %u", (unsigned)die_value); - Decision
auto_mode || current_side == PIG_CPUTrue- Execute
scheduleComputer(now_ms);
FalseContinue after this decision.
- Execute
redraw_needed = true;
Read original function source
static void rollCurrentTurn(uint32_t now_ms)
{
die_value = (uint8_t)(esp_random() % 6U) + 1U;
audioPlay(AUDIO_SFX_DICE_ROLL);
if (die_value == 1U) {
changeTurn(now_ms);
setMessage("PIG! TURN LOST");
} else {
turn_score += die_value;
snprintf(game_message, sizeof(game_message), "ROLLED %u", (unsigned)die_value);
if (auto_mode || current_side == PIG_CPU) scheduleComputer(now_ms);
}
redraw_needed = true;
}holdCurrentTurn() · source line 123
static void holdCurrentTurn(uint32_t now_ms)
Calls into this documentation:
- Execute
audioPlay(AUDIO_SFX_DICE_HOLD); - Execute
uint16_t *score = current_side == PIG_PLAYER ? &player_score : &cpu_score; - Execute
*score += turn_score; - Execute
const pig_side_t holding_side = current_side; - Execute
turn_score = 0U; - Decision
*score >= PIG_TARGET_SCORETrue- Execute
finishGame(holding_side, now_ms); - Exit this function
return;
FalseContinue after this decision.
- Execute
changeTurn(now_ms); - Execute
setMessage("SCORE BANKED"); - Execute
redraw_needed = true;
Read original function source
static void holdCurrentTurn(uint32_t now_ms)
{
audioPlay(AUDIO_SFX_DICE_HOLD);
uint16_t *score = current_side == PIG_PLAYER ? &player_score : &cpu_score;
*score += turn_score;
const pig_side_t holding_side = current_side;
turn_score = 0U;
if (*score >= PIG_TARGET_SCORE) {
finishGame(holding_side, now_ms);
return;
}
changeTurn(now_ms);
setMessage("SCORE BANKED");
redraw_needed = true;
}computerShouldHold() · source line 139
static bool computerShouldHold(void)
- Execute
const uint16_t own = current_side == PIG_PLAYER ? player_score : cpu_score; - Execute
const uint16_t other = current_side == PIG_PLAYER ? cpu_score : player_score; - Decision
own + turn_score >= PIG_TARGET_SCORETrue- Exit this function
return true;
FalseContinue after this decision.
- Execute
uint16_t hold_at = 20U; - Decision
own > other + 25UTrue- Execute
hold_at = 15U;
False- Decision
other >= 85U || other > own + 25UTrue- Execute
hold_at = 25U;
FalseContinue after this decision.
- Exit this function
return turn_score >= hold_at;
Read original function source
static bool computerShouldHold(void)
{
const uint16_t own = current_side == PIG_PLAYER ? player_score : cpu_score;
const uint16_t other = current_side == PIG_PLAYER ? cpu_score : player_score;
if (own + turn_score >= PIG_TARGET_SCORE) return true;
uint16_t hold_at = 20U;
if (own > other + 25U) hold_at = 15U;
else if (other >= 85U || other > own + 25U) hold_at = 25U;
return turn_score >= hold_at;
}startPig() · source line 150
void startPig(uint32_t now_ms)
Calls into this documentation:
- Execute
loadScores(); - Execute
running = true; - Execute
auto_mode = false; - Execute
ai_speed = AI_SPEED_DEFAULT_INDEX; - Execute
modeSelectionReset(&mode_input); - Execute
state = PIG_SCORES; - Execute
state_started = now_ms; - Execute
redraw_needed = true;
Read original function source
void startPig(uint32_t now_ms)
{
loadScores();
running = true;
auto_mode = false;
ai_speed = AI_SPEED_DEFAULT_INDEX;
modeSelectionReset(&mode_input);
state = PIG_SCORES;
state_started = now_ms;
redraw_needed = true;
}stopPig() · source line 162
void stopPig(void)
- Execute
running = false;
Read original function source
void stopPig(void) { running = false; }pigAction() · source line 164
void pigAction(bool next, bool speed, bool select, uint32_t now_ms)
Calls into this documentation:
- Decision
!runningTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
state == PIG_MODETrue- Decision
nextTrue- Execute
beginGame(now_ms);
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
auto_modeTrue- Decision
speedTrue- Execute
cycleAISpeed(&ai_speed); - Execute
scheduleComputer(now_ms); - Execute
redraw_needed = true;
FalseContinue after this decision.
- Decision
selectTrue- Execute
auto_mode = false; - Execute
state = PIG_MODE; - Execute
modeSelectionReset(&mode_input); - Execute
redraw_needed = true;
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
state == PIG_FINISHED && selectTrue- Execute
beginGame(now_ms); - Exit this function
return;
FalseContinue after this decision.
- Decision
state == PIG_PLAYING && current_side == PIG_PLAYERTrue- Decision
nextTrue- Execute
selected_action = selected_action == PIG_ROLL ? PIG_HOLD : PIG_ROLL; - Execute
redraw_needed = true;
FalseContinue after this decision.
- Decision
selectTrue- Decision
selected_action == PIG_ROLLTrue- Execute
rollCurrentTurn(now_ms);
False- Execute
holdCurrentTurn(now_ms);
FalseContinue after this decision.
FalseContinue after this decision.
Read original function source
void pigAction(bool next, bool speed, bool select, uint32_t now_ms)
{
if (!running) return;
if (state == PIG_MODE) {
if (next) beginGame(now_ms);
return;
}
if (auto_mode) {
if (speed) { cycleAISpeed(&ai_speed); scheduleComputer(now_ms); redraw_needed = true; }
if (select) { auto_mode = false; state = PIG_MODE; modeSelectionReset(&mode_input); redraw_needed = true; }
return;
}
if (state == PIG_FINISHED && select) { beginGame(now_ms); return; }
if (state == PIG_PLAYING && current_side == PIG_PLAYER) {
if (next) { selected_action = selected_action == PIG_ROLL ? PIG_HOLD : PIG_ROLL; redraw_needed = true; }
if (select) {
if (selected_action == PIG_ROLL) rollCurrentTurn(now_ms);
else holdCurrentTurn(now_ms);
}
}
}updatePig() · source line 186
void updatePig(int x_offset, int y_offset, bool joystick_valid, bool left, bool right,
uint32_t now_ms)Calls into this documentation:
- Decision
!runningTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
state == PIG_SCORES && now_ms - state_started >= PIG_SHOW_SCORES_MSTrue- Execute
state = PIG_MODE; - Execute
redraw_needed = true; - Exit this function
return;
FalseContinue after this decision.
- Decision
state == PIG_MODETrue- Decision
modeSelectionNext(&mode_input, x_offset, y_offset, joystick_valid, left, right, now_ms)True- Execute
auto_mode = !auto_mode; - Execute
redraw_needed = true;
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
state == PIG_FINISHED && auto_modeTrue- Execute
const uint32_t delay = aiRestartDelay(); - Execute
const uint32_t elapsed = now_ms - auto_restart_started; - Decision
elapsed >= delayTrue- Execute
beginGame(now_ms); - Exit this function
return;
FalseContinue after this decision.
- Execute
const uint8_t seconds_left = (uint8_t)((delay - elapsed + 999U) / 1000U); - Decision
seconds_left != auto_seconds_shownTrue- Execute
auto_seconds_shown = seconds_left; - Execute
redraw_needed = true;
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
state == PIG_PLAYING && (auto_mode || current_side == PIG_CPU) && now_ms - state_started >= aiSpeedDelay(ai_speed)True- Decision
computerShouldHold()True- Execute
holdCurrentTurn(now_ms);
False- Execute
rollCurrentTurn(now_ms);
FalseContinue after this decision.
Read original function source
void updatePig(int x_offset, int y_offset, bool joystick_valid, bool left, bool right,
uint32_t now_ms)
{
if (!running) return;
if (state == PIG_SCORES && now_ms - state_started >= PIG_SHOW_SCORES_MS) {
state = PIG_MODE;
redraw_needed = true;
return;
}
if (state == PIG_MODE) {
if (modeSelectionNext(&mode_input, x_offset, y_offset, joystick_valid, left, right, now_ms)) {
auto_mode = !auto_mode;
redraw_needed = true;
}
return;
}
if (state == PIG_FINISHED && auto_mode) {
const uint32_t delay = aiRestartDelay();
const uint32_t elapsed = now_ms - auto_restart_started;
if (elapsed >= delay) { beginGame(now_ms); return; }
const uint8_t seconds_left = (uint8_t)((delay - elapsed + 999U) / 1000U);
if (seconds_left != auto_seconds_shown) { auto_seconds_shown = seconds_left; redraw_needed = true; }
return;
}
if (state == PIG_PLAYING && (auto_mode || current_side == PIG_CPU) &&
now_ms - state_started >= aiSpeedDelay(ai_speed)) {
if (computerShouldHold()) holdCurrentTurn(now_ms);
else rollCurrentTurn(now_ms);
}
}drawPip() · source line 217
static void drawPip(uint16_t *frame, int x, int y)
Calls into this documentation:
- Execute
fill_circle(frame, x, y, 4, COLOR_WHITE);
Read original function source
static void drawPip(uint16_t *frame, int x, int y) { fill_circle(frame, x, y, 4, COLOR_WHITE); }drawDie() · source line 219
static void drawDie(uint16_t *frame)
Calls into this documentation:
- Execute
const int x = 126, y = 48, size = 68; - Execute
fill_rect(frame, x, y, size, size, COLOR_DARKGREY); - Execute
draw_rect(frame, x, y, size, size, COLOR_WHITE); - Decision
die_value == 0UTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
die_value == 1U || die_value == 3U || die_value == 5UTrue- Execute
drawPip(frame, x + 34, y + 34);
FalseContinue after this decision.
- Decision
die_value >= 2UTrue- Execute
drawPip(frame, x + 17, y + 17); - Execute
drawPip(frame, x + 51, y + 51);
FalseContinue after this decision.
- Decision
die_value >= 4UTrue- Execute
drawPip(frame, x + 51, y + 17); - Execute
drawPip(frame, x + 17, y + 51);
FalseContinue after this decision.
- Decision
die_value == 6UTrue- Execute
drawPip(frame, x + 17, y + 34); - Execute
drawPip(frame, x + 51, y + 34);
FalseContinue after this decision.
Read original function source
static void drawDie(uint16_t *frame)
{
const int x = 126, y = 48, size = 68;
fill_rect(frame, x, y, size, size, COLOR_DARKGREY);
draw_rect(frame, x, y, size, size, COLOR_WHITE);
if (die_value == 0U) return;
if (die_value == 1U || die_value == 3U || die_value == 5U) drawPip(frame, x + 34, y + 34);
if (die_value >= 2U) { drawPip(frame, x + 17, y + 17); drawPip(frame, x + 51, y + 51); }
if (die_value >= 4U) { drawPip(frame, x + 51, y + 17); drawPip(frame, x + 17, y + 51); }
if (die_value == 6U) { drawPip(frame, x + 17, y + 34); drawPip(frame, x + 51, y + 34); }
}drawPig() · source line 231
void drawPig(uint16_t *frame)
Calls into this documentation:
- Decision
!running || !redraw_neededTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
redraw_needed = false; - Execute
clear(frame, COLOR_BLACK); - Execute
char text[40]; - Decision
state == PIG_SCORESTrue- Execute
draw_text_centred(frame, 160, 8, "PIG", 4, COLOR_CYAN); - Execute
draw_text_centred(frame, 160, 45, "GAME RECORD", 2, COLOR_LIGHTGRAY); - Execute
snprintf(text, sizeof(text), "WINS %u LOSSES %u", (unsigned)scores.wins, (unsigned)scores.losses); - Execute
draw_text_centred(frame, 160, 78, text, 2, COLOR_WHITE); - Execute
snprintf(text, sizeof(text), "STREAK %u", (unsigned)scores.streak); - Execute
draw_text_centred(frame, 160, 108, text, 2, COLOR_GOLD); - Execute
draw_text_centred(frame, 160, 140, "FIRST TO 100", 1, COLOR_LIGHTGRAY); - Exit this function
return;
FalseContinue after this decision.
- Decision
state == PIG_MODETrue- Execute
draw_text_centred(frame, 160, 8, "PIG", 4, COLOR_CYAN); - Execute
draw_text_centred(frame, 160, 52, "SELECT MODE", 2, COLOR_LIGHTGRAY); - Execute
draw_text_centred(frame, 100, 84, "PLAY", 2, auto_mode ? COLOR_LIGHTGRAY : COLOR_YELLOW); - Execute
draw_text_centred(frame, 220, 84, "AUTO", 2, auto_mode ? COLOR_YELLOW : COLOR_LIGHTGRAY); - Execute
draw_text_centred(frame, 160, 115, "WIN 10 GOLD", 2, COLOR_GOLD); - Execute
draw_text_centred(frame, 160, 145, "D/B/STICK CHANGE A/K START", 1, COLOR_LIGHTGRAY); - Exit this function
return;
FalseContinue after this decision.
- Execute
draw_text(frame, 6, 3, "PIG", 3, COLOR_CYAN); - Execute
draw_text(frame, 65, 12, "FIRST TO 100", 1, COLOR_LIGHTGRAY); - Execute
drawGameGold(frame, 260, 2, 55, true); - Execute
draw_text_centred(frame, 50, 40, auto_mode ? "A" : "YOU", 2, current_side == PIG_PLAYER ? COLOR_YELLOW : COLOR_CYAN); - Execute
draw_text_centred(frame, 270, 40, auto_mode ? "B" : "CPU", 2, current_side == PIG_CPU ? COLOR_YELLOW : COLOR_CYAN); - Execute
snprintf(text, sizeof(text), "%u", (unsigned)player_score); - Execute
draw_text_centred(frame, 50, 68, text, 4, COLOR_WHITE); - Execute
snprintf(text, sizeof(text), "%u", (unsigned)cpu_score); - Execute
draw_text_centred(frame, 270, 68, text, 4, COLOR_WHITE); - Execute
drawDie(frame); - Execute
snprintf(text, sizeof(text), "TURN %u", (unsigned)turn_score); - Execute
draw_text_centred(frame, 160, 119, text, 2, COLOR_CYAN); - Execute
draw_text_centred(frame, 160, 137, game_message, 1, COLOR_WHITE); - Decision
state == PIG_PLAYING && !auto_mode && current_side == PIG_PLAYERTrue- Execute
draw_text_centred(frame, 80, 155, "ROLL", 2, selected_action == PIG_ROLL ? COLOR_YELLOW : COLOR_WHITE); - Execute
draw_text_centred(frame, 240, 155, "HOLD", 2, selected_action == PIG_HOLD ? COLOR_YELLOW : COLOR_WHITE);
False- Decision
auto_modeTrue- Decision
state == PIG_FINISHEDTrue- Execute
snprintf(text, sizeof(text), "NEXT GAME %u E SPEED C STOP", (unsigned)auto_seconds_shown);
False- Execute
snprintf(text, sizeof(text), "E SPEED %s C STOP", aiSpeedShortText(ai_speed));
- Execute
draw_text_centred(frame, 160, 155, text, 1, COLOR_LIGHTGRAY);
False- Decision
state == PIG_FINISHEDTrue- Execute
draw_text_centred(frame, 160, 155, "C NEW GAME", 1, COLOR_LIGHTGRAY);
False- Execute
draw_text_centred(frame, 160, 155, "CPU THINKING", 1, COLOR_LIGHTGRAY);
Read original function source
void drawPig(uint16_t *frame)
{
if (!running || !redraw_needed) return;
redraw_needed = false;
clear(frame, COLOR_BLACK);
char text[40];
if (state == PIG_SCORES) {
draw_text_centred(frame, 160, 8, "PIG", 4, COLOR_CYAN);
draw_text_centred(frame, 160, 45, "GAME RECORD", 2, COLOR_LIGHTGRAY);
snprintf(text, sizeof(text), "WINS %u LOSSES %u", (unsigned)scores.wins, (unsigned)scores.losses);
draw_text_centred(frame, 160, 78, text, 2, COLOR_WHITE);
snprintf(text, sizeof(text), "STREAK %u", (unsigned)scores.streak);
draw_text_centred(frame, 160, 108, text, 2, COLOR_GOLD);
draw_text_centred(frame, 160, 140, "FIRST TO 100", 1, COLOR_LIGHTGRAY);
return;
}
if (state == PIG_MODE) {
draw_text_centred(frame, 160, 8, "PIG", 4, COLOR_CYAN);
draw_text_centred(frame, 160, 52, "SELECT MODE", 2, COLOR_LIGHTGRAY);
draw_text_centred(frame, 100, 84, "PLAY", 2, auto_mode ? COLOR_LIGHTGRAY : COLOR_YELLOW);
draw_text_centred(frame, 220, 84, "AUTO", 2, auto_mode ? COLOR_YELLOW : COLOR_LIGHTGRAY);
draw_text_centred(frame, 160, 115, "WIN 10 GOLD", 2, COLOR_GOLD);
draw_text_centred(frame, 160, 145, "D/B/STICK CHANGE A/K START", 1, COLOR_LIGHTGRAY);
return;
}
draw_text(frame, 6, 3, "PIG", 3, COLOR_CYAN);
draw_text(frame, 65, 12, "FIRST TO 100", 1, COLOR_LIGHTGRAY);
drawGameGold(frame, 260, 2, 55, true);
draw_text_centred(frame, 50, 40, auto_mode ? "A" : "YOU", 2, current_side == PIG_PLAYER ? COLOR_YELLOW : COLOR_CYAN);
draw_text_centred(frame, 270, 40, auto_mode ? "B" : "CPU", 2, current_side == PIG_CPU ? COLOR_YELLOW : COLOR_CYAN);
snprintf(text, sizeof(text), "%u", (unsigned)player_score); draw_text_centred(frame, 50, 68, text, 4, COLOR_WHITE);
snprintf(text, sizeof(text), "%u", (unsigned)cpu_score); draw_text_centred(frame, 270, 68, text, 4, COLOR_WHITE);
drawDie(frame);
snprintf(text, sizeof(text), "TURN %u", (unsigned)turn_score); draw_text_centred(frame, 160, 119, text, 2, COLOR_CYAN);
draw_text_centred(frame, 160, 137, game_message, 1, COLOR_WHITE);
if (state == PIG_PLAYING && !auto_mode && current_side == PIG_PLAYER) {
draw_text_centred(frame, 80, 155, "ROLL", 2, selected_action == PIG_ROLL ? COLOR_YELLOW : COLOR_WHITE);
draw_text_centred(frame, 240, 155, "HOLD", 2, selected_action == PIG_HOLD ? COLOR_YELLOW : COLOR_WHITE);
} else if (auto_mode) {
if (state == PIG_FINISHED) snprintf(text, sizeof(text), "NEXT GAME %u E SPEED C STOP", (unsigned)auto_seconds_shown);
else snprintf(text, sizeof(text), "E SPEED %s C STOP", aiSpeedShortText(ai_speed));
draw_text_centred(frame, 160, 155, text, 1, COLOR_LIGHTGRAY);
} else if (state == PIG_FINISHED) draw_text_centred(frame, 160, 155, "C NEW GAME", 1, COLOR_LIGHTGRAY);
else draw_text_centred(frame, 160, 155, "CPU THINKING", 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 "pig.h"
#include "ai_speed.h"
#include "audio.h"
#include "game_graphics.h"
#include "game_storage.h"
#include "gold.h"
#include "mode_selection.h"
#include "PET2001_font.h"
#include "esp_random.h"
#include <stdio.h>
#define PIG_TARGET_SCORE 100U
#define PIG_SHOW_SCORES_MS 2000U
#define PIG_WIN_GOLD 10U
#define PIG_SCORE_NAMESPACE "arcade_results"
#define PIG_SCORE_KEY "pig"
typedef enum { PIG_SCORES, PIG_MODE, PIG_PLAYING, PIG_FINISHED } pig_state_t;
typedef enum { PIG_PLAYER, PIG_CPU } pig_side_t;
typedef enum { PIG_ROLL, PIG_HOLD } pig_action_t;
typedef struct {
uint32_t version;
uint32_t wins;
uint32_t losses;
uint32_t streak;
} pig_scores_t;
static pig_scores_t scores;
static pig_state_t state;
static pig_side_t current_side;
static pig_action_t selected_action;
static uint16_t player_score, cpu_score, turn_score;
static uint8_t die_value, ai_speed, auto_seconds_shown;
static uint32_t state_started, auto_restart_started;
static bool running, auto_mode, redraw_needed;
static mode_selection_input_t mode_input;
static char game_message[32];
static void setMessage(const char *message)
{
snprintf(game_message, sizeof(game_message), "%s", message);
}
static void loadScores(void)
{
bool found = false;
scores = (pig_scores_t){ .version = 1U };
if (!game_storage_read(PIG_SCORE_NAMESPACE, PIG_SCORE_KEY, &scores, sizeof(scores), &found) ||
!found || scores.version != 1U) scores = (pig_scores_t){ .version = 1U };
}
static void saveScores(void)
{
(void)game_storage_write(PIG_SCORE_NAMESPACE, PIG_SCORE_KEY, &scores, sizeof(scores));
}
static void finishGame(pig_side_t winner, uint32_t now_ms)
{
audioPlay(winner == PIG_PLAYER ? AUDIO_SFX_WAVE_CLEAR : AUDIO_SFX_GAME_OVER);
state = PIG_FINISHED;
turn_score = 0U;
auto_restart_started = now_ms;
auto_seconds_shown = (uint8_t)((aiRestartDelay() + 999U) / 1000U);
if (auto_mode) setMessage(winner == PIG_PLAYER ? "A WINS" : "B WINS");
else if (winner == PIG_PLAYER) {
++scores.wins;
++scores.streak;
addUserGoldWon(PIG_WIN_GOLD);
saveScores();
setMessage("YOU WIN +10 GOLD");
} else {
++scores.losses;
scores.streak = 0U;
saveScores();
setMessage("CPU WINS");
}
redraw_needed = true;
}
static void scheduleComputer(uint32_t now_ms) { state_started = now_ms; }
static void changeTurn(uint32_t now_ms)
{
turn_score = 0U;
current_side = current_side == PIG_PLAYER ? PIG_CPU : PIG_PLAYER;
selected_action = PIG_ROLL;
setMessage(auto_mode ? (current_side == PIG_PLAYER ? "A TURN" : "B TURN") :
(current_side == PIG_PLAYER ? "YOUR TURN" : "CPU TURN"));
if (auto_mode || current_side == PIG_CPU) scheduleComputer(now_ms);
}
static void beginGame(uint32_t now_ms)
{
player_score = 0U;
cpu_score = 0U;
turn_score = 0U;
die_value = 0U;
current_side = PIG_PLAYER;
selected_action = PIG_ROLL;
state = PIG_PLAYING;
setMessage(auto_mode ? "A STARTS" : "YOUR TURN");
if (auto_mode) scheduleComputer(now_ms);
redraw_needed = true;
}
static void rollCurrentTurn(uint32_t now_ms)
{
die_value = (uint8_t)(esp_random() % 6U) + 1U;
audioPlay(AUDIO_SFX_DICE_ROLL);
if (die_value == 1U) {
changeTurn(now_ms);
setMessage("PIG! TURN LOST");
} else {
turn_score += die_value;
snprintf(game_message, sizeof(game_message), "ROLLED %u", (unsigned)die_value);
if (auto_mode || current_side == PIG_CPU) scheduleComputer(now_ms);
}
redraw_needed = true;
}
static void holdCurrentTurn(uint32_t now_ms)
{
audioPlay(AUDIO_SFX_DICE_HOLD);
uint16_t *score = current_side == PIG_PLAYER ? &player_score : &cpu_score;
*score += turn_score;
const pig_side_t holding_side = current_side;
turn_score = 0U;
if (*score >= PIG_TARGET_SCORE) {
finishGame(holding_side, now_ms);
return;
}
changeTurn(now_ms);
setMessage("SCORE BANKED");
redraw_needed = true;
}
static bool computerShouldHold(void)
{
const uint16_t own = current_side == PIG_PLAYER ? player_score : cpu_score;
const uint16_t other = current_side == PIG_PLAYER ? cpu_score : player_score;
if (own + turn_score >= PIG_TARGET_SCORE) return true;
uint16_t hold_at = 20U;
if (own > other + 25U) hold_at = 15U;
else if (other >= 85U || other > own + 25U) hold_at = 25U;
return turn_score >= hold_at;
}
void startPig(uint32_t now_ms)
{
loadScores();
running = true;
auto_mode = false;
ai_speed = AI_SPEED_DEFAULT_INDEX;
modeSelectionReset(&mode_input);
state = PIG_SCORES;
state_started = now_ms;
redraw_needed = true;
}
void stopPig(void) { running = false; }
void pigAction(bool next, bool speed, bool select, uint32_t now_ms)
{
if (!running) return;
if (state == PIG_MODE) {
if (next) beginGame(now_ms);
return;
}
if (auto_mode) {
if (speed) { cycleAISpeed(&ai_speed); scheduleComputer(now_ms); redraw_needed = true; }
if (select) { auto_mode = false; state = PIG_MODE; modeSelectionReset(&mode_input); redraw_needed = true; }
return;
}
if (state == PIG_FINISHED && select) { beginGame(now_ms); return; }
if (state == PIG_PLAYING && current_side == PIG_PLAYER) {
if (next) { selected_action = selected_action == PIG_ROLL ? PIG_HOLD : PIG_ROLL; redraw_needed = true; }
if (select) {
if (selected_action == PIG_ROLL) rollCurrentTurn(now_ms);
else holdCurrentTurn(now_ms);
}
}
}
void updatePig(int x_offset, int y_offset, bool joystick_valid, bool left, bool right,
uint32_t now_ms)
{
if (!running) return;
if (state == PIG_SCORES && now_ms - state_started >= PIG_SHOW_SCORES_MS) {
state = PIG_MODE;
redraw_needed = true;
return;
}
if (state == PIG_MODE) {
if (modeSelectionNext(&mode_input, x_offset, y_offset, joystick_valid, left, right, now_ms)) {
auto_mode = !auto_mode;
redraw_needed = true;
}
return;
}
if (state == PIG_FINISHED && auto_mode) {
const uint32_t delay = aiRestartDelay();
const uint32_t elapsed = now_ms - auto_restart_started;
if (elapsed >= delay) { beginGame(now_ms); return; }
const uint8_t seconds_left = (uint8_t)((delay - elapsed + 999U) / 1000U);
if (seconds_left != auto_seconds_shown) { auto_seconds_shown = seconds_left; redraw_needed = true; }
return;
}
if (state == PIG_PLAYING && (auto_mode || current_side == PIG_CPU) &&
now_ms - state_started >= aiSpeedDelay(ai_speed)) {
if (computerShouldHold()) holdCurrentTurn(now_ms);
else rollCurrentTurn(now_ms);
}
}
static void drawPip(uint16_t *frame, int x, int y) { fill_circle(frame, x, y, 4, COLOR_WHITE); }
static void drawDie(uint16_t *frame)
{
const int x = 126, y = 48, size = 68;
fill_rect(frame, x, y, size, size, COLOR_DARKGREY);
draw_rect(frame, x, y, size, size, COLOR_WHITE);
if (die_value == 0U) return;
if (die_value == 1U || die_value == 3U || die_value == 5U) drawPip(frame, x + 34, y + 34);
if (die_value >= 2U) { drawPip(frame, x + 17, y + 17); drawPip(frame, x + 51, y + 51); }
if (die_value >= 4U) { drawPip(frame, x + 51, y + 17); drawPip(frame, x + 17, y + 51); }
if (die_value == 6U) { drawPip(frame, x + 17, y + 34); drawPip(frame, x + 51, y + 34); }
}
void drawPig(uint16_t *frame)
{
if (!running || !redraw_needed) return;
redraw_needed = false;
clear(frame, COLOR_BLACK);
char text[40];
if (state == PIG_SCORES) {
drawPet2001TextCentredTransparent(frame, 160, 8, "PIG", COLOR_CYAN, 4);
drawPet2001TextCentredTransparent(frame, 160, 45, "GAME RECORD", COLOR_LIGHTGRAY, 2);
snprintf(text, sizeof(text), "WINS %u LOSSES %u", (unsigned)scores.wins, (unsigned)scores.losses);
drawPet2001TextCentredTransparent(frame, 160, 78, text, COLOR_WHITE, 1);
snprintf(text, sizeof(text), "STREAK %u", (unsigned)scores.streak);
drawPet2001TextCentredTransparent(frame, 160, 108, text, COLOR_GOLD, 1);
drawPet2001TextCentredTransparent(frame, 160, 140, "FIRST TO 100", COLOR_LIGHTGRAY, 1);
return;
}
if (state == PIG_MODE) {
drawPet2001TextCentredTransparent(frame, 160, 8, "PIG", COLOR_CYAN, 4);
drawPet2001TextCentredTransparent(frame, 160, 52, "SELECT MODE", COLOR_LIGHTGRAY, 2);
drawPet2001TextCentredTransparent(frame, 100, 84, "PLAY", auto_mode ? COLOR_LIGHTGRAY : COLOR_YELLOW, 2);
drawPet2001TextCentredTransparent(frame, 220, 84, "AUTO", auto_mode ? COLOR_YELLOW : COLOR_LIGHTGRAY, 2);
drawPet2001TextCentredTransparent(frame, 160, 115, "WIN 10 GOLD", COLOR_GOLD, 2);
drawPet2001TextCentredTransparent(frame, 160, 145, "D/B/STICK CHANGE A/K START", COLOR_LIGHTGRAY, 1);
return;
}
drawPet2001TextTransparent(frame, 6, 3, "PIG", COLOR_CYAN, 3);
drawPet2001TextTransparent(frame, 84, 12, "FIRST TO 100", COLOR_LIGHTGRAY, 1);
drawGameGold(frame, 260, 2, 55, true);
drawPet2001TextCentredTransparent(frame, 50, 40, auto_mode ? "A" : "YOU", current_side == PIG_PLAYER ? COLOR_YELLOW : COLOR_CYAN, 2);
drawPet2001TextCentredTransparent(frame, 270, 40, auto_mode ? "B" : "CPU", current_side == PIG_CPU ? COLOR_YELLOW : COLOR_CYAN, 2);
snprintf(text, sizeof(text), "%u", (unsigned)player_score); drawPet2001TextCentredTransparent(frame, 50, 68, text, COLOR_WHITE, 4);
snprintf(text, sizeof(text), "%u", (unsigned)cpu_score); drawPet2001TextCentredTransparent(frame, 270, 68, text, COLOR_WHITE, 4);
drawDie(frame);
snprintf(text, sizeof(text), "TURN %u", (unsigned)turn_score); drawPet2001TextCentredTransparent(frame, 160, 119, text, COLOR_CYAN, 2);
drawPet2001TextCentredTransparent(frame, 160, 137, game_message, COLOR_WHITE, 1);
if (state == PIG_PLAYING && !auto_mode && current_side == PIG_PLAYER) {
drawPet2001TextCentredTransparent(frame, 80, 155, "ROLL", selected_action == PIG_ROLL ? COLOR_YELLOW : COLOR_WHITE, 2);
drawPet2001TextCentredTransparent(frame, 240, 155, "HOLD", selected_action == PIG_HOLD ? COLOR_YELLOW : COLOR_WHITE, 2);
} else if (auto_mode) {
if (state == PIG_FINISHED) snprintf(text, sizeof(text), "NEXT GAME %u E SPEED C STOP", (unsigned)auto_seconds_shown);
else snprintf(text, sizeof(text), "E SPEED %s C STOP", aiSpeedShortText(ai_speed));
drawPet2001TextCentredTransparent(frame, 160, 155, text, COLOR_LIGHTGRAY, 1);
} else if (state == PIG_FINISHED) drawPet2001TextCentredTransparent(frame, 160, 155, "C NEW GAME", COLOR_LIGHTGRAY, 1);
else drawPet2001TextCentredTransparent(frame, 160, 155, "CPU THINKING", COLOR_LIGHTGRAY, 1);
}