From 3e381bc3c2325f938c3b148f79877c92fcdde03f Mon Sep 17 00:00:00 2001 From: xirce Date: Fri, 1 Oct 2021 06:00:31 +0500 Subject: [PATCH 1/3] tasks 1-10 done. --- .idea/.gitignore | 5 ++ .idea/1-tic-tac-toe.iml | 12 +++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 ++ index.js | 190 +++++++++++++++++++++++++++++++++++----- 5 files changed, 201 insertions(+), 20 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/1-tic-tac-toe.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/1-tic-tac-toe.iml b/.idea/1-tic-tac-toe.iml new file mode 100644 index 0000000..0c8867d --- /dev/null +++ b/.idea/1-tic-tac-toe.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..fac583c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/index.js b/index.js index 7553909..055a0aa 100644 --- a/index.js +++ b/index.js @@ -2,65 +2,215 @@ const CROSS = 'X'; const ZERO = 'O'; const EMPTY = ' '; +const RED = '#F00'; + const container = document.getElementById('fieldWrapper'); +let field; +let currentSymbol; +let isGameOver; +let winner; +let ai; + +function Cell(symbol, x, y) { + this.symbol = symbol; + this.x = x; + this.y = y; +} + +function Field(size) { + this.source = []; + this.size = size; + this.count = 0; + + this.getRow = function (index) { + return this.source[index]; + } + + this.getColumn = function (index) { + let column = []; + for (let row = 0; row < this.size; row++) { + column[row] = this.source[row][index]; + } + return column; + } + + this.getDiagonal = function (row, col) { + let diagonal = []; + + if (row === col) { + diagonal.push([]); + for (let rowIndex = 0; rowIndex < this.size; rowIndex++) { + diagonal[diagonal.length - 1][rowIndex] = this.source[rowIndex][rowIndex]; + } + } + + if (row + col === this.size - 1) { + diagonal.push([]); + for (let rowIndex = 0; rowIndex < this.size; rowIndex++) { + diagonal[diagonal.length - 1][rowIndex] = this.source[rowIndex][this.size - rowIndex - 1]; + } + } + + return diagonal; + } +} + +function AI() { + this.makeSimpleMove = function () { + let emptyCells = getEmptyCells(); + let selectedCell = emptyCells[Math.floor(Math.random() * emptyCells.length)]; + tryMakeMove(selectedCell.x, selectedCell.y); + } + + function getEmptyCells() { + let emptyCells = []; + field.source.forEach(row => emptyCells.push(...row.filter(cell => cell.symbol === EMPTY))); + return emptyCells; + } +} + startGame(); addResetListener(); -function startGame () { - renderGrid(3); +function startGame() { + let dimension = +prompt('Введите размерность поля', 3); + + createField(dimension); + ai = new AI(); + currentSymbol = CROSS; + isGameOver = false; + winner = null; + renderGrid(dimension); +} + +function createField(dimension) { + field = new Field(dimension); + for (let row = 0; row < dimension; row++) { + field.source[row] = []; + for (let col = 0; col < dimension; col++) { + field.source[row][col] = new Cell(EMPTY, row, col); + } + } } -function renderGrid (dimension) { +function renderGrid(dimension) { container.innerHTML = ''; - for (let i = 0; i < dimension; i++) { + for (let rowIndex = 0; rowIndex < dimension; rowIndex++) { const row = document.createElement('tr'); - for (let j = 0; j < dimension; j++) { + for (let colIndex = 0; colIndex < dimension; colIndex++) { const cell = document.createElement('td'); - cell.textContent = EMPTY; - cell.addEventListener('click', () => cellClickHandler(i, j)); + cell.textContent = field.source[rowIndex][colIndex].symbol; + cell.addEventListener('click', () => cellClickHandler(rowIndex, colIndex)); row.appendChild(cell); } container.appendChild(row); } } -function cellClickHandler (row, col) { - // Пиши код тут +function updateGame(row, col) { + if (isGameOver) { + return; + } + + field.source[row][col].symbol = currentSymbol; + field.count++; + renderSymbolInCell(currentSymbol, row, col); + + if (field.count === field.size * field.size) { + isGameOver = true; + } + + if (tryFindWinner(row, col) || isGameOver) { + announceWinner(); + } + + switchCurrentSymbol(); +} + +function tryFindWinner(rowIndex, colIndex) { + let symbol = field.source[rowIndex][colIndex].symbol; + return isWinningLine(field.getRow(rowIndex), symbol) + || isWinningLine(field.getColumn(colIndex), symbol) + || field.getDiagonal(rowIndex, colIndex).some(diagonal => isWinningLine(diagonal, symbol)); +} + +function isWinningLine(cells, symbol) { + if (cells.every(cell => cell.symbol === symbol)) { + isGameOver = true; + winner = symbol; + paintCells(cells, RED); + return true; + } + + return false; +} + +function paintCells(cellsInfo, color) { + cellsInfo.forEach(cell => findCell(cell.x, cell.y).style.backgroundColor = color); +} + +function announceWinner() { + switch (winner) { + case CROSS: + alert('Крестики победили'); + break; + case ZERO: + alert('Нолики победили'); + break; + default: + alert('Победила дружба'); + break; + } +} + +function cellClickHandler(row, col) { + tryMakeMove(row, col); + + ai.makeSimpleMove(); +} + +function tryMakeMove(row, col) { + if (isGameOver || field.source[row][col].symbol !== EMPTY) { + return; + } + console.log(`Clicked on cell: ${row}, ${col}`); + updateGame(row, col); +} - /* Пользоваться методом для размещения символа в клетке так: - renderSymbolInCell(ZERO, row, col); - */ +function switchCurrentSymbol() { + currentSymbol = currentSymbol === CROSS ? ZERO : CROSS; } -function renderSymbolInCell (symbol, row, col, color = '#333') { +function renderSymbolInCell(symbol, row, col, color = '#333') { const targetCell = findCell(row, col); targetCell.textContent = symbol; targetCell.style.color = color; } -function findCell (row, col) { +function findCell(row, col) { const targetRow = container.querySelectorAll('tr')[row]; return targetRow.querySelectorAll('td')[col]; } -function addResetListener () { +function addResetListener() { const resetButton = document.getElementById('reset'); resetButton.addEventListener('click', resetClickHandler); } -function resetClickHandler () { - console.log('reset!'); +function resetClickHandler() { + startGame(); } /* Test Function */ + /* Победа первого игрока */ -function testWin () { +function testWin() { clickOnCell(0, 2); clickOnCell(0, 0); clickOnCell(2, 0); @@ -71,7 +221,7 @@ function testWin () { } /* Ничья */ -function testDraw () { +function testDraw() { clickOnCell(2, 0); clickOnCell(1, 0); clickOnCell(1, 1); @@ -84,6 +234,6 @@ function testDraw () { clickOnCell(2, 2); } -function clickOnCell (row, col) { +function clickOnCell(row, col) { findCell(row, col).click(); } From 2687fc5c00b62393a29ccdde73d37093f8ef976e Mon Sep 17 00:00:00 2001 From: xirce Date: Fri, 1 Oct 2021 13:54:33 +0500 Subject: [PATCH 2/3] ai bug fixed. --- index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 055a0aa..a5bcd40 100644 --- a/index.js +++ b/index.js @@ -166,19 +166,21 @@ function announceWinner() { } function cellClickHandler(row, col) { - tryMakeMove(row, col); - - ai.makeSimpleMove(); + if (tryMakeMove(row, col)) { + ai.makeSimpleMove(); + } } function tryMakeMove(row, col) { if (isGameOver || field.source[row][col].symbol !== EMPTY) { - return; + return false; } console.log(`Clicked on cell: ${row}, ${col}`); updateGame(row, col); + + return true; } function switchCurrentSymbol() { From 662095c701646e27d6669b5f6aec3679de2fd0b1 Mon Sep 17 00:00:00 2001 From: xirce Date: Thu, 7 Oct 2021 17:07:31 +0500 Subject: [PATCH 3/3] refactoring. --- .idea/codeStyles/codeStyleConfig.xml | 5 ++ index.js | 87 +++++++++++++++++----------- 2 files changed, 57 insertions(+), 35 deletions(-) create mode 100644 .idea/codeStyles/codeStyleConfig.xml diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/index.js b/index.js index a5bcd40..f2e65c4 100644 --- a/index.js +++ b/index.js @@ -8,12 +8,14 @@ const container = document.getElementById('fieldWrapper'); let field; let currentSymbol; -let isGameOver; let winner; let ai; +let successfulMovesCount; -function Cell(symbol, x, y) { - this.symbol = symbol; +const isGameOver = () => winner || successfulMovesCount === field.size * field.size; + +function Cell(content, x, y) { + this.content = content; this.x = x; this.y = y; } @@ -21,7 +23,17 @@ function Cell(symbol, x, y) { function Field(size) { this.source = []; this.size = size; - this.count = 0; + + this.init = function () { + for (let row = 0; row < this.size; row++) { + this.source.push([]); + for (let col = 0; col < this.size; col++) { + this.source[row].push(null); + } + } + }; + + this.init(); this.getRow = function (index) { return this.source[index]; @@ -60,12 +72,12 @@ function AI() { this.makeSimpleMove = function () { let emptyCells = getEmptyCells(); let selectedCell = emptyCells[Math.floor(Math.random() * emptyCells.length)]; - tryMakeMove(selectedCell.x, selectedCell.y); + clickOnCell(selectedCell.x, selectedCell.y); } function getEmptyCells() { let emptyCells = []; - field.source.forEach(row => emptyCells.push(...row.filter(cell => cell.symbol === EMPTY))); + field.source.forEach(row => emptyCells.push(...row.filter(cell => cell.content === EMPTY))); return emptyCells; } } @@ -79,16 +91,15 @@ function startGame() { createField(dimension); ai = new AI(); currentSymbol = CROSS; - isGameOver = false; winner = null; + successfulMovesCount = 0; renderGrid(dimension); } function createField(dimension) { field = new Field(dimension); - for (let row = 0; row < dimension; row++) { - field.source[row] = []; - for (let col = 0; col < dimension; col++) { + for (let row = 0; row < field.size; row++) { + for (let col = 0; col < field.size; col++) { field.source[row][col] = new Cell(EMPTY, row, col); } } @@ -101,7 +112,7 @@ function renderGrid(dimension) { const row = document.createElement('tr'); for (let colIndex = 0; colIndex < dimension; colIndex++) { const cell = document.createElement('td'); - cell.textContent = field.source[rowIndex][colIndex].symbol; + cell.textContent = field.source[rowIndex][colIndex].content; cell.addEventListener('click', () => cellClickHandler(rowIndex, colIndex)); row.appendChild(cell); } @@ -110,43 +121,49 @@ function renderGrid(dimension) { } function updateGame(row, col) { - if (isGameOver) { + if (isGameOver()) { return; } - field.source[row][col].symbol = currentSymbol; - field.count++; + field.source[row][col].content = currentSymbol; renderSymbolInCell(currentSymbol, row, col); - if (field.count === field.size * field.size) { - isGameOver = true; - } - - if (tryFindWinner(row, col) || isGameOver) { + if (tryFindWinner(row, col) || isGameOver()) { + winner = currentSymbol; announceWinner(); } switchCurrentSymbol(); + successfulMovesCount++; } function tryFindWinner(rowIndex, colIndex) { - let symbol = field.source[rowIndex][colIndex].symbol; - return isWinningLine(field.getRow(rowIndex), symbol) - || isWinningLine(field.getColumn(colIndex), symbol) - || field.getDiagonal(rowIndex, colIndex).some(diagonal => isWinningLine(diagonal, symbol)); -} - -function isWinningLine(cells, symbol) { - if (cells.every(cell => cell.symbol === symbol)) { - isGameOver = true; - winner = symbol; - paintCells(cells, RED); + let symbol = field.source[rowIndex][colIndex].content; + let winningLine = findWinningLine(symbol, + field.getRow(rowIndex), + field.getColumn(colIndex), + ...field.getDiagonal(rowIndex, colIndex)); + + if (winningLine) { + paintCells(winningLine, RED); return true; } return false; } +function findWinningLine(symbol, ...lines) { + let winningLine; + for (let line of lines) { + if (line.every(cell => cell.content === symbol)) { + winningLine = line; + break; + } + } + + return winningLine; +} + function paintCells(cellsInfo, color) { cellsInfo.forEach(cell => findCell(cell.x, cell.y).style.backgroundColor = color); } @@ -166,21 +183,20 @@ function announceWinner() { } function cellClickHandler(row, col) { - if (tryMakeMove(row, col)) { + tryMakeMove(row, col); + if (successfulMovesCount % 2 !== 0) { ai.makeSimpleMove(); } } function tryMakeMove(row, col) { - if (isGameOver || field.source[row][col].symbol !== EMPTY) { - return false; + if (isGameOver() || field.source[row][col].content !== EMPTY) { + return; } console.log(`Clicked on cell: ${row}, ${col}`); updateGame(row, col); - - return true; } function switchCurrentSymbol() { @@ -205,6 +221,7 @@ function addResetListener() { } function resetClickHandler() { + console.log('reset!'); startGame(); }