Skip to content

Commit f011b8d

Browse files
author
Brigit Murtaugh
committed
Enhance Pacman game with larger canvas, improved visuals, and added monster key
1 parent 6ec3c6c commit f011b8d

1 file changed

Lines changed: 241 additions & 37 deletions

File tree

pacman.html

Lines changed: 241 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,48 +13,50 @@
1313
background: #000;
1414
display: block;
1515
margin: 0 auto 20px auto;
16-
border: 2px solid #FFD700;
16+
border: 4px solid #25d6a2;
1717
}
1818
.pacman-link {
19-
color: #FFD700;
19+
color: #25d6a2;
2020
text-decoration: underline;
2121
cursor: pointer;
2222
}
2323
</style>
2424

2525
<a href="#pacman-game" class="pacman-link"><h1 id="pacman-game">Pacman Game</h1></a>
26-
<canvas id="pacmanCanvas" width="448" height="496"></canvas>
27-
<p>Use arrow keys to move Pacman. Eat all the dots!</p>
26+
<canvas id="pacmanCanvas" width="896" height="992"></canvas>
27+
<p>Use arrow keys to move Pacman. Eat all the features!</p>
2828
<script>
29-
// Simple Pacman game (mini version)
29+
// Larger Pacman game (dev container themed, fewer collectibles, bigger whales)
3030
const canvas = document.getElementById('pacmanCanvas');
3131
const ctx = canvas.getContext('2d');
32-
const tileSize = 16;
32+
const tileSize = 32;
33+
const MOVE_INTERVAL = 80; // ms, decrease for slightly faster speed
34+
// Fewer collectibles: remove about half the dots
3335
const map = [
3436
'############################',
35-
'#............##............#',
37+
'# ....##.... ....#',
3638
'#.####.#####.##.#####.####.#',
3739
'#o####.#####.##.#####.####o#',
3840
'#.####.#####.##.#####.####.#',
39-
'#..........................#',
41+
'# #',
4042
'#.####.##.########.##.####.#',
4143
'#.####.##.########.##.####.#',
42-
'#......##....##....##......#',
44+
'# ## ## ## #',
4345
'######.##### ## #####.######',
4446
'######.##### ## #####.######',
4547
'######.## ##.######',
4648
'######.## ######## ##.######',
4749
'######.## ######## ##.######',
48-
'#............##............#',
50+
'# ....##.... ....#',
4951
'#.####.#####.##.#####.####.#',
5052
'#.####.#####.##.#####.####.#',
51-
'#o..##................##..o#',
53+
'#o..## ##..o#',
5254
'###.##.##.########.##.##.###',
5355
'###.##.##.########.##.##.###',
54-
'#......##....##....##......#',
56+
'# ## ## ## #',
5557
'#.##########.##.##########.#',
5658
'#.##########.##.##########.#',
57-
'#..........................#',
59+
'# #',
5860
'############################'
5961
];
6062
let pacman = { x: 14, y: 17, dx: 0, dy: 0, nextDx: 0, nextDy: 0 };
@@ -65,21 +67,146 @@
6567
for (let y = 0; y < map.length; y++) {
6668
for (let x = 0; x < map[y].length; x++) {
6769
if (map[y][x] === '#') {
68-
ctx.fillStyle = '#2222FF';
70+
ctx.fillStyle = '#0066b8'; // Dev Container blue
6971
ctx.fillRect(x * tileSize, y * tileSize, tileSize, tileSize);
7072
} else if (map[y][x] === '.' || map[y][x] === 'o') {
71-
ctx.fillStyle = '#FFD700';
72-
ctx.beginPath();
73-
ctx.arc(x * tileSize + tileSize/2, y * tileSize + tileSize/2, map[y][x] === 'o' ? 4 : 2, 0, 2 * Math.PI);
74-
ctx.fill();
73+
// Draw a bigger Docker whale icon for features
74+
drawWhaleIcon(ctx, x * tileSize + tileSize/2, y * tileSize + tileSize/2, map[y][x] === 'o' ? 32 : 28);
7575
}
7676
}
7777
}
7878
}
79+
80+
// Draw a simple Docker whale icon using canvas primitives
81+
function drawWhaleIcon(ctx, cx, cy, size) {
82+
// Body
83+
ctx.save();
84+
ctx.translate(cx, cy);
85+
ctx.scale(size/32, size/32);
86+
ctx.beginPath();
87+
ctx.moveTo(-12, 4); ctx.bezierCurveTo(-16, 4, -16, -4, -12, -4); // tail
88+
ctx.lineTo(12, -4); ctx.bezierCurveTo(16, -4, 16, 4, 12, 4); // head
89+
ctx.quadraticCurveTo(0, 12, -12, 4); // belly
90+
ctx.closePath();
91+
ctx.fillStyle = '#2496ed'; // Docker blue
92+
ctx.fill();
93+
// Eye
94+
ctx.beginPath();
95+
ctx.arc(8, 0, 1.5, 0, 2 * Math.PI);
96+
ctx.fillStyle = '#fff';
97+
ctx.fill();
98+
// Water spout
99+
ctx.beginPath();
100+
ctx.moveTo(0, -8); ctx.lineTo(0, -16);
101+
ctx.strokeStyle = '#25d6a2';
102+
ctx.lineWidth = 2;
103+
ctx.stroke();
104+
ctx.beginPath();
105+
ctx.arc(0, -16, 2, 0, Math.PI*2);
106+
ctx.fillStyle = '#25d6a2';
107+
ctx.fill();
108+
ctx.restore();
109+
}
110+
111+
// Monster definitions
112+
const monsters = [
113+
{ name: 'YAML Yeti', color: '#007acc', x: 2, y: 2, icon: 'yeti' },
114+
{ name: 'Dependency Dragon', color: '#25d6a2', x: 25, y: 2, icon: 'dragon' },
115+
{ name: 'Build Bug', color: '#e74c3c', x: 2, y: 21, icon: 'bug' },
116+
{ name: 'Merge Monster', color: '#9b59b6', x: 25, y: 21, icon: 'merge' },
117+
{ name: 'Registry Ghost', color: '#fff', x: 14, y: 2, icon: 'ghost' },
118+
{ name: 'Port Goblin', color: '#f39c12', x: 2, y: 12, icon: 'goblin' },
119+
{ name: 'Token Troll', color: '#f1c40f', x: 25, y: 12, icon: 'troll' },
120+
{ name: 'Cache Kraken', color: '#17a2b8', x: 14, y: 21, icon: 'kraken' }
121+
];
122+
123+
function drawMonsters() {
124+
for (const m of monsters) {
125+
drawMonsterIcon(ctx, m.x * tileSize + tileSize/2, m.y * tileSize + tileSize/2, 28, m.color, m.icon);
126+
}
127+
}
128+
129+
function drawMonsterIcon(ctx, cx, cy, size, color, icon) {
130+
ctx.save();
131+
ctx.translate(cx, cy);
132+
ctx.scale(size/32, size/32);
133+
// Body
134+
ctx.beginPath();
135+
ctx.arc(0, 0, 14, Math.PI, 2 * Math.PI);
136+
ctx.lineTo(14, 14); // right foot
137+
ctx.lineTo(7, 10); // right middle
138+
ctx.lineTo(0, 14); // center foot
139+
ctx.lineTo(-7, 10); // left middle
140+
ctx.lineTo(-14, 14); // left foot
141+
ctx.closePath();
142+
ctx.fillStyle = color;
143+
ctx.fill();
144+
// Eyes
145+
ctx.beginPath();
146+
ctx.arc(-5, -2, 2.5, 0, 2 * Math.PI);
147+
ctx.arc(5, -2, 2.5, 0, 2 * Math.PI);
148+
ctx.fillStyle = '#fff';
149+
ctx.fill();
150+
ctx.beginPath();
151+
ctx.arc(-5, -2, 1, 0, 2 * Math.PI);
152+
ctx.arc(5, -2, 1, 0, 2 * Math.PI);
153+
ctx.fillStyle = '#222';
154+
ctx.fill();
155+
// Icon overlays (simple for each monster)
156+
ctx.save();
157+
switch(icon) {
158+
case 'yeti': // curly brackets
159+
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2;
160+
ctx.beginPath(); ctx.arc(-7, 4, 3, 0.5*Math.PI, 1.5*Math.PI); ctx.stroke();
161+
ctx.beginPath(); ctx.arc(7, 4, 3, 1.5*Math.PI, 0.5*Math.PI); ctx.stroke();
162+
break;
163+
case 'dragon': // chain
164+
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2;
165+
ctx.beginPath(); ctx.moveTo(-6, 6); ctx.lineTo(6, 6); ctx.stroke();
166+
ctx.beginPath(); ctx.arc(-6, 6, 2, 0, 2*Math.PI); ctx.stroke();
167+
ctx.beginPath(); ctx.arc(6, 6, 2, 0, 2*Math.PI); ctx.stroke();
168+
break;
169+
case 'bug': // bug
170+
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2;
171+
ctx.beginPath(); ctx.arc(0, 7, 4, 0, Math.PI*2); ctx.stroke();
172+
ctx.beginPath(); ctx.moveTo(-4, 7); ctx.lineTo(-8, 11); ctx.moveTo(4, 7); ctx.lineTo(8, 11); ctx.stroke();
173+
break;
174+
case 'merge': // branch
175+
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2;
176+
ctx.beginPath(); ctx.moveTo(-6, 6); ctx.lineTo(0, 0); ctx.lineTo(6, 6); ctx.stroke();
177+
ctx.beginPath(); ctx.arc(-6, 6, 2, 0, 2*Math.PI); ctx.stroke();
178+
ctx.beginPath(); ctx.arc(6, 6, 2, 0, 2*Math.PI); ctx.stroke();
179+
break;
180+
case 'ghost': // cloud
181+
ctx.strokeStyle = '#bbb'; ctx.lineWidth = 2;
182+
ctx.beginPath(); ctx.arc(0, 7, 5, Math.PI, 0); ctx.stroke();
183+
ctx.beginPath(); ctx.arc(-4, 7, 2, Math.PI, 0); ctx.stroke();
184+
ctx.beginPath(); ctx.arc(4, 7, 2, Math.PI, 0); ctx.stroke();
185+
break;
186+
case 'goblin': // plug
187+
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2;
188+
ctx.beginPath(); ctx.moveTo(0, 8); ctx.lineTo(0, 12); ctx.stroke();
189+
ctx.beginPath(); ctx.moveTo(-3, 12); ctx.lineTo(3, 12); ctx.stroke();
190+
break;
191+
case 'troll': // key
192+
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2;
193+
ctx.beginPath(); ctx.arc(-2, 8, 2, 0, 2*Math.PI); ctx.stroke();
194+
ctx.beginPath(); ctx.moveTo(0, 8); ctx.lineTo(6, 8); ctx.stroke();
195+
ctx.beginPath(); ctx.moveTo(4, 6); ctx.lineTo(4, 10); ctx.stroke();
196+
break;
197+
case 'kraken': // tentacles
198+
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2;
199+
for(let i=-2;i<=2;i++) { ctx.beginPath(); ctx.moveTo(i*3, 14); ctx.lineTo(i*3, 10); ctx.stroke(); }
200+
break;
201+
}
202+
ctx.restore();
203+
ctx.restore();
204+
}
205+
79206
function drawPacman() {
80207
ctx.fillStyle = '#FFFF00';
81208
ctx.beginPath();
82-
ctx.arc(pacman.x * tileSize + tileSize/2, pacman.y * tileSize + tileSize/2, tileSize/2-1, 0.25 * Math.PI, 1.75 * Math.PI, false);
209+
ctx.arc(pacman.x * tileSize + tileSize/2, pacman.y * tileSize + tileSize/2, tileSize/2-2, 0.25 * Math.PI, 1.75 * Math.PI, false);
83210
ctx.lineTo(pacman.x * tileSize + tileSize/2, pacman.y * tileSize + tileSize/2);
84211
ctx.fill();
85212
}
@@ -110,32 +237,109 @@
110237
}
111238
function drawScore() {
112239
ctx.fillStyle = '#FFF';
113-
ctx.font = '16px Arial';
114-
ctx.fillText('Score: ' + score, 10, canvas.height - 10);
240+
ctx.font = '28px Arial';
241+
ctx.fillText('Score: ' + score, 20, canvas.height - 20);
115242
}
116-
function gameLoop() {
243+
244+
document.addEventListener('keydown', e => {
245+
if (["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].includes(e.key)) {
246+
e.preventDefault();
247+
let moved = false;
248+
if (e.key === 'ArrowUp') { pacman.nextDx = 0; pacman.nextDy = -1; moved = true; }
249+
if (e.key === 'ArrowDown') { pacman.nextDx = 0; pacman.nextDy = 1; moved = true; }
250+
if (e.key === 'ArrowLeft') { pacman.nextDx = -1; pacman.nextDy = 0; moved = true; }
251+
if (e.key === 'ArrowRight') { pacman.nextDx = 1; pacman.nextDy = 0; moved = true; }
252+
if (moved && canMove(pacman.x + pacman.nextDx, pacman.y + pacman.nextDy)) {
253+
pacman.dx = pacman.nextDx;
254+
pacman.dy = pacman.nextDy;
255+
movePacman();
256+
eatDot();
257+
drawMap();
258+
drawPacman();
259+
drawScore();
260+
if (score >= dots) {
261+
ctx.fillStyle = '#25d6a2';
262+
ctx.font = '48px Arial';
263+
ctx.fillText('All Features Collected!', canvas.width/2 - 260, canvas.height/2);
264+
}
265+
}
266+
}
267+
});
268+
269+
function gameLoop(timestamp) {
117270
ctx.clearRect(0, 0, canvas.width, canvas.height);
118271
drawMap();
119-
movePacman();
120-
eatDot();
272+
drawMonsters();
121273
drawPacman();
122274
drawScore();
123275
if (score >= dots) {
124-
ctx.fillStyle = '#0F0';
125-
ctx.font = '32px Arial';
126-
ctx.fillText('You Win!', canvas.width/2 - 60, canvas.height/2);
276+
ctx.fillStyle = '#25d6a2';
277+
ctx.font = '48px Arial';
278+
ctx.fillText('All Features Collected!', canvas.width/2 - 260, canvas.height/2);
127279
return;
128280
}
129281
requestAnimationFrame(gameLoop);
130282
}
131-
document.addEventListener('keydown', e => {
132-
if (["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].includes(e.key)) {
133-
e.preventDefault();
134-
if (e.key === 'ArrowUp') { pacman.nextDx = 0; pacman.nextDy = -1; }
135-
if (e.key === 'ArrowDown') { pacman.nextDx = 0; pacman.nextDy = 1; }
136-
if (e.key === 'ArrowLeft') { pacman.nextDx = -1; pacman.nextDy = 0; }
137-
if (e.key === 'ArrowRight') { pacman.nextDx = 1; pacman.nextDy = 0; }
138-
}
139-
});
140-
gameLoop();
283+
284+
requestAnimationFrame(gameLoop);
285+
</script>
286+
287+
<!-- Monster Key -->
288+
<div id="monster-key" style="max-width: 600px; margin: 40px auto 0 auto; background: #111; color: #fff; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px #0002;">
289+
<h2 style="text-align:center;"><a href="#monster-key" style="color:#25d6a2;text-decoration:underline;">Monster Key</a></h2>
290+
<ul style="list-style:none; padding:0; margin:0; display:flex; flex-wrap:wrap; justify-content:space-around;">
291+
<li style="margin:10px 20px; display:flex; align-items:center;">
292+
<span id="icon-yeti"></span>
293+
<span style="margin-left:10px;">YAML Yeti <span style="color:#888;">(blue, curly brackets)</span></span>
294+
</li>
295+
<li style="margin:10px 20px; display:flex; align-items:center;">
296+
<span id="icon-dragon"></span>
297+
<span style="margin-left:10px;">Dependency Dragon <span style="color:#888;">(green, chain)</span></span>
298+
</li>
299+
<li style="margin:10px 20px; display:flex; align-items:center;">
300+
<span id="icon-bug"></span>
301+
<span style="margin-left:10px;">Build Bug <span style="color:#888;">(red, bug)</span></span>
302+
</li>
303+
<li style="margin:10px 20px; display:flex; align-items:center;">
304+
<span id="icon-merge"></span>
305+
<span style="margin-left:10px;">Merge Monster <span style="color:#888;">(purple, branch)</span></span>
306+
</li>
307+
<li style="margin:10px 20px; display:flex; align-items:center;">
308+
<span id="icon-ghost"></span>
309+
<span style="margin-left:10px;">Registry Ghost <span style="color:#888;">(white, cloud)</span></span>
310+
</li>
311+
<li style="margin:10px 20px; display:flex; align-items:center;">
312+
<span id="icon-goblin"></span>
313+
<span style="margin-left:10px;">Port Goblin <span style="color:#888;">(orange, plug)</span></span>
314+
</li>
315+
<li style="margin:10px 20px; display:flex; align-items:center;">
316+
<span id="icon-troll"></span>
317+
<span style="margin-left:10px;">Token Troll <span style="color:#888;">(yellow, key)</span></span>
318+
</li>
319+
<li style="margin:10px 20px; display:flex; align-items:center;">
320+
<span id="icon-kraken"></span>
321+
<span style="margin-left:10px;">Cache Kraken <span style="color:#888;">(teal, tentacles)</span></span>
322+
</li>
323+
</ul>
324+
</div>
325+
<script>
326+
// Draw monster icons for the key
327+
const keyIcons = [
328+
{id:'icon-yeti', color:'#007acc', icon:'yeti'},
329+
{id:'icon-dragon', color:'#25d6a2', icon:'dragon'},
330+
{id:'icon-bug', color:'#e74c3c', icon:'bug'},
331+
{id:'icon-merge', color:'#9b59b6', icon:'merge'},
332+
{id:'icon-ghost', color:'#fff', icon:'ghost'},
333+
{id:'icon-goblin', color:'#f39c12', icon:'goblin'},
334+
{id:'icon-troll', color:'#f1c40f', icon:'troll'},
335+
{id:'icon-kraken', color:'#17a2b8', icon:'kraken'}
336+
];
337+
for (const k of keyIcons) {
338+
const c = document.createElement('canvas');
339+
c.width = 36; c.height = 36;
340+
c.style.verticalAlign = 'middle';
341+
const x = c.getContext('2d');
342+
drawMonsterIcon(x, 18, 18, 32, k.color, k.icon);
343+
document.getElementById(k.id).appendChild(c);
344+
}
141345
</script>

0 commit comments

Comments
 (0)