Как сделать игру крестики нолики
Перейти к содержимому

Как сделать игру крестики нолики

Создание игры «Крестики-нолики» при помощи TypeScript, React и Mocha

Представляем вам перевод статьи Josh Kuttler, опубликованной на blog.bitsrc.io. Узнайте, как создать приложение «Крестики-нолики», используя React и TypeScript.

Простая игра в крестики-нолики создана по модульному принципу и загружена на сайт Bit. Вы можете изменять компоненты моей игры и тестировать ее онлайн на Bit PlayGround при помощи NPM, Yarn или Bit. Для этого перейдите к моей коллекции компонентов.

Когда создаешь игры типа «Крестики-нолики» по модульному принципу, трудно найти причину, по которой компоненты UI могут снова когда-либо использоваться. Поэтому я сосредоточился в основном на игровых утилитах.

Для программирования я выбрал язык TypeScript — скомпилировал код при помощи TypeScript на сайте Bit. Затем воспользовался фреймворком Mocha для тестирования.

Чтобы установить компоненты из моего проекта, сначала настройте bit.dev в качестве реестра области (скопируйте и вставьте на своем устройстве). Это следует сделать только один раз! При дальнейшем использовании сайта Bit проводить повторную настройку не понадобится.

Затем установите компонент при помощи менеджеров пакетов Yarn или NPM:

Компонент «игра»

Компонент «игра» является основным компонентом моего приложения — он создан при помощи одного компонента Board и двух компонентов Prime React.

Я использовал компоненты Button и Input-text для экрана настройки — протестировать и посмотреть их код можно здесь.

Установите компоненты PrimeReact в свой проект:

После настройки параметров можно кликнуть на «Играть» и… играть!

Компонент Board

Компонент Board создает динамическую таблицу при помощи Props, устанавливает очередь для игроков и определяет победителя. Протестировать и посмотреть код можно здесь.

Компонент Square

Компонент Square — это обычная ячейка, которая получает значение с опциональным цветом и отправляет ивент компоненту Board при изменении значения. Протестировать и посмотреть код можно здесь.

Функция Empty cell

Функция Empty cell — это вспомогательная функция для функции Winner-calc, которая проверяет, есть ли пустые ячейки в таблице игры.

Bit позволяет увидеть документы компонента и результаты тестов:

Функция Winner calculation

Winner calculation — это функция, которая вычисляет победителя по горизонтальной, вертикальной и диагональной плоскостям.

Bit позволяет увидеть документы компонента и результаты тестов:

Проект доступен в моей коллекции на Bit и в моём репозитории GitHub.

Не стесняйтесь комментировать эту статью и подписывайтесь на мой Twitter.

Как написать игру крестики нолики?

user avatar

Всё ещё ищете ответ? Посмотрите другие вопросы с метками javascript или задайте свой вопрос.

Site design / logo © 2022 Stack Exchange Inc; user contributions licensed under cc by-sa. rev 2022.6.13.42356

Нажимая «Принять все файлы cookie», вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.

Крестики-нолики

В этой статье, приуроченной к онлайн-курсу Android Developer. Basic, мы напишем свою собственную небольшую игру «Крестики-нолики». Для работы потребуется установить Android Studio. Какая у вас ОС — не имеет значения, т.к. эта IDE работает на Windows, Ubuntu или Mac. Язык программирования будет Kotlin, т.к. сейчас он является официальным языком для программирования на Android. Во время написания статьи использовалась версия Android Studio 4.1.3

Создание проекта

Для начала необходимо создать новый проект. В нашем случае необходимо выбрать Fragment + ViewModel и нажать Next.

Далее в поле Name вводите название приложения, в Package name можете внести свое имя или ник как доменное имя, но с зада на перед. Это нужно для уникальности вашего приложения в маркете и на телефоне. Все остальное можно оставить по умолчанию как на картинке. Далее нажимаем Finish и ждем, когда проект соберется и мы сможем работать.

Нам нужно добавить зависимости которых нам не хватает в build.gradle модуля в раздел dependencies. А так же включить viewBinding для того, чтобы было удобна делать связь верстки с кодом (смотри изображение)

Верстка экрана

Теперь можно преступить к верстанию экрана. Для начала мы добавим только несколько вьюшек. Для того, чтобы не нужно было у каждой вьюшке проставлять отступы от края экрана, воспользуемся специальным виджетом `Guideline`. Он позволяет выставить границы, за которые будут «цепляться» вьюшки. Если у вас экран открылся в режиме `Design`, то я рекомендую перейти в режим `Split`. Так удобнее мне объяснять и можно будет копировать однотипные элементы. Чтобы добавить Guideline можно внутри элемента `ConstraintLayout` написать `<guideline` и студия подскажет нужный нам элемент, который мы сможем использовать. Или справа нажать на кнопку `Guidelines` и выбрать горизонтальный или вертикальный guideline. Давайте добавим вертикальный. У нас на макете сразу появится справа полоса. Продублируем элемент Gidline, скопировав его полностью и вставив 3 раза. В параметр android:id напишем @+id/ плюс название в зависимости от расположения (start, end, top, bottom). В параметре android:orientation поставим vertical/horizontal в зависимости от направления линии. И изменить параметр app:layout_constraintGuide_begin на app:layout_constraintGuide_end для элементов с id=end/bottom .

