Skip to content

Commit

Permalink
feat: game
Browse files Browse the repository at this point in the history
  • Loading branch information
Philipp-Plotnikov committed Apr 20, 2024
1 parent 7b55802 commit 29f0388
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 0 deletions.
Binary file added game/GameOfLife.pptx
Binary file not shown.
57 changes: 57 additions & 0 deletions game/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./styles/main.css"/>
<script src="./src/main.js" type="text/javascript" defer></script>
<title>Gamek of life</title>
</head>
<body>
<main class="main" role="main">
<div class="epocha-time-container">Epocha time: <time class="epocha-time">00:00</time></div>
<div class="game-board">
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
<div class="game-cell"></div>
</div>
<div class="control-panel">
<button class="game-start">Start</button>
<button class="game-end">Stop</button>
</div>
</main>
</body>
</html>
152 changes: 152 additions & 0 deletions game/src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
const LIFE_COLOR = 'coral';
const GAME_CELL_CLASS = '.game-cell';
const GAME_BOARD_CLASS = '.game-board';
const GAME_START_BUTTON_CLASS = '.game-start';
const GAME_END_BUTTON_CLASS = '.game-end';
const EPOCHA_TIME_CLASS = '.epocha-time';
const ANIMATION_INTERVAL_SEC = 5;
const SECOND_MS = 1000;
const MATRIX_ROW_AMOUNT = 6;
const MATRIX_COLUMN_AMOUNT = 6;

const gameStart = document.querySelector(GAME_START_BUTTON_CLASS);
const gameEnd = document.querySelector(GAME_END_BUTTON_CLASS);
const gameBoard = document.querySelector(GAME_BOARD_CLASS);
const epochaTime = document.querySelector(EPOCHA_TIME_CLASS);
const gameBoardCellMatrix = getBoardCellMatrix();

gameBoard.addEventListener('click', (event) => {
const gameBoardCell = event.target;
if (isCellAlive(gameBoardCell)) {
markCellDeath(gameBoardCell);
return;
}
markCellAlive(gameBoardCell);
});

gameStart.addEventListener('click', async (event) => {
const button = event.currentTarget;
if (isButtonActive(button)) {
return;
}
button.dataset.isActive = 'true';
gameEnd.dataset.isActive = 'false';
if (!isTimerInProgress()) {
await animate();
return;
}
await startTimer();
if (isButtonActive(gameStart)) {
await animate();
}
});
gameEnd.addEventListener('click', (event) => {
const button = event.currentTarget;
if (isButtonActive(button)) {
return;
}
button.dataset.isActive = 'true';
gameStart.dataset.isActive = 'false';
});

async function animate() {
const deltaRow = [-1, -1, -1, 0, 0, 1, 1, 1];
const deltaColumn = [-1, 0, 1, -1, 1, -1, 0, 1];
let isChanged = false;
for (let i = 0; i < MATRIX_ROW_AMOUNT; i++) {
for (let j = 0; j < MATRIX_COLUMN_AMOUNT; j++) {
let liveNeighbours = 0;
for (let k = 0; k < deltaColumn.length; k++) {
let ni = i + deltaRow[k];
let nj = j + deltaColumn[k];
ni = ni < 0 ? MATRIX_ROW_AMOUNT - 1 : ni;
ni = ni >= MATRIX_ROW_AMOUNT ? 0 : ni;
nj = nj < 0 ? MATRIX_COLUMN_AMOUNT - 1 : nj;
nj = nj >= MATRIX_COLUMN_AMOUNT ? 0 : nj;
if (isCellAlive(gameBoardCellMatrix[ni][nj])) {
liveNeighbours++;
}
}
if (isCellAlive(gameBoardCellMatrix[i][j]) && (liveNeighbours < 2 || liveNeighbours > 3)) {
isChanged = true;
requestAnimationFrame(() => { markCellDeath(gameBoardCellMatrix[i][j]) });
continue;
}
if (!isCellAlive(gameBoardCellMatrix[i][j]) && liveNeighbours === 3) {
isChanged = true;
requestAnimationFrame(() => { markCellAlive(gameBoardCellMatrix[i][j]) });
}
}
}
if (!isChanged) {
setTimeout(() => { alert('Animation was finished with success!') });
gameStart.dataset.isActive = 'false';
return;
}
await startTimer();
if (isButtonActive(gameStart)) {
setTimeout(() => {animate()});
}
}

