User prompt
Faster movement
User prompt
Make the maze bigger and the ai move faster
Code edit (1 edits merged)
Please save this source code
User prompt
Maze Master: AI Learning Adventure
Initial prompt
A randomly generated maze with a bot that uses RL to learn the maze when it reaches a dead end it restarts with its info still the same but everytime you load the game its brain resets
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var AIBot = Container.expand(function () { var self = Container.call(this); self.visual = self.attachAsset('bot', { anchorX: 0.5, anchorY: 0.5 }); self.gridX = 0; self.gridY = 0; self.memory = {}; // Stores path memory self.currentPath = []; // Stores current path self.possibleMoves = []; // Stores possible moves from current position self.isMoving = false; self.hasWon = false; self.moveTo = function (x, y, duration) { if (self.isMoving) { return; } self.isMoving = true; tween(self, { x: x, y: y }, { duration: duration || 300, easing: tween.easeInOut, onFinish: function onFinish() { self.isMoving = false; } }); }; self.setGridPosition = function (x, y) { self.gridX = x; self.gridY = y; }; self.getMemoryKey = function (x, y) { return x + ',' + y; }; self.rememberCell = function (x, y, value) { var key = self.getMemoryKey(x, y); self.memory[key] = value; }; self.getCellMemory = function (x, y) { var key = self.getMemoryKey(x, y); return self.memory[key]; }; self.addToPath = function (x, y) { self.currentPath.push({ x: x, y: y }); }; self.reset = function () { self.currentPath = []; self.possibleMoves = []; self.isMoving = false; self.hasWon = false; }; self.clearMemory = function () { self.memory = {}; }; return self; }); var MazeCell = Container.expand(function (x, y, type) { var self = Container.call(this); self.gridX = x; self.gridY = y; self.type = type || 'wall'; self.visited = false; self.deadEnd = false; // Visual representation self.visual = self.attachAsset(self.type, { anchorX: 0.5, anchorY: 0.5 }); self.setType = function (newType) { self.type = newType; self.removeChild(self.visual); self.visual = self.attachAsset(newType, { anchorX: 0.5, anchorY: 0.5 }); }; self.markVisited = function () { if (self.type === 'path' && !self.visited) { self.visited = true; self.removeChild(self.visual); self.visual = self.attachAsset('visited', { anchorX: 0.5, anchorY: 0.5 }); } }; self.markDeadEnd = function () { if (self.type === 'path' && !self.deadEnd) { self.deadEnd = true; self.removeChild(self.visual); self.visual = self.attachAsset('deadEnd', { anchorX: 0.5, anchorY: 0.5 }); } }; return self; }); var MazeGame = Container.expand(function () { var self = Container.call(this); // Maze properties self.mazeWidth = 25; self.mazeHeight = 25; self.cellSize = 40; self.cells = []; self.startCell = { x: 1, y: 1 }; self.endCell = { x: self.mazeWidth - 2, y: self.mazeHeight - 2 }; // AI Bot self.bot = new AIBot(); self.addChild(self.bot); // Game state self.gameRunning = false; self.moveInterval = null; self.moveDelay = 50; // milliseconds between moves self.consecutiveDeadEnds = 0; self.initialize = function () { self.generateMaze(); self.positionBot(); self.bot.clearMemory(); LK.playMusic('bgMusic'); }; self.generateMaze = function () { // Clear existing cells for (var i = 0; i < self.cells.length; i++) { for (var j = 0; j < self.cells[i].length; j++) { if (self.cells[i][j]) { self.removeChild(self.cells[i][j]); } } } // Initialize all cells as walls self.cells = []; for (var x = 0; x < self.mazeWidth; x++) { self.cells[x] = []; for (var y = 0; y < self.mazeHeight; y++) { var cell = new MazeCell(x, y, 'wall'); cell.x = x * self.cellSize; cell.y = y * self.cellSize; self.cells[x][y] = cell; self.addChild(cell); } } // Generate maze using recursive backtracking self.generateMazeRecursive(self.startCell.x, self.startCell.y); // Set start and end cells self.cells[self.startCell.x][self.startCell.y].setType('start'); self.cells[self.endCell.x][self.endCell.y].setType('end'); }; self.generateMazeRecursive = function (x, y) { // Mark current cell as path self.cells[x][y].setType('path'); // Define directions: up, right, down, left var directions = [{ dx: 0, dy: -2 }, // up { dx: 2, dy: 0 }, // right { dx: 0, dy: 2 }, // down { dx: -2, dy: 0 } // left ]; // Shuffle directions shuffleArray(directions); // Explore each direction for (var i = 0; i < directions.length; i++) { var dx = directions[i].dx; var dy = directions[i].dy; var newX = x + dx; var newY = y + dy; // Check if the new cell is within bounds and is a wall if (newX > 0 && newX < self.mazeWidth - 1 && newY > 0 && newY < self.mazeHeight - 1 && self.cells[newX][newY].type === 'wall') { // Create a passage by making the cell between current and new a path self.cells[x + dx / 2][y + dy / 2].setType('path'); // Continue maze generation from the new cell self.generateMazeRecursive(newX, newY); } } }; self.positionBot = function () { // Position bot at start cell self.bot.setGridPosition(self.startCell.x, self.startCell.y); self.bot.x = self.startCell.x * self.cellSize; self.bot.y = self.startCell.y * self.cellSize; self.bot.addToPath(self.startCell.x, self.startCell.y); }; self.startGame = function () { if (self.gameRunning) { return; } self.gameRunning = true; self.moveInterval = LK.setInterval(function () { self.makeAIMove(); }, self.moveDelay); }; self.stopGame = function () { if (!self.gameRunning) { return; } self.gameRunning = false; if (self.moveInterval) { LK.clearInterval(self.moveInterval); self.moveInterval = null; } }; self.makeAIMove = function () { if (!self.gameRunning || self.bot.isMoving) { return; } // Check if bot reached the end if (self.bot.gridX === self.endCell.x && self.bot.gridY === self.endCell.y) { self.onBotReachedEnd(); return; } // Get possible moves self.getPossibleMoves(); // If no moves are available, backtrack if (self.bot.possibleMoves.length === 0) { self.onDeadEnd(); return; } // Choose best move based on memory var bestMove = self.chooseBestMove(); // Mark current cell as visited self.cells[self.bot.gridX][self.bot.gridY].markVisited(); // Remember this cell self.bot.rememberCell(self.bot.gridX, self.bot.gridY, 'visited'); // Update bot position self.bot.setGridPosition(bestMove.x, bestMove.y); self.bot.addToPath(bestMove.x, bestMove.y); // Move bot visually self.bot.moveTo(bestMove.x * self.cellSize, bestMove.y * self.cellSize); // Play move sound LK.getSound('move').play(); }; self.getPossibleMoves = function () { var x = self.bot.gridX; var y = self.bot.gridY; self.bot.possibleMoves = []; // Check four directions var directions = [{ x: x, y: y - 1 }, // up { x: x + 1, y: y }, // right { x: x, y: y + 1 }, // down { x: x - 1, y: y } // left ]; for (var i = 0; i < directions.length; i++) { var nx = directions[i].x; var ny = directions[i].y; // Check if the cell is within bounds and is a path or end if (nx >= 0 && nx < self.mazeWidth && ny >= 0 && ny < self.mazeHeight && (self.cells[nx][ny].type === 'path' || self.cells[nx][ny].type === 'end' || self.cells[nx][ny].type === 'start')) { // Check if the cell is not marked as dead end in memory var cellMemory = self.bot.getCellMemory(nx, ny); if (cellMemory !== 'deadEnd') { self.bot.possibleMoves.push({ x: nx, y: ny }); } } } }; self.chooseBestMove = function () { var moves = self.bot.possibleMoves; var bestMoves = []; var bestScore = -Infinity; // Score each move for (var i = 0; i < moves.length; i++) { var move = moves[i]; var score = self.scoreMove(move); if (score > bestScore) { bestScore = score; bestMoves = [move]; } else if (score === bestScore) { bestMoves.push(move); } } // Randomly choose among best moves return bestMoves[Math.floor(Math.random() * bestMoves.length)]; }; self.scoreMove = function (move) { var memory = self.bot.getCellMemory(move.x, move.y); // Prioritize the end cell if (move.x === self.endCell.x && move.y === self.endCell.y) { return 100; } // Avoid visited cells if (memory === 'visited') { return -10; } // Prefer unexplored cells return 10; }; self.onDeadEnd = function () { self.consecutiveDeadEnds++; // Mark current path as dead end in memory for (var i = self.bot.currentPath.length - 1; i >= 0; i--) { var pathCell = self.bot.currentPath[i]; var cellMemory = self.bot.getCellMemory(pathCell.x, pathCell.y); if (cellMemory !== 'deadEnd') { self.bot.rememberCell(pathCell.x, pathCell.y, 'deadEnd'); self.cells[pathCell.x][pathCell.y].markDeadEnd(); // Break if we find a cell with multiple exits self.getPossibleMovesForCell(pathCell.x, pathCell.y); if (self.bot.possibleMoves.length > 1) { break; } } } // Play dead end sound LK.getSound('deadend').play(); // Reset bot to start self.bot.reset(); self.positionBot(); // If we're stuck in a loop, slow down and eventually reset if (self.consecutiveDeadEnds > 10) { self.moveDelay = Math.min(1000, self.moveDelay + 50); if (self.consecutiveDeadEnds > 20) { self.initialize(); self.consecutiveDeadEnds = 0; self.moveDelay = 300; } } }; self.getPossibleMovesForCell = function (x, y) { self.bot.possibleMoves = []; // Check four directions var directions = [{ x: x, y: y - 1 }, // up { x: x + 1, y: y }, // right { x: x, y: y + 1 }, // down { x: x - 1, y: y } // left ]; for (var i = 0; i < directions.length; i++) { var nx = directions[i].x; var ny = directions[i].y; // Check if the cell is within bounds and is a path or end if (nx >= 0 && nx < self.mazeWidth && ny >= 0 && ny < self.mazeHeight && (self.cells[nx][ny].type === 'path' || self.cells[nx][ny].type === 'end' || self.cells[nx][ny].type === 'start')) { self.bot.possibleMoves.push({ x: nx, y: ny }); } } }; self.onBotReachedEnd = function () { self.stopGame(); self.bot.hasWon = true; // Play win sound LK.getSound('win').play(); // Show winning message LK.showYouWin(); }; return self; }); /**** * Initialize Game ****/ // Utility function to shuffle an array var game = new LK.Game({ backgroundColor: 0x121212 }); /**** * Game Code ****/ // Utility function to shuffle an array // Create status text function shuffleArray(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } var statusText = new Text2('AI Maze Learning', { size: 40, fill: 0xFFFFFF }); statusText.anchor.set(0.5, 0); LK.gui.top.addChild(statusText); // Create stats text var statsText = new Text2('Moves: 0 | Memory: 0', { size: 30, fill: 0xCCCCCC }); statsText.anchor.set(0.5, 0); statsText.y = 50; LK.gui.top.addChild(statsText); // Create instructions text var instructionsText = new Text2('Watch the AI learn to solve the maze.\nGreen blocks mark visited paths.\nRed blocks mark dead ends.', { size: 25, fill: 0xAAAAAA }); instructionsText.anchor.set(0.5, 1); LK.gui.bottom.addChild(instructionsText); // Create reset button var resetButton = new Text2('RESET MAZE', { size: 35, fill: 0xFFFFFF }); resetButton.anchor.set(0.5, 1); resetButton.y = -60; resetButton.interactive = true; LK.gui.bottom.addChild(resetButton); // Create maze game var mazeGame = new MazeGame(); game.addChild(mazeGame); // Position the maze in the center of the screen var centerMaze = function centerMaze() { var mazePixelWidth = mazeGame.mazeWidth * mazeGame.cellSize; var mazePixelHeight = mazeGame.mazeHeight * mazeGame.cellSize; mazeGame.x = (2048 - mazePixelWidth) / 2; mazeGame.y = (2732 - mazePixelHeight) / 2; }; // Initialize the game var initGame = function initGame() { mazeGame.initialize(); centerMaze(); mazeGame.startGame(); updateStats(); }; // Reset button handlers resetButton.down = function () { initGame(); }; // Update stats display var updateStats = function updateStats() { var memoryCount = Object.keys(mazeGame.bot.memory).length; statsText.setText('Moves: ' + mazeGame.bot.currentPath.length + ' | Memory: ' + memoryCount); }; // Initialize the game when loaded initGame(); // Game update function game.update = function () { // Update stats if game is running if (mazeGame.gameRunning) { updateStats(); } };
===================================================================
--- original.js
+++ change.js
@@ -130,9 +130,9 @@
self.addChild(self.bot);
// Game state
self.gameRunning = false;
self.moveInterval = null;
- self.moveDelay = 100; // milliseconds between moves
+ self.moveDelay = 50; // milliseconds between moves
self.consecutiveDeadEnds = 0;
self.initialize = function () {
self.generateMaze();
self.positionBot();