JavaRush /Java блог / Java-марафон 2.0 (Хрестики-нулики) /Конспект до Java-марафону: пишемо гру “Хрестики-нулики”
Hanna Moruga
41 рівень
Киев

Конспект до Java-марафону: пишемо гру “Хрестики-нулики”

Стаття з групи Java-марафон 2.0 (Хрестики-нулики)

Загальна інформація про проєкт на Java-марафоні

Знайомство з платформою JavaRush та розділом “Ігри”

Усі завдання ти будеш розв’язувати (писати код) на платформі JavaRush. Проєкт із написання гри для твоєї зручності поділено на 24 частини — підзавдання:
  • Кожне підзавдання — реалізація конкретної дії/логіки;
  • Правильність кожного завдання можна перевірити автоматично, і за секунду отримати результат за допомогою валідатора JavaRush — віртуального помічника в онлайн-плагіні для написання коду;
  • Якщо у твоєму коді щось не так, валідатор підкаже, які саме умови завдання не реалізовані у рішенні.

Огляд розділу “Ігри” та проєкту, який ти створиш

Щоб почати роботу над проєктом, відкрий розділ “Ігри” та натисни на картку з грою “Хрестики-нулики”: Конспект до Java-марафону: пишемо гру “Хрестики-нулики” - 1Після цього ти побачиш опис гри та відеоемуляцію, як вона має виглядати готовою. Щоб перейти до написання проєкту, натисни на кнопку “Написати своє розв’язання”. Після цього знизу з’явиться вікно з вебплагіном JavaRush (webIDE — онлайн-версія середовища розробки): Конспект до Java-марафону: пишемо гру “Хрестики-нулики” - 2Це наш онлайн-симулятор, який дозволяє писати власний код, коригувати його, надсилати на перевірку та отримувати результат того, чи правильно написане твоє рішення. У полі зправа міститься опис та умови до виконання завдання. Спочатку уважно прочитай умови завдання, а потім, у полі зліва, починай працювати з кодом!

Основні функції вебплагіну JavaRush

Конспект до Java-марафону: пишемо гру “Хрестики-нулики” - 3
  1. Перевірка — перевірка валідатором правильності твого рішення. Натискаєш на цю кнопку — отримуєш результат.
  2. Допомога — набір різних функцій для допомоги з кодом:
    • Допомога спільноти — перейти до обговорення цієї задачі в розділі “Допомога”: поставити своє питання чи скористатися інформацією з обговорень, які вже існують. Втім, на Java-марафоні радимо тобі обговорювати свої рішення у ком’юніті чаті в Телеграмі, оскільки гра “Хрестики-нулики” розроблена спеціально під марафон, і її пишуть лише його учасники.
    • Правильне розв’язання — можливість подивитися, як виглядає правильне рішення задачі. Радимо використовувати лише у винятковому випадку. Якщо уважно дивитися заняття з ментором, все вийде і без підказок!
    • За допомогою кнопки Повертутий мій код ти можеш повернутися до власного рішення, без підказок;
    • Обнулити розв’язання — скинути написаний код і почати спочатку.
  3. Запуск — запуск коду без перевірки рішення.
  4. Аналіз коду — можливість отримати рекомендації щодо покращення твого коду.
Після того, як ти напишеш рішення, яке пройде перевірку, ти зможеш перейти до наступного підзавдання у проєкті з написання гри.

Як здавати домашнє завдання

Усі виконані завдання перевірятиме віртуальний помічник — валідатор, вбудований в онлайн-інструмент для кодингу на сайті JavaRush. Він надаватиме відповідь за секунди. Втім, нам теж необхідно спостерігати за твоїми успіхами на марафоні. Щоб куратори марафону зарахували твоє завдання і у тебе була можливість позмагатися за подарунки від JavaRush, надсилай у Телеграм-бот до дедлайну (17:00 наступного дня після заняття) скріншот, на якому добре видно:
  1. Вікно вебплагіну JavaRush
  2. Номер завдання
  3. Успішний результат перевірки
Усе, як на цьому скріншоті: Конспект до Java-марафону: пишемо гру “Хрестики-нулики” - 4

Короткий вступ до Java-програмування

