User prompt
Kırmızı topu mavi topa yakın konuma koy
User prompt
Mavi olan yemi kırmızıya renkli yeme yakın tut
User prompt
Tüm yollar birbirine bağlı olmalı
User prompt
Haritayı yeniden yarat
User prompt
Haritayı değiştir
User prompt
Düşman karakter gibi hareket edebilmeli rastgele alanda gezmeli
User prompt
Düşman rastgele gezmeli
User prompt
Düşman takip etmeli
User prompt
Siyah alanın içinde başlasın
User prompt
Sol üstten başlasın karakterin konunu
Code edit (1 edits merged)
Please save this source code
User prompt
Maze Munch: Neon Chase
Initial prompt
Create a 2D top-down maze chase game inspired by classic Pac-Man. The player controls a yellow circular character that moves through a maze, collecting small dots to gain points. Four enemy ghosts roam the maze with unique AI patterns: one chases directly, one ambushes, one moves randomly, and one follows but stays distant. When the player collects a large power dot, all ghosts become vulnerable and can be eaten for extra points for a short time. The game ends if a ghost catches the player when not vulnerable. The map should have narrow corridors, walls, and corners, and should loop from left to right (teleport tunnels). Add a score counter, lives (3), and increasing difficulty after each level. Style: Classic arcade, retro neon visuals. Sound: 8-bit arcade sounds and background music. Controls: Arrow keys or swipe for mobile.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// --- Dot (Pellet) ---
var Dot = Container.expand(function () {
var self = Container.call(this);
var dotAsset = self.attachAsset('dot', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = dotAsset.width / 2;
return self;
});
// --- Ghost ---
var Ghost = Container.expand(function () {
var self = Container.call(this);
// Attach ghost asset (color set later)
var ghostAsset = self.attachAsset('ghostBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Ghost color
self.color = 0xff0000;
ghostAsset.tint = self.color;
// Movement speed
self.speed = 8;
// Direction
self.dir = {
x: 0,
y: 0
};
// Scatter/Chase mode
self.mode = 'chase'; // or 'scatter' or 'frightened'
self.frightenedTicks = 0;
// For collision
self.radius = ghostAsset.width / 2;
// Ghost AI type: 'blinky', 'pinky', 'inky', 'clyde'
self.ghostType = 'blinky';
// Set color
self.setColor = function (color) {
self.color = color;
ghostAsset.tint = color;
};
// Set type
self.setType = function (type) {
self.ghostType = type;
};
// Set mode
self.setMode = function (mode) {
self.mode = mode;
if (mode === 'frightened') {
ghostAsset.tint = 0x2222ff;
} else {
ghostAsset.tint = self.color;
}
};
// Move to tile
self.moveToTile = function (tile) {
self.x = tile.x * tileSize + tileSize / 2 + mazeOffsetX;
self.y = tile.y * tileSize + tileSize / 2 + mazeOffsetY;
};
// Get current tile
self.getTile = function () {
return {
x: Math.floor((self.x - mazeOffsetX) / tileSize),
y: Math.floor((self.y - mazeOffsetY) / tileSize)
};
};
// AI: get target tile
self.getTargetTile = function () {
// For random movement, just return the next tile in the current direction if possible,
// otherwise pick a random valid direction.
var curTile = self.getTile();
var dirs = [{
x: 0,
y: -1
},
// up
{
x: 1,
y: 0
},
// right
{
x: 0,
y: 1
},
// down
{
x: -1,
y: 0
} // left
];
// Try to keep going in the same direction if possible
if (canMove(curTile.x, curTile.y, self.dir.x, self.dir.y)) {
return {
x: curTile.x + self.dir.x,
y: curTile.y + self.dir.y
};
}
// Otherwise, pick a random valid direction
var options = [];
for (var i = 0; i < dirs.length; i++) {
var d = dirs[i];
if (canMove(curTile.x, curTile.y, d.x, d.y)) {
options.push(d);
}
}
if (options.length > 0) {
var idx = Math.floor(Math.random() * options.length);
var d = options[idx];
return {
x: curTile.x + d.x,
y: curTile.y + d.y
};
}
// If stuck, stay in place
return curTile;
};
// AI: choose direction
self.chooseDir = function () {
var curTile = self.getTile();
var target = self.getTargetTile();
// All possible directions
var dirs = [{
x: 0,
y: -1
},
// up
{
x: 1,
y: 0
},
// right
{
x: 0,
y: 1
},
// down
{
x: -1,
y: 0
} // left
];
// Don't reverse
var opp = {
x: -self.dir.x,
y: -self.dir.y
};
// For frightened, pick random
if (self.mode === 'frightened') {
var options = [];
for (var i = 0; i < dirs.length; i++) {
var d = dirs[i];
if ((d.x !== opp.x || d.y !== opp.y) && canMove(curTile.x, curTile.y, d.x, d.y)) {
options.push(d);
}
}
if (options.length > 0) {
var idx = Math.floor(Math.random() * options.length);
return options[idx];
}
return self.dir;
}
// Otherwise, pick direction that minimizes distance to target
var minDist = 99999,
bestDir = self.dir;
for (var i = 0; i < dirs.length; i++) {
var d = dirs[i];
if ((d.x !== opp.x || d.y !== opp.y) && canMove(curTile.x, curTile.y, d.x, d.y)) {
var nx = curTile.x + d.x,
ny = curTile.y + d.y;
var dist = (target.x - nx) * (target.x - nx) + (target.y - ny) * (target.y - ny);
if (dist < minDist) {
minDist = dist;
bestDir = d;
}
}
}
return bestDir;
};
// Update per tick
self.update = function () {
// If in tunnel, wrap
if (self.x < mazeOffsetX - tileSize / 2) {
self.x = mazeOffsetX + (mazeCols - 1) * tileSize + tileSize / 2;
}
if (self.x > mazeOffsetX + (mazeCols - 1) * tileSize + tileSize / 2) {
self.x = mazeOffsetX - tileSize / 2;
}
// Frightened mode timer
if (self.mode === 'frightened') {
self.frightenedTicks--;
if (self.frightenedTicks <= 0) {
self.setMode('chase');
}
}
// At center of tile, choose new dir
var curTile = self.getTile();
var cx = curTile.x * tileSize + tileSize / 2 + mazeOffsetX;
var cy = curTile.y * tileSize + tileSize / 2 + mazeOffsetY;
var dist = Math.abs(self.x - cx) + Math.abs(self.y - cy);
if (dist < 2) {
// Always choose direction based on AI (chase, scatter, frightened)
var newDir = self.chooseDir();
self.dir = newDir;
// Snap to center
self.x = cx;
self.y = cy;
}
// Move if possible
if (canMove(curTile.x, curTile.y, self.dir.x, self.dir.y)) {
self.x += self.dir.x * self.speed;
self.y += self.dir.y * self.speed;
}
};
return self;
});
// --- Player (Pac) ---
var Player = Container.expand(function () {
var self = Container.call(this);
// Attach yellow circle asset for player
var playerAsset = self.attachAsset('playerCircle', {
anchorX: 0.5,
anchorY: 0.5
});
// Movement speed (pixels per tick)
self.speed = 10;
// Current direction: {x: -1, y:0} etc.
self.dir = {
x: 0,
y: 0
};
self.nextDir = {
x: 0,
y: 0
};
// For smooth movement
self.targetTile = null;
// For mouth animation
self.mouthOpen = true;
self.mouthTween = null;
// For collision
self.radius = playerAsset.width / 2;
// Animate mouth (simple scaleX)
function animateMouth() {
if (self.mouthTween) tween.stop(playerAsset, {
scaleX: true
});
playerAsset.scaleX = 1;
self.mouthTween = tween(playerAsset, {
scaleX: 0.7
}, {
duration: 120,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(playerAsset, {
scaleX: 1
}, {
duration: 120,
easing: tween.easeInOut,
onFinish: animateMouth
});
}
});
}
animateMouth();
// Set direction
self.setDir = function (dx, dy) {
self.nextDir = {
x: dx,
y: dy
};
};
// Move to tile
self.moveToTile = function (tile) {
self.x = tile.x * tileSize + tileSize / 2 + mazeOffsetX;
self.y = tile.y * tileSize + tileSize / 2 + mazeOffsetY;
};
// Get current tile
self.getTile = function () {
return {
x: Math.floor((self.x - mazeOffsetX) / tileSize),
y: Math.floor((self.y - mazeOffsetY) / tileSize)
};
};
// Update per tick
self.update = function () {
// If in tunnel, wrap
if (self.x < mazeOffsetX - tileSize / 2) {
self.x = mazeOffsetX + (mazeCols - 1) * tileSize + tileSize / 2;
}
if (self.x > mazeOffsetX + (mazeCols - 1) * tileSize + tileSize / 2) {
self.x = mazeOffsetX - tileSize / 2;
}
// Try to turn if possible
var curTile = self.getTile();
if (canMove(curTile.x, curTile.y, self.nextDir.x, self.nextDir.y)) {
self.dir = {
x: self.nextDir.x,
y: self.nextDir.y
};
}
// Move if possible
if (canMove(curTile.x, curTile.y, self.dir.x, self.dir.y)) {
self.x += self.dir.x * self.speed;
self.y += self.dir.y * self.speed;
} else {
// Snap to center of tile
self.moveToTile(curTile);
}
};
return self;
});
// --- PowerDot (Power Pellet) ---
var PowerDot = Container.expand(function () {
var self = Container.call(this);
var pdAsset = self.attachAsset('powerDot', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = pdAsset.width / 2;
return self;
});
// --- Wall ---
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallAsset = self.attachAsset('wallBlock', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Simple 19x21 maze (Pac-Man like, but smaller for MVP)
// 0: empty, 1: wall, 2: dot, 3: power dot, 4: player start, 5: ghost start, 6: tunnel
// --- Maze Layout ---
// Example: A new, more open map with a different wall and dot pattern
var mazeData = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1], [1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1], [1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 5, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1], [1, 2, 2, 2, 1, 2, 2, 2, 1, 4, 1, 2, 2, 2, 1, 2, 2, 2, 1], [1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1], [1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1], [1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]];
var mazeRows = mazeData.length;
var mazeCols = mazeData[0].length;
// --- Maze Rendering ---
var tileSize = 90;
var mazeWidth = mazeCols * tileSize;
var mazeHeight = mazeRows * tileSize;
var mazeOffsetX = Math.floor((2048 - mazeWidth) / 2);
var mazeOffsetY = Math.floor((2732 - mazeHeight) / 2);
// --- Asset Initialization ---
// --- Game State ---
var player = null;
var ghosts = [];
var dots = [];
var powerDots = [];
var walls = [];
var lives = 3;
var level = 1;
var score = 0;
var gameActive = true;
var frightenedTicksMax = 360; // 6 seconds at 60fps
// --- GUI ---
var scoreTxt = new Text2('0', {
size: 100,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var livesTxt = new Text2('♥♥♥', {
size: 80,
fill: 0xFF4444
});
livesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(livesTxt);
livesTxt.y = 110;
// --- Helper: canMove ---
function canMove(x, y, dx, dy) {
var nx = x + dx,
ny = y + dy;
if (nx < 0) nx = mazeCols - 1;
if (nx >= mazeCols) nx = 0;
if (ny < 0 || ny >= mazeRows) return false;
if (mazeData[ny][nx] === 1) return false;
return true;
}
// --- Helper: reset maze ---
function resetMaze() {
// Remove old
for (var i = 0; i < dots.length; i++) dots[i].destroy();
for (var i = 0; i < powerDots.length; i++) powerDots[i].destroy();
for (var i = 0; i < walls.length; i++) walls[i].destroy();
dots = [];
powerDots = [];
walls = [];
// Place maze
for (var y = 0; y < mazeRows; y++) {
for (var x = 0; x < mazeCols; x++) {
var v = mazeData[y][x];
var px = x * tileSize + tileSize / 2 + mazeOffsetX;
var py = y * tileSize + tileSize / 2 + mazeOffsetY;
if (v === 1) {
var wall = new Wall();
wall.x = px;
wall.y = py;
game.addChild(wall);
walls.push(wall);
} else if (v === 2) {
var dot = new Dot();
dot.x = px;
dot.y = py;
game.addChild(dot);
dots.push(dot);
} else if (v === 3) {
var pd = new PowerDot();
pd.x = px;
pd.y = py;
game.addChild(pd);
powerDots.push(pd);
}
}
}
}
// --- Helper: reset player and ghosts ---
function resetActors() {
// Remove old
if (player) player.destroy();
for (var i = 0; i < ghosts.length; i++) ghosts[i].destroy();
ghosts = [];
// Place player
// Start player at the first open black area (not a wall) from the top, scanning left to right
var foundStart = false;
for (var y = 0; y < mazeRows && !foundStart; y++) {
for (var x = 0; x < mazeCols && !foundStart; x++) {
if (mazeData[y][x] !== 1) {
// not a wall
player = new Player();
player.x = x * tileSize + tileSize / 2 + mazeOffsetX;
player.y = y * tileSize + tileSize / 2 + mazeOffsetY;
game.addChild(player);
foundStart = true;
}
}
}
// Fallback in case all are walls (should never happen)
if (!player) {
player = new Player();
player.x = 1 * tileSize + tileSize / 2 + mazeOffsetX;
player.y = 1 * tileSize + tileSize / 2 + mazeOffsetY;
game.addChild(player);
}
// Place ghosts
var ghostColors = [0xff0000, 0xffb8ff, 0x00ffff, 0xffa500];
var ghostTypes = ['blinky', 'pinky', 'inky', 'clyde'];
var ghostCount = 4;
var gidx = 0;
for (var y = 0; y < mazeRows; y++) {
for (var x = 0; x < mazeCols; x++) {
if (mazeData[y][x] === 5 && gidx < ghostCount) {
var ghost = new Ghost();
ghost.x = x * tileSize + tileSize / 2 + mazeOffsetX;
ghost.y = y * tileSize + tileSize / 2 + mazeOffsetY;
ghost.setColor(ghostColors[gidx]);
ghost.setType(ghostTypes[gidx]);
ghost.dir = {
x: 0,
y: -1
};
game.addChild(ghost);
ghosts.push(ghost);
gidx++;
}
}
}
}
// --- Helper: update GUI ---
function updateGUI() {
scoreTxt.setText(score);
var lstr = '';
for (var i = 0; i < lives; i++) lstr += '♥';
livesTxt.setText(lstr);
}
// --- Helper: start level ---
function startLevel() {
resetMaze();
resetActors();
updateGUI();
gameActive = true;
}
// --- Helper: lose life ---
function loseLife() {
lives--;
updateGUI();
if (lives <= 0) {
gameActive = false;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
// Reset player/ghosts only
resetActors();
}
// --- Helper: next level ---
function nextLevel() {
level++;
// Increase ghost speed a bit
for (var i = 0; i < ghosts.length; i++) {
ghosts[i].speed += 1;
}
startLevel();
}
// --- Helper: frighten ghosts ---
function frightenGhosts() {
for (var i = 0; i < ghosts.length; i++) {
ghosts[i].setMode('frightened');
ghosts[i].frightenedTicks = frightenedTicksMax;
}
}
// --- Helper: check collision (circle) ---
function circlesCollide(a, b) {
var dx = a.x - b.x,
dy = a.y - b.y;
var r = a.radius + b.radius;
return dx * dx + dy * dy < r * r;
}
// --- Input Handling (swipe) ---
var touchStart = null;
game.down = function (x, y, obj) {
touchStart = {
x: x,
y: y
};
};
game.up = function (x, y, obj) {
if (!touchStart) return;
var dx = x - touchStart.x,
dy = y - touchStart.y;
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 30) player.setDir(1, 0);else if (dx < -30) player.setDir(-1, 0);
} else {
if (dy > 30) player.setDir(0, 1);else if (dy < -30) player.setDir(0, -1);
}
touchStart = null;
};
// --- Main Update Loop ---
game.update = function () {
if (!gameActive) return;
// Update player
if (player) player.update();
// Update ghosts
for (var i = 0; i < ghosts.length; i++) ghosts[i].update();
// --- Collisions: Player <-> Dots ---
for (var i = dots.length - 1; i >= 0; i--) {
if (circlesCollide(player, dots[i])) {
score += 10;
dots[i].destroy();
dots.splice(i, 1);
updateGUI();
}
}
// --- Collisions: Player <-> PowerDots ---
for (var i = powerDots.length - 1; i >= 0; i--) {
if (circlesCollide(player, powerDots[i])) {
score += 50;
powerDots[i].destroy();
powerDots.splice(i, 1);
updateGUI();
frightenGhosts();
}
}
// --- Collisions: Player <-> Ghosts ---
for (var i = 0; i < ghosts.length; i++) {
if (circlesCollide(player, ghosts[i])) {
if (ghosts[i].mode === 'frightened') {
score += 200;
ghosts[i].setMode('chase');
ghosts[i].moveToTile({
x: 9,
y: 6
}); // send to ghost start
updateGUI();
} else {
loseLife();
return;
}
}
}
// --- Win Condition: all dots eaten ---
if (dots.length === 0 && powerDots.length === 0) {
gameActive = false;
LK.effects.flashScreen(0x00ff00, 1000);
LK.showYouWin();
nextLevel();
}
};
// --- Start Game ---
startLevel(); ===================================================================
--- original.js
+++ change.js
@@ -350,9 +350,9 @@
// Simple 19x21 maze (Pac-Man like, but smaller for MVP)
// 0: empty, 1: wall, 2: dot, 3: power dot, 4: player start, 5: ghost start, 6: tunnel
// --- Maze Layout ---
// Example: A new, more open map with a different wall and dot pattern
-var mazeData = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1], [1, 2, 1, 3, 1, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 3, 1, 2, 1], [1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1], [1, 2, 1, 3, 1, 2, 1, 2, 2, 5, 2, 2, 1, 2, 1, 3, 1, 2, 1], [1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1], [1, 2, 1, 3, 1, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 3, 1, 2, 1], [1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]];
+var mazeData = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1], [1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1], [1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 5, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1], [1, 2, 2, 2, 1, 2, 2, 2, 1, 4, 1, 2, 2, 2, 1, 2, 2, 2, 1], [1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1], [1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1], [1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]];
var mazeRows = mazeData.length;
var mazeCols = mazeData[0].length;
// --- Maze Rendering ---
var tileSize = 90;