Skip to content

Commit

Permalink
add task solution
Browse files Browse the repository at this point in the history
  • Loading branch information
1luki9901 committed Nov 17, 2024
1 parent faeb265 commit 2743aaa
Show file tree
Hide file tree
Showing 3 changed files with 256 additions and 142 deletions.
6 changes: 1 addition & 5 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ <h1>2048</h1>
<button class="button start">Start</button>
</div>
</div>

<table class="game-field">
<tbody>
<tr class="field-row">
Expand All @@ -33,21 +32,18 @@ <h1>2048</h1>
<td class="field-cell"></td>
<td class="field-cell"></td>
</tr>

<tr class="field-row">
<td class="field-cell"></td>
<td class="field-cell"></td>
<td class="field-cell"></td>
<td class="field-cell"></td>
</tr>

<tr class="field-row">
<td class="field-cell"></td>
<td class="field-cell"></td>
<td class="field-cell"></td>
<td class="field-cell"></td>
</tr>

<tr class="field-row">
<td class="field-cell"></td>
<td class="field-cell"></td>
Expand All @@ -56,7 +52,6 @@ <h1>2048</h1>
</tr>
</tbody>
</table>

<div class="message-container">
<p class="message message-lose hidden">You lose! Restart the game?</p>
<p class="message message-win hidden">Winner! Congrats! You did it!</p>
Expand All @@ -65,6 +60,7 @@ <h1>2048</h1>
</p>
</div>
</div>
<script src="scripts/main.js"></script>
<script
src="scripts/main.js"
type="module"
Expand Down
280 changes: 197 additions & 83 deletions src/modules/Game.class.js
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;
Loading

0 comments on commit 2743aaa

Please sign in to comment.