Основні поняття мови Java, які тобі буде корисно знати

ООП — об’єктно-орієнтоване програмування. Java – об’єктно-орієнтована мова. Це означає: все, що створюється в програмі, має бути створено у вигляді об’єкту. Програма — це набір (список) команд. Спочатку виконується перша команда, потім друга, третя тощо. Коли всі команди виконані, програма завершується. Програми на мові Java виконує JVM (Java Virtual Machine — віртуальна машина Java). JVM — це спеціальна програма, яка вміє виконувати програми, написані на мові Java.

Основні принципи написання команд

Принцип перший: в мові Java кожну команду заведено писати з нового рядка. В кінці команди ставиться крапка з комою:

System.out.println("Привіт, майбутній програміст");
Принцип другий: програма не може просто складатися з команд. Команди мови Java повинні знаходитися всередині методів, а методи — всередині класів. Загальну ієрархію можна описати так: Java-програми складаються з класів, класи містять методи, а методи — команди.

Важливі принципи форматування коду

  • Не забувай ставити крапку з комою ; в кінці команди, яку ти створюєш
  • У кожної дужки чи лапки, що відкриває: (, {, , має бути дужка чи лапка, що закриває: ), },
  • Якщо твій код підсвічується червоним, це означає, що в ньому є помилка — придивися уважніше
  • Використовуй автоматичні підказки та автодоповнення — це допоможе тобі писати код правильно
  • Пам’ятай про те, що твої назви змінних, методів та класів мають бути читабельними, тобто з назви має бути зрозуміло, що вони роблять та для чого призначені

Конспект до заняття #1: створюємо заготовку гри

Зміст заняття

  • Створюємо ігрову сітку 3х3 (метод initialize())
  • Створюємо ігрову модель та її відображення +
  • Ініціалізуємо змінні, в яких гра зберігатиме свої стани (методи startGame())
  • Реалізуємо відображення клітинки (метод updateCellView())
  • Реалізуємо відображення всіх ячейок масиву model на ігровому полі (updateView())
  • Прописуємо перехоплення кліку мишкою (метод onMouseLeftClick())
У розробці гри Tic Tac Toe ми працюватимемо з публічним (public) класом TicTacToe, який успадковано від класу Game. Це клас, який написаний розробниками JavaRush, і в ньому є набір методів, які допоможуть тобі з написанням власної гри.

1. Створюємо ігрову сітку

Отже, нам потрібно зробити ігрове поле 3х3. Це можна зробити за допомогою виклику методу setScreenSize(ширина, висота);. Цей код пишеться в методі, з якого починається наша гра: public void initialize();. Де:
  • public — означає публічний метод, який може використовуватися за межами нашого класу
  • void – значення, яке вказує на те, що метод нам нічого не повертає
Для початку потрібно створити цей метод, а вже в ньому викликати метод setScreenSize().

2.1 Створюємо ігрову модель та її відображення

Ігри такого плану складаються з двох частин — модель та уявлення. Уявлення – це те, що бачить гравець (наше ігрове поле). Модель — це всі дані, які зберігають стан гри. Тож окрім ігрового поля нам потрібно зробити модель — масив, де будуть зберігатися дані. У нашому випадку — квадратний масив.
  • private — означає приватний метод, який буде використовуватися лише в межах нашого класу
  • [] — квадратні дужки позначають квадратний масив
У нашому двомірному масиві будуть використовуватися такі цифри для позначення того, що відбувається на ігровому полі:
  • 0 — пусто (порожня клітинка)
  • 1 — х (хід першого гравця, який ставить хрестик)
  • 2 — 0 (хід другого гравця, який ставить нулик)

2.2 Фіксуємо поточного гравця

Після моделі потрібно створити змінну private int currentPlayer, в якій будемо запам’ятовувати, який зараз гравець ходить (1 — перший гравець, 2 — другий).

3. Ініціалізуємо змінні, в яких гра зберігає свої стани

Пишемо метод для ініціалізації моделі: public void startGame():
  • щоб заповнити масив нулями, використаємо два вкладених цикли for
  • змінній currentPlayer надайте значення 1 (1 — хід першого гравця)
