about summary refs log tree commit diff stats
path: root/html/broughlike
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2024-10-30 19:39:34 -0400
committerelioat <elioat@tilde.institute>2024-10-30 19:39:34 -0400
commitc8a8b22d8749ccafd89f058a9c675bef1b6e54bd (patch)
tree45072eadf64f65f5311fdb112133a6432514821f /html/broughlike
parentbc572f84356a417f4714d723eeb1a498aa1d340f (diff)
downloadtour-c8a8b22d8749ccafd89f058a9c675bef1b6e54bd.tar.gz
*
Diffstat (limited to 'html/broughlike')
-rw-r--r--html/broughlike/broughlike.js809
-rw-r--r--html/broughlike/index.html822
2 files changed, 810 insertions, 821 deletions
diff --git a/html/broughlike/broughlike.js b/html/broughlike/broughlike.js
new file mode 100644
index 0000000..e492a0c
--- /dev/null
+++ b/html/broughlike/broughlike.js
@@ -0,0 +1,809 @@
+const COLORS = {
+    grid: '#2d2d2d',
+    walls: '#2d2d2d',
+    exit: 'teal',
+    diamond: 'gold',
+    pentagon: 'blueviolet', 
+    player: 'rgba(0, 237, 209, ',
+    enemy: 'rgba(255, 155, 28, ',
+    boss: 'rgba(255, 14, 0, ',
+    combatDotPlayer: '#00edd1',
+    combatDotEnemy: '#ff731c',
+    combatDotBoss: '#b70030'
+};
+
+const GRID_SIZE = 6;
+const PLAYER_HEALTH = 12;
+const PLAYER_MAX_HEALTH = 16;
+const PLAYER_BASE_DAMAGE = 1;
+const ENEMY_CHASE_DISTANCE = 4;
+const ENEMY_BOSS_OCCURRENCE = 10;
+const MIN_ENEMIES_ON_LEVEL = 1;
+const MAX_ENEMIES_ON_LEVEL = 4;
+const MAX_ENEMY_HEALTH = 7;
+const MIN_ENEMY_HEALTH = 2;
+const WALLS_MIN = 7;
+const WALLS_MAX = 20;
+const ITEMS_MIN = 1;
+const ITEMS_MAX = 3;
+const DOTS_PER_HIT = 7;
+
+let highScore = localStorage.getItem('highScore') || 0;
+
+const canvas = document.getElementById('gameCanvas');
+const ctx = canvas.getContext('2d');
+let tileSize = canvas.width / GRID_SIZE;
+
+const player = {
+    x: 0,
+    y: 0,
+    health: PLAYER_HEALTH,
+    score: 0,
+    damage: PLAYER_BASE_DAMAGE,
+    totalDamageDone: 0,
+    totalDamageTaken: 0,
+    cellsTraveled: 0,
+    killCount: 0,
+    itemsCollected: 0,
+};
+
+const exit = { x: Math.floor(Math.random() * GRID_SIZE), y: Math.floor(Math.random() * GRID_SIZE) };
+let walls = [];
+let enemies = [];
+let items = [];
+let combatDots = {};
+
+function isValidMove(newX, newY) {
+    return (
+        newX >= 0 && newX < GRID_SIZE &&
+        newY >= 0 && newY < GRID_SIZE &&
+        !walls.some(wall => wall.x === newX && wall.y === newY)
+    );
+}
+
+function generateExit() {
+    let distance = 0;
+    do {
+        exit.x = Math.floor(Math.random() * GRID_SIZE);
+        exit.y = Math.floor(Math.random() * GRID_SIZE);
+        distance = Math.abs(exit.x - player.x) + Math.abs(exit.y - player.y);
+    } while (distance < 4);
+}
+
+function generateEnemies() {
+    enemies = [];
+    // Generate between 0 and MAX_ENEMIES_ON_LEVEL enemies if the player's score is 4 or lower
+    // Generate between MIN_ENEMIES_ON_LEVEL and MAX_ENEMIES_ON_LEVEL enemies if the player's score is 5 or higher
+    const numEnemies = player.score > 4 
+    ? Math.floor(Math.random() * (MAX_ENEMIES_ON_LEVEL - MIN_ENEMIES_ON_LEVEL + 1)) + MIN_ENEMIES_ON_LEVEL 
+    : Math.floor(Math.random() * (MAX_ENEMIES_ON_LEVEL + 1));
+    for (let i = 0; i < numEnemies; i++) {
+        let enemyX, enemyY;
+        do {
+            enemyX = Math.floor(Math.random() * GRID_SIZE);
+            enemyY = Math.floor(Math.random() * GRID_SIZE);
+        } while (
+            (enemyX === player.x && enemyY === player.y) ||
+            (enemyX === exit.x && enemyY === exit.y) ||
+            walls.some(wall => wall.x === enemyX && wall.y === enemyY) ||
+            (Math.abs(enemyX - player.x) + Math.abs(enemyY - player.y) < 2) // Ensure enemy is at least 2 spaces away from player
+        );
+        enemies.push({
+            color: COLORS.enemy,
+            x: enemyX,
+            y: enemyY,
+            health: Math.floor(Math.random() * (MAX_ENEMY_HEALTH - MIN_ENEMY_HEALTH + 1)) + MIN_ENEMY_HEALTH
+        });
+    }
+
+    // Generate a boss enemy every ENEMY_BOSS_OCCURRENCE levels
+    if (player.score % ENEMY_BOSS_OCCURRENCE === 0 && player.score > 0) {
+        let bossX, bossY;
+        do {
+            bossX = exit.x; // Boss enemies always appear at the exit
+            bossY = exit.y; // This ensures that they're not in little rooms that the player can't reach
+        } while (
+            (Math.abs(bossX - player.x) + Math.abs(bossY - player.y) < 2) // Ensure boss is at least 2 spaces away from player
+        );
+        enemies.push({
+            isBoss: true,
+            color: COLORS.boss,
+            x: bossX,
+            y: bossY,
+            health: MAX_ENEMY_HEALTH + 2
+        });
+    }
+}
+
+function generateWallsNaive() {
+    walls = [];
+    let numWalls = Math.floor(Math.random() * (WALLS_MAX - WALLS_MIN + 1)) + WALLS_MIN;
+    while (walls.length < numWalls) {
+        const wallX = Math.floor(Math.random() * GRID_SIZE);
+        const wallY = Math.floor(Math.random() * GRID_SIZE);
+
+        if (
+            (wallX === player.x && wallY === player.y) ||                    // Check that a wall is not placed on the starting position
+            (wallX === exit.x && wallY === exit.y) ||                        // Check that a wall is not placed on the exit
+            enemies.some(enemy => enemy.x === wallX && enemy.y === wallY) || // Check that a wall is not placed on any enemies
+            items.some(item => item.x === wallX && item.y === wallY)         // Check that a wall is not placed on any items
+        ) continue;
+
+        if (!walls.some(wall => wall.x === wallX && wall.y === wallY)) {
+            walls.push({ x: wallX, y: wallY });
+        }
+    }
+
+    if (!isPassable()) {
+        generateWallsNaive();
+    }
+}
+
+function generateWallsDrunkardsWalk() {
+    walls = [];
+    const numWalls = Math.floor(Math.random() * (WALLS_MAX - WALLS_MIN + 1)) + WALLS_MIN;
+    let wallX = Math.floor(Math.random() * GRID_SIZE);
+    let wallY = Math.floor(Math.random() * GRID_SIZE);
+
+    while (walls.length < numWalls) {
+    if (
+        (wallX !== player.x || wallY !== player.y) &&                     // Check that a wall is not placed on the starting position
+        (wallX !== exit.x || wallY !== exit.y) &&                         // Check that a wall is not placed on the exit
+        !enemies.some(enemy => enemy.x === wallX && enemy.y === wallY) && // Check that a wall is not placed on any enemies
+        !items.some(item => item.x === wallX && item.y === wallY) &&      // Check that a wall is not placed on any items
+        !walls.some(wall => wall.x === wallX && wall.y === wallY)         // Check that a wall is not placed on an existing wall
+    ) {
+        walls.push({ x: wallX, y: wallY });
+    }
+
+    // Randomly move to a neighboring cell
+    const direction = Math.floor(Math.random() * 4);
+    switch (direction) {
+        case 0: wallX = Math.max(0, wallX - 1); break;             // Move left
+        case 1: wallX = Math.min(GRID_SIZE - 1, wallX + 1); break; // Move right
+        case 2: wallY = Math.max(0, wallY - 1); break;             // Move up
+        case 3: wallY = Math.min(GRID_SIZE - 1, wallY + 1); break; // Move down
+    }
+    }
+
+    if (!isPassable()) {
+        generateWallsDrunkardsWalk();
+    }
+}
+
+function generateWallsCellularAutomata() {
+    walls = [];
+    const map = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
+
+    // Initialize a map with random walls
+    for (let x = 0; x < GRID_SIZE; x++) {
+        for (let y = 0; y < GRID_SIZE; y++) {
+            if (Math.random() < 0.4) {
+            map[x][y] = 1;
+            }
+        }
+    }
+
+    for (let i = 0; i < 4; i++) {
+        // Create a new map to store the next state of the cellular automata
+        const newMap = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
+
+        // Iterate over each cell in the grid
+        for (let x = 0; x < GRID_SIZE; x++) {
+            for (let y = 0; y < GRID_SIZE; y++) {
+                // Count the number of neighboring walls
+                const neighbors = countNeighbors(map, x, y);
+
+                if (map[x][y] === 1) {
+                    // If the cell is a wall, it stays a wall if it has 4 or more neighbors
+                    newMap[x][y] = neighbors >= 4 ? 1 : 0;
+                } else {
+                    // If the cell is empty, it turn into a wall if it has 5 or more neighbors
+                    newMap[x][y] = neighbors >= 5 ? 1 : 0;
+                }
+            }
+        }
+
+        // Update the original map with the new state
+        map.forEach((row, x) => row.forEach((cell, y) => map[x][y] = newMap[x][y]));
+    }
+
+    // Convert map to walls array
+    for (let x = 0; x < GRID_SIZE; x++) {
+        for (let y = 0; y < GRID_SIZE; y++) {
+            if (map[x][y] === 1 && 
+            (x !== player.x || y !== player.y) && 
+            (x !== exit.x || y !== exit.y) && 
+            !enemies.some(enemy => enemy.x === x && enemy.y === y) && 
+            !items.some(item => item.x === x && item.y === y)) {
+                walls.push({ x, y });
+            }
+        }
+    }
+
+    if (!isPassable()) {
+        generateWallsCellularAutomata();
+    }
+}
+
+function countNeighbors(map, x, y) {
+    let count = 0;
+    for (let dx = -1; dx <= 1; dx++) {
+        for (let dy = -1; dy <= 1; dy++) {
+            if (dx === 0 && dy === 0) continue;
+            const nx = x + dx;
+            const ny = y + dy;
+            if (nx >= 0 && nx < GRID_SIZE && ny >= 0 && ny < GRID_SIZE) {
+                count += map[nx][ny];
+            } else {
+                count++; // Consider out-of-bounds bits as walls
+            }
+        }
+    }
+    return count;
+}
+
+function generateWallsRSP() {
+    walls = [];
+    const MIN_ROOM_SIZE = 2;
+
+    function splitNode(x, y, width, height) {
+        const splitHorizontally = Math.random() > 0.5;
+        const max = (splitHorizontally ? height : width) - MIN_ROOM_SIZE;
+
+        if (max <= MIN_ROOM_SIZE) return [{ x, y, width, height }];
+
+        const split = Math.floor(Math.random() * (max - MIN_ROOM_SIZE)) + MIN_ROOM_SIZE;
+
+        if (splitHorizontally) {
+            return [
+                ...splitNode(x, y, width, split),
+                ...splitNode(x, y + split, width, height - split)
+            ];
+        } else {
+            return [
+                ...splitNode(x, y, split, height),
+                ...splitNode(x + split, y, width - split, height)
+            ];
+        }
+    }
+
+    function createRoom(node) {
+        const roomWidth = Math.floor(Math.random() * (node.width - 1)) + 1;
+        const roomHeight = Math.floor(Math.random() * (node.height - 1)) + 1;
+        const roomX = node.x + Math.floor(Math.random() * (node.width - roomWidth));
+        const roomY = node.y + Math.floor(Math.random() * (node.height - roomHeight));
+        return { x: roomX, y: roomY, width: roomWidth, height: roomHeight };
+    }
+
+    const nodes = splitNode(0, 0, GRID_SIZE, GRID_SIZE);
+    const rooms = nodes.map(createRoom);
+
+    rooms.forEach(room => {
+    for (let x = room.x; x < room.x + room.width; x++) {
+        for (let y = room.y; y < room.y + room.height; y++) {
+            if (
+                (x !== player.x || y !== player.y) &&
+                (x !== exit.x || y !== exit.y) &&
+                !enemies.some(enemy => enemy.x === x && enemy.y === y) &&
+                !items.some(item => item.x === x && item.y === y)
+            ) {
+                walls.push({ x, y });
+            }
+        }
+    }});
+
+    if (!isPassable()) {
+        generateWallsRSP();
+    }
+}
+
+function generateWalls() {
+    const wallGenerators = [
+        { name: 'RSP Tree', func: generateWallsRSP },
+        { name: 'Naive', func: generateWallsNaive },
+        { name: 'Cellular Automata', func: generateWallsCellularAutomata },
+        { name: 'Drunkard\'s Walk', func: generateWallsDrunkardsWalk }
+    ];
+    const randomIndex = Math.floor(Math.random() * wallGenerators.length);
+    const selectedGenerator = wallGenerators[randomIndex];
+    console.log(`Wall generator: ${selectedGenerator.name}`);
+    selectedGenerator.func();
+}
+
+function generateItems() {
+    items = [];
+    const numItems = Math.floor(Math.random() * (ITEMS_MAX - ITEMS_MIN + 1)) + ITEMS_MIN;
+    for (let i = 0; i < numItems; i++) {
+        let itemX, itemY;
+        do {
+            itemX = Math.floor(Math.random() * GRID_SIZE);
+            itemY = Math.floor(Math.random() * GRID_SIZE);
+        } while (
+            (itemX === player.x && itemY === player.y) ||
+            (itemX === exit.x && itemY === exit.y) ||
+            walls.some(wall => wall.x === itemX && wall.y === itemY) ||
+            enemies.some(enemy => enemy.x === itemX && enemy.y === itemY) ||
+            items.some(item => item.x === itemX && item.y === itemY)
+        );
+        const itemType = Math.random() < 0.5 ? 'diamond' : 'pentagon'; // 50% chance for each type
+        items.push({ x: itemX, y: itemY, type: itemType });
+    }
+}
+
+function isPassable() {
+    const visited = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(false));
+
+    function dfs(x, y) {
+        if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE) return false; // Are the coordinates in bounds?
+        if (visited[x][y]) return false;
+        if (walls.some(wall => wall.x === x && wall.y === y)) return false;
+        visited[x][y] = true;
+        if (x === exit.x && y === exit.y) return true;
+        return dfs(x + 1, y) || dfs(x - 1, y) || dfs(x, y + 1) || dfs(x, y - 1);
+    }
+
+    return dfs(player.x, player.y);
+}
+
+function drawGrid() {
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    ctx.strokeStyle = COLORS.grid;
+    for (let row = 0; row < GRID_SIZE; row++) {
+        for (let col = 0; col < GRID_SIZE; col++) {
+            ctx.strokeRect(col * tileSize, row * tileSize, tileSize, tileSize);
+        }
+    }
+}
+
+function drawExit() {
+    const x = exit.x * tileSize + tileSize / 2;
+    const y = exit.y * tileSize + tileSize / 2;
+    ctx.beginPath();
+    ctx.moveTo(x, y - tileSize / 3);
+    ctx.lineTo(x + tileSize / 3, y + tileSize / 3);
+    ctx.lineTo(x - tileSize / 3, y + tileSize / 3);
+    ctx.closePath();
+    ctx.fillStyle = COLORS.exit;
+    ctx.fill();
+}
+
+function drawWalls() {
+    ctx.fillStyle = COLORS.walls;
+    walls.forEach(wall => {
+        ctx.fillRect(wall.x * tileSize, wall.y * tileSize, tileSize, tileSize);
+    });
+}
+
+function drawItems() {
+    items.forEach(item => {
+        const x = item.x * tileSize + tileSize / 2;
+        const y = item.y * tileSize + tileSize / 2;
+        ctx.fillStyle = item.type === 'diamond' ? COLORS.diamond : COLORS.pentagon;
+        ctx.beginPath();
+        if (item.type === 'diamond') {
+            ctx.moveTo(x, y - tileSize / 4);
+            ctx.lineTo(x + tileSize / 4, y);
+            ctx.lineTo(x, y + tileSize / 4);
+            ctx.lineTo(x - tileSize / 4, y);
+        } else {
+            const sides = 5;
+            const radius = tileSize / 4;
+            for (let i = 0; i < sides; i++) {
+                const angle = (i * 2 * Math.PI) / sides - Math.PI / 2;
+                const pointX = x + radius * Math.cos(angle);
+                const pointY = y + radius * Math.sin(angle);
+                if (i === 0) ctx.moveTo(pointX, pointY);
+                else ctx.lineTo(pointX, pointY);
+            }
+        }
+        ctx.closePath();
+        ctx.fill();
+    });
+}
+
+function drawCharacterBorder(x, y, radius, damageTaken) {
+    const dashLength = 5;
+    const gapLength = Math.max(1, damageTaken * 2); // More damage, larger gaps
+
+    ctx.lineWidth = 2;
+    ctx.strokeStyle = '#2d2d2d';
+    ctx.setLineDash([dashLength, gapLength]);
+    ctx.beginPath();
+    ctx.arc(x, y, radius, 0, 2 * Math.PI);
+    ctx.stroke();
+    ctx.setLineDash([]); // Reset to a solid line
+}
+
+function drawEnemies() {
+    enemies.forEach(enemy => {
+        const x = enemy.x * tileSize + tileSize / 2;
+        const y = enemy.y * tileSize + tileSize / 2;
+        const opacity = enemy.health / MAX_ENEMY_HEALTH; // Opacity based on health
+        const radius = tileSize / 3;
+        const damageTaken = MAX_ENEMY_HEALTH - enemy.health;
+
+        ctx.beginPath();
+        ctx.arc(x, y, radius, 0, 2 * Math.PI);
+        ctx.fillStyle = `${enemy.color}${opacity})`;
+        ctx.fill();
+
+        drawCharacterBorder(x, y, radius, damageTaken);
+    });
+}
+
+function drawPlayer() {
+    const x = player.x * tileSize + tileSize / 2;
+    const y = player.y * tileSize + tileSize / 2;
+    const radius = tileSize / 3;
+    const playerOpacity = player.health / PLAYER_HEALTH; // Opacity based on health
+    ctx.beginPath();
+    for (let i = 0; i < 6; i++) {
+        const angle = (Math.PI / 3) * i;
+        const hexX = x + radius * Math.cos(angle);
+        const hexY = y + radius * Math.sin(angle);
+        if (i === 0) {
+            ctx.moveTo(hexX, hexY);
+        } else {
+            ctx.lineTo(hexX, hexY);
+        }
+    }
+    ctx.closePath();
+    ctx.fillStyle = `${COLORS.player}${playerOpacity})`;
+    ctx.fill();
+    ctx.lineWidth = 2;
+    ctx.strokeStyle = '#2d2d2d';
+    ctx.stroke();
+}
+
+function drawCombatDots() {
+    for (const key in combatDots) {
+        const [cellX, cellY] = key.split(',').map(Number);
+        const dots = combatDots[key];
+        dots.forEach(dot => {
+            ctx.beginPath();
+            ctx.arc(cellX * tileSize + dot.x, cellY * tileSize + dot.y, 2, 0, Math.PI * 2);
+            ctx.fillStyle = dot.color;
+            ctx.fill();
+            ctx.closePath();
+        });
+    }
+}
+
+function handleItemCollection() {
+    const collectedItem = items.find(item => item.x === player.x && item.y === player.y);
+    if (collectedItem) {
+        player.itemsCollected++;
+        if (collectedItem.type === 'diamond') {
+            player.damage += 3;
+            console.log("Collected diamond! +3 damage on this level.");
+        } else if (collectedItem.type === 'pentagon') {
+            const healAmount = Math.floor(Math.random() * 2) + 1;
+            player.health = Math.min(player.health + healAmount, PLAYER_MAX_HEALTH);
+            console.log("Collected pentagon! Healed " + healAmount + " health.");
+        }
+        items = items.filter(item => item !== collectedItem); // Remove collected item
+    }
+}
+
+function addCombatDots(x, y, color) {
+    const cellsToFill = [
+        [x, y],        // Center
+        [x - 1, y],    // Left
+        [x + 1, y],    // Right
+        [x, y - 1],    // Up
+        [x, y + 1],    // Down
+        [x - 1, y - 1], // Top-left
+        [x + 1, y - 1], // Top-right
+        [x - 1, y + 1], // Bottom-left
+        [x + 1, y + 1]  // Bottom-right
+    ];
+
+    cellsToFill.forEach(([cellX, cellY]) => {
+        if (cellX >= 0 && cellX < GRID_SIZE && cellY >= 0 && cellY < GRID_SIZE) {
+            const key = `${cellX},${cellY}`;
+            if (!combatDots[key]) {
+                combatDots[key] = [];
+            }
+            for (let i = 0; i < DOTS_PER_HIT; i++) {
+                combatDots[key].push({
+                    x: Math.random() * tileSize,
+                    y: Math.random() * tileSize,
+                    color: color
+                });
+            }
+        }
+    });
+}
+
+function movePlayer(dx, dy) {
+    const newX = player.x + dx;
+    const newY = player.y + dy;
+    if (isValidMove(newX, newY) && !enemies.some(enemy => enemy.x === newX && enemy.y === newY)) {
+        if (newX !== player.x || newY !== player.y) player.cellsTraveled++;
+        player.x = newX;
+        player.y = newY;
+        handleItemCollection(); // Did the player collect an item?
+        checkPlayerAtExit();    // Did the player get to the exit after moving?
+    } else {
+        // If an enemy is in the target cell, player stays put and does combat
+        const enemyInTargetCell = enemies.find(enemy => enemy.x === newX && enemy.y === newY);
+        if (enemyInTargetCell) {
+            handleDamage(player, enemyInTargetCell);
+        }
+    }
+    moveEnemies();
+    render();
+}
+
+function moveEnemies() {
+    enemies.forEach(enemy => {
+        const distanceToPlayer = Math.abs(enemy.x - player.x) + Math.abs(enemy.y - player.y);
+        const distanceToExit = Math.abs(enemy.x - exit.x) + Math.abs(enemy.y - exit.y);
+
+        // If the enemy is closer to the exit than the player, move towards the exit
+        // Bosses are more aggressive about chasing the player
+        const target = distanceToPlayer <= (enemy.isBoss ? ENEMY_CHASE_DISTANCE + 2 : ENEMY_CHASE_DISTANCE) ? player : exit;
+        const path = findPath(enemy, target);
+
+        if (path.length > 1) {
+            const nextStep = path[1];
+            const enemyInNextStep = enemies.find(e => e.x === nextStep.x && e.y === nextStep.y);
+
+            // Is the next step occupied by an enemy?
+            if (!enemyInNextStep && !(nextStep.x === player.x && nextStep.y === player.y)) {
+                // Move to the next place
+                enemy.x = nextStep.x;
+                enemy.y = nextStep.y;
+            } else if (nextStep.x === player.x && nextStep.y === player.y) {
+                // If the player is in the next step, stay put and do combat
+                handleDamage(player, enemy);
+            }
+        }
+    });
+}
+
+function findPath(start, goal) {
+    const queue = [{ x: start.x, y: start.y, path: [] }];
+    const visited = new Set();
+    visited.add(`${start.x},${start.y}`);
+
+    while (queue.length > 0) {
+        const { x, y, path } = queue.shift();
+        const newPath = [...path, { x, y }];
+
+        if (x === goal.x && y === goal.y) {
+            return newPath;
+        }
+
+        const directions = [
+            { dx: 1, dy: 0 },
+            { dx: -1, dy: 0 },
+            { dx: 0, dy: 1 },
+            { dx: 0, dy: -1 }
+        ];
+
+        directions.forEach(({ dx, dy }) => {
+            const newX = x + dx;
+            const newY = y + dy;
+            const key = `${newX},${newY}`;
+
+            // Check if the new position is within the level and if it is passable
+            if (
+                newX >= 0 && newX < GRID_SIZE &&
+                newY >= 0 && newY < GRID_SIZE &&
+                // Have we already been here?
+                !visited.has(key) &&
+                // Is it a wall?
+                !walls.some(wall => wall.x === newX && wall.y === newY) &&
+                // Is there an enemy already there?
+                !enemies.some(enemy => enemy.x === newX && enemy.y === newY)
+            ) {
+                queue.push({ x: newX, y: newY, path: newPath });
+                visited.add(key);
+            }
+        });
+    }
+
+    return [];
+}
+
+let combatAnimationEnabled = localStorage.getItem('combatAnimationEnabled');
+if (combatAnimationEnabled === null) {
+    combatAnimationEnabled = true; // default to on...is that a good idea?
+    localStorage.setItem('combatAnimationEnabled', combatAnimationEnabled);
+} else {
+    combatAnimationEnabled = combatAnimationEnabled === 'true';
+}
+document.getElementById('toggleShake').textContent = combatAnimationEnabled ? 'Turn Shake Off' : 'Turn Shake On';
+
+function toggleShake() {
+    combatAnimationEnabled = !combatAnimationEnabled;
+    localStorage.setItem('combatAnimationEnabled', combatAnimationEnabled);
+    document.getElementById('toggleShake').textContent = combatAnimationEnabled ? 'Turn Shake Off' : 'Turn Shake On';
+}
+
+function combatAnimation() {
+    const canvas = document.getElementById('gameCanvas');
+    canvas.classList.add('shake');
+    canvas.addEventListener('animationend', () => {
+        canvas.classList.remove('shake');
+    }, { once: true });
+}
+
+function handleDamage(player, enemy) {
+    const enemyMisses = Math.random() < 0.5; // 50% chance the enemy misses you
+    const cellX = player.x;
+    const cellY = player.y;
+
+    if (!enemyMisses) {
+        player.health--;
+        player.totalDamageTaken++;
+        addCombatDots(cellX, cellY, COLORS.combatDotPlayer); // Add dots for player damage
+        console.log("Enemy hit! Player health: " + player.health);
+    } else {                
+        console.log("Enemy missed!");
+    }
+
+    enemy.health = enemy.health - player.damage;
+    player.totalDamageDone++;
+    addCombatDots(cellX, cellY, enemy.isBoss ? COLORS.combatDotBoss : COLORS.combatDotEnemy); // Add dots for enemy damage
+    console.log("Player hit! Enemy health: " + enemy.health);
+
+    if (combatAnimationEnabled) {
+        combatAnimation(); // Trigger the shake animation
+    }
+
+    if (enemy.health <= 0) {
+        player.killCount++;
+        enemies = enemies.filter(e => e !== enemy);                
+        if (enemy.isBoss) {
+            // Defeating a boss restores 3 player health
+            player.health = Math.min(player.health + 3, PLAYER_MAX_HEALTH);
+            console.log("Defeated a boss! Healed " + 3 + " health.");
+        }
+    }
+
+    if (player.health <= 0) {
+        if (player.score > highScore) {
+            highScore = player.score;
+            localStorage.setItem('highScore', highScore);
+        }
+        alert(`Score: ${player.score}\nDistance Traveled: ${player.cellsTraveled}\nTotal Damage Dealt: ${player.totalDamageDone}\nTotal Damage Received: ${player.totalDamageTaken}\nCircles Vanquished: ${player.killCount}\n\nHigh Score: ${highScore}`);
+        resetGame();
+    }
+}
+
+function resetGame() {
+    const canvas = document.getElementById('gameCanvas');
+    if (canvas.classList.contains('shake')) {
+        canvas.classList.remove('shake');
+    }
+    player.health = PLAYER_HEALTH;
+    player.damage = PLAYER_BASE_DAMAGE;
+    player.bonusDamageTurns = 0;
+    player.totalDamageDone = 0;
+    player.totalDamageTaken = 0;
+    player.cellsTraveled = 0;
+    player.score = 0;
+    player.killCount = 0;
+    player.itemsCollected = 0;
+    player.x = 0;
+    player.y = 0;
+    combatDots = {};
+    generateExit();
+    generateEnemies();
+    generateItems();
+    generateWalls();
+    render();
+}
+
+function checkPlayerAtExit() {
+    if (player.x === exit.x && player.y === exit.y) {
+        player.score += 1;
+        player.damage = PLAYER_BASE_DAMAGE;
+        console.groupCollapsed("Level complete! " + player.score);
+        console.log("Score: " + player.score);
+        console.log("Current health: " + player.health);
+        console.log("Distance Traveled: " + player.cellsTraveled);
+        console.log("Total Damage Dealt: " + player.totalDamageDone);
+        console.log("Total Damage Received: " + player.totalDamageTaken);
+        console.log("Circles Vanquished: " + player.killCount);
+        console.log("Items Collected: " + player.itemsCollected);
+        console.log("High Score: " + highScore);
+        console.groupEnd();
+        combatDots = {};
+        generateExit();
+        generateEnemies();
+        generateItems();
+        generateWalls();
+        render();
+    }
+}
+
+function render() {
+    drawGrid();
+    drawPlayer();
+    drawExit();
+    drawItems();
+    drawEnemies();
+    drawWalls();
+    drawCombatDots();
+}
+
+const directionMap = {
+    ArrowUp: [0, -1],
+    ArrowDown: [0, 1],
+    ArrowLeft: [-1, 0],
+    ArrowRight: [1, 0],
+    w: [0, -1],
+    s: [0, 1],
+    a: [-1, 0],
+    d: [1, 0],
+    h: [-1, 0],
+    j: [0, 1],
+    k: [0, -1],
+    l: [1, 0]
+};
+
+document.addEventListener('keydown', (e) => {
+    const direction = directionMap[e.key];
+    if (direction) {
+        movePlayer(...direction);
+        checkPlayerAtExit();
+        render();
+    }
+});
+
+let touchStartX = 0;
+let touchStartY = 0;
+
+canvas.addEventListener('touchstart', (e) => {
+    e.preventDefault(); // Prevent scrolling on touchstart
+    touchStartX = e.touches[0].clientX;
+    touchStartY = e.touches[0].clientY;
+});
+
+canvas.addEventListener('touchend', (e) => {
+    e.preventDefault(); // Prevent scrolling on touchend
+    const touchEndX = e.changedTouches[0].clientX;
+    const touchEndY = e.changedTouches[0].clientY;
+    const dx = touchEndX - touchStartX;
+    const dy = touchEndY - touchStartY;
+
+    if (Math.abs(dx) > Math.abs(dy)) {
+        // Horizontal swipe
+        if (dx > 0) {
+            movePlayer(1, 0); // Swipe right
+        } else {
+            movePlayer(-1, 0); // Swipe left
+        }
+    } else {
+        // Vertical swipe
+        if (dy > 0) {
+            movePlayer(0, 1); // Swipe down
+        } else {
+            movePlayer(0, -1); // Swipe up
+        }
+    }
+
+    render();
+}, { passive: false }); // TIL you can use passive set to false to help make preventDefault actually work? Feels like superstition
+
+const resizeCanvas = () => {
+    const rect = canvas.getBoundingClientRect();
+    canvas.width = rect.width;
+    canvas.height = rect.height;
+    tileSize = canvas.width / GRID_SIZE; // Update tile size based on canvas dimensions
+    render();
+};
+
+window.addEventListener('resize', resizeCanvas);
+resizeCanvas();
+
+// Initial level setup
+generateExit();
+generateEnemies();
+generateItems();
+generateWalls();
+render();
\ No newline at end of file
diff --git a/html/broughlike/index.html b/html/broughlike/index.html
index b318def..62a43ac 100644
--- a/html/broughlike/index.html
+++ b/html/broughlike/index.html
@@ -56,829 +56,9 @@
         <p><a href="about.html">About</a></p>
         <div>
             <button class="toggleButton" id="toggleShake" onclick="toggleShake()">Turn Shake Off</button>
