diff options
-rw-r--r-- | js/puzzle-dungeon/commandHandler.js | 152 | ||||
-rw-r--r-- | js/puzzle-dungeon/game.js | 267 | ||||
-rw-r--r-- | js/puzzle-dungeon/index.html | 28 | ||||
-rw-r--r-- | js/puzzle-dungeon/parser.js | 15 |
4 files changed, 321 insertions, 141 deletions
diff --git a/js/puzzle-dungeon/commandHandler.js b/js/puzzle-dungeon/commandHandler.js index 12140c2..b6a8246 100644 --- a/js/puzzle-dungeon/commandHandler.js +++ b/js/puzzle-dungeon/commandHandler.js @@ -1,28 +1,138 @@ -const createCommandHandler = () => { - const commands = {}; +// commandHandler.js +import { player, grid, drawGrid, updatePlayerPosition, updatePlayerStatus, alertGameOver } from './game.js'; - const registerCommand = (name, callback) => { - commands[name] = callback; - }; +let commandQueue = []; +let processingCommands = false; - const executeCommand = (parsedCommand) => { - const { command, args } = parsedCommand; - if (commands[command]) { - commands[command](...args); +export function handleCommand(command) { + const [action, itemOrSteps] = command.split(' '); + + if (action === 'get' || action === 'use' || isMovementCommand(action)) { + commandQueue.push({ action, itemOrSteps }); + } + + if (!processingCommands) { + processCommandQueue(); + } +} + +function processCommandQueue() { + if (commandQueue.length === 0) { + processingCommands = false; + return; + } + + processingCommands = true; + const { action, itemOrSteps } = commandQueue.shift(); + + if (action === 'get') { + handleGetCommand(itemOrSteps, processCommandQueue); + } else if (action === 'use') { + handleUseCommand(itemOrSteps, processCommandQueue); + } else if (isMovementCommand(action)) { + const steps = parseInt(itemOrSteps, 10) || 1; + handleMovement(action, steps, processCommandQueue); + } +} + +function handleGetCommand(item, callback) { + const { x, y } = player.position; + const cell = grid[x][y]; + if (cell && cell.type === item) { + player.inventory.push(cell.type); + grid[x][y] = null; // Remove the item from the grid + console.log(`Picked up ${item}`); + flashPlayer(callback); + } else { + player.health -= 1; + console.log(`Tried to get ${item} but it's not there, health is now ${player.health}`); + if (player.health <= 0) { + alertGameOver(); + return; + } + flashPlayer(callback); + } +} + +function handleUseCommand(item, callback) { + const itemIndex = player.inventory.indexOf(item); + if (itemIndex > -1) { + player.inventory.splice(itemIndex, 1); // Remove the item from the inventory + if (item === 'potion') { + player.health += 1; // Increase health by 1 + console.log(`Used ${item}, health is now ${player.health}`); + flashPlayer(callback); + } else if (item === 'food') { + player.endurance = Math.min(player.endurance + 2, 10); // Increase endurance by 2, up to a max of 10 + console.log(`Used ${item}, endurance is now ${player.endurance}`); + flashPlayer(callback); } else { - console.error(`Unknown command: ${command}`); + callback(); + } + } else { + player.health -= 1; + console.log(`Tried to use ${item} but it's not in inventory, health is now ${player.health}`); + if (player.health <= 0) { + alertGameOver(); + return; + } + flashPlayer(callback); + } +} + +function handleMovement(direction, steps, callback) { + let stepCount = 0; + + const moveInterval = setInterval(() => { + if (stepCount >= steps) { + clearInterval(moveInterval); + callback(); + return; + } + + const { x, y } = player.position; + if (direction === 'up' && y > 0) { + updatePlayerPosition(x, y - 1); + } else if (direction === 'down' && y < grid[0].length - 1) { + updatePlayerPosition(x, y + 1); + } else if (direction === 'left' && x > 0) { + updatePlayerPosition(x - 1, y); + } else if (direction === 'right' && x < grid.length - 1) { + updatePlayerPosition(x + 1, y); + } + console.log(`Moved ${direction}, new position is (${player.position.x}, ${player.position.y})`); + + player.steps += 1; + if (player.steps % 8 === 0) { + player.endurance = Math.max(player.endurance - 1, 0); // Decrease endurance but not below 0 + if (player.endurance <= 0) { + player.health -= 1; + console.log(`Endurance depleted, health is now ${player.health}`); + if (player.health <= 0) { + alertGameOver(); + return; + } + } + console.log(`Endurance decreased, endurance is now ${player.endurance}`); } - }; - const executeCommands = (parsedCommands) => { - parsedCommands.forEach(cmd => executeCommand(cmd)); - }; + drawGrid(grid); + updatePlayerStatus(); + stepCount++; + }, 500); // Adjust the interval time for speed of animation +} - return { - registerCommand, - executeCommand, - executeCommands - }; -}; +function flashPlayer(callback) { + player.flashing = true; + drawGrid(grid); + setTimeout(() => { + player.flashing = false; + drawGrid(grid); + updatePlayerStatus(); + if (callback) callback(); + }, 250); // Flash duration +} -export default createCommandHandler; +function isMovementCommand(action) { + return ['up', 'down', 'left', 'right'].includes(action); +} diff --git a/js/puzzle-dungeon/game.js b/js/puzzle-dungeon/game.js index f742844..4ca91d3 100644 --- a/js/puzzle-dungeon/game.js +++ b/js/puzzle-dungeon/game.js @@ -1,131 +1,182 @@ -const canvas = document.getElementById('gameCanvas'); -const ctx = canvas.getContext('2d'); -const cellSize = canvas.width / 10; -const stepDuration = 200; // Duration for each step +// game.js +export let grid = createGrid(10, 10); // Example grid size +export let player = { + position: { x: 0, y: 0 }, + inventory: [], + health: 10, + endurance: 10, + score: 0, + steps: 0, + flashing: false +}; +let targetPosition = { x: 0, y: 0 }; -let player = { x: 0, y: 0 }; - -function drawGrid() { - ctx.clearRect(0, 0, canvas.width, canvas.height); - for (let x = 0; x <= canvas.width; x += cellSize) { - ctx.moveTo(x, 0); - ctx.lineTo(x, canvas.height); - } - for (let y = 0; y <= canvas.height; y += cellSize) { - ctx.moveTo(0, y); - ctx.lineTo(canvas.width, y); +function createGrid(rows, cols) { + let grid = []; + for (let i = 0; i < rows; i++) { + let row = []; + for (let j = 0; j < cols; j++) { + row.push(null); + } + grid.push(row); } - ctx.strokeStyle = '#ddd'; - ctx.stroke(); + return grid; } -function drawPlayer() { - ctx.fillStyle = 'blue'; - ctx.fillRect(player.x * cellSize, player.y * cellSize, cellSize, cellSize); +function generateCollectables(grid, numCollectables) { + const collectableTypes = ['potion', 'shield', 'food']; + const collectableColors = { + 'potion': 'purple', + 'shield': 'gold', + 'food': 'red' + }; + for (let i = 0; i < numCollectables; i++) { + let x, y; + do { + x = Math.floor(Math.random() * grid.length); + y = Math.floor(Math.random() * grid[0].length); + } while (x === 0 && y === 0); // Ensure no items at (0,0) + const type = collectableTypes[Math.floor(Math.random() * collectableTypes.length)]; + grid[x][y] = { type, color: collectableColors[type] }; + } } -document.getElementById('commandForm').addEventListener('submit', function (e) { - e.preventDefault(); - const commands = document.getElementById('commands').value.trim(); - try { - const parsedCommands = parseLispCommands(commands); - console.log("Parsed Commands:", JSON.stringify(parsedCommands, null, 2)); // Debugging output - executeCommands(parsedCommands); - } catch (error) { - console.error("Error parsing commands:", error); +function generateDamagingSpaces(grid, numSpaces) { + for (let i = 0; i < numSpaces; i++) { + let x, y; + do { + x = Math.floor(Math.random() * grid.length); + y = Math.floor(Math.random() * grid[0].length); + } while (x === 0 && y === 0); // Ensure no damaging spaces at (0,0) + grid[x][y] = { type: 'damage', color: 'pink' }; } -}); +} -function parseLispCommands(commands) { - const parse = input => { - const tokens = tokenize(input); - const ast = buildAST(tokens); - return ast; - }; +function generateTargetBlock(grid) { + let x, y; + do { + x = Math.floor(Math.random() * grid.length); + y = Math.floor(Math.random() * grid[0].length); + } while (x === 0 && y === 0); // Ensure no target block at (0,0) + grid[x][y] = { type: 'target', color: 'green' }; // Target block represented by 'X' + targetPosition = { x, y }; // Store the target block position +} - const tokenize = input => { - return input.replace(/\(/g, ' ( ').replace(/\)/g, ' ) ').trim().split(/\s+/); - }; +export function drawGrid(grid) { + const canvas = document.getElementById('gameCanvas'); + const ctx = canvas.getContext('2d'); + const cellSize = Math.min(canvas.width / grid.length, canvas.height / grid[0].length); + + ctx.clearRect(0, 0, canvas.width, canvas.height); - const buildAST = tokens => { - if (!tokens.length) throw new SyntaxError("Unexpected end of input"); + grid.forEach((row, x) => { + row.forEach((cell, y) => { + ctx.strokeStyle = 'black'; + ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize); - const token = tokens.shift(); - if (token === '(') { - const list = []; - while (tokens[0] !== ')') { - list.push(buildAST(tokens)); - if (!tokens.length) throw new SyntaxError("Missing closing parenthesis"); + if (cell && cell.color) { + ctx.fillStyle = cell.color; + ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize); + ctx.fillStyle = 'black'; + ctx.font = `${cellSize / 2}px Arial`; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + let char = ''; + if (cell.type === 'potion') char = 'p'; + if (cell.type === 'shield') char = 's'; + if (cell.type === 'food') char = 'f'; + if (cell.type === 'damage') char = '!'; + if (cell.type === 'target') char = 'X'; + ctx.fillText(char, x * cellSize + cellSize / 2, y * cellSize + cellSize / 2); } - tokens.shift(); - return list; - } else if (token === ')') { - throw new SyntaxError("Unexpected ')'"); - } else { - return isNaN(token) ? token : Number(token); - } - }; + }); + }); - return parse(commands); + // Draw player + ctx.fillStyle = player.flashing ? 'white' : 'blue'; + ctx.fillRect(player.position.x * cellSize, player.position.y * cellSize, cellSize, cellSize); + ctx.fillStyle = 'black'; + ctx.font = `${cellSize / 2}px Arial`; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText('@', player.position.x * cellSize + cellSize / 2, player.position.y * cellSize + cellSize / 2); } -function executeCommands(commands) { - if (commands[0] !== 'move') { - console.error("Invalid command: root command must be 'move'"); - return; - } +export function initializeGame() { + grid = createGrid(10, 10); // Reset grid + generateCollectables(grid, 10); // Adjust the number as needed + generateDamagingSpaces(grid, Math.floor(Math.random() * 7) + 1); // Random number of damaging spaces, no more than 7 + generateTargetBlock(grid); // Add target block + player.position = { x: 0, y: 0 }; // Reset player position + player.inventory = []; // Clear inventory + player.health = 10; // Reset health + player.endurance = 10; // Reset endurance + player.steps = 0; // Reset steps + resizeCanvas(); + drawGrid(grid); + updatePlayerStatus(); +} - let delay = 0; - commands.slice(1).forEach(moveCommand => { - const steps = moveCommand[1]; - for (let i = 0; i < steps; i++) { - setTimeout(() => { - movePlayer([moveCommand[0], 1]); - drawGrid(); - drawPlayer(); - }, delay); - delay += stepDuration; - } - if (moveCommand[0] === 'rest') { - setTimeout(() => { - drawGrid(); - drawPlayer(); - }, delay); - delay += stepDuration; - } - }); +export function resizeCanvas() { + const canvas = document.getElementById('gameCanvas'); + const width = Math.min(window.innerWidth, window.innerHeight) * 0.9; + canvas.width = width; + canvas.height = width; + drawGrid(grid); } -function movePlayer(moveCommand) { - const direction = moveCommand[0]; - const steps = moveCommand[1]; +export function updatePlayerPosition(newX, newY) { + player.position.x = newX; + player.position.y = newY; + checkForDamageOrTarget(); + drawGrid(grid); + updatePlayerStatus(); +} - if (typeof direction !== 'string' || typeof steps !== 'number') { - console.error(`Invalid move command: ${JSON.stringify(moveCommand)}`); - return; - } +export function updatePlayerStatus() { + document.getElementById('playerPosition').textContent = `Position: (${player.position.x}, ${player.position.y})`; + document.getElementById('playerHealth').textContent = `Health: ${player.health}`; + document.getElementById('playerEndurance').textContent = `Endurance: ${player.endurance}`; + document.getElementById('playerInventory').textContent = `Inventory: [${player.inventory.join(', ')}]`; + document.getElementById('playerScore').textContent = `Score: ${player.score}`; +} - switch (direction) { - case 'north': - player.y = Math.max(0, player.y - steps); - break; - case 'south': - player.y = Math.min(9, player.y + steps); - break; - case 'east': - player.x = Math.min(9, player.x + steps); - break; - case 'west': - player.x = Math.max(0, player.x - steps); - break; - case 'rest': - // No movement, just wait for the duration - break; - default: - console.error(`Unknown direction: ${direction}`); - return; +function checkForDamageOrTarget() { + const { x, y } = player.position; + const cell = grid[x][y]; + if (cell && cell.type === 'damage') { + const shieldIndex = player.inventory.indexOf('shield'); + if (shieldIndex > -1) { + player.inventory.splice(shieldIndex, 1); // Use one shield + console.log('Used shield to avoid damage'); + } else { + player.health -= 2; + console.log(`Stepped on damaging space, health is now ${player.health}`); + if (player.health <= 0) { + alertGameOver(); + return; + } + flashPlayer(); + } + } else if (x === targetPosition.x && y === targetPosition.y) { + player.score += 1; + console.log(`Reached target block, score is now ${player.score}`); + initializeGame(); // Generate new level and reset player position } } -drawGrid(); -drawPlayer(); +export function alertGameOver() { + alert('You have lost the game!'); + initializeGame(); +} + +function flashPlayer(callback) { + player.flashing = true; + drawGrid(grid); + setTimeout(() => { + player.flashing = false; + drawGrid(grid); + updatePlayerStatus(); + if (callback) callback(); + }, 250); // Flash duration +} diff --git a/js/puzzle-dungeon/index.html b/js/puzzle-dungeon/index.html index ed13d3d..c183ea0 100644 --- a/js/puzzle-dungeon/index.html +++ b/js/puzzle-dungeon/index.html @@ -6,19 +6,39 @@ <style> canvas { border: 1px solid black; + display: block; + margin: 0 auto; } </style> </head> <body> - <canvas id="gameCanvas" width="500" height="500"></canvas> + <canvas id="gameCanvas"></canvas> <form id="commandForm"> <textarea id="commands" rows="10" cols="30"></textarea><br> <button type="submit">Submit</button> </form> + <div id="playerStatus"> + <h3>Player Status</h3> + <p id="playerPosition">Position: (0, 0)</p> + <p id="playerHealth">Health: 10</p> + <p id="playerEndurance">Endurance: 10</p> + <p id="playerInventory">Inventory: []</p> + <p id="playerScore">Score: 0</p> + </div> <script type="module"> - import './parser.js'; - import './commandHandler.js'; - import './game.js'; + import { parseCommands } from './parser.js'; + import { initializeGame, resizeCanvas } from './game.js'; + + document.getElementById('commandForm').addEventListener('submit', function(event) { + event.preventDefault(); + const commands = document.getElementById('commands').value; + parseCommands(commands); + }); + + window.addEventListener('resize', resizeCanvas); + + // Initialize the game + initializeGame(); </script> </body> </html> diff --git a/js/puzzle-dungeon/parser.js b/js/puzzle-dungeon/parser.js index ec7f33f..f962677 100644 --- a/js/puzzle-dungeon/parser.js +++ b/js/puzzle-dungeon/parser.js @@ -1,10 +1,9 @@ -export function parseCommands(input) { - const commands = input.split('\n').map(cmd => cmd.trim()).filter(cmd => cmd.length > 0); - return commands.map(command => { - const parts = command.split(' '); - return { - command: parts[0], - args: parts.slice(1) - }; +// parser.js +import { handleCommand } from './commandHandler.js'; + +export function parseCommands(commands) { + const commandList = commands.trim().split('\n'); + commandList.forEach(command => { + handleCommand(command.trim()); }); } |