Ігрове поле називається представленням, або view. Отже, public void updateView() — метод для оновлення нашого представлення (ігрового поля). Метод startGame() потрібно десь викликати. Додайте виклик методу startGame() до кінця методу initialize().

4. Реалізуємо відображення клітинки

Тепер, після оновлення, потрібно відобразити у правильній ячейці (з координатами х, у) правильне значення (після ходу гравця). Для цього створимо метод: public void updateCellView(). setCellValue — метод для відображення значення в ячейці, вже написаний в ігровому рушії JavaRush. Логіка роботи методу updateCellView(int x, int y, int value):
  • Якщо value дорівнює 0, у клітинці треба відобразити порожній рядок — " " (пробіл)
  • Якщо value дорівнює 1, у клітинці треба відобразити "X" (Великий латинський ікс)
  • Якщо value дорівнює 2, у клітинці треба відобразити нолик "О" (велика латинська).

5. Реалізуємо відображення всіх ячейок масиву model на ігровому полі

Створи метод public void updateView() для відображення всіх ячейок масиву model на ігровому полі. Додай його виклик до кінця методу initialize(). Всередині методу updateView() потрібно пройтися всіма ячейками масиву model і відобразити значення кожної з них на екрані:
  • Для цього потрібно написати два вкладені цикли, які проходять по всіх осередках масиву model
  • За допомогою виклику метод updateCellView(x, y, value) відобрази значення кожної ячейки на екрані

6. Додаємо обробку натискань мишкою

Нам потрібен метод onMouseLeftClick(). Цей метод буде викликатися, коли користувач клікатеме на полі лівою кнопкою мишки, і передаватимуться координати x, y ігрового поля, на які клікнув користувач. Всередині методу onMouseLeftClick() потрібно зробити 3 речі:
  • Записати в ячейку model[x][y] значення currentPlayer.
  • Відобразити модель на екрані, викликавши метод updateView().
  • Переключити поточного гравця. Змінити 1 на 2, а 2 на 1.
updateView — метод, завдяки якому зміни, внесені в моделі, відображатимуться на екрані — в ігровому полі.

Домашнє завдання

  • Відтвори все, що зробив ментор на відео заняття — напиши шість методів, які допоможуть створити заготовку гри у підзавданнях 1-6.
  • Самостійно виконай підзавдання №7. Розфарбуй Х в червоний, а О — у синій кольори. Використовуй для цього метод setCellValueEx().

Конспект до заняття #2: додаємо ходи по черзі

Щоб продовжити роботу над проєктом, відкрий розділ “Ігри” та натисни на картку з грою “Хрестики-нулики”. Якщо у тебе є активний обліковий запис, твій прогрес у написанні гри зберігається. Просто вікрий картку з грою та продовжуй роботу над завданнями.

Зміст заняття

  • Прописуємо перевірку того, чи є знак у певній клітинці (перевірка у методі onMouseLeftClick())
  • Фіксуємо статус завершення гри (isGameStopped(), startGame())
  • Прописуємо умову, що не можна робити клік, коли гру завершено (перевірка у методі onMouseLeftClick())
  • Робимо перевірку виграшу (метод checkWin())
  • Прописуємо повідомлення про перемогу в грі

1. Перевіряємо, чи є в клітинці певний знак

Почнемо з того, що заборонимо ставити хрестики/нулики до клітинки, в якій щось вже є. Для цього потрібно додати перевірку в нашому методі onMouseLeftClick() за допомогою умовного оператора if.

2. Фіксуємо кінець гри та забороняємо гравцеві робити хід після цього

Також потрібно заборонити гравцеві робити хід, якщо гра вже скінчилася. Для цього нам, звісно, потрібен статус завершення гри. Для цього ми використовуємо boolean (логічну) змінну: private boolean isGameStopped. Стартовим значенням для неї буде false. Перевірку того, чи завершена гра, ми маємо додати до методу onMouseLeftClick(). Якщо гра зупинена — перерви виконання методу onMouseLeftClick() виконавши оператор return.

3. Робимо перевірку виграшу

