Представляем вам перевод статьи Josh Kuttler, опубликованной на blog.bitsrc.io. Узнайте, как создать приложение «Крестики-нолики», используя React и TypeScript.
Простая игра в крестики-нолики создана по модульному принципу и загружена на сайт Bit. Вы можете изменять компоненты моей игры и тестировать ее онлайн на Bit PlayGround при помощи NPM, Yarn или Bit. Для этого перейдите к моей коллекции компонентов.
Когда создаешь игры типа «Крестики-нолики» по модульному принципу, трудно найти причину, по которой компоненты UI могут снова когда-либо использоваться. Поэтому я сосредоточился в основном на игровых утилитах.
Для программирования я выбрал язык TypeScript — скомпилировал код при помощи TypeScript на сайте Bit. Затем воспользовался фреймворком Mocha для тестирования.
Чтобы установить компоненты из моего проекта, сначала настройте bit.dev в качестве реестра области (скопируйте и вставьте на своем устройстве). Это следует сделать только один раз! При дальнейшем использовании сайта Bit проводить повторную настройку не понадобится.
npm config set '@bit:registry' https://node.bit.dev
Затем установите компонент при помощи менеджеров пакетов Yarn или NPM:
npm i @bit/joshk.tic-tac-toe-game.game
yarn add @bit/joshk.tic-tac-toe-game.game
Компонент «игра»
Компонент «игра» является основным компонентом моего приложения — он создан при помощи одного компонента Board и двух компонентов Prime React.
Я использовал компоненты Button и Input-text для экрана настройки — протестировать и посмотреть их код можно здесь.
Установите компоненты PrimeReact в свой проект:
yarn add @bit/primefaces.primereact.inputtext
yarn add @bit/primefaces.primereact.button
После настройки параметров можно кликнуть на «Играть» и… играть!
Компонент Board
Компонент Board создает динамическую таблицу при помощи Props, устанавливает очередь для игроков и определяет победителя. Протестировать и посмотреть код можно здесь.
Компонент Square
Компонент Square — это обычная ячейка, которая получает значение с опциональным цветом и отправляет ивент компоненту Board при изменении значения. Протестировать и посмотреть код можно здесь.
Функция Empty cell
Функция Empty cell — это вспомогательная функция для функции Winner-calc, которая проверяет, есть ли пустые ячейки в таблице игры.
Bit позволяет увидеть документы компонента и результаты тестов:
/**
* @description
* check if 2d array have an empty cell
* @param {{Array.<string[]>}} matrix 2d array
* @param {number} rowsNum number of rows
* @param {number} colsNum number of columns
* @returns {boolean} return true if empty cell was found, and false if not.
* @example
* import haveEmptyCell from '@bit/joshk.tic-tac-toe-game.utils.have-empty-cell';
*
* const matrix = [
* ['X', 'O', 'X'],
* ['O', 'X', 'O'],
* ['O', 'X', 'O']
* ];
* const result = haveEmptyCell(matrix, 3, 3);
*
* export default result
* @example
* import haveEmptyCell from '@bit/joshk.tic-tac-toe-game.utils.have-empty-cell';
*
* const matrix = [
* ['X', 'O', 'X'],
* ['O', '', 'O'],
* ['O', 'X', 'O']
* ];
* const result = haveEmptyCell(matrix, 3, 3);
*
* export default result
* @example
* import haveEmptyCell from '@bit/joshk.tic-tac-toe-game.utils.have-empty-cell';
*
* const matrix = [
* ['X', 'O', 'X'],
* ['O', , 'O'],
* ['O', 'X', 'O']
* ];
* const result = haveEmptyCell(matrix, 3, 3);
*
* export default result
* @example
* import haveEmptyCell from '@bit/joshk.tic-tac-toe-game.utils.have-empty-cell';
*
* const matrix = [
* ['X', 'O', 'X'],
* ['O', null, 'O'],
* ['O', 'X', 'O']
* ];
* const result = haveEmptyCell(matrix, 3, 3);
*
* export default result
*/
function haveEmptyCell(matrix: Array<Array<string>>, rowsNum: number, colsNum: number): boolean {
let empty: boolean = false;
for (let x = 0; x < rowsNum; x++) {
for (let y = 0; y < colsNum; y++) {
const element: any = matrix[x][y];
if (!element) {
empty = true;
break;
}
}
if (empty)
break;
}
return empty;
}
export default haveEmptyCell
Функция Winner calculation
Winner calculation — это функция, которая вычисляет победителя по горизонтальной, вертикальной и диагональной плоскостям.
Bit позволяет увидеть документы компонента и результаты тестов:
/**
* @description
* check winner horizontal, vertical and diagonal
* @param {Array.<string[]>} matrix 2d array with X and O
* @param {number} rowsNum number of rows
* @param {number} colsNum number of columns
* @param {number} numToWin the number of matching to win
* @param {number} lastRow the row number of the square player click
* @param {number} lastCol the column number of the square player click
* @returns {string} return the winner, X or O or '' if no one win.
* @example
* import winnerCalc from '@bit/joshk.tic-tac-toe-game.utils.winner-calc';
*
* const matrix = [
* ['O', 'O', 'X'],
* ['O', 'X', ''],
* ['X', '', '']
* ];
* const result = winnerCalc(matrix, 3, 3, 3, 0, 2);
*
* export default result
*/
import haveEmptyCell from '../HaveEmptyCell'
function winnerCalc(matrix: Array<Array<string>>, rowsNum: number, colsNum: number, numToWin: number, lastRow: number, lastCol: number): string {
let winner: string = '';
let match: number = 0;
const lastValue: string = matrix[lastRow][lastCol];
//check Horizontal
for (let c = 0; c < colsNum; c++) {
let currentValue = matrix[lastRow][c];
if (currentValue === lastValue)
match++;
else match = 0;
if (match === numToWin) {
winner = lastValue;
break;
}
}
if (winner !== '')
return winner;
match = 0;
//check Vertical
for (let r = 0; r < rowsNum; r++) {
let currentValue = matrix[r][lastCol];
if (currentValue === lastValue)
match++;
else match = 0;
if (match === numToWin) {
winner = lastValue;
break;
}
}
if (winner !== '')
return winner;
//check diagonal top-left to bottom-right - include middle
match = 0;
for (let r = 0; r <= rowsNum - numToWin; r++)
{
let rowPosition = r;
for (let column = 0; column < colsNum && rowPosition < rowsNum; column++)
{
const currentValue = matrix[rowPosition][column];
if (currentValue === lastValue)
match++;
else match = 0;
if (match === numToWin)
{
winner = lastValue;
break;
}
rowPosition++;
}
if (winner !== '') break;
}
if (winner !== '')
return winner;
//check diagonal top-left to bottom-right - after middle
match = 0;
for (let c = 1; c <= colsNum - numToWin; c++)
{
let columnPosition = c;
for (let row = 0; row < rowsNum && columnPosition < colsNum; row++)
{
let currentValue = matrix[row][columnPosition];
if (currentValue === lastValue)
match++;
else match = 0;
if (match === numToWin)
{
winner = lastValue;
break;
}
columnPosition++;
}
if (winner !== '') break;
}
if (winner !== '')
return winner;
//check diagonal bottom-left to top-right - include middle
match = 0;
for (let r = rowsNum - 1; r >= rowsNum - numToWin - 1; r--)
{
let rowPosition = r;
for (let column = 0; column < colsNum && rowPosition < rowsNum && rowPosition >= 0; column++)
{
let currentValue = matrix[rowPosition][column];
if (currentValue === lastValue)
match++;
else match = 0;
if (match === numToWin)
{
winner = lastValue;
break;
}
rowPosition--;
}
if (winner !== '') break;
}
if (winner !== '')
return winner;
//check diagonal bottom-left to top-right - after middle
match = 0;
for (let c = 1; c < colsNum; c++)
{
let columnPosition = c;
for (let row = rowsNum - 1; row < rowsNum && row >= 0 && columnPosition < colsNum && columnPosition >= 1; row--)
{
console.log(`[${row}][${columnPosition}]`);
let currentValue = matrix[row][columnPosition];
if (currentValue === lastValue)
match++;
else match = 0;
if (match === numToWin)
{
winner = lastValue;
break;
}
columnPosition++;
}
if (winner !== '') break;
}
if (winner !== '')
return winner;
if(haveEmptyCell(matrix, rowsNum, colsNum) === false) {
winner = '-1';
}
return winner;
}
export default winnerCalc
Проект доступен в моей коллекции на Bit и в моём репозитории GitHub.
Не стесняйтесь комментировать эту статью и подписывайтесь на мой Twitter.
Автор: Plarium