Maze Chase
Source: src/maze_chase.c · 344 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
absoluteValue() · source line 59
static int absoluteValue(int value)
- Exit this function
return value < 0 ? -value : value;
Read original function source
static int absoluteValue(int value) { return value < 0 ? -value : value; }wallAt() · source line 61
static bool wallAt(int column, int row)
- Exit this function
return row < 0 || row >= MAZE_ROWS || column < 0 || column >= MAZE_COLUMNS || maze_layout[row][column] == '#';
Read original function source
static bool wallAt(int column, int row)
{
return row < 0 || row >= MAZE_ROWS || column < 0 || column >= MAZE_COLUMNS ||
maze_layout[row][column] == '#';
}oppositeDirection() · source line 67
static direction_t oppositeDirection(direction_t direction)
- Decision
direction == DIRECTION_UPTrue- Exit this function
return DIRECTION_DOWN;
FalseContinue after this decision.
- Decision
direction == DIRECTION_DOWNTrue- Exit this function
return DIRECTION_UP;
FalseContinue after this decision.
- Decision
direction == DIRECTION_LEFTTrue- Exit this function
return DIRECTION_RIGHT;
FalseContinue after this decision.
- Exit this function
return direction == DIRECTION_RIGHT ? DIRECTION_LEFT : DIRECTION_NONE;
Read original function source
static direction_t oppositeDirection(direction_t direction)
{
if (direction == DIRECTION_UP) return DIRECTION_DOWN;
if (direction == DIRECTION_DOWN) return DIRECTION_UP;
if (direction == DIRECTION_LEFT) return DIRECTION_RIGHT;
return direction == DIRECTION_RIGHT ? DIRECTION_LEFT : DIRECTION_NONE;
}directionOffset() · source line 75
static void directionOffset(direction_t direction, int *column, int *row)
- Execute
*column = 0; - Execute
*row = 0; - Decision
direction == DIRECTION_UPTrue- Execute
*row = -1;
False- Decision
direction == DIRECTION_DOWNTrue- Execute
*row = 1;
False- Decision
direction == DIRECTION_LEFTTrue- Execute
*column = -1;
False- Decision
direction == DIRECTION_RIGHTTrue- Execute
*column = 1;
FalseContinue after this decision.
Read original function source
static void directionOffset(direction_t direction, int *column, int *row)
{
*column = 0;
*row = 0;
if (direction == DIRECTION_UP) *row = -1;
else if (direction == DIRECTION_DOWN) *row = 1;
else if (direction == DIRECTION_LEFT) *column = -1;
else if (direction == DIRECTION_RIGHT) *column = 1;
}nextTile() · source line 85
static bool nextTile(const maze_actor_t *actor, direction_t direction, int *column, int *row)
Calls into this documentation:
- Execute
int dx, dy; - Execute
directionOffset(direction, &dx, &dy); - Execute
*column = actor->column + dx; - Execute
*row = actor->row + dy; - Decision
*row == TUNNEL_ROW && *column < 0True- Execute
*column = MAZE_COLUMNS - 1;
FalseContinue after this decision.
- Decision
*row == TUNNEL_ROW && *column >= MAZE_COLUMNSTrue- Execute
*column = 0;
FalseContinue after this decision.
- Exit this function
return !wallAt(*column, *row);
Read original function source
static bool nextTile(const maze_actor_t *actor, direction_t direction, int *column, int *row)
{
int dx, dy;
directionOffset(direction, &dx, &dy);
*column = actor->column + dx;
*row = actor->row + dy;
if (*row == TUNNEL_ROW && *column < 0) *column = MAZE_COLUMNS - 1;
if (*row == TUNNEL_ROW && *column >= MAZE_COLUMNS) *column = 0;
return !wallAt(*column, *row);
}resetActors() · source line 96
static void resetActors(void)
- Execute
player = (maze_actor_t){1, 7, DIRECTION_RIGHT}; - Execute
const maze_actor_t starts[GHOST_COUNT] = { {4, 1, DIRECTION_LEFT}, {5, 1, DIRECTION_RIGHT}, {6, 1, DIRECTION_LEFT}, {7, 1, DIRECTION_RIGHT} }; - FOR loop
unsigned i = 0; i < GHOST_COUNT; ++iRepeat body while the loop condition permits; then continue below.
- Execute
ghosts[i] = starts[i];
Read original function source
static void resetActors(void)
{
player = (maze_actor_t){1, 7, DIRECTION_RIGHT};
const maze_actor_t starts[GHOST_COUNT] = {
{4, 1, DIRECTION_LEFT}, {5, 1, DIRECTION_RIGHT},
{6, 1, DIRECTION_LEFT}, {7, 1, DIRECTION_RIGHT}
};
for (unsigned i = 0; i < GHOST_COUNT; ++i) ghosts[i] = starts[i];
}resetMaze() · source line 106
static void resetMaze(void)
Calls into this documentation:
- Execute
pellets_left = 0U; - FOR loop
int row = 0; row < MAZE_ROWS; ++rowRepeat body while the loop condition permits; then continue below.
- FOR loop
int column = 0; column < MAZE_COLUMNS; ++columnRepeat body while the loop condition permits; then continue below.
- Execute
pellets[row][column] = wallAt(column, row) ? 0U : 1U; - Decision
pellets[row][column] != 0UTrue- Execute
++pellets_left;
FalseContinue after this decision.
- Execute
pellets[1][1] = 2U; - Execute
pellets[1][MAZE_COLUMNS - 2] = 2U; - Execute
pellets[7][MAZE_COLUMNS - 2] = 2U; - Execute
pellets[5][1] = 2U; - FOR loop
unsigned i = 0; i < GHOST_COUNT; ++iRepeat body while the loop condition permits; then continue below.
- Execute
const int row = ghosts[i].row; - Execute
const int column = ghosts[i].column; - Decision
pellets[row][column] != 0UTrue- Execute
--pellets_left;
FalseContinue after this decision.
- Execute
pellets[row][column] = 0U;
- Decision
pellets[player.row][player.column] != 0UTrue- Execute
--pellets_left;
FalseContinue after this decision.
- Execute
pellets[player.row][player.column] = 0U;
Read original function source
static void resetMaze(void)
{
pellets_left = 0U;
for (int row = 0; row < MAZE_ROWS; ++row) {
for (int column = 0; column < MAZE_COLUMNS; ++column) {
pellets[row][column] = wallAt(column, row) ? 0U : 1U;
if (pellets[row][column] != 0U) ++pellets_left;
}
}
pellets[1][1] = 2U;
pellets[1][MAZE_COLUMNS - 2] = 2U;
pellets[7][MAZE_COLUMNS - 2] = 2U;
pellets[5][1] = 2U;
for (unsigned i = 0; i < GHOST_COUNT; ++i) {
const int row = ghosts[i].row;
const int column = ghosts[i].column;
if (pellets[row][column] != 0U) --pellets_left;
pellets[row][column] = 0U;
}
if (pellets[player.row][player.column] != 0U) --pellets_left;
pellets[player.row][player.column] = 0U;
}beginRound() · source line 129
static void beginRound(uint32_t now_ms)
Calls into this documentation:
- Execute
resetActors(); - Execute
resetMaze(); - Execute
frightened_until = 0U; - Execute
ghosts_release_at = now_ms + GHOST_RELEASE_DELAY_MS; - Execute
player_last_step = now_ms; - Execute
ghost_last_step = now_ms; - Execute
state = MAZE_PLAYING;
Read original function source
static void beginRound(uint32_t now_ms)
{
resetActors();
resetMaze();
frightened_until = 0U;
ghosts_release_at = now_ms + GHOST_RELEASE_DELAY_MS;
player_last_step = now_ms;
ghost_last_step = now_ms;
state = MAZE_PLAYING;
}loseLife() · source line 140
static void loseLife(uint32_t now_ms)
Calls into this documentation:
- Execute
audioPlay(AUDIO_SFX_PLAYER_EXPLOSION); - Decision
lives != 0UTrue- Execute
--lives;
FalseContinue after this decision.
- Execute
state_started = now_ms; - Execute
state = lives == 0U ? MAZE_FINISHED : MAZE_LIFE_LOST;
Read original function source
static void loseLife(uint32_t now_ms)
{
audioPlay(AUDIO_SFX_PLAYER_EXPLOSION);
if (lives != 0U) --lives;
state_started = now_ms;
state = lives == 0U ? MAZE_FINISHED : MAZE_LIFE_LOST;
}collectPellet() · source line 148
static void collectPellet(uint32_t now_ms)
Calls into this documentation:
- Execute
uint8_t *pellet = &pellets[player.row][player.column]; - Decision
*pellet == 0UTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
const bool power_pellet = *pellet == 2U; - Execute
score += power_pellet ? 50U : 10U; - Execute
audioPlay(power_pellet ? AUDIO_SFX_POWER_UP : AUDIO_SFX_MAZE_PELLET); - Decision
power_pelletTrue- Execute
frightened_until = now_ms + POWER_MS;
FalseContinue after this decision.
- Execute
*pellet = 0U; - Decision
pellets_left != 0UTrue- Execute
--pellets_left;
FalseContinue after this decision.
- Decision
pellets_left == 0UTrue- Execute
audioPlay(AUDIO_SFX_WAVE_CLEAR); - Execute
++round_number; - Execute
resetMaze(); - Execute
resetActors(); - Execute
ghosts_release_at = now_ms + GHOST_RELEASE_DELAY_MS;
FalseContinue after this decision.
Read original function source
static void collectPellet(uint32_t now_ms)
{
uint8_t *pellet = &pellets[player.row][player.column];
if (*pellet == 0U) return;
const bool power_pellet = *pellet == 2U;
score += power_pellet ? 50U : 10U;
audioPlay(power_pellet ? AUDIO_SFX_POWER_UP : AUDIO_SFX_MAZE_PELLET);
if (power_pellet) frightened_until = now_ms + POWER_MS;
*pellet = 0U;
if (pellets_left != 0U) --pellets_left;
if (pellets_left == 0U) {
audioPlay(AUDIO_SFX_WAVE_CLEAR);
++round_number;
resetMaze();
resetActors();
ghosts_release_at = now_ms + GHOST_RELEASE_DELAY_MS;
}
}movePlayer() · source line 167
static void movePlayer(direction_t requested, uint32_t now_ms)
Calls into this documentation:
- Execute
int column, row; - Decision
requested != DIRECTION_NONE && nextTile(&player, requested, &column, &row)True- Execute
player.direction = requested;
FalseContinue after this decision.
- Decision
!nextTile(&player, player.direction, &column, &row)True- Exit this function
return;
FalseContinue after this decision.
- Execute
player.column = column; - Execute
player.row = row; - Execute
collectPellet(now_ms);
Read original function source
static void movePlayer(direction_t requested, uint32_t now_ms)
{
int column, row;
if (requested != DIRECTION_NONE && nextTile(&player, requested, &column, &row)) player.direction = requested;
if (!nextTile(&player, player.direction, &column, &row)) return;
player.column = column;
player.row = row;
collectPellet(now_ms);
}moveGhost() · source line 177
static void moveGhost(unsigned index, uint32_t now_ms)
Calls into this documentation:
- Execute
maze_actor_t *ghost = &ghosts[index]; - Execute
direction_t selected = DIRECTION_NONE; - Execute
int best_distance = 0x7fffffff; - Execute
const bool frightened = (int32_t)(frightened_until - now_ms) > 0; - Execute
const direction_t choices[] = {DIRECTION_UP, DIRECTION_LEFT, DIRECTION_DOWN, DIRECTION_RIGHT}; - FOR loop
unsigned i = 0; i < sizeof(choices) / sizeof(choices[0]); ++iRepeat body while the loop condition permits; then continue below.
- Execute
int column, row; - Decision
!nextTile(ghost, choices[i], &column, &row)True- Next loop iteration
continue;
FalseContinue after this decision.
- Decision
choices[i] == oppositeDirection(ghost->direction) && selected != DIRECTION_NONETrue- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
const int distance = absoluteValue(column - player.column) + absoluteValue(row - player.row); - Execute
const int target_distance = frightened ? -distance : distance + (int)((index * 3U + i) % 4U); - Decision
target_distance < best_distanceTrue- Execute
best_distance = target_distance; - Execute
selected = choices[i];
FalseContinue after this decision.
- Execute
int column, row; - Decision
selected != DIRECTION_NONE && nextTile(ghost, selected, &column, &row)True- Execute
ghost->direction = selected; - Execute
ghost->column = column; - Execute
ghost->row = row;
FalseContinue after this decision.
Read original function source
static void moveGhost(unsigned index, uint32_t now_ms)
{
maze_actor_t *ghost = &ghosts[index];
direction_t selected = DIRECTION_NONE;
int best_distance = 0x7fffffff;
const bool frightened = (int32_t)(frightened_until - now_ms) > 0;
const direction_t choices[] = {DIRECTION_UP, DIRECTION_LEFT, DIRECTION_DOWN, DIRECTION_RIGHT};
for (unsigned i = 0; i < sizeof(choices) / sizeof(choices[0]); ++i) {
int column, row;
if (!nextTile(ghost, choices[i], &column, &row)) continue;
if (choices[i] == oppositeDirection(ghost->direction) && selected != DIRECTION_NONE) continue;
const int distance = absoluteValue(column - player.column) + absoluteValue(row - player.row);
const int target_distance = frightened ? -distance : distance + (int)((index * 3U + i) % 4U);
if (target_distance < best_distance) {
best_distance = target_distance;
selected = choices[i];
}
}
int column, row;
if (selected != DIRECTION_NONE && nextTile(ghost, selected, &column, &row)) {
ghost->direction = selected;
ghost->column = column;
ghost->row = row;
}
}checkGhostHits() · source line 203
static void checkGhostHits(uint32_t now_ms)
Calls into this documentation:
- FOR loop
unsigned i = 0; i < GHOST_COUNT; ++iRepeat body while the loop condition permits; then continue below.
- Decision
ghosts[i].column != player.column || ghosts[i].row != player.rowTrue- Next loop iteration
continue;
FalseContinue after this decision.
- Decision
(int32_t)(frightened_until - now_ms) > 0True- Execute
score += 200U; - Execute
ghosts[i] = (maze_actor_t){5, 1, DIRECTION_LEFT}; - Execute
audioPlay(AUDIO_SFX_HIT);
False- Execute
loseLife(now_ms);
- Exit this function
return;
Read original function source
static void checkGhostHits(uint32_t now_ms)
{
for (unsigned i = 0; i < GHOST_COUNT; ++i) {
if (ghosts[i].column != player.column || ghosts[i].row != player.row) continue;
if ((int32_t)(frightened_until - now_ms) > 0) {
score += 200U;
ghosts[i] = (maze_actor_t){5, 1, DIRECTION_LEFT};
audioPlay(AUDIO_SFX_HIT);
} else {
loseLife(now_ms);
}
return;
}
}startMazeChase() · source line 218
void startMazeChase(uint32_t now_ms)
- Execute
score = 0U; - Execute
round_number = 1U; - Execute
lives = 3U; - Execute
controls_ready = false; - Execute
running = true; - Execute
state = MAZE_READY; - Execute
state_started = now_ms;
Read original function source
void startMazeChase(uint32_t now_ms)
{
score = 0U;
round_number = 1U;
lives = 3U;
controls_ready = false;
running = true;
state = MAZE_READY;
state_started = now_ms;
}stopMazeChase() · source line 229
void stopMazeChase(void)
Calls into this documentation:
- Execute
cancelHighScoreEntry(); - Execute
running = false;
Read original function source
void stopMazeChase(void)
{
cancelHighScoreEntry();
running = false;
}mazeChaseAction() · source line 235
void mazeChaseAction(bool action, uint32_t now_ms)
Calls into this documentation:
- Decision
!running || !actionTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
state == MAZE_READYTrue- Execute
beginRound(now_ms); - Execute
audioPlay(AUDIO_SFX_GAME_START);
FalseContinue after this decision.
Read original function source
void mazeChaseAction(bool action, uint32_t now_ms)
{
if (!running || !action) return;
if (state == MAZE_READY) {
beginRound(now_ms);
audioPlay(AUDIO_SFX_GAME_START);
}
}updateMazeChase() · source line 244
void updateMazeChase(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 == MAZE_SCORESTrue- Decision
updateHighScoreEntry(x_offset, y_offset, joystick_valid, now_ms)True- Execute
state = MAZE_READY;
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
state == MAZE_FINISHEDTrue- Decision
now_ms - state_started >= LIFE_LOST_MSTrue- Execute
startHighScoreEntry(HIGH_SCORE_MAZE_CHASE, score, now_ms); - Execute
state = MAZE_SCORES;
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
state == MAZE_LIFE_LOSTTrue- Decision
now_ms - state_started >= LIFE_LOST_MSTrue- Execute
resetActors(); - Execute
player_last_step = now_ms; - Execute
ghost_last_step = now_ms; - Execute
ghosts_release_at = now_ms + GHOST_RELEASE_DELAY_MS; - Execute
state = MAZE_PLAYING;
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Decision
state != MAZE_PLAYINGTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
!joystick_validTrue- Execute
controls_ready = false;
False- Decision
absoluteValue(x_offset) < 350 && absoluteValue(y_offset) < 350True- Execute
controls_ready = true;
FalseContinue after this decision.
- Execute
direction_t requested = DIRECTION_NONE; - Decision
left != rightTrue- Execute
requested = left ? DIRECTION_LEFT : DIRECTION_RIGHT;
False- Decision
joystick_valid && controls_ready && absoluteValue(x_offset) > 500True- Execute
requested = x_offset < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
False- Decision
joystick_valid && controls_ready && absoluteValue(y_offset) > 500True- Execute
requested = y_offset > 0 ? DIRECTION_UP : DIRECTION_DOWN;
FalseContinue after this decision.
- WHILE loop
now_ms - player_last_step >= PLAYER_STEP_MS && state == MAZE_PLAYINGRepeat body while the loop condition permits; then continue below.
- Execute
player_last_step += PLAYER_STEP_MS; - Execute
movePlayer(requested, now_ms); - Execute
checkGhostHits(now_ms);
- Decision
(int32_t)(now_ms - ghosts_release_at) < 0True- Execute
ghost_last_step = now_ms; - Exit this function
return;
FalseContinue after this decision.
- Execute
const uint32_t ghost_step = GHOST_STEP_MS > round_number * 5U ? GHOST_STEP_MS - round_number * 5U : 90U; - WHILE loop
now_ms - ghost_last_step >= ghost_step && state == MAZE_PLAYINGRepeat body while the loop condition permits; then continue below.
- Execute
ghost_last_step += ghost_step; - FOR loop
unsigned i = 0; i < GHOST_COUNT && state == MAZE_PLAYING; ++iRepeat body while the loop condition permits; then continue below.
- Execute
moveGhost(i, now_ms); - Execute
checkGhostHits(now_ms);
Read original function source
void updateMazeChase(int x_offset, int y_offset, bool joystick_valid, bool left, bool right,
uint32_t now_ms)
{
if (!running) return;
if (state == MAZE_SCORES) {
if (updateHighScoreEntry(x_offset, y_offset, joystick_valid, now_ms)) state = MAZE_READY;
return;
}
if (state == MAZE_FINISHED) {
if (now_ms - state_started >= LIFE_LOST_MS) {
startHighScoreEntry(HIGH_SCORE_MAZE_CHASE, score, now_ms);
state = MAZE_SCORES;
}
return;
}
if (state == MAZE_LIFE_LOST) {
if (now_ms - state_started >= LIFE_LOST_MS) {
resetActors();
player_last_step = now_ms;
ghost_last_step = now_ms;
ghosts_release_at = now_ms + GHOST_RELEASE_DELAY_MS;
state = MAZE_PLAYING;
}
return;
}
if (state != MAZE_PLAYING) return;
if (!joystick_valid) controls_ready = false;
else if (absoluteValue(x_offset) < 350 && absoluteValue(y_offset) < 350) controls_ready = true;
direction_t requested = DIRECTION_NONE;
if (left != right) requested = left ? DIRECTION_LEFT : DIRECTION_RIGHT;
else if (joystick_valid && controls_ready && absoluteValue(x_offset) > 500) requested = x_offset < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
else if (joystick_valid && controls_ready && absoluteValue(y_offset) > 500) requested = y_offset > 0 ? DIRECTION_UP : DIRECTION_DOWN;
while (now_ms - player_last_step >= PLAYER_STEP_MS && state == MAZE_PLAYING) {
player_last_step += PLAYER_STEP_MS;
movePlayer(requested, now_ms);
checkGhostHits(now_ms);
}
if ((int32_t)(now_ms - ghosts_release_at) < 0) {
// Keep the movement clock current during the release delay: the chasers
// begin with one normal step, rather than catching up two seconds at once.
ghost_last_step = now_ms;
return;
}
const uint32_t ghost_step = GHOST_STEP_MS > round_number * 5U ? GHOST_STEP_MS - round_number * 5U : 90U;
while (now_ms - ghost_last_step >= ghost_step && state == MAZE_PLAYING) {
ghost_last_step += ghost_step;
for (unsigned i = 0; i < GHOST_COUNT && state == MAZE_PLAYING; ++i) {
moveGhost(i, now_ms);
checkGhostHits(now_ms);
}
}
}drawMazeChase() · source line 297
void drawMazeChase(uint16_t *frame)
Calls into this documentation:
- Decision
!running || frame == NULLTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
state == MAZE_SCORESTrue- Execute
drawHighScoreEntry(frame); - Exit this function
return;
FalseContinue after this decision.
- Execute
clear(frame, COLOR_BLACK); - Decision
state == MAZE_READYTrue- Execute
draw_text_centred(frame, LCD_WIDTH / 2, 20, "MAZE CHASE", 2, COLOR_YELLOW); - Execute
draw_text_centred(frame, LCD_WIDTH / 2, 55, "CLEAR THE MAZE", 1, COLOR_CYAN); - Execute
draw_text_centred(frame, LCD_WIDTH / 2, 73, "AVOID THE CHASERS", 1, COLOR_MAGENTA); - Execute
draw_text_centred(frame, LCD_WIDTH / 2, 110, "D/B OR STICK MOVE", 1, COLOR_LIGHTGRAY); - Execute
draw_text_centred(frame, LCD_WIDTH / 2, 145, "A/K START", 2, COLOR_YELLOW); - Exit this function
return;
FalseContinue after this decision.
- Execute
char text[28]; - Execute
snprintf(text, sizeof(text), "SCORE %u", (unsigned)score); - Execute
draw_text(frame, 3, 0, text, 1, COLOR_WHITE); - Execute
snprintf(text, sizeof(text), "LIVES %u", (unsigned)lives); - Execute
draw_text_centred(frame, LCD_WIDTH / 2, 0, text, 1, COLOR_CYAN); - Execute
snprintf(text, sizeof(text), "ROUND %u", (unsigned)round_number); - Execute
draw_text(frame, 255, 0, text, 1, COLOR_YELLOW); - FOR loop
int row = 0; row < MAZE_ROWS; ++rowRepeat body while the loop condition permits; then continue below.
- FOR loop
int column = 0; column < MAZE_COLUMNS; ++columnRepeat body while the loop condition permits; then continue below.
- Execute
const int x = MAZE_X + column * TILE_SIZE; - Execute
const int y = MAZE_Y + row * TILE_SIZE; - Decision
wallAt(column, row)True- Execute
fill_rect(frame, x, y, TILE_SIZE, TILE_SIZE, RGB565(0, 30, 190));
False- Decision
pellets[row][column] == 2UTrue- Execute
fill_circle(frame, x + TILE_CENTRE, y + TILE_CENTRE, 6, COLOR_WHITE);
False- Decision
pellets[row][column] == 1UTrue- Execute
fill_circle(frame, x + TILE_CENTRE, y + TILE_CENTRE, 2, COLOR_YELLOW);
FalseContinue after this decision.
- Execute
const int player_x = MAZE_X + player.column * TILE_SIZE + TILE_CENTRE; - Execute
const int player_y = MAZE_Y + player.row * TILE_SIZE + TILE_CENTRE; - Execute
fill_circle(frame, player_x, player_y, 8, COLOR_YELLOW); - Decision
player.direction == DIRECTION_RIGHTTrue- Execute
fill_rect(frame, player_x + 4, player_y - 4, 5, 9, COLOR_BLACK);
False- Decision
player.direction == DIRECTION_LEFTTrue- Execute
fill_rect(frame, player_x - 8, player_y - 4, 5, 9, COLOR_BLACK);
False- Decision
player.direction == DIRECTION_UPTrue- Execute
fill_rect(frame, player_x - 4, player_y - 8, 9, 5, COLOR_BLACK);
False- Decision
player.direction == DIRECTION_DOWNTrue- Execute
fill_rect(frame, player_x - 4, player_y + 4, 9, 5, COLOR_BLACK);
FalseContinue after this decision.
- Execute
const bool frightened = (int32_t)(frightened_until - player_last_step) > 0; - FOR loop
unsigned i = 0; i < GHOST_COUNT; ++iRepeat body while the loop condition permits; then continue below.
- Execute
const int x = MAZE_X + ghosts[i].column * TILE_SIZE + TILE_CENTRE; - Execute
const int y = MAZE_Y + ghosts[i].row * TILE_SIZE + TILE_CENTRE; - Execute
const uint16_t colour = frightened ? COLOR_DARKBLUE : ghost_colours[i]; - Execute
fill_circle(frame, x, y - 2, 8, colour); - Execute
fill_rect(frame, x - 8, y - 2, 17, 10, colour); - Execute
fill_rect(frame, x - 4, y, 2, 4, COLOR_WHITE); - Execute
fill_rect(frame, x + 4, y, 2, 4, COLOR_WHITE);
- Decision
state == MAZE_LIFE_LOSTTrue- Execute
draw_text_centred(frame, LCD_WIDTH / 2, 83, "LIFE LOST", 2, COLOR_RED);
False- Decision
state == MAZE_FINISHEDTrue- Execute
draw_text_centred(frame, LCD_WIDTH / 2, 83, "GAME OVER", 2, COLOR_RED);
FalseContinue after this decision.
Read original function source
void drawMazeChase(uint16_t *frame)
{
if (!running || frame == NULL) return;
if (state == MAZE_SCORES) { drawHighScoreEntry(frame); return; }
clear(frame, COLOR_BLACK);
if (state == MAZE_READY) {
draw_text_centred(frame, LCD_WIDTH / 2, 20, "MAZE CHASE", 2, COLOR_YELLOW);
draw_text_centred(frame, LCD_WIDTH / 2, 55, "CLEAR THE MAZE", 1, COLOR_CYAN);
draw_text_centred(frame, LCD_WIDTH / 2, 73, "AVOID THE CHASERS", 1, COLOR_MAGENTA);
draw_text_centred(frame, LCD_WIDTH / 2, 110, "D/B OR STICK MOVE", 1, COLOR_LIGHTGRAY);
draw_text_centred(frame, LCD_WIDTH / 2, 145, "A/K START", 2, COLOR_YELLOW);
return;
}
char text[28];
snprintf(text, sizeof(text), "SCORE %u", (unsigned)score);
draw_text(frame, 3, 0, text, 1, COLOR_WHITE);
snprintf(text, sizeof(text), "LIVES %u", (unsigned)lives);
draw_text_centred(frame, LCD_WIDTH / 2, 0, text, 1, COLOR_CYAN);
snprintf(text, sizeof(text), "ROUND %u", (unsigned)round_number);
draw_text(frame, 255, 0, text, 1, COLOR_YELLOW);
for (int row = 0; row < MAZE_ROWS; ++row) for (int column = 0; column < MAZE_COLUMNS; ++column) {
const int x = MAZE_X + column * TILE_SIZE;
const int y = MAZE_Y + row * TILE_SIZE;
if (wallAt(column, row)) fill_rect(frame, x, y, TILE_SIZE, TILE_SIZE, RGB565(0, 30, 190));
else if (pellets[row][column] == 2U) fill_circle(frame, x + TILE_CENTRE, y + TILE_CENTRE, 6, COLOR_WHITE);
else if (pellets[row][column] == 1U) fill_circle(frame, x + TILE_CENTRE, y + TILE_CENTRE, 2, COLOR_YELLOW);
}
const int player_x = MAZE_X + player.column * TILE_SIZE + TILE_CENTRE;
const int player_y = MAZE_Y + player.row * TILE_SIZE + TILE_CENTRE;
fill_circle(frame, player_x, player_y, 8, COLOR_YELLOW);
if (player.direction == DIRECTION_RIGHT) fill_rect(frame, player_x + 4, player_y - 4, 5, 9, COLOR_BLACK);
else if (player.direction == DIRECTION_LEFT) fill_rect(frame, player_x - 8, player_y - 4, 5, 9, COLOR_BLACK);
else if (player.direction == DIRECTION_UP) fill_rect(frame, player_x - 4, player_y - 8, 9, 5, COLOR_BLACK);
else if (player.direction == DIRECTION_DOWN) fill_rect(frame, player_x - 4, player_y + 4, 9, 5, COLOR_BLACK);
const bool frightened = (int32_t)(frightened_until - player_last_step) > 0;
for (unsigned i = 0; i < GHOST_COUNT; ++i) {
const int x = MAZE_X + ghosts[i].column * TILE_SIZE + TILE_CENTRE;
const int y = MAZE_Y + ghosts[i].row * TILE_SIZE + TILE_CENTRE;
const uint16_t colour = frightened ? COLOR_DARKBLUE : ghost_colours[i];
fill_circle(frame, x, y - 2, 8, colour);
fill_rect(frame, x - 8, y - 2, 17, 10, colour);
fill_rect(frame, x - 4, y, 2, 4, COLOR_WHITE);
fill_rect(frame, x + 4, y, 2, 4, COLOR_WHITE);
}
if (state == MAZE_LIFE_LOST) draw_text_centred(frame, LCD_WIDTH / 2, 83, "LIFE LOST", 2, COLOR_RED);
else if (state == MAZE_FINISHED) draw_text_centred(frame, LCD_WIDTH / 2, 83, "GAME OVER", 2, COLOR_RED);
}Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "maze_chase.h"
#include "audio.h"
#include "game_graphics.h"
#include "PET2001_font.h"
#include "high_scores.h"
#include <stdio.h>
#define MAZE_COLUMNS 11
#define MAZE_ROWS 9
#define TILE_SIZE 18
#define MAZE_X ((LCD_WIDTH - MAZE_COLUMNS * TILE_SIZE) / 2)
#define MAZE_Y 8
#define TUNNEL_ROW 4
#define TILE_CENTRE (TILE_SIZE / 2)
#define PLAYER_STEP_MS 105U
#define GHOST_STEP_MS 145U
#define GHOST_RELEASE_DELAY_MS 2000U
#define POWER_MS 6000U
#define LIFE_LOST_MS 900U
#define GHOST_COUNT 4U
typedef enum { DIRECTION_NONE, DIRECTION_UP, DIRECTION_DOWN, DIRECTION_LEFT, DIRECTION_RIGHT } direction_t;
typedef enum { MAZE_READY, MAZE_PLAYING, MAZE_LIFE_LOST, MAZE_FINISHED, MAZE_SCORES } maze_state_t;
typedef struct { int column; int row; direction_t direction; } maze_actor_t;
static uint8_t pellets[MAZE_ROWS][MAZE_COLUMNS];
static maze_actor_t player;
static maze_actor_t ghosts[GHOST_COUNT];
static maze_state_t state;
static uint32_t score;
static uint32_t round_number;
static uint32_t state_started;
static uint32_t player_last_step;
static uint32_t ghost_last_step;
static uint32_t frightened_until;
static uint32_t ghosts_release_at;
static uint8_t lives;
static uint16_t pellets_left;
static bool controls_ready;
static bool running;
static const uint16_t ghost_colours[GHOST_COUNT] = { COLOR_RED, COLOR_MAGENTA, COLOR_CYAN, COLOR_YELLOW };
// Each '.' is one traversable maze unit. The corridors form connected loops and
// crossings, giving the player several escape choices without dead ends.
static const char maze_layout[MAZE_ROWS][MAZE_COLUMNS + 1] = {
"###########",
"#.........#",
"#.###.###.#",
"#.........#",
"...........",
"#.........#",
"#.###.###.#",
"#.........#",
"###########",
};
static int absoluteValue(int value) { return value < 0 ? -value : value; }
static bool wallAt(int column, int row)
{
return row < 0 || row >= MAZE_ROWS || column < 0 || column >= MAZE_COLUMNS ||
maze_layout[row][column] == '#';
}
static direction_t oppositeDirection(direction_t direction)
{
if (direction == DIRECTION_UP) return DIRECTION_DOWN;
if (direction == DIRECTION_DOWN) return DIRECTION_UP;
if (direction == DIRECTION_LEFT) return DIRECTION_RIGHT;
return direction == DIRECTION_RIGHT ? DIRECTION_LEFT : DIRECTION_NONE;
}
static void directionOffset(direction_t direction, int *column, int *row)
{
*column = 0;
*row = 0;
if (direction == DIRECTION_UP) *row = -1;
else if (direction == DIRECTION_DOWN) *row = 1;
else if (direction == DIRECTION_LEFT) *column = -1;
else if (direction == DIRECTION_RIGHT) *column = 1;
}
static bool nextTile(const maze_actor_t *actor, direction_t direction, int *column, int *row)
{
int dx, dy;
directionOffset(direction, &dx, &dy);
*column = actor->column + dx;
*row = actor->row + dy;
if (*row == TUNNEL_ROW && *column < 0) *column = MAZE_COLUMNS - 1;
if (*row == TUNNEL_ROW && *column >= MAZE_COLUMNS) *column = 0;
return !wallAt(*column, *row);
}
static void resetActors(void)
{
player = (maze_actor_t){1, 7, DIRECTION_RIGHT};
const maze_actor_t starts[GHOST_COUNT] = {
{4, 1, DIRECTION_LEFT}, {5, 1, DIRECTION_RIGHT},
{6, 1, DIRECTION_LEFT}, {7, 1, DIRECTION_RIGHT}
};
for (unsigned i = 0; i < GHOST_COUNT; ++i) ghosts[i] = starts[i];
}
static void resetMaze(void)
{
pellets_left = 0U;
for (int row = 0; row < MAZE_ROWS; ++row) {
for (int column = 0; column < MAZE_COLUMNS; ++column) {
pellets[row][column] = wallAt(column, row) ? 0U : 1U;
if (pellets[row][column] != 0U) ++pellets_left;
}
}
pellets[1][1] = 2U;
pellets[1][MAZE_COLUMNS - 2] = 2U;
pellets[7][MAZE_COLUMNS - 2] = 2U;
pellets[5][1] = 2U;
for (unsigned i = 0; i < GHOST_COUNT; ++i) {
const int row = ghosts[i].row;
const int column = ghosts[i].column;
if (pellets[row][column] != 0U) --pellets_left;
pellets[row][column] = 0U;
}
if (pellets[player.row][player.column] != 0U) --pellets_left;
pellets[player.row][player.column] = 0U;
}
static void beginRound(uint32_t now_ms)
{
resetActors();
resetMaze();
frightened_until = 0U;
ghosts_release_at = now_ms + GHOST_RELEASE_DELAY_MS;
player_last_step = now_ms;
ghost_last_step = now_ms;
state = MAZE_PLAYING;
}
static void loseLife(uint32_t now_ms)
{
audioPlay(AUDIO_SFX_PLAYER_EXPLOSION);
if (lives != 0U) --lives;
state_started = now_ms;
state = lives == 0U ? MAZE_FINISHED : MAZE_LIFE_LOST;
}
static void collectPellet(uint32_t now_ms)
{
uint8_t *pellet = &pellets[player.row][player.column];
if (*pellet == 0U) return;
const bool power_pellet = *pellet == 2U;
score += power_pellet ? 50U : 10U;
audioPlay(power_pellet ? AUDIO_SFX_POWER_UP : AUDIO_SFX_MAZE_PELLET);
if (power_pellet) frightened_until = now_ms + POWER_MS;
*pellet = 0U;
if (pellets_left != 0U) --pellets_left;
if (pellets_left == 0U) {
audioPlay(AUDIO_SFX_WAVE_CLEAR);
++round_number;
resetMaze();
resetActors();
ghosts_release_at = now_ms + GHOST_RELEASE_DELAY_MS;
}
}
static void movePlayer(direction_t requested, uint32_t now_ms)
{
int column, row;
if (requested != DIRECTION_NONE && nextTile(&player, requested, &column, &row)) player.direction = requested;
if (!nextTile(&player, player.direction, &column, &row)) return;
player.column = column;
player.row = row;
collectPellet(now_ms);
}
static void moveGhost(unsigned index, uint32_t now_ms)
{
maze_actor_t *ghost = &ghosts[index];
direction_t selected = DIRECTION_NONE;
int best_distance = 0x7fffffff;
const bool frightened = (int32_t)(frightened_until - now_ms) > 0;
const direction_t choices[] = {DIRECTION_UP, DIRECTION_LEFT, DIRECTION_DOWN, DIRECTION_RIGHT};
for (unsigned i = 0; i < sizeof(choices) / sizeof(choices[0]); ++i) {
int column, row;
if (!nextTile(ghost, choices[i], &column, &row)) continue;
if (choices[i] == oppositeDirection(ghost->direction) && selected != DIRECTION_NONE) continue;
const int distance = absoluteValue(column - player.column) + absoluteValue(row - player.row);
const int target_distance = frightened ? -distance : distance + (int)((index * 3U + i) % 4U);
if (target_distance < best_distance) {
best_distance = target_distance;
selected = choices[i];
}
}
int column, row;
if (selected != DIRECTION_NONE && nextTile(ghost, selected, &column, &row)) {
ghost->direction = selected;
ghost->column = column;
ghost->row = row;
}
}
static void checkGhostHits(uint32_t now_ms)
{
for (unsigned i = 0; i < GHOST_COUNT; ++i) {
if (ghosts[i].column != player.column || ghosts[i].row != player.row) continue;
if ((int32_t)(frightened_until - now_ms) > 0) {
score += 200U;
ghosts[i] = (maze_actor_t){5, 1, DIRECTION_LEFT};
audioPlay(AUDIO_SFX_HIT);
} else {
loseLife(now_ms);
}
return;
}
}
void startMazeChase(uint32_t now_ms)
{
score = 0U;
round_number = 1U;
lives = 3U;
controls_ready = false;
running = true;
state = MAZE_READY;
state_started = now_ms;
}
void stopMazeChase(void)
{
cancelHighScoreEntry();
running = false;
}
void mazeChaseAction(bool action, uint32_t now_ms)
{
if (!running || !action) return;
if (state == MAZE_READY) {
beginRound(now_ms);
audioPlay(AUDIO_SFX_GAME_START);
}
}
void updateMazeChase(int x_offset, int y_offset, bool joystick_valid, bool left, bool right,
uint32_t now_ms)
{
if (!running) return;
if (state == MAZE_SCORES) {
if (updateHighScoreEntry(x_offset, y_offset, joystick_valid, now_ms)) state = MAZE_READY;
return;
}
if (state == MAZE_FINISHED) {
if (now_ms - state_started >= LIFE_LOST_MS) {
startHighScoreEntry(HIGH_SCORE_MAZE_CHASE, score, now_ms);
state = MAZE_SCORES;
}
return;
}
if (state == MAZE_LIFE_LOST) {
if (now_ms - state_started >= LIFE_LOST_MS) {
resetActors();
player_last_step = now_ms;
ghost_last_step = now_ms;
ghosts_release_at = now_ms + GHOST_RELEASE_DELAY_MS;
state = MAZE_PLAYING;
}
return;
}
if (state != MAZE_PLAYING) return;
if (!joystick_valid) controls_ready = false;
else if (absoluteValue(x_offset) < 350 && absoluteValue(y_offset) < 350) controls_ready = true;
direction_t requested = DIRECTION_NONE;
if (left != right) requested = left ? DIRECTION_LEFT : DIRECTION_RIGHT;
else if (joystick_valid && controls_ready && absoluteValue(x_offset) > 500) requested = x_offset < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
else if (joystick_valid && controls_ready && absoluteValue(y_offset) > 500) requested = y_offset > 0 ? DIRECTION_UP : DIRECTION_DOWN;
while (now_ms - player_last_step >= PLAYER_STEP_MS && state == MAZE_PLAYING) {
player_last_step += PLAYER_STEP_MS;
movePlayer(requested, now_ms);
checkGhostHits(now_ms);
}
if ((int32_t)(now_ms - ghosts_release_at) < 0) {
// Keep the movement clock current during the release delay: the chasers
// begin with one normal step, rather than catching up two seconds at once.
ghost_last_step = now_ms;
return;
}
const uint32_t ghost_step = GHOST_STEP_MS > round_number * 5U ? GHOST_STEP_MS - round_number * 5U : 90U;
while (now_ms - ghost_last_step >= ghost_step && state == MAZE_PLAYING) {
ghost_last_step += ghost_step;
for (unsigned i = 0; i < GHOST_COUNT && state == MAZE_PLAYING; ++i) {
moveGhost(i, now_ms);
checkGhostHits(now_ms);
}
}
}
void drawMazeChase(uint16_t *frame)
{
if (!running || frame == NULL) return;
if (state == MAZE_SCORES) { drawHighScoreEntry(frame); return; }
clear(frame, COLOR_BLACK);
if (state == MAZE_READY) {
drawPet2001TextCentredTransparent(frame, LCD_WIDTH / 2, 20, "MAZE CHASE", COLOR_YELLOW, 2);
drawPet2001TextCentredTransparent(frame, LCD_WIDTH / 2, 55, "CLEAR THE MAZE", COLOR_CYAN, 1);
drawPet2001TextCentredTransparent(frame, LCD_WIDTH / 2, 73, "AVOID THE CHASERS", COLOR_MAGENTA, 1);
drawPet2001TextCentredTransparent(frame, LCD_WIDTH / 2, 110, "D/B OR STICK MOVE", COLOR_LIGHTGRAY, 1);
drawPet2001TextCentredTransparent(frame, LCD_WIDTH / 2, 145, "A/K START", COLOR_YELLOW, 2);
return;
}
char text[28];
snprintf(text, sizeof(text), "SCORE %u", (unsigned)score);
drawPet2001TextTransparent(frame, 3, 0, text, COLOR_WHITE, 1);
snprintf(text, sizeof(text), "LIVES %u", (unsigned)lives);
drawPet2001TextCentredTransparent(frame, LCD_WIDTH / 2, 0, text, COLOR_CYAN, 1);
snprintf(text, sizeof(text), "ROUND %u", (unsigned)round_number);
drawPet2001TextTransparent(frame, 255, 0, text, COLOR_YELLOW, 1);
for (int row = 0; row < MAZE_ROWS; ++row) for (int column = 0; column < MAZE_COLUMNS; ++column) {
const int x = MAZE_X + column * TILE_SIZE;
const int y = MAZE_Y + row * TILE_SIZE;
if (wallAt(column, row)) fill_rect(frame, x, y, TILE_SIZE, TILE_SIZE, RGB565(0, 30, 190));
else if (pellets[row][column] == 2U) fill_circle(frame, x + TILE_CENTRE, y + TILE_CENTRE, 6, COLOR_WHITE);
else if (pellets[row][column] == 1U) fill_circle(frame, x + TILE_CENTRE, y + TILE_CENTRE, 2, COLOR_YELLOW);
}
const int player_x = MAZE_X + player.column * TILE_SIZE + TILE_CENTRE;
const int player_y = MAZE_Y + player.row * TILE_SIZE + TILE_CENTRE;
fill_circle(frame, player_x, player_y, 8, COLOR_YELLOW);
if (player.direction == DIRECTION_RIGHT) fill_rect(frame, player_x + 4, player_y - 4, 5, 9, COLOR_BLACK);
else if (player.direction == DIRECTION_LEFT) fill_rect(frame, player_x - 8, player_y - 4, 5, 9, COLOR_BLACK);
else if (player.direction == DIRECTION_UP) fill_rect(frame, player_x - 4, player_y - 8, 9, 5, COLOR_BLACK);
else if (player.direction == DIRECTION_DOWN) fill_rect(frame, player_x - 4, player_y + 4, 9, 5, COLOR_BLACK);
const bool frightened = (int32_t)(frightened_until - player_last_step) > 0;
for (unsigned i = 0; i < GHOST_COUNT; ++i) {
const int x = MAZE_X + ghosts[i].column * TILE_SIZE + TILE_CENTRE;
const int y = MAZE_Y + ghosts[i].row * TILE_SIZE + TILE_CENTRE;
const uint16_t colour = frightened ? COLOR_DARKBLUE : ghost_colours[i];
fill_circle(frame, x, y - 2, 8, colour);
fill_rect(frame, x - 8, y - 2, 17, 10, colour);
fill_rect(frame, x - 4, y, 2, 4, COLOR_WHITE);
fill_rect(frame, x + 4, y, 2, 4, COLOR_WHITE);
}
if (state == MAZE_LIFE_LOST) drawPet2001TextCentredTransparent(frame, LCD_WIDTH / 2, 83, "LIFE LOST", COLOR_RED, 2);
else if (state == MAZE_FINISHED) drawPet2001TextCentredTransparent(frame, LCD_WIDTH / 2, 83, "GAME OVER", COLOR_RED, 2);
}