Щоб дізнатися, чи закінчилася гра, додаємо метод перевірки, чи є переможець: public boolean checkWin(), який приймає координати клітинки, в яку поставили знак (Х або О) , і номер гравця, що походив. Метод boolean (логічний), оскільки він має перевіряти, чи є переможець, чи ні. У першому випадку має повертати true, у другому — false. Його потрібно написати після методу для ходу поточного гравця, перед передачею ходу до наступного гравця.

4. Прописуємо повідомлення про перемогу в грі

Для початку в кінці методу onMouseLeftClick() потрібно перед перемиканням номера гравця додати таку перевірку: якщо є гравець, який виграв, то а) закінчити гру, б) відобразити переможця:
  • Якщо checkWin() повернув true, то потрібно встановити змінну isGameStopped значення true
  • Відобразити переможця за допомогою виклику методу showMessageDialog()
  • Завершити виконання методу
Далі створюємо метод, який виводитиме повідомлення в разі перемоги одного з гравців: showMessageDialog(). У дужках ми можемо встановити текст повідомлення та його колір. Використовуй такі параметри:
  • cellColor — колір діалогового вікна
  • text — текст повідомлення
  • textColor — колір тексту
  • textSize — розмір тексту у відсотках від висоти вікна

Домашнє завдання

  1. Відтвори все, що зробив ментор на відео заняття, у підзавданнях 8-11.
  2. Самостійно виконай підзавдання №12. Додатково напиши перевірку, яка покаже, чи заповнені клітинки по діагоналі (одній і другій) трьома хрестиками чи нуликами, щоб завершити перевірку того, чи здобув якийсь із гравців перемогу. Це потрібно зробити в методі checkWin(). Метод повинен повернути true, якщо є вертикаль, горизонталь або діагональ, заповнена значенням n. В іншому разі метод повинен повернути false.

Конспект до заняття #3: перезавантаження гри

Зміст заняття

  • Виносимо код до методу setSignAndCheck()
  • Прописуємо перевірку на нічию
  • Додаємо перевірку, чи є на ігровому полі порожня клітинка (метод hasEmptyCell())
  • Прописуємо перезавантаження гри — метод onKeyPress()
  • Додаємо рестарт незавершеної гри

1. Виносимо наш код до методу setSignAndCheck()

Настав час провести невеличкий рефакторинг нашого коду. Рефакторинг (від англ. refactoring) — зміна внутрішньої структури програми, яка не впливає на її зовнішню поведінку. По суті це спрощення та покращення коду, який вже існує, щоб надати йому більш компактного та чибательного вигляду, підвищити продуктивність та уникнути помилок в коді у майбутньому. Нам потрібно винести частину з коду з методу onMouseLeftClick() (присвячену ходу гравця та перевірці на перемогу в грі) до окремого методу — public void setSignAndCheck(int x, int y). У відео ментор наочно демонструє, як це зробити. У методі onMouseLeftClick() замініть весь цей код на виклик setSignAndCheck(x, y).

2. Прописуємо перевірку на нічию

А що робити у випадку, коли ігра скінчилася, доступних клітинок для ходу нема, але переможця теж неможливо визначити? Нам потрібно прописати в коді обробку нічії як результату гри. Створи новий метод — public boolean hasEmptyCell() — він перевірятиме, чи є ще на полі порожні клітинки. У цьому методі треба пройтися всіма значеннями в масиві model і повернути true, якщо хоча б одне значення дорівнює нулю. Інакше метод повинен повернути false.

3. Додаємо перевірку, чи є на ігровому полі порожня клітинка

Якщо порожніх клітин на полі не залишилося і немає переможця, потрібно відобразити повідомлення про нічию. Наприкінці методу setSignAndCheck() за допомогою hasEmptyCell() потрібно перевірити наявність порожніх клітин на поле, і якщо порожні клітини не залишилося — завершити гру:
  • показати повідомлення про нічию.
  • встановити isGameStopped значення true.

4. Додаємо презавантаження гри

Умовою для перезавантаження гри та скидання всіх попередніх результатів ми зробимо натискання на пробіл. Для цього нам потрібно перевизначити метод, який вже є у класу Game (нашого ігрового рушія). Обирай метод onKeyPress — саме те, що нам потрібно. У методі потрібно прописати цикл, зміст якого такий: якщо наша гра завершена, і користувач натиснув на клавішу пробіл (SPACE), то ми маємо заново ініціалізувати модель та оновити view (ігрове представлення).