Теперь добавим отображение того, чей сейчас ход Х или О. Для этого добавим 3 ImageView. Но для начала, давайте создадим сами иконки Х и О, а так же ß для того, чтобы показывать чей ход.

Для этого справа в области project щелкните правой кнопкой мыши (ПКМ) выберите New → Vector Asset.

В появившемся окне щелкнуть по изображению в пункте Clip Art, найти по поиску back стрелку назад и нажать OK. Я выбрал, чтобы концы были скругленными, для этого во втором выпадающем списке выбрать Round. Вы можете посмотреть, как выглядят остальные и выбрать понравившийся. Размер я задал 64 в пункте Size, а цвет в Color: F44336. Вы же в палитре можете выбрать тот цвет, который Вам больше всего понравился. Название в пункте Name я указал ic_arrow, чтоб проще было использовать. Для иконок, принято название начинать с ic_ и в snake case, т.е. с подчеркиванием. И нажимаем Next → Finish. Теперь самостоятельно сделайте крестик и нолик. Для них я выбрал иконку close и panorama fish eye соответственно и выбрал цвет 454FCE, а названия дал ic_cross и ic_circle.

После всех этих действий, в разделе res → drawable появятся новые файлы с нашими иконками. Я рекомендую значение цвета из параметра android:tint перенести в параметр android:fillColor , а сам tint удалить. На некоторых китайских телефонах, отображение ведет себя не корректно и качество иконки ухудшается.

Теперь давайте вернемся к верстке и добавим 3 элемента ImageView. Для этого перейдем в режим Design (это как один из способов верстания экрана), найдем ImageView и перетащим его на макет. В появившемся экране выберем иконку ic_cross. В поле id напишем название cross. Таким же способом перетащим еще 2 элемента для ic_arrow и ic_circle и дадим им имя id direction и circle соответственно. Теперь выделим каждый элемент поочередно и потянув за верхний круг свяжем вьюшку с верхним guideline.

Теперь сделаем из них цепочку, чтоб они были посередине и склеены друг с другом. Для этого выделяем эти 3 элемента вышкой, нажимаем ПКМ → Chains → Create Horizontal Chain.

Затем снова ПКМ → Chains → Horizontal Chain Style → packed. Попробуйте другие стили и посмотрите, чем они отличаются друг от друга.

Теперь они склеились вместе и находятся посередине, как я и хотел.

Добавим еще один элемент Barrier, который служит для того, чтобы следующий элемент расположить после других, не зависимо от того, какого размера будет каждый. Нажмем на кнопку Guidelines → Add Horizontal Barrier.

И установим следующие значения:

У вас должно получиться что-то вроде этого.

Теперь осталось добавить последний элемент верстки это GridLayout. Но его нужно выбрать из подключенной библиотеки и выбрать androidx.gridlayout.widget.GridLayout. Его уже нужно добавить вручную.

Аргументы app:columnCount и app:rowCount отвечают за количество колонок и строк соответственно. Устанавливаем значение 3, т.к. поле у нас 3х3. Аргументы app:layout_constraintDimensionRatio отвечает за соотношение сторон. Для того, что бы у нас был квадрат нужно его выставить в 1:1, вот из-за этого аргументаи пришлось подключать библиотеку, т.к. на версиях Android 5 этот аргумент не работает. И для того, чтобы соотношение работало, необходимо установить android:layout_width и android:layout_height в 0dp. Аргументы app:layout_constraintXXX_toXXX отвечают за привязку нашей вьюшки к другим элементам. Ну и запишем id=»@+id/field» , а во внутрь добавим ImageView. Но так как их понадобиться 9 штук и чтобы у каждой вьюшке не прописывать одни и те же аргументы, можно создать стиль. Для этого щелкаем ПКМ по values → New → Values Resource File.

В появившемся окне, в поле File name указываем styles и нажимаем OK. В созданном файле сделаем следующую запись:

Аргументы layout_columnWeight и layout_rowWeight служат для установки весов вьюшек и чтобы они равными размерами распределялись по ширине и высоте соответственно. Для того, чтобы эти веса заработали, необходимо android:layout_width и android:layout_height установить в 0dp. А в android:background прописываем атрибут, который будет браться из темы и отображать нажатие на вьюшку. Теперь осталось подключить этот стиль к нашей вьюшке. Возвращаемся к нашей верстке и у созданной ImageView меняем на следующее:

