diff --git a/src/snake.ts b/src/snake.ts index ff79f02..d5781a5 100644 --- a/src/snake.ts +++ b/src/snake.ts @@ -27,6 +27,11 @@ class Point { this.y = y; } + copy(other: Point) { + this.x = other.x; + this.y = other.y; + } + add(other: Point) { var result = new Point; result.x = this.x + other.x; @@ -46,7 +51,7 @@ class SnakeCore { canvas: HTMLCanvasElement; context: CanvasRenderingContext2D; grid: number; - speed: number; + timeout: number; width: number; height: number; board: number[][]; @@ -59,7 +64,7 @@ class SnakeCore { this.canvas = document.getElementById('snake') as HTMLCanvasElement; this.context = this.canvas.getContext('2d') as CanvasRenderingContext2D; this.grid = 25; // size of grid squares - this.speed = 10; // speed in ms + this.timeout = 100; // speed in ms this.width = 25; this.height = 15; this.board = []; @@ -88,6 +93,12 @@ class SnakeCore { this.board[i][j] = 0; } } + snake.context.fillStyle = "black"; + for (let i = 0; i < snake.height; i++) { + for (let j = 0; j < snake.width; j++) { + snake.context.fillRect(j * snake.grid, i * snake.grid, snake.grid, snake.grid); + } + } } foodRegen() { @@ -102,6 +113,53 @@ class SnakeCore { return; } } + + // Simulate game logic + simulate() { + // Move snake + // TODO: Fix teleporting snake + let next: Point = new Point; + next.copy(bot.nextMove()); + if (next.x < 0 || next.x > snake.width || next.y < 0 || next.y > snake.height) { + snake.gameover = true; + return; + } + if (isBitSet(snake.board[next.y][next.x], 0) && (snake.body.length > 1)) { + snake.gameover = true; // Game should end (Snake touching snake) + return; + } + + snake.board[next.y][next.x] = bitSet(snake.board[next.y][next.x], 0); + snake.body.push(next); + if (!isBitSet(snake.board[next.y][next.x], 1)) { + let old: Point = snake.body.shift() as Point; + snake.board[old.y][old.x] = bitClear(snake.board[old.y][old.x], 0); + } else { + snake.board[next.y][next.x] = bitClear(snake.board[next.y][next.x], 1); + snake.foodAte = true; + while (bot.path.length > 0) + bot.path.pop(); + } + } + + // Draw game to canvas + draw() { + // Clear the screen + snake.context.clearRect(0, 0, snake.canvas.width, snake.canvas.height); + + // Draw game + for (let i = 0; i < snake.height; i++) { + for (let j = 0; j < snake.width; j++) { + if (isBitSet(snake.board[i][j], 0)) + snake.context.fillStyle = "green"; + else if (isBitSet(snake.board[i][j], 1)) + snake.context.fillStyle = "red"; + else + snake.context.fillStyle = "black"; + snake.context.fillRect(j * snake.grid, i * snake.grid, snake.grid, snake.grid); + } + } + } } class Bot { @@ -120,9 +178,11 @@ class Bot { if (isBitSet(snake.board[current.y][current.x], 2)) continue; this.pathUntrimmed.push(current); - let locals: Point[] = new Array[4]; - for (var i = 0; i < 4; i++) - locals[i] = new Point(current.x, current.y); + let locals: Point[] = new Array(4); + for (var i = 0; i < 4; i++) { + locals[i] = new Point; + locals[i].copy(current); + } locals[0].y += 1; locals[1].x += 1; locals[2].y -= 1; @@ -152,13 +212,12 @@ class Bot { this.path.push(this.pathUntrimmed.pop() as Point); // Push food location while (this.pathUntrimmed.length !== 0) { if (!reachedSnake) { - let location = this.pathUntrimmed[this.pathUntrimmed.length - 1]; + let location: Point = new Point; + location.copy(this.pathUntrimmed[this.pathUntrimmed.length - 1]); if (isBitSet(snake.board[location.y][location.x], 0)) reachedSnake = true; var delta = new Point; - let connection = this.path[this.path.length - 1]; - delta.x = location.x - connection.x; - delta.y = location.y - connection.y; + delta = location.subtract(this.path[this.path.length - 1]); if ((Math.abs(delta.x) + Math.abs(delta.y)) === 1) this.path.push(location); } @@ -182,95 +241,51 @@ class Bot { 4 = down */ nextMove() { - var next: Point = this.path.pop() as Point; - var head: Point = snake.head; - var delta = new Point(0, 0); - delta = next.subtract(head); + var next: Point = new Point; + next.copy(this.path.pop()); + var delta = new Point; + delta = next.subtract(snake.head); if (delta.x > 1) { - delta.x = 1; - console.log("[ERR] delta.x > 1, clamping"); + console.log("[ERR] delta.x > 1"); } else if (delta.x < -1) { - delta.x = -1; - console.log("[ERR] delta.x < 1, clamping"); + console.log("[ERR] delta.x < 1"); } if (delta.y > 1) { - delta.y = 1; - console.log("[ERR] delta.y > 1, clamping"); + console.log("[ERR] delta.y > 1"); } else if (delta.y < -1) { - delta.y = -1; - console.log("[ERR] delta.y < 1, clamping"); + console.log("[ERR] delta.y < 1"); } - return delta; + return next; } autoplay() { - if (this.path.length === 0) + if (this.path.length === 0) { this.bfs(); this.trim(); this.unvisit(); + } return this.nextMove(); - /* - let delta = this.headPos - this.path.pop(); - if (delta.x > 0) - return 0; - if (delta.y > 0) - return 1; - if (delta.x < 0) - return 2; - if (delta.y < 0) - return 4; - return 0; // Safety guard if above doesn't return - */ } } const snake: SnakeCore = new SnakeCore(); // Singleton for snake game const bot: Bot = new Bot(); // Singleton for bot playing +// game loop function snakeloop() { - snake.context.clearRect(0, 0, snake.canvas.width, snake.canvas.height); // Reset of needed if (snake.gameover) snake.reset(); // Input bot.autoplay(); - - // Move snake - // TODO: Fix teleporting snake - let next = bot.nextMove().add(snake.head); - if (next.x < 0 || next.x > snake.width || next.y < 0 || next.y > snake.height) - snake.gameover = true; - if (isBitSet(snake.board[next.y][next.x], 0) && (snake.body.length > 1)) - snake.gameover = true; // Game should end (Snake touching snake) - snake.board[next.y][next.x] = bitSet(snake.board[next.y][next.x], 0); - snake.body.push(next); - if (!isBitSet(snake.board[next.y][next.x], 1)) { - let old: Point = snake.body.shift() as Point; - snake.board[old.y][old.x] = bitClear(snake.board[old.y][old.x], 0); - } else { - snake.board[next.y][next.x] = bitClear(snake.board[next.y][next.x], 1); - snake.foodAte = true; - while (bot.path.length > 0) - bot.path.pop(); - } + snake.simulate(); // Regenerate food if needed snake.foodRegen(); - // Draw game - for (let i = 0; i < snake.height; i++) { - for (let j = 0; j < snake.width; j++) { - if (isBitSet(snake.board[i][j], 0)) - snake.context.fillStyle = "green"; - else if (isBitSet(snake.board[i][j], 1)) - snake.context.fillStyle = "red"; - else - snake.context.fillStyle = "black"; - snake.context.fillRect(j * snake.grid, i * snake.grid, snake.grid, snake.grid); - } - } + snake.draw(); } // start the game -setInterval(snakeloop, snake.speed); +setInterval(snakeloop, snake.timeout);