5. Додаємо рестарт незавершеної гри

Також додаємо можливість перезапустити незавершену гру в будь-який час за допомогою натискання Escape. Якщо користувач натиснув ESC, треба стартувати гру заново. Стара логіка теж має працювати: якщо користувач натиснув пробіл, і гра була закінчена, то потрібно стартувати гру заново. Приклад перевірки натискання клавіші пропуску:

if (key == Key.SPACE) { ... }

Домашнє завдання

  1. Відтвори все, що зробив ментор на відео у підзавданнях 13-17.
  2. Самостійно виконай підзавдання №18. перепиши текст повідомлень: замість “Перемога гравця N” — “You win”, замість “Нічия” — “Game over”.
  3. "You Win!" потрібно відобразити зеленим кольором, якщо перший гравець виграв.
  4. "Game Over" потрібно відображати червоним, якщо перший гравець програв — виграв другий.
  5. Зміни потрібно робити у методі setSignAndCheck().

Конспект до заняття #4: прописуємо ходи комп’ютера

Зміст заняття

  • Створюємо метод для ходу комп’ютера — computerTurn()
  • Вчимо комп’ютер “ходити”
  • Створюємо метод checkFutureWin(), який передбачає перемогу гравця
  • Покращуємо логіку гри комп’ютера

1. Додаємо ходи комп’ютера

Щоб додати хід комп’ютера, використовуємо метод public void computerTurn() та додаємо умову, завдяки якій комп’ютер завжди робитиме хід у центральну клітинку, якщо вона буде вільною після ходу гравця, в іншому випадку — у сусідню. Метод computerTurn() потрібно викликати в самому кінці методу onMouseLeftClick() і після цього ще раз змінити поточного гравця: currentPlayer = 3 - currentPlayer;.

2. Вчимо комп’ютер “ходити”

Метод computerTurn() повинен робити хід у певну клітину. Під "походити в клітинку" мається на увазі виклик методу setSignAndCheck() з координатами клітинки. Потрібно реалізувати дві прості стратегії:
  • Перевіримо, чи вільна центральна клітина, і якщо вільна, то йдемо на неї
  • В іншому випадку йдемо на першу знайдену вільну клітинку
Важливо: потрібно робити лише один хід — не більше. Якщо комп'ютер зробив хід, то метод computerTurn() потрібно завершити.

3. Передбачаємо перемогу гравця

Щоб ускладнити “мислення” комп’ютера, нам знадобиться метод checkFutureWin(). Додай метод public boolean checkFutureWin(int x, int y, int n). Він повинен повернути true, якщо гравець n виграє гру після ходу на клітинку (x, y). В іншому разі метод повинен повернути false.

4. Покращуємо логіку гри комп’ютера

З’єднуємо методи computerTurn() та checkFutureWin(), щоб покращити логіку методу computerTurn(). Після перевірки центральної клітини додай таку логіку: перебираємо всі ячейкм масиву model і перевіряємо: якщо можна виграти гру, зробивши хід в клітинку (x,y), то робимо хід у клітинку (x,y).

Домашнє завдання

  1. Відтвори все, що зробив ментор на відео заняття у підзавданнях 19-22.
  2. Самостійно виконай підзавдання №23. Покращи метод ComputerTurn(): напиши метод checkEnemyWin(), який передбачає перемогу супротивника і перешкоджає йому.
Нова логіка методу computerTurn() має виглядати таким чином:
  • робимо спробу походити в центральну клітинку
  • робимо хід, якщо можемо виграти одним ходом
  • заважаємо противнику, якщо він може виграти
  • робимо хід в першу порожню клітку, що попалася
Ось і все! Ти молодець!

Важливо: підзавдання №24 — це твій простір для додаткової кастомізації гри. Можливо, ти хочеш щось змінити, покращити чи додати до своїх “Хрестиків-нуликів”, щоб гра стала унікальною.

Якщо тобі подобається гра, якою вона вже вийшла, і ти не плануєш писати додатковий код — просто натисни на “Перевірку”, валідатор “зарахує” твій код, і ти зможеш опублікувати готову гру.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