style=»@style/CellStyle» – позволяет применить наш стиль для разных вьюшек, а tools:src=»https://otus.ru/journal/krestiki-noliki/@drawable/ic_cross» позволяет отображать картинку на макете, но когда проект будет создан и установлен на телефоне, то картинки не будет. Продублируем этот элемент, чтобы в общем счете их получилось 9. В итоге должно получиться следующее:

Соединяем верстку с кодом (Binding)

Теперь нужно перейти во фрагмент MainFragment и поправить то, что было сгенерировано студией и добавить binding, который связывает верстку с кодом.

А оставшийся метод onActivityCreated полностью удалите. Все элементы, которые студия подчеркнет, нужно импортировать с помощью подсказок.

Пишем код

Теперь давайте перейдем в класс MainViewModel и уберем сгенерированные TODO. Нам необходимо хранить состояния ячеек (пустая, крестик, нолик) и для этого создадим массив этих состояний потому, что в Gridlayout все ячейки хранятся списком. А для расчета того кто победил, будем переводить индекс в массиве в координаты [строка:колонка], но об этом позже.

Создадим enum class для хранения состояния. В нем будет храниться картинка и возможность по ней кликать, ведь когда мы уже поставили символ или игра закончилась, то уже кликать по ячейкам нельзя. Напишем следующее в этом же файле в самом низу за пределами `>`.

Создадим поле с массивом этих состояний, я назову ее matrix для простоты. Так же добавим поле для хранения текущего состояния, т.е. чей сейчас ход и создадим метод initGame для инициализации начального состояния игры при первом запуске приложения (init – вызывается при создании класса MainViewModel ) и при сбросе игры ( onReloadClick ).

Создадим метод обработки кликов по ячейкам и пропишем в нем для начала смену хода и сохранение в массиве состояние ячейки. Так же создадим поле с LiveData, для того, чтобы на экране смогли отображать кто сейчас ходит.

Здесь mCurrentMove является изменяемым и он закрыт для обращения из вне класса, а currentMove является не изменяемым и он доступен снаружи класса.

Давайте подпишемся на изменение хода и будем отображать на экране чей сейчас ход. Для этого перейдем в MainFragment , переопределим метод onViewCreated и добавим в него следующее.

binding.field.forEachIndexed – к каждой вьюшке находящейся в GridLayout устанавливаем слушатель на клик.

binding.direction – это наша стрелочка на экране. Соберите проект и запустите его, посмотрите что происходит когда мы кликаем по ячейкам. Попробуйте убрать комментирование со строки со scaleY, а строку с rotation закомментировать или убрать и посмотреть что получилось.

Добавим отображение иконке в той ячейке, по которой мы кликнули. Чтобы не перерисовывать весь массив ячеек, а менять значение только той, по которой мы кликнули, будем передавать индекс ячейки и ее новое значение. Для этого добавим еще одно поле mCellStateByIndex, в которую будет передаваться индекс и новое состояние ячейки.

SingleLiveEvent – этот класс служит для того, чтобы при смене конфигурации (например, переворота экрана) не вызывалось снова событие на отрисовку вьюшки. Этот класс нужно создать отдельным файлом.

Добавим еще состояние нашей игры (запущена и остановлена), чтобы нельзя было кликать по ячейкам, когда игра завершилась и LiveData, в которой будет храниться состояние игры и список состояний ячеек

Создадим enum GameStatus и напишем его ниже CellState.

Добавим поле mStates и сделаем несколько изменений.

Создаем свою вьюшку и рисуем на ней сетку

Создадим новый файл. Для этого в правой части проекта щелкните ПКМ по ru.otus.tictactoe → New → Kotlin Class/File и введите название FieldView.

И напишите следующее в этот файл:

Обязательно нужно наследоваться от androidx.gridlayout.widget.GridLayout .

Еще нужно добавить в ресурсы в которых будет указан цвет и размер линии. Для этого ПКМ по res → New → Android Resource File.

В появившемся окне, в поле File name: написать values и нажать OK.

Появится новый файл values.xml. откройте его и напишите следующее:

Теперь ошибки должны исчезнуть.

Осталось поменять нашу вьюшку в верстке.

Вычисляем, кто выиграл

Для начала создадим sealed class с возможными вариантами выигрыша (по горизонтали, по вертикали, по диагонали и нет выигрыша).

Создадим методы расчета выигрышного положения.

Сбрасывание состояния игры

Чтобы постоянно не приходилось перезапускать игру, давайте сделаем сброс к начальному состоянию игры. Для этого добавим кнопку в тулбаре.

Добавьте иконку cached и назовите ее ic_reload. Добавьте ресурс, как это делали с цветом и размером, только в открывшемся окне, поменяйте Resource type на Menu и в File name напишите menu_reload. А в ресурсах strings добавьте название кнопки

Теперь осталось только добавить ее и реализовать обработку этой кнопки. Для этого переходим в MainFragment и добавляем следующее.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *