generated from mate-academy/gulp-template
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
256 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,146 +1,260 @@ | ||
'use strict'; | ||
|
||
/** | ||
* This class represents the game. | ||
* Now it has a basic structure, that is needed for testing. | ||
* Feel free to add more props and methods if needed. | ||
*/ | ||
class Game { | ||
constructor(initialState = null) { | ||
this.size = 4; | ||
this.board = initialState || this.createEmptyBoard(); | ||
/** | ||
* Creates a new game instance. | ||
* | ||
* @param {number[][]} initialState | ||
* The initial state of the board. | ||
* @default | ||
* [[0, 0, 0, 0], | ||
* [0, 0, 0, 0], | ||
* [0, 0, 0, 0], | ||
* [0, 0, 0, 0]] | ||
* | ||
* If passed, the board will be initialized with the provided | ||
* initial state. | ||
*/ | ||
|
||
constructor(initialState = this.getInitialState()) { | ||
// eslint-disable-next-line no-console | ||
console.log(initialState); | ||
this.state = initialState; | ||
this.score = 0; | ||
this.status = 'idle'; | ||
} | ||
|
||
createEmptyBoard() { | ||
return Array.from({ length: this.size }, () => Array(this.size).fill(0)); | ||
moveLeft() { | ||
this.moveInline('left'); | ||
} | ||
moveRight() { | ||
this.moveInline('right'); | ||
} | ||
moveUp() { | ||
this.moveBlock('up'); | ||
} | ||
moveDown() { | ||
this.moveBlock('down'); | ||
} | ||
|
||
/** | ||
* @returns {number} | ||
*/ | ||
|
||
getScore() { | ||
return this.score; | ||
} | ||
|
||
/** | ||
* @returns {number[][]} | ||
*/ | ||
|
||
getState() { | ||
return this.state; | ||
} | ||
|
||
/** | ||
* Returns the current game status. | ||
* | ||
* @returns {string} One of: 'idle', 'playing', 'win', 'lose' | ||
* | ||
* `idle` - the game has not started yet (the initial state); | ||
* `playing` - the game is in progress; | ||
* `win` - the game is won; | ||
* `lose` - the game is lost | ||
*/ | ||
|
||
getStatus() { | ||
return this.status; | ||
} | ||
|
||
/** | ||
* Starts the game. | ||
*/ | ||
|
||
start() { | ||
this.score = 0; | ||
this.status = 'playing'; | ||
this.addRandomTile(); | ||
this.addRandomTile(); | ||
this.addRandomSell(); | ||
this.addRandomSell(); | ||
} | ||
|
||
// Add your own methods here | ||
/** | ||
* Resets the game. | ||
*/ | ||
|
||
restart() { | ||
this.board = this.createEmptyBoard(); | ||
this.score = 0; | ||
this.status = 'idle'; | ||
this.start(); | ||
this.score = 0; | ||
this.state = this.getInitialState(); | ||
} | ||
|
||
addRandomTile() { | ||
const emptyCells = []; | ||
// Add your own methods here | ||
moveBlock(direction) { | ||
if (this.getStatus() === 'playing') { | ||
let isMove = false; | ||
|
||
this.state.forEach((row, rowIndex) => { | ||
const column = []; | ||
|
||
// eslint-disable-next-line no-shadow | ||
for (let r = 0; r < this.size; r++) { | ||
// eslint-disable-next-line no-shadow | ||
for (let c = 0; c < this.size; c++) { | ||
if (this.board[r][c] === 0) { | ||
emptyCells.push({ r, c }); | ||
row.forEach((_, cellIndex) => { | ||
column.push(this.state[cellIndex][rowIndex]); | ||
}); | ||
|
||
let notEmptyCells = this.getNotEmptyCells(column, direction); | ||
|
||
notEmptyCells = this.mergeCells(notEmptyCells, direction); | ||
|
||
for (let i = 0; i < 4; i++) { | ||
this.state[i][rowIndex] = notEmptyCells[i] || 0; | ||
|
||
if (this.state[i][rowIndex] !== column[i]) { | ||
isMove = true; | ||
} | ||
} | ||
}); | ||
|
||
if (isMove) { | ||
this.addRandomSell(); | ||
this.checkWinLose(); | ||
} | ||
} | ||
} | ||
|
||
if (emptyCells.length === 0) { | ||
return; | ||
} | ||
moveInline(direction) { | ||
if (this.getStatus() === 'playing') { | ||
let isMove; | ||
|
||
const { r, c } = emptyCells[Math.floor(Math.random() * emptyCells.length)]; | ||
this.state.forEach((row) => { | ||
const oldRow = [...row]; | ||
let notEmptyCells = this.getNotEmptyCells(row, direction); | ||
|
||
this.board[r][c] = Math.random() < 0.9 ? 2 : 4; | ||
} | ||
notEmptyCells = this.mergeCells(notEmptyCells, direction); | ||
|
||
moveLeft() { | ||
let moved = false; | ||
row.forEach((_, i) => { | ||
row[i] = notEmptyCells[i] || 0; | ||
}); | ||
|
||
for (let r = 0; r < this.size; r++) { | ||
const row = this.board[r].filter((val) => val); | ||
|
||
for (let i = 0; i < row.length - 1; i++) { | ||
if (row[i] === row[i + 1]) { | ||
row[i] *= 2; | ||
this.score += row[i]; | ||
row.splice(i + 1, 1); | ||
row.push(0); | ||
if (!oldRow.every((cell, index) => cell === row[index])) { | ||
isMove = true; | ||
} | ||
} | ||
}); | ||
|
||
while (row.length < this.size) { | ||
row.push(0); | ||
if (isMove) { | ||
this.addRandomSell(); | ||
this.checkWinLose(); | ||
} | ||
} | ||
} | ||
|
||
getInitialState() { | ||
return new Array(4).fill(0).map(() => new Array(4).fill(0)); | ||
} | ||
|
||
addRandomSell() { | ||
const freeIndex = []; | ||
|
||
if (this.board[r].toString() !== row.toString()) { | ||
moved = true; | ||
for (let row = 0; row < 4; row++) { | ||
for (let col = 0; col < 4; col++) { | ||
if (this.state[row][col] === 0) { | ||
freeIndex.push([row, col]); | ||
} | ||
} | ||
this.board[r] = row; | ||
} | ||
|
||
if (moved) { | ||
this.addRandomTile(); | ||
} | ||
} | ||
const getRandIndex = Math.floor(Math.random() * freeIndex.length); | ||
|
||
moveRight() { | ||
this.board.forEach((row) => row.reverse()); | ||
this.moveLeft(); | ||
this.board.forEach((row) => row.reverse()); | ||
} | ||
const getNum = Math.random() < 0.9 ? 2 : 4; | ||
|
||
moveUp() { | ||
this.transpose(); | ||
this.moveLeft(); | ||
this.transpose(); | ||
} | ||
const [newRow, newCol] = freeIndex[getRandIndex]; | ||
|
||
moveDown() { | ||
this.transpose(); | ||
this.moveRight(); | ||
this.transpose(); | ||
this.state[newRow][newCol] = getNum; | ||
} | ||
|
||
transpose() { | ||
this.board = this.board[0].map( | ||
(_, colIndex) => this.board.map((row) => row[colIndex]), | ||
// eslint-disable-next-line function-paren-newline | ||
); | ||
} | ||
getNotEmptyCells(position, direction) { | ||
const notEmptyCells = position.filter((cell) => cell > 0); | ||
|
||
getScore() { | ||
return this.score; | ||
} | ||
if (direction === 'right' || direction === 'down') { | ||
while (notEmptyCells.length < 4) { | ||
notEmptyCells.unshift(0); | ||
} | ||
} | ||
|
||
getState() { | ||
return this.board; | ||
if (direction === 'left' || direction === 'up') { | ||
while (notEmptyCells.length < 4) { | ||
notEmptyCells.push(0); | ||
} | ||
} | ||
|
||
return notEmptyCells; | ||
} | ||
|
||
getStatus() { | ||
if (this.board.flat().includes(2048)) { | ||
return 'win'; | ||
combineCells(cells, direction) { | ||
for (let i = 0; i < cells.length - 1; i++) { | ||
const valueOne = cells[i]; | ||
const valueTwo = cells[i + 1]; | ||
|
||
if (valueOne === valueTwo && valueOne > 0) { | ||
this.score += valueOne + valueTwo; | ||
cells[i] = valueOne + valueTwo; | ||
cells.splice(i + 1, 1); | ||
} | ||
} | ||
|
||
if (!this.canMove()) { | ||
return 'lose'; | ||
if (direction === 'right' || direction === 'down') { | ||
return this.getNotEmptyCells(cells, direction); | ||
} | ||
|
||
return this.status; | ||
return cells; | ||
} | ||
|
||
canMove() { | ||
for (let r = 0; r < this.size; r++) { | ||
for (let c = 0; c < this.size; c++) { | ||
if (this.board[r][c] === 0) { | ||
hasMove() { | ||
for (let row = 0; row < 4; row++) { | ||
for (let col = 0; col < 4; col++) { | ||
const current = this.state[row][col]; | ||
|
||
if (current === 0) { | ||
return true; | ||
} | ||
|
||
if (c < this.size - 1 && this.board[r][c] === this.board[r][c + 1]) { | ||
if ((row < 3 ? this.state[row + 1][col] : 0) === current) { | ||
return true; | ||
} | ||
|
||
if (r < this.size - 1 && this.board[r][c] === this.board[r + 1][c]) { | ||
if ((col < 3 ? this.state[row][col + 1] : 0) === current) { | ||
return true; | ||
} | ||
// if (current === right) { | ||
// return true; | ||
// } | ||
|
||
// if (current === down) { | ||
// return true; | ||
// } | ||
|
||
// if (down === 0 || right === 0) { | ||
// return true; | ||
// } | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
checkWinLose() { | ||
if (this.state.some((row) => row.includes(2048))) { | ||
this.status = 'win'; | ||
} | ||
|
||
if (!this.hasMove()) { | ||
this.status = 'lose'; | ||
} | ||
} | ||
} | ||
|
||
module.exports = Game; |
Oops, something went wrong.