Создание игры «Крестики-нолики» при помощи TypeScript, React и Mocha
Представляем вам перевод статьи 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 <>> matrix 2d array * @param rowsNum number of rows * @param colsNum number of columns * @returns 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>, 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 > matrix 2d array with X and O * @param rowsNum number of rows * @param colsNum number of columns * @param numToWin the number of matching to win * @param lastRow the row number of the square player click * @param lastCol the column number of the square player click * @returns 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, 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 rowPosition++; > if (winner !== '') break; > if (winner !== '') return winner; //check diagonal top-left to bottom-right - after middle match = 0; for (let c = 1; c 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(`[$][$]`); 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.
- разработка игр
- разработка приложений
- игры
- начинающим
- своими руками
- крестики-нолики
- крестики нолики
- typescript
- mocha
- react
- программирование игр
- программирование
- программирование для начинающих
Как сделать игру крестики нолики
«Крестики нолики» — простая, весёлая и интересная игра, в которую любят играть как дети, так и взрослые. Малыши 4-5 лет уже вполне смогут понять правила и смысл игры.
Обычно все играют в эту игру на бумаге, рисуя клеточки, крестики и нолики ручкой или карандашом. А можно ведь смастерить «крестики» и «нолики» своими руками из подручных материалов. Так играть в эту игру будет еще интереснее. Атмосфернее, так сказать. 😉 И в таком виде её можно даже взять с собой в дорогу. Играть в поезде или на даче. А дома она станет замечательной настольной игрой для всей семьи.
К тому же, эта игра — развивающая, как и все настольные игры. Она развивает логику и воображение, тренирует память и внимательность.
Как сделать игру «Крестики нолики» своими руками?
Нашу игру «Крестики нолики» мы сделали из крышек от бутылок из-под кефира и молока.
Сначала мы положили крышку на бумагу и обвели её карандашом. Получился круг. Сделали 10 таких кружочков. Разукрасили 5 из них желтым цветом, а 5 — красным. Черным маркером сверху нарисовали крестики и нолики. Получилось 5 ноликов и 5 крестиков.
Затем я вырезала наши кружочки, и прикрепила их к крышкам с помощью скотча.
Правила игры
Правила, думаю, помнят все. Но еще раз напомню. Играют 2 игрока. У одного — крестики, у другого — нолики. Ходят по очереди. Нужно поставить свою фигурку в свободную клеточку поля. Клеточек всего 9 (3 на 3).
Цель — поставить в один ряд 3 крестика или 3 нолика (по горизонтали, по вертикали или по диагонали), и не дать другому игроку сделать то же самое.
Хранение игры
Мы решили хранить наши «крестики» и «нолики» в мешочке. Так они не потеряются и надолго сохранятся. А еще в мешочке их удобно брать с собой.
С одной стороны мешочка я нарисовала клеточки для игры. Так мешочек превратился еще и в игровое поле. 😉
Другую сторону мешочка дочка украсила своим рисунком.
Красиво, правда ведь! Покажу крупным планом. Там пальмы, какие-то животные гуляют под ними. В небо летят воздушные шарики. Светит солнышко среди облаков. Летают птицы. Очень всё красочно нарисовано.
Вот такой замечательный мешочек получился, и замечательная настольная игра в нём. 😉
Видео «Игра «Крестики нолики» своими руками из крышек»:
До новых встреч и весёлых вам игр! 😉
Антонина Ефимова Весна,
автор блога «В гостях у Весны»
P.S. Я буду вам благодарна, если вы поделитесь данной статьей в социальных сетях, рассказав о ней своим друзьям.
- Наши настольные игры
- Кукольный театр на палочках от мороженого
- Кукольный домик своими руками
- Геометрик или доска с резинками своими руками
- Осенние поделки в садик
- Задания и сюрпризы по адвент-календарю
- Цветочное домино своими руками
- Календарь для ребенка своими руками
- Домик из картона со свечками
- Машины человечки
Как сделать игру крестики нолики
Java — это не только язык, это целая экосистема, включающая в себя средства разработки, платформу для запуска готовых приложений, огромный свод документации и активное сообщество. Одним из преимуществ Java на начальном этапе была кроссплатформенность (принцип — «написано один раз — запускается везде»). Дело в том, что программа на Java исполняется не на прямую процессором компьютера, а виртуальной машиной Java (JVM). Это позволяет абстрагироваться от многих нюансов конкретных платформ. Программу, написанную на Java, можно без изменений кода запускать на Windows, Linux, MacOS и других операционных системах (если, конечно, программа не использует специфичные для ОС функции). Кто застал начало 2000х, наверное помнит огромное количество мобильных телефонов (тогда еще они не были смартфонами), на каждом телефоне была по сути своя маленькая ОС, но при этом почти на каждом можно было запустить Java игру или приложение.
На сегодняшний день Java по-прежнему входит в топ языков для изучения, а Java как платформа — в топ используемых технологий в мире IT и смежных областях.
Создание проекта, первые шаги
Сегодня мы начнем изучать Java, причем сразу с примера игры Крестики-Нолики.
Итак, поехали. Надеюсь как установить java SDK ты уже разобрался. Мы будем писать код в IDE IntelliJ IDEA, но если у вас какая-то другая, например Eclipse, то разницы большой не будет.
Итак, создаем новый проект: нажимаем «create new project», выбираем java и щелкаем «next» до окна, где требуется ввести имя проекта, вводим TicTacToe (крестики-нолики). В некоторых случаях на этапе создания потребуется выбрать шаблон проекта, тогда смело выбирай что-либо похожее на JavaConsoleApplication.
После этого нажимаем «Finish». Idea немного подумает и сгенерирует нам проект с классом Main, в котором определена функция main().
Давайте разберемся, что здесь что. Слева открыто окно структуры проекта «Project», как мы видим в папке src в пакете com.company находится единственный java-файл нашей программы с именем Main. Справа показано его содержимое. Тут надо сделать небольшое отступление, дело в том, что в Java почти все представлено классами. В том числе и файлы программы описывают классы, причем имя файла должно совпадать с классом, который описывается в этом файле (например, у нас файл Main.java описывает класс Main). Пусть слово «класс» не смущает на первом этапе. Пока лишь отмечу, что для глубокого изучения Java так или иначе придется познакомиться с объектно-ориентированным подходом. В двух словах, класс можно воспринимать как шаблон, идею, а экземпляры класса — как реализацию этой идеи. Экземпляры класса называются его объектами. Например, вы можете представить идею стола (нечто, на что можно класть предметы), однако конкретных экземпляров такого стола огромное множество (на одной ножке, на четырех, круглые, квадратные, из разных материалов). Примерно так соотносятся классы и объекты в объектно-ориентированном программировании.
Внутри нашего класса Main описана функция main(), в Java с этой функции начинается исполнение программы, это точка входа в наше приложение. Сейчас там написан только автоматический комментарий (комментарии в Java начинаются с двух символов //). Попробуем кое-что добавить в наш код и проверить работоспособность приложения. Внутри функции main() допишем две строки:
Встроенная функция println() просто выводит на экран текстовую информацию. Запустим наше приложение (нажимаем shift-F10 или зеленый треугольник). Внизу, во вкладке run появится вывод нашей программы:
Функция main() отработала и закончилась, вместе с ней закончилась наша программа.
В игре пользователю конечно захочется взаимодействовать с программой более продвинутым способом, поэтому нам понадобится окно. Набираем внутри функции main() следующие строки:
Смысл большинства строк понятен из комментариев к ним, отдельно отмечу строку window.setLayout() — здесь устанавливается менеджер расположения, который будет применяется к компонентам, добавляемым в наше окно. Менеджер BorderLayout может располагать новые компоненты относительно сторон света (North(верх), West(слева), East(справа), South(низ)), Center (центр)). По умолчанию он располагает компоненты по центру. Подробнее с менеджерами расположения можно познакомиться в документации.
Теперь, если запустить нашу программу, мы увидим окно:
Пока в этом окне ничего нет. Создадим свой компонент, который и будет отрисовывать графику игры.
Свой компонент для рисования
Очевидно, что рисовать в консоли у нас не получится, нужен какой-то компонент для более продвинутого взаимодействия с пользователем. Для этой цели создадим еще один класс, назовем его TicTacToe. Щелкаем правой клавишей мыши на имени пакета приложения (в данном случае это com.company)
И в появившемся меню выбираем пункт «New» → «Java Class». В окне создания класса набираем его имя «TicTacToe» и нажимаем «Enter».
У нас в проекте появился еще один класс. В главное окно можно добавлять только объекты класса JComponent, кроме того, нам нужна область для рисования. Поэтому наследуем наш класс TicTacToe от JComponent. Ой сколько непонятных слов! Сейчас постараюсь пояснить. Наследование классов — это как создание уточненного шаблона на базе существующего. Например, есть класс Стол, описывающий идею стола вообще. Но нам очень часто приходится создавать столы на четырех ногах с деревянной столешницей, поэтому для удобства мы можем уточнить идею класса Стол и создать шаблон ДеревянныйСтол — он будет иметь все те же основные свойства, что и родительская идея, но зато часть свойств у него уже определены и понятны — это число ног, равное четырем и материал столешницы — дерево. С JComponent то же самое — данный класс реализует идею некоторого графического компонента пользовательского интерфейса. Такой компонент можно добавить в окно и он умеет как-то себя отрисовывать. Например, класс JButton — наследник класса JComponent, это компонент, который выглядит, как кнопка и умеет показывать анимацию клика мышкой. Здесь же, наследуя класс JComponent, мы создадим свой компонент, в котором сможем рисовать то, что нам нужно.
Итак дописываем extends JComponent в строку описания класса:
Слово extends говорит о том, что наш класс TicTacToe расширяет (наследует) класс JComponent.
У всех компонентов есть метод paintComponent(), который отвечает за их отрисовку. В параметры этого метода приходит объект Graphics, с помощью которого мы и будем рисовать то, что нам необходимо. Давайте переопределим метод paintComponent так, чтобы он рисовал окружность (это необязательно, но для проверки, что у нас все работает как надо, будет хорошим тоном это сделать).
Переопределим метод paintComponent() в классе TicTacToe следующим образом:
метод setColor() объекта graphics, как очевидно из названия, устанавливает цвет, которым мы будем рисовать, а метод drawOval(x ,y, w, h) — в общем случае рисует овал с координатами центра x, y, шириной — w и высотой h. В данном случае рисуется окружность, так как ширина и высота заданы одинаковые — 100. Замечу, что экранные координаты отсчитываются от левого верхнего угла. То есть 0 по вертикали находится вверху.
Чтобы проверить, как выглядит наш объект класса TicTacToe надо создать его экземпляр и добавить в главное окно в качестве дочернего компонента. Создание новых объектов в Java осуществляется с помощью ключевого слова new. Например, если у нас есть класс Стол и мы хотим создать объект этого класса (настоящий конкретный стол), то мы должны написать что-то такое: стол = new Стол(). Здесь «стол» имя, по которому мы будем обращаться к нашему объекту (взаимодействовать с ним), а Стол — имя класса, объект которого мы создаем. Замечу сразу, что вместо «стол» мы могли написать любое имя, например «fdgdgdgfd», но программисты обычно стараются давать «говорящие» имена объектам, чтобы код было легче читать. Чтобы создать экземпляр класса TicTacToe мы можем также написать game = new TicTacToe(), а потом добавить его в окно методом add().
Теперь код класса Main выглядит вот так:
Если теперь запустить нашу программу, то мы увидим окно с окружностью:
Ну что ж. Рисовать в базе мы научились. Время приступать к созданию игры.
Создание игрового поля
Вернемся к классу TicTacToe. Для начала необходимо нарисовать игровое поле, состоящее из девяти клеточек. Для этого давайте нарисуем две горизонтальные и две вертикальные линии на нашем поле. Чтобы это сделать, воспользуемся методом drawLine(x1,y1,x2,y2) объекта Graphics, который приходит к нам в метод paintComponent() в качестве параметра. Метод drawLine() рисует линию от точки с координатами x1,y1 до точки x2,y2. Давайте подумаем как нарисовать игровое поле.
Если мы разобьем высоту поля на три (у нас же три клетки в ряду), то получим высоту одной клетки (назовем ее dh). Узнать высоту всего компонента можно методом getHeight(). Значит, мы должны нарисовать первую горизонтальную линию от точки 0,dh до точки w, dh, где w — ширина поля. Но это только одна горизонтальная линия, вторую рисуем также, но координаты будут уже: начало — 0, 2*dh, конец w, 2*dh. По аналогии, если высота поля равна h, а ширина одной клетки равна dw, то вертикальные линии рисуются в координатах dw,0 — dw,h и dw*2, 0 — dw*2, h.
Теперь давайте немного поговорим о переменных. Если помните — в алгебре за буквой могло скрываться какое-то значение, например выражение x = 2*a, подразумевало, что на место буквы а можно подставить любое значение и вычислить x. Примерно то же самое происходит с переменными в программировании. Имя переменной (идентификатор) сопоставлен с некоторым значением и «хранит» его «в себе» (на самом деле, с объектами классов все несколько сложнее, там мы имеем дело со ссылками, но это пока за рамками данного материала). Помимо этого, в программах есть разные типы данных. Наверное, вы согласитесь, что строку и целое число надо хранить в памяти как-то по-разному? Даже целые и дробные числа требуют разного подхода, поэтому в программах данные соответствуют определенным типам. В нашем примере нам уже понадобились значения ширины и высоты ячейки игрового поля dw и dh. Чтобы вычислить и сохранить их значения в памяти, воспользуемся следующими выражениями:
Здесь int — означает тип данных «целое число». Выражение int a = 10 объявляет переменную с именем a и задает ей сразу значение 10. В нашем примере создаются четыре переменных, значения w и h получаются из методов самого компонента TicTacToe, а dw и dh вычисляются. Обратите внимание, что при делении w / 3 получается целый тип данных. В Java, как и во многих других языках, деление целого на целое дает в результате целое. При этом дробная часть просто отбрасывается (округления нет). Заметьте, что здесь не используется слово «new», так как создаются не объекты, а переменные простых (скалярных) типов данных, в данном случае типа int.
Мы могли бы уже написать код для рисования всех линий, но мы же программисты, а программисты любят все упрощать, правда для этого они пишут много дополнительного кода. Представим, что у нас было бы поле не 3 на 3 клетки а, например, 15х15. Как бы мы его разлиновали? Вручную набирать код для рисования 28 линий это уж слишком. К счастью, во всех языках программирования (привет ассемблер) есть конструкции, позволяющие повторить заданное число раз тот или иной участок кода — циклы. Разберем, как автоматизировать рисование линий, заметим, что все горизонтальные линии содержат одни и те же значения координат по горизонтали (от начала до конца ширины игрового поля), при этом их координаты по вертикали различаются на dh. У первой линии высота dh, у второй 2*dh, и так далее. Для вертикальных линий рассуждения аналогичны, только в рассуждении приведенном выше надо поменять вертикальные и горизонтальные координаты местами.
Попробуем рисовать линии с помощью цикла, в классе TicTacToe создадим свой метод с названием drawGrid(), он будет у нас отвечать за рисование линий сетки игрового поля:
Еще раз пробежимся по коду. Первые четыре строки метода — необходимые нам значения ширины, высоты игрового поля и ширины, высоты одной ячейки. Цикл начинается с ключевого слова for, в скобках после него указывается переменная, которая будет счетчиком (у нас она еще и объявляется сразу int i = 1), условие при ложности которого цикл прервется и выражение изменяющее переменную-счетчик (i++ увеличивает i каждую итерацию цикла на единицу). Внутри цикла каждую итерацию рисуются очередные горизонтальная и вертикальная линии поля.
Добавим вызов нашего метода drawGrid() в метод отрисовки всего компонента paintComponent():
#3 – Создание игры «Крестики Нолики»
В этом уроке разработаем небольшую игру на базе языка Джава. Вы создадите игру Крестики Нолики. В игре будет графический интерфейс и набор стандартных действий.
Видеоурок
JavaFx позволяет создавать приложения с любым необходимым дизайном. Помимо стандартных приложений вы можете создавать небольшие игры. Такие игры не смогут похвастаться невероятной графикой или действиями. Зато они будут описаны на основе Java и будут содержать все основные механики.
В уроке мы создали игру Крестики Нолики. К игре можно добавить больше стилей, тем самым сделав ее более привлекательной для конечного игрока.