From 59981daf60d5a6f14b3668062526786378a342f5 Mon Sep 17 00:00:00 2001 From: Joanthan Bernard Date: Mon, 8 Dec 2014 19:49:28 -0600 Subject: [PATCH] Day 1: Snake --- snake/snake.html | 37 +++++++++ snake/snake.js | 203 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 snake/snake.html create mode 100644 snake/snake.js diff --git a/snake/snake.html b/snake/snake.html new file mode 100644 index 0000000..f3d01c7 --- /dev/null +++ b/snake/snake.html @@ -0,0 +1,37 @@ + + + + + + +

Snake

+ + + + + + + + +
+ + + + + diff --git a/snake/snake.js b/snake/snake.js new file mode 100644 index 0000000..c928488 --- /dev/null +++ b/snake/snake.js @@ -0,0 +1,203 @@ +(function() { + + var S = window.Snake = {}; + + var ROWS = S.ROWS = 50; + var COLS = S.COLS = 50; + + var board; + var body = new Array(); + var headCur = coord2idx(Math.floor((ROWS - 1) / 2), Math.floor((COLS - 1) / 2)); + var direction = 1; + var g = S.graphicsContext = null; + var canvas = S.canvas = null; + var cellWidth, cellHeight; + var foodEaten = false; + var dead = true; + var skipTicks; + var nextGameTick; + var cmdQueue = new Array(); + + function coord2idx(row, col) { return (COLS * row) + col; } + function idx2row(idx) { return Math.floor(idx/COLS); } + function idx2col(idx) { return idx % COLS; } + + function initialize(options) { //givenCanvas, keyContext, fps) { + + if (!options.canvas) { + alert("Missing the canvas element."); + return false } + + canvas = options.canvas; + g = S.graphicsContext = canvas.getContext("2d"); + + if (options.rows) S.ROWS = ROWS = options.rows; + if (options.cols) S.COLS = COLS = options.cols; + + board = new Array(ROWS * COLS); + + cellWidth = canvas.width / COLS; + cellHeight = canvas.height / ROWS; + + if (options.keyContext) options.keyContext.onkeydown = handleKey; + else canvas.onkeydown = handleKey; + + g.fillStyle = "white"; + g.fillRect(0, 0, canvas.width, canvas.height); + + g.font = "24px sans-serif"; + g.fillStyle = "black"; + g.fillText("Press Enter to begin.", 50, Math.floor(canvas.height/2), canvas.width - 100); + + if (options.fps) skipTicks = Math.ceil(1000 / options.fps); + else skipTicks = 150; } + + function startGame(canvas, keyContext) { + while (body.length > 0) body.pop(); + while (cmdQueue.length > 0) cmdQueue.pop(); + + headCur = coord2idx(Math.floor((ROWS - 1) / 2), Math.floor((COLS - 1) / 2)); + direction = 1; + dead = false; + nextGameTick = (new Date).getTime() + skipTicks; + + body.push(headCur); + + // Wipe the board and draw the walls. + var i; + for (i = 0; i < board.length; i++ ) { + if (Math.floor(i / COLS) == 0 || (i % COLS) == 0) board[i] = 'W'; + else if (Math.floor(i / COLS) == (ROWS - 1) || + (i % COLS) == (COLS - 1)) board[i] = 'W'; + else board[i] = ''; } + + // Write the initial body segment onto the board. + board[headCur] = 'S'; + + // Create a new food item and add it to the board. + board[newFoodLocation()] = 'F'; + + // Draw the board + for (i = 0; i < board.length; i++) { paintIdx(i); } + + gameLoop(); } + + function gameLoop() { + if (dead) return // break the callback loop + + if ((new Date).getTime() > nextGameTick) { + updateAndDraw(); + nextGameTick += skipTicks; } + + requestAnimationFrame(gameLoop); } + + // For this game I want the game logic and the refresh rate to be tied + // together. The snake moves on step per animation frame. We do limit the + // animation frame to 1fps. This works because there are no other elements + // being animated other than the snake. + function updateAndDraw() { + if (!g) return false; + + foodEaten = false; + + // 1. Read up to one command from the user. + if (cmdQueue.length > 0) { + var cmd = cmdQueue.shift(); + if (cmd == 37) direction = -1; // Left + else if (cmd == 38) direction = -COLS; // Up + else if (cmd == 39) direction = 1; // Right + else if (cmd == 40) direction = COLS; /* Down */ } + + // 2. Find the head's next spot. + headCur = headCur + direction; + + // 3. First check to see if we've eaten food (since it will affect + // collision detection for the body). This is also where growth + // happens via not moving the tail. + if (board[headCur] == 'F') foodEaten = true; + + // 4. Remove the tail. + if (!foodEaten) { + var tailIdx = body.shift(); + // Value on the board doesn't matter, just not W,S, or F + board[tailIdx] = ''; + paintIdx(tailIdx); } + + + // 5. Detect wall and snake collisions + if (board[headCur] == 'W' || board[headCur] == 'S') { + die(); return } + + // 6. Move the head + body.push(headCur); + board[headCur] = 'S'; + paintIdx(headCur); + + // 7. Create more food if needed. + if (foodEaten) { + var foodLoc = newFoodLocation(); + board[foodLoc] = 'F'; + paintIdx(foodLoc); } } + + function newFoodLocation() { + var emptySpaces = new Array(); + for (var i = 0; i < board.length; i++) + if (board[i] != 'W' && board[i] != 'S') + emptySpaces.push(i); + + return emptySpaces[Math.floor(Math.random() * emptySpaces.length)] } + + function die() { + paintIdxColor(headCur, "red"); + dead = true; + //g.fillStyle = "white"; + //g.fillRect(0, 0, canvas.width, canvas.height); + + g.font = "24px sans-serif"; + g.fillStyle = "red"; + g.fillText("Press Enter to retry.", 50, Math.floor(canvas.height/2), canvas.width - 100); } + + function handleKey(ke) { + var key = ke.keyCode ? ke.keyCode : ke.which; + + // Enter, start a new game. + if (key == 13) startGame(); + + // Queue directions in the command queue + else if ((key > 36) && (key < 41)) cmdQueue.push(key); } + + function paintCoord(row, col) { + var idx = coord2idx(row, col); + if (board[idx] == 'W') g.fillStyle = "black"; + else if (board[idx] == 'F') g.fillStyle = "yellow"; + else if (board[idx] == 'S') g.fillStyle = "blue"; + else g.fillStyle = "white"; + + g.fillRect(col, row, cellWidth, cellHeight); } + + function paintIdxColor(idx, color) { + g.fillStyle = color; + + g.fillRect( + (idx % COLS) * cellWidth, + Math.floor(idx / COLS) * cellHeight, + cellWidth, cellHeight); } + + function paintIdx(idx) { + if (board[idx] == 'W') g.fillStyle = "black"; + else if (board[idx] == 'F') g.fillStyle = "yellow"; + else if (board[idx] == 'S') g.fillStyle = "blue"; + else g.fillStyle = "white"; + + g.fillRect( + (idx % COLS) * cellWidth, + Math.floor(idx / COLS) * cellHeight, + cellWidth, cellHeight); } + + S.initialize = initialize; + + // Exports for debugging + S.board = board; + S.body = body; + S.updateAndDraw = updateAndDraw; +})();