Skip to content

Commit

Permalink
add task solution
Browse files Browse the repository at this point in the history
  • Loading branch information
MaksymShevch committed Oct 22, 2024
1 parent a6d3781 commit 5b89976
Show file tree
Hide file tree
Showing 3 changed files with 334 additions and 62 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ You can change the HTML/CSS layout if you need it.
## Deploy and Pull Request
1. Replace `<your_account>` with your Github username in the link
- [DEMO LINK](https://<your_account>.github.io/js_2048_game/)
- [DEMO LINK](https://MaksymShevch.github.io/js_2048_game/)
2. Follow [this instructions](https://mate-academy.github.io/layout_task-guideline/)
- Run `npm run test` command to test your code;
- Run `npm run test:only -- -n` to run fast test ignoring linter;
Expand Down
317 changes: 260 additions & 57 deletions src/modules/Game.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,266 @@
* Feel free to add more props and methods if needed.
*/
class Game {
/**
* 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) {
// eslint-disable-next-line no-console
console.log(initialState);
}

moveLeft() {}
moveRight() {}
moveUp() {}
moveDown() {}

/**
* @returns {number}
*/
getScore() {}

/**
* @returns {number[][]}
*/
getState() {}

/**
* 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() {}

/**
* Starts the game.
*/
start() {}

/**
* Resets the game.
*/
restart() {}

// Add your own methods here
constructor(
initialState = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
],
) {
this.score = 0;
this.status = 'idle';
this.initialState = initialState;
this.state = this.copyState(this.initialState);
}

copyState(state) {
return state.map((row) => [...row]);
}

addRandomTile() {
const emptyTiles = [];

for (let row = 0; row < 4; row++) {
for (let col = 0; col < 4; col++) {
if (this.state[row][col] === 0) {
emptyTiles.push([row, col]);
}
}
}

if (emptyTiles.length > 0) {
const randomIndex = Math.floor(Math.random() * emptyTiles.length);

const [row, col] = emptyTiles[randomIndex];

this.state[row][col] = Math.random() < 0.9 ? 2 : 4;
}
}

moveLeft() {
if (this.status !== 'playing') {
return;
}

const moved = this.move('left');

if (moved) {
this.addRandomTile();
this.checkGameState();
}
}

moveRight() {
if (this.status !== 'playing') {
return;
}

const moved = this.move('right');

if (moved) {
this.addRandomTile();
this.checkGameState();
}
}

moveUp() {
if (this.status !== 'playing') {
return;
}

const moved = this.move('up');

if (moved) {
this.addRandomTile();
this.checkGameState();
}
}

moveDown() {
if (this.status !== 'playing') {
return;
}

const moved = this.move('down');

if (moved) {
this.addRandomTile();
this.checkGameState();
}
}

move(direction) {
const originalState = this.copyState(this.state);

const combineRow = (row) => {
const newRow = row.filter((n) => n !== 0);

for (let i = 0; i < newRow.length - 1; i++) {
if (newRow[i] === newRow[i + 1]) {
newRow[i] *= 2;
newRow[i + 1] = 0;
this.score += newRow[i];
}
}

return newRow.filter((n) => n !== 0);
};

const moveRowLeft = (row) => {
const newRow = combineRow(row);

while (newRow.length < 4) {
newRow.push(0);
}

return newRow;
};

const moveRowRight = (row) => {
const copyRow = [...row];

const newRow = combineRow(copyRow.reverse());

while (newRow.length < 4) {
newRow.push(0);
}

return newRow.reverse();
};

const moveStateLeft = (state) => {
return state.map((row) => moveRowLeft(row));
};

const moveStateRight = (state) => {
return state.map((row) => moveRowRight(row));
};

switch (direction) {
case 'left':
this.state = moveStateLeft(this.state);
break;

case 'right':
this.state = moveStateRight(this.state);
break;

case 'up':
this.state = this.transposeState(
moveStateLeft(this.transposeState(this.state)),
);
break;

case 'down':
this.state = this.transposeState(
moveStateRight(this.transposeState(this.state)),
);
break;
}

return !this.areStatesEqual(this.state, originalState);
}

checkGameState() {
for (let row = 0; row < 4; row++) {
for (let col = 0; col < 4; col++) {
if (this.state[row][col] === 2048) {
this.status = 'win';

return;
}
}
}

if (this.hasEmptyCells() || this.canCombine()) {
return;
}
this.status = 'lose';
}

hasEmptyCells() {
for (let row = 0; row < 4; row++) {
for (let col = 0; col < 4; col++) {
if (this.state[row][col] === 0) {
return true;
}
}
}

return false;
}

canCombine() {
for (let row = 0; row < 4; row++) {
for (let col = 0; col < 4; col++) {
const current = this.state[row][col];

if (col < 3 && current === this.state[row][col + 1]) {
return true;
}

if (row < 3 && current === this.state[row + 1][col]) {
return true;
}
}
}

return false;
}

transposeState(state) {
const result = [];

for (let col = 0; col < 4; col++) {
result[col] = [];

for (let row = 0; row < 4; row++) {
result[col].push(state[row][col]);
}
}

return result;
}

getScore() {
return this.score;
}

getState() {
return this.state;
}

getStatus() {
return this.status;
}

start() {
if (this.status === 'idle') {
this.status = 'playing';
this.addRandomTile();
this.addRandomTile();
}
}

restart() {
this.state = this.copyState(this.initialState);
this.score = 0;
this.status = 'idle';
}

areStatesEqual(state1, state2) {
for (let row = 0; row < 4; row++) {
for (let col = 0; col < 4; col++) {
if (state1[row][col] !== state2[row][col]) {
return false;
}
}
}

return true;
}
}

module.exports = Game;
Loading

0 comments on commit 5b89976

Please sign in to comment.