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();