async function startTimer() {
const promise = new Promise((resolve) => {
let currentInterval = +(epochaTime.dataset.time) || 0;
const interval = setInterval(() => {
if (isButtonActive(gameEnd)) {
clearInterval(interval);
resolve();
return;
}
if (currentInterval < ANIMATION_INTERVAL_SEC) {
currentInterval++;
epochaTime.dataset.time = currentInterval.toString();
requestAnimationFrame(() => {
epochaTime.textContent = `${(currentInterval / 60).toFixed(0).padStart(2, '0')}:${(currentInterval % 60).toFixed(0).padStart(2, '0')}`;
});
return;
}
requestAnimationFrame(() => {
epochaTime.textContent = '00:00';
epochaTime.dataset.time = '';
})
clearInterval(interval);
resolve();
}, SECOND_MS);
});
return promise;
}

function getBoardCellMatrix() {
const gameBoardCellList = document.querySelectorAll(GAME_CELL_CLASS);
const gameBoardCellMatrix = [];
for (let i = 0; i < MATRIX_ROW_AMOUNT; i++) {
gameBoardCellMatrix[i] = [];
for (let j = 0; j < MATRIX_COLUMN_AMOUNT; j++) {
gameBoardCellMatrix[i][j] = gameBoardCellList[i*MATRIX_COLUMN_AMOUNT + j];
}
}
return gameBoardCellMatrix;
}

function isCellAlive(cell) {
return cell.dataset.isAlive === 'true';
}

function isButtonActive(button) {
return button.dataset.isActive === 'true';
}

function isTimerInProgress() {
return !!epochaTime.dataset.time && epochaTime.dataset.time !== '0';
}

function markCellAlive(cell) {
cell.style.backgroundColor = LIFE_COLOR;
cell.dataset.isAlive = 'true';
}

function markCellDeath(cell) {
cell.style.backgroundColor = '';
cell.dataset.isAlive = 'false';
}
91 changes: 91 additions & 0 deletions game/styles/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
* {
margin: 0;
padding: 0;
outline: 0;
color: black;
}

body {
width: 100%;
height: 100vh;
font-size: 16px;
}

.main {
position: relative;
display: block;
top: 50vh;
transform: translateY(-50%);
}

.epocha-time-container {
margin-left: 50%;
display: inline-block;
transform: translateX(-50%);
font-size: 1.4rem;
}

.game-board {
display: flex;
margin: auto;
margin-top: 2rem;
position: relative;
width: 60vh;
border: 1px solid black;
height: 60vh;
flex-wrap: wrap;
flex-direction: row;
}

.game-cell {
border: 1px solid black;
box-sizing: border-box;
width: calc(100%/6);
cursor: pointer;
}

.game-cell:hover {
background-color:coral
}

.control-panel {
margin-top: 4rem;
display: flex;
justify-content: center;
gap: 6rem;
}

.game-start {
background-color: transparent;
border: 1px solid black;
padding: 0.7rem 2.3rem;
outline: 0;
border-radius: 1rem;
cursor: pointer;
font-size: 1rem;
}

.game-end {
background-color: transparent;
border: 1px solid black;
padding: 0.7rem 2.3rem;
outline: 0;
border-radius: 1rem;
cursor: pointer;
font-size: 1rem;
}

.game-start:hover, .game-end:hover {
color: white;
background-color: black;
transition: all 0.3s ease-in-out;
box-shadow: 1px 1px 4px 2px gray;
}


@media screen and (orientation: portrait) {
.game-board {
width: 60vw;
height: 60vw;
}
}

0 comments on commit 29f0388

Please sign in to comment.