Skip to content

Commit 21aacde

Browse files
author
DavidQ
committed
BUILD_PR_GAMES_PACMANLITE_GAMEPLAY_MIGRATION_TO_NEXT
Migrated gameplay to PacmanLite_next.
1 parent 5821354 commit 21aacde

17 files changed

Lines changed: 790 additions & 42 deletions

docs/dev/CODEX_COMMANDS.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ MODEL: GPT-5.4
22
REASONING: high
33

44
COMMAND:
5-
Execute BUILD_PR_GAMES_PACMANLITE_NEXT_TEMPLATE_BASELINE
5+
Execute BUILD_PR_GAMES_PACMANLITE_GAMEPLAY_MIGRATION_TO_NEXT
66

77
Rules:
8-
- Copy from games/_template/**
9-
- Create games/PacmanLite_next/**
10-
- Do NOT modify games/PacmanLite/**
11-
- No gameplay
12-
- Canvas text required
13-
- Fail fast on ambiguity
8+
- Read games/PacmanLite/**
9+
- Write games/PacmanLite_next/**
10+
- Correct folder placement
11+
- No guessing
12+
- Do NOT modify original

docs/dev/COMMIT_COMMENT.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
BUILD_PR_GAMES_PACMANLITE_NEXT_TEMPLATE_BASELINE
2-
Created PacmanLite_next baseline from template.
1+
BUILD_PR_GAMES_PACMANLITE_GAMEPLAY_MIGRATION_TO_NEXT
2+
Migrated gameplay to PacmanLite_next.

docs/dev/NEXT_COMMAND.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Create BUILD_PR_GAMES_PACMANLITE_GAMEPLAY_MIGRATION_TO_NEXT
1+
Create BUILD_PR_GAMES_PACMANLITE_CLEAR_DESTINATION
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# BUILD PR — Games PacmanLite Gameplay Migration To Next
2+
3+
## Purpose
4+
Migrate PacmanLite gameplay into games/PacmanLite_next.
5+
6+
## Scope
7+
- Read from games/PacmanLite/**
8+
- Write to games/PacmanLite_next/**
9+
- Do NOT modify original
10+
11+
## Rules
12+
- Place files in correct destination folders
13+
- No guessing destinations
14+
- Preserve structure
15+
- Wire index.html to boot gameplay
16+
17+
## Acceptance Criteria
18+
- PacmanLite_next runs gameplay
19+
- Canvas visible
20+
- No console errors
21+
- Original unchanged
22+
23+
## Output
24+
<project folder>/tmp/BUILD_PR_GAMES_PACMANLITE_GAMEPLAY_MIGRATION_TO_NEXT_delta.zip
27 KB
Loading
Lines changed: 62 additions & 0 deletions
Loading
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
Toolbox Aid
3+
David Quesenberry
4+
03/25/2026
5+
PacmanLiteConfig.js
6+
*/
7+
const PacmanLiteConfig = Object.freeze({
8+
tileSize: 32,
9+
playerSpeed: 192,
10+
ghostSpeed: 120,
11+
spawnPlayerTile: Object.freeze({ x: 9, y: 9 }),
12+
spawnGhostTile: Object.freeze({ x: 13, y: 9 }),
13+
});
14+
15+
export default PacmanLiteConfig;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Toolbox Aid
3+
David Quesenberry
4+
03/25/2026
5+
PacmanLiteDebugOverlay.js
6+
*/
7+
export default class PacmanLiteDebugOverlay {
8+
render(renderer, { world }) {
9+
const info = [
10+
`P TILE: ${world.debug.playerTile.x},${world.debug.playerTile.y}`,
11+
`G TILE: ${world.debug.ghostTile.x},${world.debug.ghostTile.y}`,
12+
`QUEUE: ${world.debug.queuedDirection || '-'}`,
13+
`TARGET: ${world.debug.targetTile.x},${world.debug.targetTile.y}`,
14+
`PELLETS: ${world.grid.pelletCount()}`,
15+
];
16+
renderer.drawRect(622, 64, 300, 166, 'rgba(2, 6, 23, 0.86)');
17+
renderer.strokeRect(622, 64, 300, 166, '#334155', 1);
18+
info.forEach((line, index) => {
19+
renderer.drawText(line, 638, 82 + (index * 28), {
20+
color: '#94a3b8',
21+
font: '15px monospace',
22+
textBaseline: 'top',
23+
});
24+
});
25+
}
26+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
Toolbox Aid
3+
David Quesenberry
4+
03/25/2026
5+
PacmanLiteGhostController.js
6+
*/
7+
import { DIRS, chooseDirectionTowardTarget } from './PacmanLiteNavigator.js';
8+
9+
function near(a, b, epsilon = 1.2) {
10+
return Math.abs(a - b) <= epsilon;
11+
}
12+
13+
export default class PacmanLiteGhostController {
14+
constructor({ speed = 120 } = {}) {
15+
this.speed = speed;
16+
}
17+
18+
update(dtSeconds, { grid, ghost, playerTile }) {
19+
const ghostTile = grid.worldToTile(ghost.x, ghost.y);
20+
const center = grid.tileToWorld(ghostTile.x, ghostTile.y);
21+
const atCenter = near(ghost.x, center.x) && near(ghost.y, center.y);
22+
23+
if (atCenter) {
24+
ghost.x = center.x;
25+
ghost.y = center.y;
26+
const next = chooseDirectionTowardTarget(
27+
grid,
28+
ghostTile.x,
29+
ghostTile.y,
30+
ghost.direction,
31+
playerTile.x,
32+
playerTile.y,
33+
);
34+
if (next) {
35+
ghost.direction = next;
36+
}
37+
}
38+
39+
if (!ghost.direction) {
40+
return;
41+
}
42+
const vec = DIRS[ghost.direction];
43+
ghost.x += vec.x * this.speed * dtSeconds;
44+
ghost.y += vec.y * this.speed * dtSeconds;
45+
}
46+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
Toolbox Aid
3+
David Quesenberry
4+
03/25/2026
5+
PacmanLiteGrid.js
6+
*/
7+
const ROWS = [
8+
'#################',
9+
'#...............#',
10+
'#.###.#####.###.#',
11+
'#.#...#...#...#.#',
12+
'#.#.###.#.###.#.#',
13+
'#...#...#...#...#',
14+
'###.#.#####.#.###',
15+
'#...#...#...#...#',
16+
'#.#####.#.#####.#',
17+
'#.......#.......#',
18+
'#.#####.#.#####.#',
19+
'#...#...#...#...#',
20+
'###.#.#####.#.###',
21+
'#...#...#...#...#',
22+
'#.#.###.#.###.#.#',
23+
'#.#...#...#...#.#',
24+
'#.###.#####.###.#',
25+
'#...............#',
26+
'#################',
27+
];
28+
29+
export default class PacmanLiteGrid {
30+
constructor({ tileSize = 32 } = {}) {
31+
this.tileSize = tileSize;
32+
this.rows = ROWS;
33+
this.height = this.rows.length;
34+
this.width = this.rows[0].length;
35+
this.pellets = new Set();
36+
this.seedPellets();
37+
}
38+
39+
key(x, y) {
40+
return `${x},${y}`;
41+
}
42+
43+
seedPellets() {
44+
this.pellets.clear();
45+
for (let y = 0; y < this.height; y += 1) {
46+
for (let x = 0; x < this.width; x += 1) {
47+
if (this.isWalkable(x, y)) {
48+
this.pellets.add(this.key(x, y));
49+
}
50+
}
51+
}
52+
}
53+
54+
isInside(x, y) {
55+
return x >= 0 && y >= 0 && x < this.width && y < this.height;
56+
}
57+
58+
isWalkable(x, y) {
59+
if (!this.isInside(x, y)) {
60+
return false;
61+
}
62+
return this.rows[y][x] !== '#';
63+
}
64+
65+
tileToWorld(x, y) {
66+
const center = this.tileSize * 0.5;
67+
return {
68+
x: (x * this.tileSize) + center,
69+
y: (y * this.tileSize) + center,
70+
};
71+
}
72+
73+
worldToTile(px, py) {
74+
return {
75+
x: Math.floor(px / this.tileSize),
76+
y: Math.floor(py / this.tileSize),
77+
};
78+
}
79+
80+
hasPellet(x, y) {
81+
return this.pellets.has(this.key(x, y));
82+
}
83+
84+
consumePellet(x, y) {
85+
return this.pellets.delete(this.key(x, y));
86+
}
87+
88+
pelletCount() {
89+
return this.pellets.size;
90+
}
91+
}

0 commit comments

Comments
 (0)