-            <!-- <button class="toggleButton" id="toggleHardMode" onclick="toggleHardMode()">Hard Mode On</button> -->
         </div>
     </div>
     <canvas id="gameCanvas"></canvas>
-    <script>
-
-        const COLORS = {
-            grid: '#2d2d2d',
-            walls: '#2d2d2d',
-            exit: 'teal',
-            diamond: 'gold',
-            pentagon: 'blueviolet', 
-            player: 'rgba(0, 237, 209, ',
-            enemy: 'rgba(255, 155, 28, ',
-            boss: 'rgba(255, 14, 0, ',
-            combatDotPlayer: '#00edd1',
-            combatDotEnemy: '#ff731c',
-            combatDotBoss: '#b70030'
-        };
-
-        // let hardMode = localStorage.getItem('hardMode') || false;
-        // let hardModeModifier = hardMode ? 10 : 0;
-        // const toggleHardMode = () => {
-        //     hardMode = !hardMode;
-        //     localStorage.setItem('hardMode', hardMode);
-        //     document.getElementById('toggleHardMode').textContent = hardMode ? 'Hard Mode Off' : 'Hard Mode On';
-        //     hardModeModifier = hardMode ? 10 : 0;
-        //     resetGame();
-        // };
-
-        const GRID_SIZE = 6;
-        const PLAYER_HEALTH = 12;
-        const PLAYER_MAX_HEALTH = 16;
-        const PLAYER_BASE_DAMAGE = 1;
-        const ENEMY_CHASE_DISTANCE = 4;
-        const ENEMY_BOSS_OCCURRENCE = 10;
-        const MIN_ENEMIES_ON_LEVEL = 1;
-        const MAX_ENEMIES_ON_LEVEL = 4;
-        const MAX_ENEMY_HEALTH = 7;
-        const MIN_ENEMY_HEALTH = 2;
-        const WALLS_MIN = 7;
-        const WALLS_MAX = 20;
-        const ITEMS_MIN = 1;
-        const ITEMS_MAX = 3;
-        const DOTS_PER_HIT = 7;
-
-        let highScore = localStorage.getItem('highScore') || 0;
-
-        const canvas = document.getElementById('gameCanvas');
-        const ctx = canvas.getContext('2d');
-        let tileSize = canvas.width / GRID_SIZE;
-
-        const player = {
-            x: 0,
-            y: 0,
-            health: PLAYER_HEALTH,
-            score: 0,
-            damage: PLAYER_BASE_DAMAGE,
-            totalDamageDone: 0,
-            totalDamageTaken: 0,
-            cellsTraveled: 0,
-            killCount: 0,
-            itemsCollected: 0,
-            lineSegments: 0,
-            inventory: []
-        };
-
-        const exit = { x: Math.floor(Math.random() * GRID_SIZE), y: Math.floor(Math.random() * GRID_SIZE) };
-        let walls = [];
-        let enemies = [];
-        let items = [];
-        let combatDots = {};
-
-        function isValidMove(newX, newY) {
-            return (
-                newX >= 0 && newX < GRID_SIZE &&
-                newY >= 0 && newY < GRID_SIZE &&
-                !walls.some(wall => wall.x === newX && wall.y === newY)
-            );
-        }
-
-        function generateExit() {
-            let distance = 0;
-            do {
-                exit.x = Math.floor(Math.random() * GRID_SIZE);
-                exit.y = Math.floor(Math.random() * GRID_SIZE);
-                distance = Math.abs(exit.x - player.x) + Math.abs(exit.y - player.y);
-            } while (distance < 4);
-        }
-
-        function generateEnemies() {
-            enemies = [];
-            // Generate between 0 and MAX_ENEMIES_ON_LEVEL enemies if the player's score is 4 or lower
-            // Generate between MIN_ENEMIES_ON_LEVEL and MAX_ENEMIES_ON_LEVEL enemies if the player's score is 5 or higher
-            const numEnemies = player.score > 4 
-            ? Math.floor(Math.random() * (MAX_ENEMIES_ON_LEVEL - MIN_ENEMIES_ON_LEVEL + 1)) + MIN_ENEMIES_ON_LEVEL 
-            : Math.floor(Math.random() * (MAX_ENEMIES_ON_LEVEL + 1));
-            for (let i = 0; i < numEnemies; i++) {
-                let enemyX, enemyY;
-                do {
-                    enemyX = Math.floor(Math.random() * GRID_SIZE);
-                    enemyY = Math.floor(Math.random() * GRID_SIZE);
-                } while (
-                    (enemyX === player.x && enemyY === player.y) ||
-                    (enemyX === exit.x && enemyY === exit.y) ||
-                    walls.some(wall => wall.x === enemyX && wall.y === enemyY)
-                );
-                enemies.push({
-                    color: COLORS.enemy,
-                    x: enemyX,
-                    y: enemyY,
-                    health: Math.floor(Math.random() * (MAX_ENEMY_HEALTH - MIN_ENEMY_HEALTH + 1)) + MIN_ENEMY_HEALTH
-                });
-            }
-
-            // Generate a boss enemy every ENEMY_BOSS_OCCURRENCE levels
-            if (player.score % ENEMY_BOSS_OCCURRENCE === 0 && player.score > 0) {
-                let bossX = exit.x; // Boss enemies always appear at the exit
-                let bossY = exit.y; // This ensures that they're not in little rooms that the player can't reach
-                enemies.push({
-                    isBoss: true,
-                    color: COLORS.boss,
-                    x: bossX,
-                    y: bossY,
-                    health: MAX_ENEMY_HEALTH + 2
-                });
-            }
-        }
-
-        function generateWallsNaive() {
-            walls = [];
-            let numWalls = Math.floor(Math.random() * (WALLS_MAX - WALLS_MIN + 1)) + WALLS_MIN;
-            while (walls.length < numWalls) {
-                const wallX = Math.floor(Math.random() * GRID_SIZE);
-                const wallY = Math.floor(Math.random() * GRID_SIZE);
-
-                if (
-                    (wallX === player.x && wallY === player.y) ||                    // Check that a wall is not placed on the starting position
-                    (wallX === exit.x && wallY === exit.y) ||                        // Check that a wall is not placed on the exit
-                    enemies.some(enemy => enemy.x === wallX && enemy.y === wallY) || // Check that a wall is not placed on any enemies
-                    items.some(item => item.x === wallX && item.y === wallY)         // Check that a wall is not placed on any items
-                ) continue;
-
-                if (!walls.some(wall => wall.x === wallX && wall.y === wallY)) {
-                    walls.push({ x: wallX, y: wallY });
-                }
-            }
-
-            if (!isPassable()) {
-                generateWallsNaive();
-            }
-        }
-
-        function generateWallsDrunkardsWalk() {
-            walls = [];
-            const numWalls = Math.floor(Math.random() * (WALLS_MAX - WALLS_MIN + 1)) + WALLS_MIN;
-            let wallX = Math.floor(Math.random() * GRID_SIZE);
-            let wallY = Math.floor(Math.random() * GRID_SIZE);
-
-            while (walls.length < numWalls) {
-            if (
-                (wallX !== player.x || wallY !== player.y) &&                     // Check that a wall is not placed on the starting position
-                (wallX !== exit.x || wallY !== exit.y) &&                         // Check that a wall is not placed on the exit
-                !enemies.some(enemy => enemy.x === wallX && enemy.y === wallY) && // Check that a wall is not placed on any enemies
-                !items.some(item => item.x === wallX && item.y === wallY) &&      // Check that a wall is not placed on any items
-                !walls.some(wall => wall.x === wallX && wall.y === wallY)         // Check that a wall is not placed on an existing wall
-            ) {
-                walls.push({ x: wallX, y: wallY });
-            }
-
-            // Randomly move to a neighboring cell
-            const direction = Math.floor(Math.random() * 4);
-            switch (direction) {
-                case 0: wallX = Math.max(0, wallX - 1); break;             // Move left
-                case 1: wallX = Math.min(GRID_SIZE - 1, wallX + 1); break; // Move right
-                case 2: wallY = Math.max(0, wallY - 1); break;             // Move up
-                case 3: wallY = Math.min(GRID_SIZE - 1, wallY + 1); break; // Move down
-            }
-            }
-
-            if (!isPassable()) {
-                generateWallsDrunkardsWalk();
-            }
-        }
-
-        function generateWallsCellularAutomata() {
-            walls = [];
-            const map = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
-
-            // Initialize a map with random walls
-            for (let x = 0; x < GRID_SIZE; x++) {
-                for (let y = 0; y < GRID_SIZE; y++) {
-                    if (Math.random() < 0.4) {
-                    map[x][y] = 1;
-                    }
-                }
-            }
-
-            for (let i = 0; i < 4; i++) {
-                // Create a new map to store the next state of the cellular automata
-                const newMap = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
-
-                // Iterate over each cell in the grid
-                for (let x = 0; x < GRID_SIZE; x++) {
-                    for (let y = 0; y < GRID_SIZE; y++) {
-                        // Count the number of neighboring walls
-                        const neighbors = countNeighbors(map, x, y);
-
-                        if (map[x][y] === 1) {
-                            // If the cell is a wall, it stays a wall if it has 4 or more neighbors
-                            newMap[x][y] = neighbors >= 4 ? 1 : 0;
-                        } else {
-                            // If the cell is empty, it turn into a wall if it has 5 or more neighbors
-                            newMap[x][y] = neighbors >= 5 ? 1 : 0;
-                        }
-                    }
-                }
-
-                // Update the original map with the new state
-                map.forEach((row, x) => row.forEach((cell, y) => map[x][y] = newMap[x][y]));
-            }
-
-            // Convert map to walls array
-            for (let x = 0; x < GRID_SIZE; x++) {
-                for (let y = 0; y < GRID_SIZE; y++) {
-                    if (map[x][y] === 1 && 
-                    (x !== player.x || y !== player.y) && 
-                    (x !== exit.x || y !== exit.y) && 
-                    !enemies.some(enemy => enemy.x === x && enemy.y === y) && 
-                    !items.some(item => item.x === x && item.y === y)) {
-                        walls.push({ x, y });
-                    }
-                }
-            }
-
-            if (!isPassable()) {
-                generateWallsCellularAutomata();
-            }
-        }
-
-        function countNeighbors(map, x, y) {
-            let count = 0;
-            for (let dx = -1; dx <= 1; dx++) {
-                for (let dy = -1; dy <= 1; dy++) {
-                    if (dx === 0 && dy === 0) continue;
-                    const nx = x + dx;
-                    const ny = y + dy;
-                    if (nx >= 0 && nx < GRID_SIZE && ny >= 0 && ny < GRID_SIZE) {
-                        count += map[nx][ny];
-                    } else {
-                        count++; // Consider out-of-bounds bits as walls
-                    }
-                }
-            }
-            return count;
-        }
-
-        function generateWallsRSP() {
-            walls = [];
-            const MIN_ROOM_SIZE = 2;
-
-            function splitNode(x, y, width, height) {
-                const splitHorizontally = Math.random() > 0.5;
-                const max = (splitHorizontally ? height : width) - MIN_ROOM_SIZE;
-
-                if (max <= MIN_ROOM_SIZE) return [{ x, y, width, height }];
-
-                const split = Math.floor(Math.random() * (max - MIN_ROOM_SIZE)) + MIN_ROOM_SIZE;
-
-                if (splitHorizontally) {
-                    return [
-                        ...splitNode(x, y, width, split),
-                        ...splitNode(x, y + split, width, height - split)
-                    ];
-                } else {
-                    return [
-                        ...splitNode(x, y, split, height),
-                        ...splitNode(x + split, y, width - split, height)
-                    ];
-                }
-            }
-
-            function createRoom(node) {
-                const roomWidth = Math.floor(Math.random() * (node.width - 1)) + 1;
-                const roomHeight = Math.floor(Math.random() * (node.height - 1)) + 1;
-                const roomX = node.x + Math.floor(Math.random() * (node.width - roomWidth));
-                const roomY = node.y + Math.floor(Math.random() * (node.height - roomHeight));
-                return { x: roomX, y: roomY, width: roomWidth, height: roomHeight };
-            }
-
-            const nodes = splitNode(0, 0, GRID_SIZE, GRID_SIZE);
-            const rooms = nodes.map(createRoom);
-
-            rooms.forEach(room => {
-            for (let x = room.x; x < room.x + room.width; x++) {
-                for (let y = room.y; y < room.y + room.height; y++) {
-                    if (
-                        (x !== player.x || y !== player.y) &&
-                        (x !== exit.x || y !== exit.y) &&
-                        !enemies.some(enemy => enemy.x === x && enemy.y === y) &&
-                        !items.some(item => item.x === x && item.y === y)
-                    ) {
-                        walls.push({ x, y });
-                    }
-                }
-            }});
-
-            if (!isPassable()) {
-                generateWallsRSP();
-            }
-        }
-
-        function generateWalls() {
-            const wallGenerators = [
-                { name: 'RSP Tree', func: generateWallsRSP },
-                { name: 'Naive', func: generateWallsNaive },
-                { name: 'Cellular Automata', func: generateWallsCellularAutomata },
-                { name: 'Drunkard\'s Walk', func: generateWallsDrunkardsWalk }
-            ];
-            const randomIndex = Math.floor(Math.random() * wallGenerators.length);
-            const selectedGenerator = wallGenerators[randomIndex];
-            console.log(`Wall generator: ${selectedGenerator.name}`);
-            selectedGenerator.func();
-        }
-
-        function generateItems() {
-            items = [];
-            const numItems = Math.floor(Math.random() * (ITEMS_MAX - ITEMS_MIN + 1)) + ITEMS_MIN;
-            for (let i = 0; i < numItems; i++) {
-                let itemX, itemY;
-                do {
-                    itemX = Math.floor(Math.random() * GRID_SIZE);
-                    itemY = Math.floor(Math.random() * GRID_SIZE);
-                } while (
-                    (itemX === player.x && itemY === player.y) ||
-                    (itemX === exit.x && itemY === exit.y) ||
-                    walls.some(wall => wall.x === itemX && wall.y === itemY) ||
-                    enemies.some(enemy => enemy.x === itemX && enemy.y === itemY) ||
-                    items.some(item => item.x === itemX && item.y === itemY)
-                );
-                const itemType = Math.random() < 0.5 ? 'diamond' : 'pentagon'; // 50% chance for each type
-                items.push({ x: itemX, y: itemY, type: itemType });
-            }
-        }
-
-        function isPassable() {
-            const visited = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(false));
-
-            function dfs(x, y) {
-                if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE) return false; // Are the coordinates in bounds?
-                if (visited[x][y]) return false;
-                if (walls.some(wall => wall.x === x && wall.y === y)) return false;
-                visited[x][y] = true;
-                if (x === exit.x && y === exit.y) return true;
-                return dfs(x + 1, y) || dfs(x - 1, y) || dfs(x, y + 1) || dfs(x, y - 1);
-            }
-
-            return dfs(player.x, player.y);
-        }
-
-        function drawGrid() {
-            ctx.clearRect(0, 0, canvas.width, canvas.height);
-            ctx.strokeStyle = COLORS.grid;
-            for (let row = 0; row < GRID_SIZE; row++) {
-                for (let col = 0; col < GRID_SIZE; col++) {
-                    ctx.strokeRect(col * tileSize, row * tileSize, tileSize, tileSize);
-                }
-            }
-        }
-
-        function drawExit() {
-            const x = exit.x * tileSize + tileSize / 2;
-            const y = exit.y * tileSize + tileSize / 2;
-            ctx.beginPath();
-            ctx.moveTo(x, y - tileSize / 3);
-            ctx.lineTo(x + tileSize / 3, y + tileSize / 3);
-            ctx.lineTo(x - tileSize / 3, y + tileSize / 3);
-            ctx.closePath();
-            ctx.fillStyle = COLORS.exit;
-            ctx.fill();
-        }
-
-        function drawWalls() {
-            ctx.fillStyle = COLORS.walls;
-            walls.forEach(wall => {
-                ctx.fillRect(wall.x * tileSize, wall.y * tileSize, tileSize, tileSize);
-            });
-        }
-
-        function drawItems() {
-            items.forEach(item => {
-                const x = item.x * tileSize + tileSize / 2;
-                const y = item.y * tileSize + tileSize / 2;
-                ctx.fillStyle = item.type === 'diamond' ? COLORS.diamond : COLORS.pentagon;
-                ctx.beginPath();
-                if (item.type === 'diamond') {
-                    ctx.moveTo(x, y - tileSize / 4);
-                    ctx.lineTo(x + tileSize / 4, y);
-                    ctx.lineTo(x, y + tileSize / 4);
-                    ctx.lineTo(x - tileSize / 4, y);
-                } else {
-                    const sides = 5;
-                    const radius = tileSize / 4;
-                    for (let i = 0; i < sides; i++) {
-                        const angle = (i * 2 * Math.PI) / sides - Math.PI / 2;
-                        const pointX = x + radius * Math.cos(angle);
-                        const pointY = y + radius * Math.sin(angle);
-                        if (i === 0) ctx.moveTo(pointX, pointY);
-                        else ctx.lineTo(pointX, pointY);
-                    }
-                }
-                ctx.closePath();
-                ctx.fill();
-            });
-        }
-
-        function drawCharacterBorder(x, y, radius, damageTaken) {
-            const dashLength = 5;
-            const gapLength = Math.max(1, damageTaken * 2); // More damage, larger gaps
-
-            ctx.lineWidth = 2;
-            ctx.strokeStyle = '#2d2d2d';
-            ctx.setLineDash([dashLength, gapLength]);
-            ctx.beginPath();
-            ctx.arc(x, y, radius, 0, 2 * Math.PI);
-            ctx.stroke();
-            ctx.setLineDash([]); // Reset to a solid line
-        }
-
-        function drawEnemies() {
-            enemies.forEach(enemy => {
-                const x = enemy.x * tileSize + tileSize / 2;
-                const y = enemy.y * tileSize + tileSize / 2;
-                const opacity = enemy.health / MAX_ENEMY_HEALTH; // Opacity based on health
-                const radius = tileSize / 3;
-                const damageTaken = MAX_ENEMY_HEALTH - enemy.health;
-
-                ctx.beginPath();
-                ctx.arc(x, y, radius, 0, 2 * Math.PI);
-                ctx.fillStyle = `${enemy.color}${opacity})`;
-                ctx.fill();
-
-                drawCharacterBorder(x, y, radius, damageTaken);
-            });
-        }
-
-        function drawPlayer() {
-            const x = player.x * tileSize + tileSize / 2;
-            const y = player.y * tileSize + tileSize / 2;
-            const radius = tileSize / 3;
-            const playerOpacity = player.health / PLAYER_HEALTH; // Opacity based on health
-            ctx.beginPath();
-            for (let i = 0; i < 6; i++) {
-                const angle = (Math.PI / 3) * i;
-                const hexX = x + radius * Math.cos(angle);
-                const hexY = y + radius * Math.sin(angle);
-                if (i === 0) {
-                    ctx.moveTo(hexX, hexY);
-                } else {
-                    ctx.lineTo(hexX, hexY);
-                }
-            }
-            ctx.closePath();
-            ctx.fillStyle = `${COLORS.player}${playerOpacity})`;
-            ctx.fill();
-            ctx.lineWidth = 2;
-            ctx.strokeStyle = '#2d2d2d';
-            ctx.stroke();
-        }
-
-        function drawCombatDots() {
-            for (const key in combatDots) {
-                const [cellX, cellY] = key.split(',').map(Number);
-                const dots = combatDots[key];
-                dots.forEach(dot => {
-                    ctx.beginPath();
-                    ctx.arc(cellX * tileSize + dot.x, cellY * tileSize + dot.y, 2, 0, Math.PI * 2);
-                    ctx.fillStyle = dot.color;
-                    ctx.fill();
-                    ctx.closePath();
-                });
-            }
-        }
-
-        function handleItemCollection() {
-            const collectedItem = items.find(item => item.x === player.x && item.y === player.y);
-            if (collectedItem) {
-                player.itemsCollected++;
-                if (collectedItem.type === 'diamond') {
-                    player.damage += 3;
-                    console.log("Collected diamond! +3 damage on this level.");
-                } else if (collectedItem.type === 'pentagon') {
-                    const healAmount = Math.floor(Math.random() * 2) + 1;
-                    player.health = Math.min(player.health + healAmount, PLAYER_MAX_HEALTH);
-                    console.log("Collected pentagon! Healed " + healAmount + " health.");
-                }
-                items = items.filter(item => item !== collectedItem); // Remove collected item
-            }
-        }
-
-        function addCombatDots(x, y, color) {
-            const cellsToFill = [
-                [x, y],        // Center
-                [x - 1, y],    // Left
-                [x + 1, y],    // Right
-                [x, y - 1],    // Up
-                [x, y + 1],    // Down
-                [x - 1, y - 1], // Top-left
-                [x + 1, y - 1], // Top-right
-                [x - 1, y + 1], // Bottom-left
-                [x + 1, y + 1]  // Bottom-right
-            ];
-
-            cellsToFill.forEach(([cellX, cellY]) => {
-                if (cellX >= 0 && cellX < GRID_SIZE && cellY >= 0 && cellY < GRID_SIZE) {
-                    const key = `${cellX},${cellY}`;
-                    if (!combatDots[key]) {
-                        combatDots[key] = [];
-                    }
-                    for (let i = 0; i < DOTS_PER_HIT; i++) {
-                        combatDots[key].push({
-                            x: Math.random() * tileSize,
-                            y: Math.random() * tileSize,
-                            color: color
-                        });
-                    }
-                }
-            });
-        }
-
-        function movePlayer(dx, dy) {
-            const newX = player.x + dx;
-            const newY = player.y + dy;
-            if (isValidMove(newX, newY) && !enemies.some(enemy => enemy.x === newX && enemy.y === newY)) {
-                if (newX !== player.x || newY !== player.y) player.cellsTraveled++;
-                player.x = newX;
-                player.y = newY;
-                handleItemCollection(); // Did the player collect an item?
-                checkPlayerAtExit();    // Did the player get to the exit after moving?
-            } else {
-                // If an enemy is in the target cell, player stays put and does combat
-                const enemyInTargetCell = enemies.find(enemy => enemy.x === newX && enemy.y === newY);
-                if (enemyInTargetCell) {
-                    handleDamage(player, enemyInTargetCell);
-                }
-            }
-            moveEnemies();
-            render();
-        }
-        
-        function moveEnemies() {
-            enemies.forEach(enemy => {
-                const distanceToPlayer = Math.abs(enemy.x - player.x) + Math.abs(enemy.y - player.y);
-                const distanceToExit = Math.abs(enemy.x - exit.x) + Math.abs(enemy.y - exit.y);
-        
-                // If the enemy is closer to the exit than the player, move towards the exit
-                // Bosses are more aggressive about chasing the player
-                const target = distanceToPlayer <= (enemy.isBoss ? ENEMY_CHASE_DISTANCE + 2 : ENEMY_CHASE_DISTANCE) ? player : exit;
-                const path = findPath(enemy, target);
-        
-                if (path.length > 1) {
-                    const nextStep = path[1];
-                    const enemyInNextStep = enemies.find(e => e.x === nextStep.x && e.y === nextStep.y);
-        
-                    // Is the next step occupied by an enemy?
-                    if (!enemyInNextStep && !(nextStep.x === player.x && nextStep.y === player.y)) {
-                        // Move to the next place
-                        enemy.x = nextStep.x;
-                        enemy.y = nextStep.y;
-                    } else if (nextStep.x === player.x && nextStep.y === player.y) {
-                        // If the player is in the next step, stay put and do combat
-                        handleDamage(player, enemy);
-                    }
-                }
-            });
-        }
-
-        function findPath(start, goal) {
-            const queue = [{ x: start.x, y: start.y, path: [] }];
-            const visited = new Set();
-            visited.add(`${start.x},${start.y}`);
-
-            while (queue.length > 0) {
-                const { x, y, path } = queue.shift();
-                const newPath = [...path, { x, y }];
-
-                if (x === goal.x && y === goal.y) {
-                    return newPath;
-                }
-
-                const directions = [
-                    { dx: 1, dy: 0 },
-                    { dx: -1, dy: 0 },
-                    { dx: 0, dy: 1 },
-                    { dx: 0, dy: -1 }
-                ];
-
-                directions.forEach(({ dx, dy }) => {
-                    const newX = x + dx;
-                    const newY = y + dy;
-                    const key = `${newX},${newY}`;
-
-                    // Check if the new position is within the level and if it is passable
-                    if (
-                        newX >= 0 && newX < GRID_SIZE &&
-                        newY >= 0 && newY < GRID_SIZE &&
-                        // Have we already been here?
-                        !visited.has(key) &&
-                        // Is it a wall?
-                        !walls.some(wall => wall.x === newX && wall.y === newY) &&
-                        // Is there an enemy already there?
-                        !enemies.some(enemy => enemy.x === newX && enemy.y === newY)
-                    ) {
-                        queue.push({ x: newX, y: newY, path: newPath });
-                        visited.add(key);
-                    }
-                });
-            }
-
-            return [];
-        }
-
-        let combatAnimationEnabled = localStorage.getItem('combatAnimationEnabled');
-        if (combatAnimationEnabled === null) {
-            combatAnimationEnabled = true; // default to on...is that a good idea?
-            localStorage.setItem('combatAnimationEnabled', combatAnimationEnabled);
-        } else {
-            combatAnimationEnabled = combatAnimationEnabled === 'true';
-        }
-        document.getElementById('toggleShake').textContent = combatAnimationEnabled ? 'Turn Shake Off' : 'Turn Shake On';
-
-        function toggleShake() {
-            combatAnimationEnabled = !combatAnimationEnabled;
-            localStorage.setItem('combatAnimationEnabled', combatAnimationEnabled);
-            document.getElementById('toggleShake').textContent = combatAnimationEnabled ? 'Turn Shake Off' : 'Turn Shake On';
-        }
-
-        function combatAnimation() {
-            const canvas = document.getElementById('gameCanvas');
-            canvas.classList.add('shake');
-            canvas.addEventListener('animationend', () => {
-                canvas.classList.remove('shake');
-            }, { once: true });
-        }
-
-        function handleDamage(player, enemy) {
-            const enemyMisses = Math.random() < 0.5; // 50% chance the enemy misses you
-            const cellX = player.x;
-            const cellY = player.y;
-
-            if (!enemyMisses) {
-                player.health--;
-                player.totalDamageTaken++;
-                addCombatDots(cellX, cellY, COLORS.combatDotPlayer); // Add dots for player damage
-                console.log("Enemy hit! Player health: " + player.health);
-            } else {                
-                console.log("Enemy missed!");
-            }
-
-            enemy.health = enemy.health - player.damage;
-            player.totalDamageDone++;
-            addCombatDots(cellX, cellY, enemy.isBoss ? COLORS.combatDotBoss : COLORS.combatDotEnemy); // Add dots for enemy damage
-            console.log("Player hit! Enemy health: " + enemy.health);
-
-            if (combatAnimationEnabled) {
-                combatAnimation(); // Trigger the shake animation
-            }
-
-            if (enemy.health <= 0) {
-                player.killCount++;
-                enemies = enemies.filter(e => e !== enemy);                
-                if (enemy.isBoss) {
-                    player.lineSegments += 2;
-                } else {
-                    player.lineSegments++;
-                }
-            }
-
-            if (player.health <= 0) {
-                if (player.score > highScore) {
-                    highScore = player.score;
-                    localStorage.setItem('highScore', highScore);
-                }
-                alert(`Score: ${player.score}\nDistance Traveled: ${player.cellsTraveled}\nTotal Damage Dealt: ${player.totalDamageDone}\nTotal Damage Received: ${player.totalDamageTaken}\nCircles Vanquished: ${player.killCount}\n\nHigh Score: ${highScore}`);
-                resetGame();
-            }
-        }
-
-        function resetGame() {
-            const canvas = document.getElementById('gameCanvas');
-            if (canvas.classList.contains('shake')) {
-                canvas.classList.remove('shake');
-            }
-            player.health = PLAYER_HEALTH;
-            player.damage = PLAYER_BASE_DAMAGE;
-            player.bonusDamageTurns = 0;
-            player.totalDamageDone = 0;
-            player.totalDamageTaken = 0;
-            player.cellsTraveled = 0;
-            player.score = 0;
-            player.killCount = 0;
-            player.itemsCollected = 0;
-            player.x = 0;
-            player.y = 0;
-            player.lineSegments = 0;
-            player.inventory = [];
-            combatDots = {};
-            generateExit();
-            generateEnemies();
-            generateItems();
-            generateWalls();
-            render();
-        }
-
-        function checkPlayerAtExit() {
-            if (player.x === exit.x && player.y === exit.y) {
-                player.score += 1;
-                player.damage = PLAYER_BASE_DAMAGE;
-                console.groupCollapsed("Level complete! " + player.score);
-                console.log("Score: " + player.score);
-                console.log("Current health: " + player.health);
-                console.log("Distance Traveled: " + player.cellsTraveled);
-                console.log("Total Damage Dealt: " + player.totalDamageDone);
-                console.log("Total Damage Received: " + player.totalDamageTaken);
-                console.log("Circles Vanquished: " + player.killCount);
-                console.log("Items Collected: " + player.itemsCollected);
-                console.log("High Score: " + highScore);
-                console.groupEnd();
-                combatDots = {};
-                generateExit();
-                generateEnemies();
-                generateItems();
-                generateWalls();
-                render();
-            }
-        }
-
-        function render() {
-            drawGrid();
-            drawPlayer();
-            drawExit();
-            drawItems();
-            drawEnemies();
-            drawWalls();
-            drawCombatDots();
-        }
-
-        const directionMap = {
-            ArrowUp: [0, -1],
-            ArrowDown: [0, 1],
-            ArrowLeft: [-1, 0],
-            ArrowRight: [1, 0],
-            w: [0, -1],
-            s: [0, 1],
-            a: [-1, 0],
-            d: [1, 0],
-            h: [-1, 0],
-            j: [0, 1],
-            k: [0, -1],
-            l: [1, 0]
-        };
-
-        document.addEventListener('keydown', (e) => {
-            const direction = directionMap[e.key];
-            if (direction) {
-                movePlayer(...direction);
-                checkPlayerAtExit();
-                render();
-            }
-        });
-
-        let touchStartX = 0;
-        let touchStartY = 0;
-
-        canvas.addEventListener('touchstart', (e) => {
-            e.preventDefault(); // Prevent scrolling on touchstart
-            touchStartX = e.touches[0].clientX;
-            touchStartY = e.touches[0].clientY;
-        });
-
-        canvas.addEventListener('touchend', (e) => {
-            e.preventDefault(); // Prevent scrolling on touchend
-            const touchEndX = e.changedTouches[0].clientX;
-            const touchEndY = e.changedTouches[0].clientY;
-            const dx = touchEndX - touchStartX;
-            const dy = touchEndY - touchStartY;
-
-            if (Math.abs(dx) > Math.abs(dy)) {
-                // Horizontal swipe
-                if (dx > 0) {
-                    movePlayer(1, 0); // Swipe right
-                } else {
-                    movePlayer(-1, 0); // Swipe left
-                }
-            } else {
-                // Vertical swipe
-                if (dy > 0) {
-                    movePlayer(0, 1); // Swipe down
-                } else {
-                    movePlayer(0, -1); // Swipe up
-                }
-            }
-
-            render();
-        }, { passive: false }); // TIL you can use passive set to false to help make preventDefault actually work? Feels like superstition
-
-        const resizeCanvas = () => {
-            const rect = canvas.getBoundingClientRect();
-            canvas.width = rect.width;
-            canvas.height = rect.height;
-            tileSize = canvas.width / GRID_SIZE; // Update tile size based on canvas dimensions
-            render();
-        };
-
-        window.addEventListener('resize', resizeCanvas);
-        resizeCanvas();
-
-        // Initial level setup
-        generateExit();
-        generateEnemies();
-        generateItems();
-        generateWalls();
-        render();
-    </script>
+    <script src="./broughlike.js"></script>
 </body>
 </html>
\ No newline at end of file