From debac8b84941ea207be99118fce068443a6b89ae Mon Sep 17 00:00:00 2001 From: kirill_khnychkin Date: Wed, 28 Apr 2021 15:33:55 +0300 Subject: [PATCH] finished base game logic; added score counter; --- .gitignore | 1 + js/elem.js | 14 +++++++ js/fruit.js | 8 ++++ js/main.js | 25 +++++++++++- js/random.js | 17 ++++++++ js/snake.js | 111 +++++++++++++++++++++++++++++++-------------------- js/wall.js | 8 ++++ style.css | 8 ++++ 8 files changed, 147 insertions(+), 45 deletions(-) create mode 100644 .gitignore create mode 100644 js/elem.js create mode 100644 js/fruit.js create mode 100644 js/random.js create mode 100644 js/wall.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..757fee3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea \ No newline at end of file diff --git a/js/elem.js b/js/elem.js new file mode 100644 index 0000000..5d2b168 --- /dev/null +++ b/js/elem.js @@ -0,0 +1,14 @@ +export default class Elem { + constructor(matrix, cords) { + this.matrix = matrix; + this.cords = cords; + this.value = ''; + } + + show() { + // assign value to the cell for each set of x-y coordinates + for(let cord of this.cords) { + this.matrix.setCell(cord[0], cord[1], this.value); + } + } +} \ No newline at end of file diff --git a/js/fruit.js b/js/fruit.js new file mode 100644 index 0000000..5213b4e --- /dev/null +++ b/js/fruit.js @@ -0,0 +1,8 @@ +import Elem from './elem.js' + +export default class Fruit extends Elem { + constructor(matrix, cords) { + super(matrix, cords); + this.value = 'fruit'; + } +} \ No newline at end of file diff --git a/js/main.js b/js/main.js index 76c87d7..79b5c3d 100644 --- a/js/main.js +++ b/js/main.js @@ -1,5 +1,7 @@ +import Fruit from './fruit.js'; import Matrix from './matrix.js'; import Snake from './snake.js'; +import Utility from './random.js' window.onload = function (e) { let field = document.querySelector('.fields'); @@ -11,22 +13,41 @@ window.onload = function (e) { cellSize: 40 } + // snake starting position + const snakeStartPosition = [[10, 5], [9, 5]]; + // create gameboard(matrix) and snake objects let matrix = new Matrix(field, boardDimensions); - let snake = new Snake(matrix, 5, 1, 'right'); + let snake = new Snake(matrix, snakeStartPosition, 'right'); // create gameboard and snake on the page matrix.create(); snake.show(); snake.initInputListener(); + // create first fruit + (new Fruit(matrix, Utility.generateFruit(matrix, snake))).show(); + + // game score + let score = 0; + // game loop let gameLoop = setInterval(() => { if(!snake.snekDead) { - snake.move(snake.direction); + if(snake.ateFruit) { + score++; + snake.ateFruit = false; + + //todo: add walls to .fruit() + (new Fruit(matrix, Utility.generateFruit(matrix, snake))).show(); + } + + snake.move(); } else { console.log('snek is dead'); + console.log(`your score is: ${score}`); clearInterval(gameLoop); + //todo: add overlay and score screen with restart button } }, 500); diff --git a/js/random.js b/js/random.js new file mode 100644 index 0000000..0f39fd7 --- /dev/null +++ b/js/random.js @@ -0,0 +1,17 @@ +export default class Utility { + static generateFruit(matrix, snake, walls) { + let position = []; + + do { + position[0] = Math.floor(Math.random() * matrix.boardDimensions.x); + position[1] = Math.floor(Math.random() * matrix.boardDimensions.y); + } while (matrix.getCell(position[0], position[1]) !== '') + + return [position]; + } + + //todo: finish the wall and add it to the main.js + static generateWall(matrix) { + + } +} \ No newline at end of file diff --git a/js/snake.js b/js/snake.js index 9e7c2d3..3a0d80a 100644 --- a/js/snake.js +++ b/js/snake.js @@ -1,98 +1,123 @@ -export default class Snake { +import Elem from './elem.js' - constructor(matrix, x, y, direction) { - this.matrix = matrix; - this.x = x; - this.y = y; - this.direction = direction; +export default class Snake extends Elem { + constructor(matrix, coords, direction) { + super(matrix, coords); + this.value = 'snake'; + this.direction = direction; + this.newDirection = direction; this.snekDead = false; - } - - show() { - this.matrix.setCell(this.x, this.y, 'snake'); + this.ateFruit = false; } - move(direction) { - // save current snake position - let lastCoords = { - x: this.x, - y: this.y - } + move() { + // update the direction to the last inputed by player + this.direction = this.newDirection; - switch(direction) { + // make a copy of a snake head coordinates + let snakeHead = Array.from(this.cords[0]); // array copy, not a reference! + + // change snake head coordinates + switch(this.direction) { case 'right': - this.x++; + snakeHead[0]++; break; case 'left': - this.x--; + snakeHead[0]--; break; case 'up': - this.y--; + snakeHead[1]--; break; case 'down': - this.y++; + snakeHead[1]++; break; } - if (this._snekDeadCheck()) { + // check if the game is over + if (this._snekDeadCheck(snakeHead)) { this.snekDead = true; return; } + + // remove the last cell of the snake (in the array, not on the board) + // in order to move the whole snake forward + let tail = this.cords.pop(); - // remove snake from current cell - this.matrix.setCell(lastCoords.x, lastCoords.y, ''); + // if snake ate the fruit -> extend the snake + if (this._isAteFruit(snakeHead)) { + this.ateFruit = true; + this._extendSnake(); + } - // "move" (assign) snake to the next cell - this.matrix.setCell(this.x, this.y, 'snake'); + // remove last snake cell from the board + this.matrix.setCell(tail[0], tail[1], ''); - + // add snakeHead (with the updated coordinates) to the start of the + // snake array, basically moving the head forward (in the array, not on the board) + this.cords.unshift(snakeHead); + + // add this new head to the gameboard (displaying it) + this.matrix.setCell(snakeHead[0], snakeHead[1], 'snake'); } initInputListener() { - let lastDirection = null; - window.addEventListener('keydown', (e) => { switch(e.key) { case 'w': case 'W': + case 'ц': + case 'Ц': case 'ArrowUp': - if (lastDirection === 'down') break; - this.direction = 'up'; - lastDirection = 'up'; + if (this.direction === 'down') break; + this.newDirection = 'up'; break; case 'a': case 'A': + case 'ф': + case 'Ф': case 'ArrowLeft': - if (lastDirection === 'right') break; - this.direction = 'left'; - lastDirection = 'left'; + if (this.direction === 'right') break; + this.newDirection = 'left'; break; case 's': case 'S': + case 'ы': + case 'Ы': case 'ArrowDown': - if (lastDirection === 'up') break; - this.direction = 'down'; - lastDirection = 'down'; + if (this.direction === 'up') break; + this.newDirection = 'down'; break; case 'd': case 'D': + case 'в': + case 'В': case 'ArrowRight': - if (lastDirection === 'left') break; - this.direction = 'right'; - lastDirection = 'right'; + if (this.direction === 'left') break; + this.newDirection = 'right'; break; } }) } - _snekDeadCheck() { - return this.x < 1 || this.x > this.matrix.boardDimensions.x || this.y < 1 || this.y > this.matrix.boardDimensions.y; + _snekDeadCheck(snakeHead) { + // returns true if snake head go over the game board borders or rams into wall or itself + return snakeHead[0] < 1 || snakeHead[0] > this.matrix.boardDimensions.x || snakeHead[1] < 1 || snakeHead[1] > this.matrix.boardDimensions.y || this.matrix.getCell(snakeHead[0], snakeHead[1]) === 'wall' || this.matrix.getCell(snakeHead[0], snakeHead[1]) === 'snake'; + } + + _isAteFruit(snakeHead) { + return this.matrix.getCell(snakeHead[0], snakeHead[1]) === 'fruit'; + } + + _extendSnake() { + // add last element of the snake to the end of the snake array + // extending it by 1 element + this.cords.push(this.cords[this.cords.length - 1]); } } diff --git a/js/wall.js b/js/wall.js new file mode 100644 index 0000000..e8347ac --- /dev/null +++ b/js/wall.js @@ -0,0 +1,8 @@ +import Elem from './elem.js' + +export default class Wall extends Elem { + constructor(matrix, cords) { + super(matrix, cords); + this.value = 'wall'; + } +} \ No newline at end of file diff --git a/style.css b/style.css index 7a18bd7..5633a80 100644 --- a/style.css +++ b/style.css @@ -32,5 +32,13 @@ html { } [data-game="snake"] { + background-color: greenyellow; +} + +[data-game="snake--head"] { background-color: green; +} + +[data-game="wall"] { + background-color: blue; } \ No newline at end of file