Please use this identifier to cite or link to this item: https://er.chdtu.edu.ua/handle/ChSTU/9767
Title: Програмна реалізація кастомізованих правил у грі "Шашки"
Authors: Куницька , Світлана Юріївна
Сасько, Олег Ігорович
Keywords: інженерія програмного забезпечення;веб-система;патерн стратегія;кастомізація ігрових правил;багатокористувацька гра;синхронізація в реальному часі;рейтинг ЕЛО;рекурсивні алгоритми;software engineering;web system;strategy pattern;game rule customization;multiplayer game;real-time synchronization;ELO rating;recursive algorithms
Issue Date: 17-Jun-2026
Abstract: АНОТАЦІЯ Кваліфікаційна робота бакалавра на тему «Програмна реалізація кастомізованих правил у грі "Шашки"» містить _155_ сторінок, _4_ таблиць, _21_ рисунків, список використаних джерел з _12_ найменувань, 4 додатків. Метою роботи є розробка веборієнтованої багатокористувацької системи гри в шашки з підтримкою динамічного налаштування правил та ігрових модифікаторів на основі формалізованої моделі ігрової логіки. Завдання: рефакторинг монолітного коду у модульну архітектуру; інтеграція патерна Strategy; розробка бази даних H2; створення адаптивного інтерфейсу на HTML5 Canvas та Tailwind CSS; реалізація WebSocket-з'єднання для синхронізації дошки, використовувати . Задачі: 1 Проаналізувати існуючі ігрові платформи та обґрунтувати вибір технологічного стека для реалізації кросплатформеної версії системи з підтримкою локального мультиплеєра. 2 Спроектувати розширену архітектуру програмного комплексу, що включає модулі керування ігровою статистикою, систему аудіосупроводу та механізми збереження прогресу користувача. 3 Розробити адаптивний графічний інтерфейс користувача з підтримкою високої роздільної здатності та інтегрувати ігрові сервіси для обліку статистики і відображення ігрових станів. 4 Провести комплексне тестування ігрового процесу, включаючи перевірку мультиплеєра, коректність роботи рейтингової системи та стабільність веб-версії застосунку. Об'єктом є процеси проектування та програмної реалізації веборієнтованих багатокористувацьких ігрових систем з динамічним керуванням ігровою логікою та модифікацією правил у реальному часі. Предметом є архітектурні патерни (зокрема, патерн Strategy), клієнт-серверні моделі взаємодії через протокол WebSocket, рекурсивні алгоритми валідації ходів та знаходження максимального взяття фігур, а також методи математичного розрахунку рейтингу користувачів за системою Ело (Elo). Висновок: розроблено UML-моделі системи; створено сервер на Java 21 та Spring Boot 3; реалізовано рекурсивні алгоритми максимального взяття для поля 10х10; інтегровано адаптивний фронтенд. Також розроблено підсистему авторизації із шифруванням паролів BCrypt та модуль розрахунку індивідуального рейтингу за системою Ело. Проведені JUnit та системні тести підтвердили стійкість системи та швидкий відгук (80-120 мс).
ABSTRACT The bachelor's qualification thesis on the topic «Software implementation of customized rules in the game "Checkers"» contains _155_ pages, 4 tables, 21 figures, a list of references with 12 items, and 4 appendices. The aim of the work is to develop a web-based multiplayer checkers game system with support for dynamic rule customization and gameplay modifiers based on a formalized model of game logic. Tasks: refactoring monolithic code into a modular architecture; integrating the Strategy pattern; developing an H2 database; creating a responsive user interface using HTML5 Canvas and Tailwind CSS; implementing a WebSocket connection for board state synchronization. Objectives: 1 To analyze existing game platforms and justify the choice of the technology stack for implementing a cross-platform version of the system with local multiplayer support. 2 To design an extended architecture of the software system, including modules for managing game statistics, an audio accompaniment system, and mechanisms for saving user progress. 3 To develop a responsive graphical user interface with high-resolution support and integrate game services to keep track of statistics and display game states. 4 To perform comprehensive testing of the gameplay, including multiplayer verification, correctness of the rating system, and stability of the web version of the application. The object of the study is the processes of design and software implementation of web-based multiplayer game systems with dynamic control of game logic and real-time rule modification. The subject of the study is architectural patterns (specifically, the Strategy pattern), client-server interaction models via the WebSocket protocol, recursive algorithms for move validation and maximum piece capture determination, as well as methods for mathematical calculation of user ratings using the Elo system. Conclusion: UML models of the system have been developed; a server based on Java 21 and Spring Boot 3 has been created; recursive maximum capture algorithms for a 10x10 board have been implemented; a responsive frontend has been integrated. Additionally, an authorization subsystem with BCrypt password hashing and an individual rating calculation module based on the Elo system have been developed. The conducted JUnit and system tests confirmed the system stability and rapid response time (80-120 ms).
URI: https://er.chdtu.edu.ua/handle/ChSTU/9767
Appears in Collections:121 Інженерія програмного забезпечення (Інженерія програмного забезпечення)

Files in This Item:
File Description SizeFormat 
Кваліфікаційнв робота бакалавра Сасько Олег Ігорович.pdf
  Restricted Access
8.57 MBAdobe PDFView/Open Request a copy


Items in DSpace are protected by copyright, with all rights reserved, unless otherwise indicated.

Extracted text
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ 
ЧЕРКАСЬКИЙ ДЕРЖАВНИЙ ТЕХНОЛОГІЧНИЙ УНІВЕРСИТЕТ 
Факультет інформаційних технологій і систем 
Кафедра програмного забезпечення автоматизованих систем 
 
 
ПОЯСНЮВАЛЬНА ЗАПИСКА 
до кваліфікаційної роботи бакалавра 
     на тему: «Програмна реалізація кастомізованих правил у грі "Шашки"» 
 
                                                          Виконав: студент 4 курсу, групи ПЗ-2204 
                                                          спеціальності 121 «Інженерія програмного                       
                                                          забезпечення»  
 
Студент Сасько О.І. 
 (прізвище та ініціали) 
Керівник  Куницька С. Ю. 
 (прізвище та ініціали) 
Рецензент  Захарова М.В. 
 (прізвище та ініціали) 
 
 
 
 Черкаси 2026 
 
 
ЧДТУ.262260.021 ПЗ 
 Черкаський державний технологічний університет  
повне найменування вищого навчального закладу 
Факультет           інформаційних технологій і систем  
Кафедра             програмного забезпечення автоматизованих систем  
Освітній рівень   бакалавр  
Спеціальність     121 «Інженерія програмного забезпечення»  
Освітня програма  Інженерія програмного забезпечення  
 
ЗАТВЕРДЖУЮ 
Зав. кафедри ПЗАС, професор 
____________________ Сергій ГОЛУБ  
«___» _______________ 2026 року 
 
З А В Д А Н Н Я 
НА КВАЛІФІКАЦІЙНУ РОБОТУ СТУДЕНТУ 
Саську Олегу Ігоровичу 
(прізвище, ім’я, по батькові) 
1. Тему проекту (роботи) «Програмна реалізація кастомізованих правил у грі "Шашки"»  
Керівник проекту (роботи) Куницька Світлана Юріївна, к.т.н., доцент  
(прізвище, ім’я , по батькові, науковий ступінь, вчене звання) 
Затверджені наказом Черкаського державного технологічного університету від «_12.03__» 
___________ 2026 року №56/03-03________________ 
2. Строк подання студентом проекту (роботи) ____________________ 2026 року 
3. Вхідні дані до проекту (роботи) стандарти програмного забезпечення; вимоги до проекту: 
правові вимоги; технічні вхідні дані  
4. Зміст розрахунково-пояснювальної записки (перелік питань, які потрібно розробити) Вступ; 
Існуючі методи та засоби розв‘язання поставлених завдань; Проектування та моделювання 
програмного забезпечення; Розробка та тестування програмного забезпечення; Висновки; 
Список використаних джерел; Додатки. 
5. Перелік графічного матеріалу (з точним зазначенням обов’язкових робіт проекту): таблиці, 
діаграми, схеми, роздатковий матеріал, блок-схеми, слайди презентації; 
6. Консультанти розділів роботи 
Прізвище, ініціали та  Підпис, дата 
Розділ 
посади консультанта Завдання видав Завдання прийняв 
1 Куницька С.Ю., к.т.н., доцент.   
2 Куницька С.Ю., к.т.н., доцент.   
3 Куницька С.Ю., к.т.н., доцент.   
2 
ЧДТУ.262260.021 ПЗ 
7. Дата видачі завдання        
КАЛЕНДАРНИЙ ПЛАН 
Строк 
виконання 
№ п/п Назва етапів випускної роботи етапів Примітки 
кваліфікаційної 
роботи 
1 Постановка задачі. Затвердження завдання 12.04.2026 виконано 
 1 етап   
1 Аналіз шляхів вирішення поставленої задачі 18.04.2026 виконано 
2 Вибір кінцевого варіанту проектного рішення 23.04.2026 виконано 
3 Оформлення первісної редакції роботи 02.05.2026 виконано 
 2 етап   
1 Узгодження прийнятих проектних рішень з 12.05.2026 виконано 
керівником 
2 Оформлення пояснювальної записки  16.05.2026 виконано 
3 Попередній захист роботи 20.05.2026 виконано 
4 Затвердження роботи 25.05.2026 виконано 
5 Рецензування роботи 28.05.2026 виконано 
6 Захист роботи 01.06.2026 виконано 
 
 
 
Студент    Сасько О.І. 
       (підпис)                          (прізвище та ініціали) 
 
Керівник роботи    Куницька С.Ю. 
        (підпис)                          (прізвище та ініціали)
3 
АНОТАЦІЯ 
Кваліфікаційна робота бакалавра на тему «Програмна реалізація 
кастомізованих правил у грі "Шашки"» містить _155_ сторінок, _4_ таблиць, _21_ 
рисунків, список використаних джерел з _12_ найменувань, 4 додатків. 
Метою роботи є розробка веборієнтованої багатокористувацької системи 
гри в шашки з підтримкою динамічного налаштування правил та ігрових 
модифікаторів на основі формалізованої моделі ігрової логіки. 
Завдання: рефакторинг монолітного коду у модульну архітектуру; 
інтеграція патерна Strategy; розробка бази даних H2; створення адаптивного 
інтерфейсу на HTML5 Canvas та Tailwind CSS; реалізація WebSocket-з'єднання для 
синхронізації дошки, використовувати . 
Задачі:  
1 Проаналізувати існуючі ігрові платформи та обґрунтувати вибір 
технологічного стека для реалізації кросплатформеної версії системи з 
підтримкою локального мультиплеєра. 
2  Спроектувати розширену архітектуру програмного комплексу, що включає 
модулі керування ігровою статистикою, систему аудіосупроводу та 
механізми збереження прогресу користувача.  
3  Розробити адаптивний графічний інтерфейс користувача з підтримкою 
високої роздільної здатності та інтегрувати ігрові сервіси для обліку 
статистики і відображення ігрових станів.  
4 Провести комплексне тестування ігрового процесу, включаючи перевірку 
мультиплеєра, коректність роботи рейтингової системи та стабільність 
веб-версії застосунку. 
Об'єктом є процеси проектування та програмної реалізації веборієнтованих 
багатокористувацьких ігрових систем з динамічним керуванням ігровою логікою 
та модифікацією правил у реальному часі. 
Предметом є архітектурні патерни (зокрема, патерн Strategy), клієнт-
серверні моделі взаємодії через протокол WebSocket, рекурсивні алгоритми 
 
 
ЧДТУ.262260.021 ПЗ 
валідації ходів та знаходження максимального взяття фігур, а також методи 
математичного розрахунку рейтингу користувачів за системою Ело (Elo). 
Висновок: розроблено UML-моделі системи; створено сервер на Java 21 та 
Spring Boot 3; реалізовано рекурсивні алгоритми максимального взяття для поля 
10х10; інтегровано адаптивний фронтенд. Також розроблено підсистему 
авторизації із шифруванням паролів BCrypt та модуль розрахунку індивідуального 
рейтингу за системою Ело. Проведені JUnit та системні тести підтвердили стійкість 
системи та швидкий відгук (80-120 мс). 
КЛЮЧОВІ СЛОВА: інженерія програмного забезпечення, веб-система, 
патерн стратегія, кастомізація ігрових правил, багатокористувацька гра, 
синхронізація в реальному часі, рейтинг ЕЛО, рекурсивні алгоритми. 
5 
ABSTRACT 
The bachelor's qualification thesis on the topic «Software implementation of 
customized rules in the game "Checkers"» contains _155_ pages, 4  tables,  21  figures, a 
list of references with  12  items, and 4 appendices. 
The aim of the work is to develop a web-based multiplayer checkers game system 
with support for dynamic rule customization and gameplay modifiers based on a 
formalized model of game logic. 
Tasks: refactoring monolithic code into a modular architecture; integrating the 
Strategy pattern; developing an H2 database; creating a responsive user interface using 
HTML5 Canvas and Tailwind CSS; implementing a WebSocket connection for board 
state synchronization. 
Objectives: 
1 To analyze existing game platforms and justify the choice of the technology 
stack for implementing a cross-platform version of the system with local 
multiplayer support. 
2 To design an extended architecture of the software system, including modules 
for managing game statistics, an audio accompaniment system, and 
mechanisms for saving user progress. 
3 To develop a responsive graphical user interface with high-resolution support 
and integrate game services to keep track of statistics and display game states. 
4 To perform comprehensive testing of the gameplay, including multiplayer 
verification, correctness of the rating system, and stability of the web version 
of the application. 
The object of the study is the processes of design and software implementation of 
web-based multiplayer game systems with dynamic control of game logic and real-time 
rule modification. 
The subject of the study is architectural patterns (specifically, the Strategy 
pattern), client-server interaction models via the WebSocket protocol, recursive 
 
 
ЧДТУ.262260.021 ПЗ 
algorithms for move validation and maximum piece capture determination, as well as 
methods for mathematical calculation of user ratings using the Elo system. 
Conclusion: UML models of the system have been developed; a server based on 
Java 21 and Spring Boot 3 has been created; recursive maximum capture algorithms for 
a 10x10 board have been implemented; a responsive frontend has been integrated. 
Additionally, an authorization subsystem with BCrypt password hashing and an 
individual rating calculation module based on the Elo system have been developed. The 
conducted JUnit and system tests confirmed the system stability and rapid response time 
(80-120 ms). 
KEYWORDS: software engineering, web system, strategy pattern, game rule 
customization, multiplayer game, real-time synchronization, ELO rating, recursive 
algorithms. 
7 
ЗМІСТ 
ВСТУП………………………………...…....…………………………………...…... .. 10 
РОЗДІЛ 1 ІСНУЮЧІ МЕТОДИ ТА ЗАСОБИ РОЗВ’ЯЗАННЯ ПОСТАВЛЕНИХ 
ЗАВДАНЬ ...................................................................................................................... 14 
1.1. Аналіз існуючих аналогів ................................................................................. 14 
1.2. Огляд інструментарію та засобів створення веб-додатку ............................. 18 
1.3. Помилка! Закладку не визначено.19 
1.4. Висновки до розділу 1 ....................................................................................... 22 
РОЗДІЛ 2 ВПРОВАДЖЕННЯ РЕЗУЛЬТАТІВ ДОСЛІДЖЕНЬ У ПРАКТИКУ 
ПРОЕКТУВАННЯ ПРОГРАМНОГО ЗАБЕЗПЕЧЕННЯ ІНФОРМАЦІЙНИХ 
СИСТЕМ ........................................................................................................................ 24 
2.1. Моделювання предметної області ................................................................... 24 
2.1.1. Предметна область моделювання. Модель предметної області. Словник 
предметної області. ............................................................................................... 24 
2.1.2. Елементи моделювання предметної області ............................................ 26 
2.1.3. Робоча область моделювання .................................................................... 26 
2.2. Формування та аналіз вимог ............................................................................. 26 
2.2.1. Формування вимог до програмного забезпечення. Первинні і детальні 
вимоги. Вимоги замовника і розробника. Функціональні та нефункціональні 
вимоги .................................................................................................................... 26 
2.2.2. Формування вимог за допомогою діаграми прецедентів  ...................... 28 
2.3. Проектування логічної структури програмного комплексу .......................... 30 
2.3.1. Діаграми класів ........................................................................................... 30 
2.3.2. Діаграми пакетів ......................................................................................... 39 
2.4. Архітектурне проектування .............................................................................. 40 
2.4.1. Діаграма компонентів ................................................................................ 40 
 
ЧДТУ 262260.021 ПЗ 
Зм. Лист № документа Підпис Дата 
Розроб. Сасько О.І. Програмна реалізація кастомізован их Літ. Лист Листів 
Керівник Куницька С.Ю  правил у грі "Шашки" Н 3  
Рецензент СЗСахСаСрСо.вЮа .МС..ЮВ. Д ФІТІС, 
(Пояснювальна записка) 
Н.ко.н тр. Півен ь О.Б.  
 Кафедра ПЗАС, гр. ПЗ-2204 
Зат в. Голуб С.В. При 
этом сами 
2.4.2. Розгортання програмної системи на апаратних засобах. Діаграма 
розгортання ........................................................................................................... 41 
2.5. Моделювання поведінки системи .................................................................... 43 
2.5.1. Діаграма діяльності .................................................................................... 43 
2.5.2. Діаграма послідовності .............................................................................. 44 
2.5.3. Діаграма комунікації .................................................................................. 44 
2.5.4. Діаграма скінченного автомату ................................................................. 45 
Висновки до розділу 2 .......................................................................................... 47 
РОЗДІЛ 3 РОЗРОБКА ТА ТЕСТУВАННЯ ПРОГРАМНОГО ЗАБЕЗПЕЧЕННЯ .. 49 
3.1. Розробка програмного комплексу .................................................................... 49 
3.1.1. Обґрунтування засобів реалізації .............................................................. 49 
3.1.2. Опис структурної (функціональної) схеми .............................................. 51 
3.1.3. Опис логічної схеми системи .................................................................... 55 
3.1.4. Розробка бази даних ................................................................................... 55 
 
3.1.5. Розробка інтерфейсу користувача ............................................................. 59 
 3.1.6. Опис розробки програмних компонентів ................................................. 60 
3.2. Тестування системи ........................................................................................... 61 
3.2.1. Модульне тестування ................................................................................. 61 
3.2.2. Інтеграційне тестування ............................................................................. 63 
3.2.3. Системне тестування .................................................................................. 65 
3.2.4. Приймальне тестування ............................................................................. 66 
3.3. Приклади впровадженого програмного комплексу ....................................... 67 
Висновки до розділу 3 .......................................................................................... 72 
ВИСНОВКИ .................................................................................................................. 74 
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ ..................................................................... 77 
ДОДАТОК А ................................................................................................................. 78 
ДОДАТОК Б .................................................................................................................. 80 
ДОДАТОК В         ....................................................................................................... 135 
ДОДАТОК Г        ......................................................................................................... 144 
Лист 
 ЧДТУ 262260.021 ПЗ 
 
4 
Зм  Лист № докум. Підп. Дата 
 
ЧДТУ.262260.021 ПЗ 
ВСТУП 
Актуальність теми. Дана тема кваліфікаційної роботи: «Програмна 
реалізація кастомізованих правил у грі "Шашки"», належить спеціальності 
інженерія програмного забезпечення. Ця тема зумовлена необхідністю переходу 
від жорстко закодованих ігрових алгоритмів до гнучких систем, здатних 
адаптуватися під різноманітні міжнародні стандарти без зміни ядра програми. 
Розробка базується на модернізації існуючого прототипу, що дозволяє дослідити 
процес рефакторингу та впровадження складних архітектурних патернів у вже 
діючий продукт. 
Мета і завдання розробки. Метою розробки є архітектурне проєктування, 
програмна реалізація та верифікація кросплатформеного програмного 
забезпечення з гнучкою ізольованою архітектурою підсистем, що забезпечує 
динамічне конфігурування алгоритмів бізнес-логіки (правил) та підтримку 
варіативності структур даних ігрового поля в рантаймі. Завдання даної 
кваліфікаційної роботи передбачає проведення глибокого рефакторингу та 
модернізації існуючого програмного коду, створеного на початкових етапах 
навчання, з метою переходу від жорстко детермінованої логіки британських шашок 
до універсальної модульної архітектури. У межах реалізації проекту необхідно 
спроектувати цілісну систему класів на основі партерна Strategy, що дозволить 
відокремити ігрові правила від компонентів візуалізації та моделі даних. Важливим 
етапом є програмне втілення математичних моделей для різних варіацій гри, 
включаючи розробку алгоритмів для міжнародних шашок на полі 10х10, що 
потребує впровадження рекурсивних методів для пошуку максимально можливої 
кількості взяттів за один хід. 
Задачі розробки: 
1 Проаналізувати архітектурні рішення існуючих програмних платформ та 
обґрунтувати вибір технологічного стека для реалізації 
кросплатформеного програмного забезпечення з підтримкою локальної 
мережевої взаємодії компонентів. 
10 
ЧДТУ.262260.021 ПЗ 
2 Спроектувати розширену архітектуру програмного комплексу, що 
включає модулі керування ігровою статистикою, систему аудіосупроводу 
та механізми збереження прогресу користувача.  
3 Розробити адаптивні компоненти представлення програмного 
забезпечення з підтримкою високої роздільної здатності та інтегрувати 
програмні модулі обробки користувацької статистики й візуалізації 
поточних станів системи. 
4 Провести комплексне тестування розробленого програмного 
забезпечення, включаючи верифікацію мережевої взаємодії компонентів 
у реальному часі, коректність роботи алгоритмів рейтингової системи та 
стабільність функціонування веб-версії застосунку. 
Об’єкт розробки. Об’єктом розробки є процеси проектування та програмної 
реалізації веборієнтованих багатокористувацьких ігрових систем з динамічним 
керуванням ігровою логікою та модифікацією правил у реальному часі. 
Предмет розробки. Предметом розробки є архітектурні патерни (зокрема, 
патерн Strategy), клієнт-серверні моделі взаємодії через протокол WebSocket, 
рекурсивні алгоритми валідації ходів та знаходження максимального взяття фігур, 
а також методи математичного розрахунку рейтингу користувачів за системою Ело 
(Elo). 
Методи проектування та конструювання. Методи проектування: Для 
успішної реалізації програмного продукту та забезпечення його відповідності 
встановленим функціональним вимогам щодо гнучкості ігрових правил було 
застосовано комплекс сучасних методів та підходів до проектування складних 
інформаційних систем. Провідним у роботі став об’єктно-орієнтований підхід, 
який дозволив спроектувати гнучку та модульну архітектуру програмного 
забезпечення, що забезпечує легкість масштабування та підтримки коду. 
Ключовим архітектурним рішенням стало впровадження патерна Strategy, завдяки 
якому логіку кожного варіанта гри було інкапсульовано в окремі класи, що 
походять від базової абстрактної структури AbstractRuleStrategy. Математичний 
апарат системи базується на використанні рекурсивних алгоритмів та методів 
11 
ЧДТУ.262260.021 ПЗ 
комбінаторного пошуку, що є необхідним для коректної обробки ігрових станів, 
обчислення ланцюгів взяття та точного розрахунку правила максимального 
захоплення фігур у міжнародних шашках. 
У процесі розробки алгоритмів валідації ходів, контролю перетворення фігур 
у дамки та обробки логіки «летючих дамок» активно застосовувалися методи 
структурного та об'єктного програмування, що гарантує логічну послідовність та 
стабільність виконання обчислювальних операцій навіть при зміні розмірності 
ігрового поля. Технологічний стек проекту реалізовано через інструментальні 
методи розробки мовою Java із залученням стандартних бібліотек для проведення 
складних логічних обчислень та бібліотеки Swing для конструювання інтуїтивно 
зрозумілого графічного інтерфейсу користувача, який динамічно адаптується під 
обраний набір правил. Особлива увага була приділена рефакторингу коду, 
створеного на етапі курсового проектування, що дозволило інтегрувати нові 
функціональні модулі без порушення цілісності вже існуючої системи. 
Важливою складовою етапу проектування стало використання уніфікованої 
мови моделювання Unified Modeling Language (UML), у межах якої було 
розроблено низку діаграм для візуалізації структури та динамічної поведінки 
системи. Застосування UML-діаграм дозволило розробнику детально змоделювати 
життєвий цикл об'єктів, визначити сценарії взаємодії користувача із застосунком 
та глибше зрозуміти внутрішню логіку функціонування системи ще до етапу 
безпосереднього написання програмного коду. Зокрема, діаграми класів допомогли 
чітко розмежувати відповідальність між моделлю даних CheckersData та 
стратегіями правил, а діаграми послідовностей дозволили наочно представити 
процес передачі управління під час виконання складних ігрових маневрів. 
Опис отриманих результатів. У результаті було створено програмний 
комплекс «Checkers: Rule Constructor», який складається з адаптивного графічного 
інтерфейсу, універсального ігрового двигуна та бібліотеки кастомізованих 
стратегій правил. Програма дозволяє реалізовувати ігрові сесії за різними 
національними стандартами, динамічно змінювати розмірність ігрового поля та 
забезпечувати коректну валідацію складних ходів згідно з обраною стратегією. 
12 
ЧДТУ.262260.021 ПЗ 
Система забезпечує повну кросплатформеність завдяки використанню середовища 
виконання Java та високу точність обчислення ігрових станів при рекурсивному 
пошуку варіантів взяття. 
Практичне значення отриманих результатів. Розроблене програмне 
забезпечення надає користувачам універсальний інструмент для моделювання 
ігрових процесів із варіативними правилами, що суттєво розширює можливості 
стандартних ігрових додатків. Завдяки впровадженню гнучкої архітектури на 
основі патерна Strategy, система може бути використана як база для створення 
професійних тренажерів з логічних ігор або як ефективне методичне забезпечення 
при вивченні сучасних принципів об’єктно-орієнтованого проектування та 
рефакторингу ПЗ. 
Особистий внесок автора. Роботу було апробовано на Днях студентської 
науки ЧДТУ, Черкаси, 2026, Україна на тему: «Програмна реалізація 
кастомізованих правил у грі "Шашки"» 
13 
ЧДТУ.262260.021 ПЗ 
РОЗДІЛ 1. ІСНУЮЧІ МЕТОДИ ТА ЗАСОБИ РОЗВ’ЯЗАННЯ 
ПОСТАВЛЕНИХ ЗАВДАНЬ 
1.1. Аналіз існуючих аналогів 
Процес цифровізації настільних ігор є однією з найдавніших та найбільш 
фундаментальних тем у галузі комп'ютерних наук. Шашки, як об'єкт дослідження, 
займають особливе місце завдяки своїй математичній визначеності та складності 
ігрового дерева. Історія створення програм для гри в шашки почалася ще в середині 
XX століття, коли Крістофер Стречі написав одну з перших ігрових програм для 
комп'ютера Ferranti Mark 1. Важливою віхою в історії ШІ стало створення програми 
«Chinook» під керівництвом Джонатана Шеффера, яка після десятиліть аналізу 
довела, що при ідеальній грі обох сторін партія в англійські шашки (checkers) 
завжди закінчується нічиєю. Це свідчить про високу складність алгоритмізації цієї 
гри та необхідність побудови систем, здатних опрацьовувати величезну кількість 
комбінаторних станів (приблизно 1020). 
У сучасних умовах веброрієнтовані системи для гри в шашки повинні не 
лише забезпечувати коректність правил, а й відповідати суворим вимогам 
масштабованості, інформаційної безпеки та високої якості користувацького 
досвіду (UX). У процесі виконання роботи було проведено детальний аналіз 
найбільш популярних платформ-аналогів, що представлені на світовому ринку, з 
метою виявлення їхніх архітектурних та функціональних особливостей. 
1 Платформа Lidraughts (lidraughts.org). На сьогодні Lidraughts є золотим 
стандартом серед безкоштовних ігрових сервісів з відкритим вихідним кодом. 
Платформа є спеціалізованим відгалуженням (форком) відомого шахового сервера 
Lichess, що визначає її високу технологічну планку та філософію відсутності 
реклами. 
1 Технічна архітектура та стек: система побудована на базі мови Scala для 
серверної частини (бекенд), що дозволяє використовувати модель акторів 
для обробки тисяч одночасних ігрових сесій без блокування потоків. На 
фронтенді застосовується легкий фреймворк Mithril.js. Обмін даними між 
14 
ЧДТУ.262260.021 ПЗ 
клієнтом та сервером відбувається в реальному часі за допомогою 
протоколу WebSockets, що є критично важливим для ігор з обмеженням 
часу (бліц, куля), де затримка навіть у 100 мс може бути вирішальною. 
2 Функціональні можливості: користувачам пропонується широкий вибір 
варіацій правил: міжнародні (10x10), російські, бразильські, фризькі, 
анти-шашки та інші. Платформа інтегрована з потужним двигуном 
штучного інтелекту Scan, що надає можливість проводити глибокий 
комп'ютерний аналіз партій після їх завершення. 
3 Аналіз UI/UX: як показано на рисунку 1.1, інтерфейс виконаний у стилі 
Material Design, де центральним об'єктом є ігрове поле, а допоміжні 
інструменти (чат, історія ходів, годинник) розташовані ергономічно. 
4 Критична оцінка: попри відкритість коду, архітектура Lidraughts є 
надзвичайно складною для кастомізації або використання окремих 
компонентів у невеликих навчальних проєктах. Система потребує 
специфічних знань Scala та значних серверних потужностей для 
розгортання повної інфраструктури. 
 
 
Рисунок 1.1 – Головне ігрове вікно платформи Lidraughts 
15 
ЧДТУ.262260.021 ПЗ 
2 Портал PlayOK (playok.com). PlayOK – один із найстаріших ігрових 
порталів у світі, який раніше мав назву Kurnik. Він представляє «класичну школу» 
розробки ігрових веб-систем та має величезну базу лояльних гравців з усього світу: 
‒ еволюція технологічного стека: спочатку сервіс базувався на Java-
апплетах, що сьогодні вважається вразливою та застарілою технологією. 
Із розвитком стандартів вебу розробники перейшли на чистий HTML5 та 
JavaScript. Проте серверна логіка залишається написаною на Java, що 
забезпечує високу стабільність роботи протягом десятиліть; 
‒ візуальна концепція та UX: аналіз ігрового поля (рис. 1.2) демонструє 
консервативний підхід. Використання статичних таблиць та мінімальної 
кількості анімацій робить систему надзвичайно легкою для браузера, що 
дозволяє грати на застарілих пристроях. Однак з погляду сучасного 
дизайну такий інтерфейс виглядає морально застарілим і не відповідає 
актуальним трендам веброзробки; 
‒ недоліки архітектури: головною проблемою платформи є її монолітність 
та відсутність публічного API для інтеграції з іншими сервісами. Також 
спостерігається обмеженість у налаштуванні індивідуальних правил гри, 
оскільки логіка жорстко інтегрована в монолітну структуру сервера. 
 
 
Рисунок 1.2 – Візуальне оформлення ігрової кімнати на порталі PlayOK 
16 
ЧДТУ.262260.021 ПЗ 
3 Сервіс 247 Checkers (247checkers.com). Це популярний приклад веб-
додатка, орієнтованого на казуальних гравців, які не бажають проходити процес 
реєстрації. 
Переваги: надзвичайна простота доступу – гра запускається в один клік. 
Використовується клієнтський JavaScript для обробки логіки гри проти комп'ютера, 
що мінімізує навантаження на сервер. 
Недоліки: відсутність системи профілів, рейтингу та можливості гри з 
реальними людьми. Велика кількість рекламних банерів негативно впливає на 
користувацький досвід та відволікає від процесу гри. 
Для систематизації отриманих даних та обґрунтування вибору технологій для 
власної розробки було складено розширену порівняльну таблицю 1.1. 
Таблиця 1.1  
Порівняльний аналіз технічних та функціональних характеристик аналогів 
Критерій Lidraughts PlayOK 247 Розроблювана 
порівняння Checkers система 
Архітектура Мікросервісна Монолітна Клієнтська Модульна 
бекенду (Scala) (Java) (JS) (Spring Boot) 
Метод обміну WebSockets HTTP Local JS REST API / 
даними Polling / WS AJAX 
Гнучкість Висока Низька Відсутня Максимальна 
логіки (складна) (Strategy) 
Безпека даних JWT / OAuth2 Session ID Відсутня Spring Security / 
BCrypt 
Збереження PostgreSQL / MySQL Local H2 (Embedded) / 
стану Redis Storage JPA 
 
 
17 
ЧДТУ.262260.021 ПЗ 
Продовження таблиці 1.1 
Адаптивність Повна Часткова Повна Повна (Tailwind 
UI (Responsive) CSS) 
 
1.2. Огляд інструментарію та засобів створення веб-додатку 
Для розв'язання поставлених завдань у кваліфікаційній роботі було обрано 
екосистему мови програмування Java, яка є індустріальним стандартом для 
розробки складних, стійких до відмов серверних систем. 
1 Мова програмування Java 17 та фреймворк Spring Boot 3. Вибір версії 
Java 17 LTS обумовлений її стабільністю, довгостроковою підтримкою та 
впровадженням сучасних засобів, таких як Sealed Classes (які ідеально підходять 
для моделювання ігрових фігур) та Records (для передачі даних між шарами 
системи – DTO). 
Spring Boot: цей фреймворк обрано як основний засіб автоматизації 
розробки. Він реалізує принцип «Convention over Configuration», що дозволяє 
зосередитися на бізнес-логіці гри, а не на інфраструктурних налаштуваннях. 
Використання механізму Dependency Injection (DI) дозволяє створювати 
слабкозв’язані компоненти, що значно спрощує подальше тестування та 
розширення функціоналу. 
Maven: виступає в ролі інструмента автоматизації збирання проєкту та 
керування зовнішніми бібліотеками. Це гарантує, що проєкт буде успішно зібраний 
на будь-якій машині без конфліктів версій. 
2 Рівень доступу до даних: Spring Data JPA та Hibernate. У процесі 
розробки виникає проблема ефективного та безпечного збереження ігрових сесій 
та профілів користувачів. 
‒ Hibernate (ORM): дозволяє абстрагуватися від написання сирого SQL-
коду. Кожен об'єкт у Java (наприклад, GameSession або Move) 
автоматично мапиться на відповідну таблицю в базі даних, що вирішує 
проблему «Object-Relational Impedance Mismatch». 
18 
ЧДТУ.262260.021 ПЗ 
‒ Spring Data JPA: спрощує створення репозиторіїв. Використання 
стандартних інтерфейсів дозволяє маніпулювати даними через методи-
запити. Як зазначає К. Боер, такий підхід не лише прискорює розробку, а 
й захищає систему від критичних вразливостей, таких як SQL-ін'єкції. 
3 База даних H2 та її роль у проєкті. Для реалізації проекту обрано 
реляційну СУБД H2. Її головні переваги в контексті роботи: 
‒ Embedded Mode: база даних зберігається як звичайний файл у директорії 
проекту. Це робить систему повністю автономною – її можна 
продемонструвати на будь-якому комп'ютері без встановлення важких 
серверних СУБД. 
‒ висока швидкість: H2 забезпечує миттєву відповідь при запитах, що 
важливо для динамічного оновлення стану ігрової дошки. 
4 Безпека та захист: Spring Security. Захист інформації користувачів є 
пріоритетним завданням. Spring Security дозволяє впровадити: 
‒ BCrypt: хешування паролів із використанням солі, що унеможливлює їх 
викрадення навіть при доступі до бази даних. 
‒ захист від атак: вбудовані механізми захисту від CSRF та Session 
Fixation. 
5 Патерни проєктування як методологічна основа. Для того, щоб система 
була гнучкою, було використано наступні патерни проєктування: 
‒ Strategy Pattern (Стратегія): це ключовий елемент розробки. Логіка 
перевірки ходів винесена в окремі стратегії (наприклад, 
RussianCheckersStrategy). Сервіс гри викликає методи стратегії, не знаючи 
нюансів конкретних правил. Це дозволяє додавати нові види шашок 
(міжнародні, бразильські), не змінюючи основний код контролера. 
‒ MVC (Model-View-Controller): забезпечує архітектурне розділення 
даних, інтерфейсу та бізнес-логіки. 
‒ Lombok: бібліотека для автоматичної генерації геттерів, сеттерів та 
конструкторів, що робить код більш лаконічним та зрозумілим. 
1.3. Інструкція користувачу 
19 
ЧДТУ.262260.021 ПЗ 
Нижче наведено детальний опис функціональних можливостей 
інформаційної технології та покрокова інструкція для взаємодії користувача з 
системою. 
Етап 1: Початок роботи, реєстрація та авторизація. Система розроблена 
як сучасний веб-додаток, тому для коректної роботи необхідний браузер із 
підтримкою стандартів HTML5. 
1 Реєстрація профілю: на головній сторінці необхідно натиснути кнопку 
«Зареєструватися». Користувач вказує унікальний логін та пароль. 
Система проводить автоматичну перевірку: якщо логін вже існує в базі 
даних H2, буде виведено повідомлення про помилку. Пароль автоматично 
хешується через BCrypt перед збереженням. 
2 Вхід до системи: після реєстрації необхідно ввести облікові дані. У разі 
успішної автентифікації Spring Security створює безпечну сесію, і 
користувач отримує доступ до ігрового лобі. 
Етап 2: Управління ігровими сесіями в лобі. Після входу користувач 
потрапляє до розділу вибору ігор. 
3 Створення нової гри: користувач натискає «Створити гру». У 
діалоговому вікні обирається тип правил (наприклад, «Російські 
шашки»). Після підтвердження в базі даних створюється запис 
GameSession зі статусом WAITING. Користувач автоматично 
призначається гравцем за білі фігури. 
4 Приєднання до існуючої гри: у списку «Доступні ігри» відображаються 
сесії, створені іншими користувачами. Натиснувши кнопку 
«Приєднатися», другий користувач стає гравцем за чорні фігури, і статус 
гри змінюється на IN_PROGRESS. 
Етап 3: Ігровий процес (Взаємодія з дошкою). Центральним елементом 
інтерфейсу є інтерактивне ігрове поле. 
5 Вибір фігури: клацніть лівою кнопкою миші на фігуру вашого кольору. 
Система надсилає запит на бекенд, де активна стратегія (Strategy) аналізує 
20 
ЧДТУ.262260.021 ПЗ 
позицію та повертає список координат клітинок, куди дозволено зробити 
хід. Ці клітинки підсвічуються на екрані. 
6 Здійснення ходу: натисніть на підсвічену клітинку. Система виконає 
валідацію ходу на сервері, оновить координати фігури в БД та передасть 
право ходу супернику. 
7 Обробка правил взяття: якщо за правилами обраного типу шашок биття 
є обов'язковим, система заблокує всі ходи, крім тих, що ведуть до взяття 
фігури суперника. 
8 Перетворення на дамку: при досягненні фігурою протилежної сторони 
поля, вона автоматично отримує статус «дамки». Це змінює її візуальний 
вигляд (додається корона) та розширює можливості пересування згідно з 
правилами. 
Етап 4: Обробка помилок та завершення гри 
1 Некоректні дії: при спробі зробити хід не за правилами (наприклад, хід 
на зайняту клітинку або чужою фігурою) система виведе попередження у 
вигляді спливаючого повідомлення. 
2 Завершення партії: гра завершується, коли один із гравців втрачає всі 
фігури або не має доступних ходів. Система виводить фінальне вікно з 
результатом матчу. 
3 Статистика: після завершення гри дані про результат автоматично 
оновлюються в профілях обох гравців, що дозволяє відстежувати вінрейт 
та загальну кількість зіграних партій у розділі «Мій профіль». 
 
21 
ЧДТУ.262260.021 ПЗ 
ВИСНОВКИ ДО ПЕРШОГО РОЗДІЛУ 
Проведений ґрунтовний аналіз існуючих методів та засобів розв'язання 
поставлених завдань дозволив сформулювати комплексні висновки щодо 
подальшого напрямку розробки інформаційної технології. Тут було повністю 
виконано першу важливу науково-практичну задачу дослідження, а саме: 
«Проаналізувати архітектурні рішення існуючих програмних платформ та 
обґрунтувати вибір технологічного стека для реалізації кросплатформеного 
програмного забезпечення з підтримкою локальної мережевої взаємодії 
компонентів» 
У результаті розв'язання цієї задачі було безпосередньо отримано такі 
наукові та практичні результати: 
1 аналітичний огляд та класифікацію існуючих ігрових платформ-
аналогів (Lidraughts.org, PlayOK.com, 247 Checkers). Отримано 
порівняльну характеристику за критеріями гнучкості архітектури, 
методів обміну даними та кросплатформеності, що дозволило виявити 
їхні критичні обмеження – жорстке зашивання (монолітність) ігрових 
правил, відсутність динамічної кастомізації умов гри в рантаймі та 
використання неефективного HTTP-polling обміну, який перевантажує 
мережу; 
2 науково-технічне обґрунтування вибору технологічного стека для 
усунення виявлених архітектурних проблем. Визначено, що 
використання Java 21 LTS та фреймворку Spring Boot 3 на серверній 
частині гарантує високу швидкодію та безпеку обробки ігрових даних; 
вбудована реляційна СУБД H2 у Embedded-режимі забезпечує нульову 
конфігурацію при розгортанні, а поєднання HTML5 Canvas та Tailwind 
CSS на клієнтській частині дозволяє уникнути навантаження на DOM-
дерево браузера та забезпечити плавний рендеринг інтерфейсу зі 
швидкістю 60 кадрів на секунду; 
3 обґрунтування мережевої моделі реального часу на базі протоколу 
WebSocket (STOMP). Отримано технічне підтвердження можливості 
22 
ЧДТУ.262260.021 ПЗ 
організації дуплексного асинхронного обміну даними між гравцями, що 
знижує час відгуку системи (затримку/latency) до оптимальних 80–120 мс, 
забезпечуючи комфортний ігровий процес; 
4 концептуальне обґрунтування використання архітектурного патерна 
Strategy. Отримано модель динамічної взаємозамінності ігрових правил 
(Classic 8x8, International 10x10, Brazilian, кастомні модифікатори), яка 
виключає виникнення спагеті-коду та дозволяє додавати нові ігрові 
механіки (наприклад, модифікатор «Камікадзе») без модифікації та 
перекомпіляції основного ядра ігрової сесії. 
Таким чином, у результаті виконання першої задачі було отримано детальний 
аналітичний звіт та теоретико-технологічний фундамент. Це дозволило 
сформувати чітку постановку задачі на проектування та розробку веб-системи, що 
безпосередньо виконується у наступних розділах. 
23 
ЧДТУ.262260.021 ПЗ 
РОЗДІЛ 2. ПРОЕКТУВАННЯ ТА МОДЕЛЮВАННЯ ПРОГРАМНОГО 
ЗАБЕЗПЕЧЕННЯ 
2.1. Моделювання предметної області 
Проектування будь-якої інформаційної системи починається з глибокого 
аналізу предметної області. У контексті розробки веборієнтованої системи для гри 
в шашки, цей процес передбачає створення абстракції, яка відображає не лише 
фізичні компоненти (дошка, фігури), а й складні алгоритмічні взаємозв’язки між 
гравцями, правилами та станами системи. Моделювання дозволяє виявити 
потенційні конфлікти в логіці на ранніх етапах, що є критично важливим для 
спеціальності «Інженерія програмного забезпечення». 
2.1.1. Предметна область моделювання. Модель предметної області. Словник 
предметної області 
Предметна область моделювання даної розробки – це інтелектуальне 
змагання двох опонентів у шашки, що відбувається в цифровому середовищі. 
Модель предметної області базується на об'єктно-орієнтованому аналізі, де 
головними об’єктами є ігрові сесії, профілі користувачів та стратегії валідації ходів.  
Модель предметної області базується на об'єктно-орієнтованому аналізі та 
абстрагуванні ключових концептів реального світу у програмні сутності. 
Концептуальний каркас моделі визначається взаємодією таких 
системоутворюючих елементів: 
‒ сутність «Користувач» (User/Player) – інкапсулює у собі персональні 
дані, параметри безпеки (хеш пароля) та кількісні показники майстерності 
(рейтинг Ело, лічильники перемог і поразок); 
‒ сутність «Ігрова сесія» (Game Session) – виступає центральним 
контенерним об'єктом, який агрегує в собі ігрове поле, координує 
черговість ходів, фіксує поточний статус партії та пов'язує двох 
конкретних гравців-опонентів; 
24 
ЧДТУ.262260.021 ПЗ 
‒ сутність «Ігрова дошка» (Board) – представляє координатно-матричну 
сітку заданої розмірності (8 X 8 або 10 X 10), що керує життєвим циклом 
та просторовим розміщенням фігур; 
‒ сутність «Стратегія правил» (Rule Strategy) – є ключовим 
поведінковим ядром системи, яке ізолює алгоритми перевірки 
легітимності ходів, обов'язкових взяттів та трансформацій фігур від 
загального контексту ігрового рушія. 
Зв'язки в моделі предметної області побудовані за принципом мінімальної 
пов'язаності. Ігрова сесія створюється за запитом користувачів і динамічно 
компонується об'єктом дошки та активованою стратегією правил. Усі зміни стану 
дошки внаслідок ходів валідуються через сервіс правил і миттєво транслюються 
опонентам через WebSocket-канали, що забезпечує цілісність та синхронність 
моделі. 
Для забезпечення однозначного розуміння термінології усіма учасниками 
процесу розробки було сформовано словник предметної області (табл. 2.1). 
Таблиця 2.1 
Словник предметної області системи «Checkers Professional» 
Термін Термін Опис поняття у межах системи 
(укр.) (англ.) 
Гравець Player Авторизований користувач, що має власний 
рейтинг та статистику перемог/поразок. 
Ігрова сесія Game Тимчасовий об'єкт у БД, що синхронізує стан 
Session дошки між двома гравцями. 
Стан дошки Board State Матричне представлення розташування білих та 
чорних фігур у поточний момент часу. 
Стратегія Rule Набір алгоритмів, що визначають легітимність 
правил Strategy ходів для конкретного типу шашок. 
 
25 
ЧДТУ.262260.021 ПЗ 
Продовження таблиці 2.1 
Адміністратор Administrator Користувач із правами доступу до панелі 
моніторингу та бази даних H2. 
Дамка King Статус фігури, що досягла останньої горизонталі 
та отримала розширені права ходу. 
 
2.1.2. Елементи моделювання предметної області 
У даній роботі для моделювання використовується мова UML (Unified 
Modeling Language), яка є стандартом де-факто в інженерії програмного 
забезпечення. Вибір UML обумовлений потребою в точній візуалізації 
архітектурних рішень перед початком написання програмного коду. 
Процес моделювання включає використання статичних та динамічних 
описів. Статичні елементи (класи, пакети, вузли розгортання) визначають 
структуру системи – «з чого вона складається». Динамічні елементи (діаграми 
діяльності, послідовності, станів) описують поведінку – «як вона працює». Це 
дозволяє розробнику абстрагуватися від синтаксису мови Java та зосередитися на 
логічній цілісності системи, забезпечуючи її масштабованість та надійність у 
майбутньому. 
2.1.3. Робоча область моделювання 
Робоча область моделювання (Modeling Workspace) визначає межі системи та 
технологічний стек, у якому вона функціонує. Для «Checkers Professional» це 
клієнт-серверне середовище. На боці клієнта робоча область обмежена 
функціоналом браузера (рендеринг ігрового поля через HTML5 Canvas), а на боці 
сервера – середовищем виконання Java Virtual Machine (JVM). Весь процес від 
моменту авторизації гравця до фіксації результату матчу в базі даних H2 
розглядається як єдиний інформаційний потік, що проходить крізь усі рівні 
архітектури. 
2.2. Формування та аналіз вимог 
26 
ЧДТУ.262260.021 ПЗ 
На етапі аналізу вимог відбувається трансформація бізнес-цілей користувача 
у технічні специфікації для розробника. 
2.2.1. Формування вимог до програмного забезпечення. Первинні і детальні 
вимоги. Вимоги замовника і розробника. Функціональні та нефункціональні 
вимоги 
Первинні вимоги (вимоги замовника) орієнтовані на користувацький 
досвід: можливість грати з суперником через мережу, підтримка різних типів 
правил та наявність рейтингової системи. 
Детальні вимоги (вимоги розробника) включають технічні аспекти 
реалізації: використання Spring Boot для бекенду, Spring Security для захисту даних 
та Hibernate для взаємодії з реляційною БД. 
Систематизація вимог наведена у таблиці 2.2. 
  Таблиця 2.2 
 Специфікація функціональних та нефункціональних вимог 
Тип вимоги Ідентифікатор Опис вимоги 
Функціональна FR1 Система повинна забезпечувати реєстрацію 
та вхід з валідацією ролей (Admin/User). 
Функціональна FR2 Можливість створення ігрової кімнати з 
вибором стратегії (International/British). 
Функціональна FR3 Серверна перевірка кожного ходу на 
відповідність обраним правилам перед 
записом у БД. 
Нефункціональна NFR1 Час відгуку сервера на хід користувача не 
повинен перевищувати 200 мс. 
Нефункціональна NFR2 Паролі користувачів повинні зберігатися в 
БД у зашифрованому вигляді (BCrypt). 
 
27 
ЧДТУ.262260.021 ПЗ 
Продовження таблиці 2.2 
Нефункціональна NFR3 Інтерфейс системи повинен бути адаптивним 
(responsive) для роботи на смартфонах. 
 
2.2.2. Формування вимог за допомогою діаграми прецедентів 
Для проектування веборієнтованої системи «Шашки Professional» 
першочерговим завданням є формалізація функціональних вимог. Найбільш 
ефективним методом представлення цих вимог в об'єктно-орієнтованому аналізі є 
використання діаграм прецедентів (Use Case Diagrams) уніфікованої мови 
моделювання UML. Цей підхід дозволяє чітко окреслити межі програмного 
продукту, визначити ролі зовнішніх користувачів (акторів) та зафіксувати їхню 
безпосередню взаємодію з функціональними модулями системи. 
У розробленій системі виділено дві основні категорії діючих осіб (акторів): 
1 Користувач (User) – основний актор системи, що представляє гравця. Він 
може взаємодіяти з інтерфейсом у статусі гостя або авторизованого 
користувача. Йому доступні функції створення ігрових сесій, приєднання 
до існуючих кімнат, безпосередній ігровий процес та перегляд особистої 
статистики. 
2 Адміністратор (Admin) – системна роль із розширеними правами, 
призначена для моніторингу активних ігрових сесій та керування 
обліковими записами з метою забезпечення безпеки та стабільності 
роботи платформи. 
Загальну логіку взаємодії визначених акторів із функціоналом системи 
«Шашки Professional» наведено на рисунку 2.1: 
 
28 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 2.1 – Діаграма прецедентів системи «Шашки Professional» 
 
Аналіз діаграми дозволяє деталізувати ключові функціональні прецеденти та 
зв'язки між ними: 
‒ реєстрація та авторизація в системі: забезпечує ідентифікацію гравця та 
безпечне збереження його даних у базі даних H2 за допомогою 
шифрування BCrypt. 
‒ перегляд профілю та статистики (ELO): відображає індивідуальні 
показники успішності гравця, включаючи його поточний рейтинг, 
розрахований за математичною моделлю Ело. 
‒ перегляд глобальної таблиці лідерів: забезпечує виведення списку 
найкращих гравців системи для підтримки змагального процесу. 
‒ керування параметрами інтерфейсу: надає можливість кастомізації 
графічної оболонки Canvas (зміна візуальних тем поля) та налаштування 
звукових ефектів у реальному часі. 
‒ мтворення нової ігрової сесії: прецедент, який ініціює нову гру. Він 
обов'язково включає (<<include>>) два допоміжні процеси: 
29 
ЧДТУ.262260.021 ПЗ 
‒ Вибір кастомної стратегії правил (8х8 / 10х10 / Brazilian) – завантажує 
відповідний клас-реалізацію патерна Strategy на бекенді; 
‒ Генерація випадкового модифікатора у рантаймі – опціонально додає 
унікальні правила до поточної сесії (наприклад, модифікатор 
«Камікадзе»). 
‒ Приєднання до існуючої гри: дозволяє другому користувачеві 
підключитися через лобі до створеної кімнати як опоненту (гравцю за 
Чорних). 
‒ Здійснення ігрового ходу в реальному часі: основний прецедент ігрового 
циклу, який ініціюється кліком по Canvas-полю. Для успішного 
завершення цієї дії система обов'язково виконує (<<include>>) наступні 
процеси: 
‒ Автоматична валідація ходу (Strategy pattern) – серверна перевірка 
коректності траєкторії фігури та обов'язковості взяття згідно з 
активованою стратегією правил; 
‒ Синхронізація стану дошки через WebSocket – миттєва передача 
координат верифікованого ходу опоненту без перезавантаження 
сторінки. 
‒ Моніторинг активних сесій та Керування користувачами: спеціалізовані 
прецеденти для актора «Адміністратор», що забезпечують контроль за 
станом бази даних та активністю ігрових WebSocket-каналів. 
Таким чином, розроблена діаграма прецедентів дозволила зафіксувати 
функціональні межі системи «Шашки Professional» та визначити взаємозв'язки між 
модулями ігрової логіки, що виступає основою для подальшого проектування 
класів та структур бази даних. 
2.3. Проектування логічної структури програмного комплексу 
Логічна структура описує внутрішню організацію компонентів системи та 
їхню ієрархію. 
2.3.1. Діаграми класів 
30 
ЧДТУ.262260.021 ПЗ 
Для кращого розуміння системної архітектури та взаємодії між основними 
програмними модулями було розроблено концептуальну діаграму класів (рисунок 
2.2).  
 
Рисунок 2.2 – Концептуальна діаграма класів системи «Шашки Professional» 
Архітектурна гнучкість системи та її відповідність принципам SOLID 
базується на використанні чотирьох ключових типів зв'язків UML: 
1 Успадкування (Generalization / Inheritance): 
‒ зв'язок відображений між класами User та Admin (User <|-- Admin). 
Клас Admin виступає спеціалізованим нащадком базового класу 
користувача. Це дозволяє уникнути дублювання коду, забезпечуючи 
автоматичне наслідування адміністратором облікових даних, полів 
статистики та методів авторизації, одночасно розширюючи його 
можливості специфічними функціями моніторингу та 
адміністрування. 
2 Композиція (Composition): 
‒ цей сильний тип зв'язку застосовано для відношень між GameSession 
та Board (GameSession *-- Board), а також між Board та Piece (Board 
*-- Piece). 
‒ композиція означає, що життєвий цикл дошки повністю керується 
ігровою сесією: у разі знищення чи закриття сесії GameSession об'єкт 
Board також припиняє своє існування. Аналогічно, фігури Piece є 
невід'ємною структурною частиною шахівниці Board. Вони не можуть 
існувати автономно поза контекстом координатної сітки ігрового 
поля. 
3 Агрегація (Aggregation): 
31 
ЧДТУ.262260.021 ПЗ 
‒ відношення слабкого зв'язку зафіксовано між класом GameSession та 
інтерфейсом RuleStrategy (GameSession o-- RuleStrategy). Агрегація 
вказує на те, що сесія посилається на стратегію правил, проте стратегія 
є незалежним архітектурним компонентом (сервісом), життєвий цикл 
якого не залежить від конкретної гри. Це уможливлює повторне 
використання об'єктів правил у різних сесіях. 
4 Асоціація (Association): 
‒ пряма спрямована асоціація існує між класом GameSession та класом 
User (GameSession --> User). Сесія зберігає посилання на двох 
користувачів системи, які виступають у ролі активних опонентів (RED 
та BLACK). 
5 Реалізація (Realization): 
‒ відношення реалізації пов'язує інтерфейс RuleStrategy із класами 
конкретних стратегій: ClassicRulesStrategy, 
InternationalRulesStrategy, BrazilianRulesStrategy та 
KamikazeRulesStrategy. Це класичний прояв патерна проектування 
Strategy, який гарантує, що кожна окрема варіація правил суворо 
дотримується єдиного контракту поведінки, заданого інтерфейсом. 
6 Залежність (Dependency): 
‒ зв'язок слабкої залежності (..>) відображає взаємодію класів 
GameSession та RuleStrategy із допоміжним класом Move. Об'єкт 
ходу Move виступає виключно як легковаговий контейнер передачі 
координат (DTO) під час виклику методів валідації ходу, не 
зберігаючи постійного стану всередині ігрових об'єктів. 
Для реалізації гнучкого ігрового рушія, який підтримує динамічну зміну 
правил, у роботі було розроблено об'єктно-орієнтовану логічну структуру системи. 
На основі спроектованої діаграми класів (рисунок 2.3) проведемо детальний аналіз 
кожного класу, його атрибутів, методів та ролі в загальній архітектурі додатка. 
 
32 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 2.3 – Загальна діаграма класів системи 
Керування профілями користувачів, їх ігровим прогресом та безпекою 
здійснюється за допомогою класів User та Admin. Клас Admin є спеціалізованим 
розширенням класу User, що реалізує адміністративні функції. 
1 Клас User (Користувач системи) 
Даний клас представляє сутність зареєстрованого гравця у системі та 
безпосередньо відображається на таблицю Users реляційної бази даних за 
допомогою технології JPA Hibernate. Він зберігає всю необхідну статистику 
користувача, що використовується для побудови глобального лідерборду. 
‒ Атрибути класу: 
‒ id: Long – унікальний ідентифікатор користувача у системі 
(первинний ключ у БД). 
‒ username: String – унікальне ім'я (логін) користувача для 
автентифікації. 
33 
ЧДТУ.262260.021 ПЗ 
‒ passwordHash: String – хешований за допомогою алгоритму BCrypt 
пароль користувача (забезпечує неможливість отримання пароля у 
відкритому вигляді при витоку БД). 
‒ email: String – адреса електронної пошти для відновлення доступу. 
‒ eloRating: int – поточний числовий показник рейтингу сили гравця за 
математичною системою Ело (початкове значення за замовчуванням 
– $1000$). 
‒ winsCount: int – загальна кількість зафіксованих перемог користувача 
в офіційних іграх. 
‒ lossesCount: int – загальна кількість поразок у системі. 
‒ drawsCount: int – кількість зіграних партій, які завершилися нічиєю. 
‒ Методи класу: 
‒ updateEloRating(newElo: int): void – оновлює числовий рейтинг 
користувача після завершення матчу. 
‒ incrementWins(): void – інкрементує лічильник перемог користувача 
на одиницю. 
‒ incrementLosses(): void – інкрементує лічильник поразок. 
‒ calculateWinRate(): double – обчислює процентне співвідношення 
перемог до загальної кількості зіграних партій. 
2 Клас Admin (Адміністратор системи) 
Успадковує всі поля та базові методи класу User. Він призначений для 
виконання операцій системного адміністрування, моніторингу працездатності 
вбудованої СУБД H2 та регулювання конфліктів усередині ігрових сесій. 
‒ Атрибути класу: 
‒ adminRoleLevel: String – рівень доступу адміністратора (наприклад, 
ROLE_SUPER_ADMIN, ROLE_MODERATOR). 
‒ lastActionTimestamp: LocalDateTime – час останньої зафіксованої 
активності адміністратора в системі. 
‒ Методи класу: 
34 
ЧДТУ.262260.021 ПЗ 
‒ monitorDB_H2(): DatabaseStats – ініціює системний моніторинг 
вбудованої бази даних H2. Повертає звіт про кількість активних 
записів, розмір таблиць, швидкість виконання транзакцій та стан пулу 
з'єднань. 
‒ manageUsers(userId: Long, action: UserAction): void – забезпечує 
керування обліковими записами (блокування порушників, зміна 
ролей, скидання паролів). 
‒ forceTerminateSession(sessionId: UUID): boolean – примусово завершує 
активну ігрову сесію у разі виявлення підозрілої активності чи обриву 
WebSocket-з'єднання з боку одного з гравців. 
Ігрова сесія та бізнес-моделі (GameSession, Board, Piece) - це група класів 
утворює безпосередній контекст гри в шашки, керує поточним станом поля та 
координує чергу ходів. 
1 Клас GameSession (Ігрова сесія) 
Центральний керуючий клас ігрового двигуна, який зв'язує двох опонентів, 
дошку та поточний набір правил. 
‒ Атрибути класу: 
‒ sessionId: UUID – унікальний ідентифікатор поточної ігрової сесії. 
‒ playerRed: User – посилання на об'єкт користувача, що грає червоними 
(або білими) фігурами та здійснює перший хід. 
‒ playerBlack: User – посилання на об'єкт користувача, що грає чорними 
фігурами. 
‒ board: Board – об'єкт, який представляє поточний стан шахівниці та 
розташування фігур. 
‒ ruleStrategy: RuleStrategy – посилання на активований інтерфейс 
стратегії правил. Це поле є основою патерна Strategy. 
‒ currentTurn: Color – колір фігур гравця, чий хід є активним на даний 
момент. 
35 
ЧДТУ.262260.021 ПЗ 
‒ status: GameStatus – поточний стан сесії 
(WAITING_FOR_OPPONENT, IN_PROGRESS, FINISHED, 
ABANDONED). 
‒ Методи класу: 
‒ initializeGame(): void – розставляє фігури на дошці відповідно до 
обраного розміру поля та ініціює перший хід. 
‒ makeMove(move: Move): MoveResult – приймає об'єкт ходу від 
контролера, передає його на валідацію в ruleStrategy і, у разі успіху, 
оновлює стан дошки board, перевіряє умови перемоги та передає чергу 
ходу. 
‒ switchTurn(): void – змінює значення currentTurn на протилежний 
колір. 
‒ checkWinCondition(): GameResult – перевіряє, чи залишилися у 
поточного гравця доступні фігури та ходи. Якщо ходи заблоковані або 
фігур немає, фіксує поразку. 
2 Клас Board (Шахівниця) 
Інкапсулює двовимірну сітку ігрового поля та надає методи доступу до 
клітинок. 
‒ Атрибути класу: 
‒ grid: Piece[][] – двовимірна матриця, елементи якої містять посилання 
на об'єкти Piece або значення null, якщо клітинка порожня. 
‒ width: int – ширина поля (8 або 10). 
‒ height: int – висота поля (8 або 10). 
‒ Методи класу: 
‒ getPiece(row: int, col: int): Piece – повертає об'єкт фігури за вказаними 
координатами. 
‒ setPiece(row: int, col: int, piece: Piece): void – встановлює фігуру на 
вказану клітинку. 
36 
ЧДТУ.262260.021 ПЗ 
‒ movePiece(fromRow: int, fromCol: int, toRow: int, toCol: int): void – 
здійснює фізичне переміщення фігури у матриці та видаляє збиті 
фігури. 
‒ clear(): void – повністю очищує шахівницю. 
3 Клас Piece (Ігрова фігура) 
Описує характеристики конкретної шашки на полі. 
‒ Атрибути класу: 
‒ color: Color – колір фігури (RED або BLACK). 
‒ type: PieceType – тип фігури (MAN – звичайна шашка, KING – дамка). 
‒ Методи класу: 
‒ promoteToKing(): void – змінює тип фігури зі звичайної шашки на 
дамку. 
‒ isKing(): boolean – повертає логічне значення, чи є фігура дамкою. 
Для уникнення жорсткого зв'язку (tight coupling) між класом GameSession та 
алгоритмами пересування фігур, у системі впроваджено поведінковий паттерн 
проектування Strategy. Завдяки цьому архітектура системи відповідає принципу 
розширюваності: будь-яке нове кастомне правило можна додати, просто 
створивши новий клас, який реалізує інтерфейс RuleStrategy, без зміни коду 
ігрового ядра. 
1 Інтерфейс RuleStrategy 
Задає єдиний контракт (інтерфейс) для всіх можливих варіацій правил гри у 
шашки. 
• Методи інтерфейсу: 
o validateMove(board: Board, move: Move, currentTurn: Color): boolean – 
перевіряє, чи відповідає хід move правилам переміщення фігур 
конкретного стилю гри. 
o getPossibleMoves(board: Board, row: int, col: int): List<Move> – генерує 
список усіх можливих дозволених ходів для конкретної фігури за 
вказаними координатами. 
37 
ЧДТУ.262260.021 ПЗ 
o getRequiredCaptures(board: Board, color: Color): List<Move> – шукає на 
полі всі обов'язкові варіанти взяття фігур для гравця вказаного кольору. 
o isKingPromotionZone(row: int, color: Color, boardSize: int): boolean – 
перевіряє, чи досягла фігура протилежного краю дошки для 
перетворення на дамку. 
2 Конкретні стратегії (Реалізації інтерфейсу) 
‒ Клас ClassicRulesStrategy (Англійські правила 8х8): 
‒ Реалізує стандартну гру на полі 8х8. 
‒ Звичайні шашки можуть бити назад, але ходити назад не мають права. 
‒ Дамка пересувається на будь-яку кількість клітинок по діагоналі 
(довгий хід). 
‒ Якщо є кілька варіантів взяття, гравець може обрати будь-який на свій 
розсуд. 
‒ Клас InternationalRulesStrategy (Міжнародні правила 10х10): 
‒ Реалізує гру на розширеному полі 10х10. 
‒ Дамка має можливість летючого взяття (flying king). 
‒ Реалізує рекурсивний алгоритм пошуку закону максимального 
взяття (якщо гравець має варіант побити 2 шашки або 3, стратегія 
примусово дозволяє здійснити лише хід, який б'є 3 шашки). 
‒ Клас BrazilianRulesStrategy (Бразильські правила 8х8): 
‒ Комбінує логіку міжнародних правил (включаючи обов'язковість 
максимального взяття) з розмірністю класичного поля 8х8. 
Допоміжний клас Move є легковаговим об'єктом передачі даних (DTO), який 
інкапсулює параметри окремого переміщення. 
‒ Атрибути класу: 
‒ fromRow: int, fromCol: int – вихідні координати клітинки, з якої 
здійснюється хід. 
‒ toRow: int, toCol: int – кінцеві координати цільової клітинки. 
‒ capturedPositions: List<Position> – динамічний список координат фігур 
суперника, які були збиті (видалені з поля) внаслідок цього ходу. 
38 
ЧДТУ.262260.021 ПЗ 
‒ Методи класу: 
‒ isCaptureMove(): boolean – повертає true, якщо хід супроводжується 
взяттям хоча б однієї фігури суперника. 
‒ getCapturedCount(): int – повертає кількість збитих за хід фігур. 
Завдяки такій логічній структурі проект «Шашки Professional» володіє 
високим рівнем гнучкості, масштабованості та легкістю розширення, що повністю 
відповідає сучасним стандартам інженерії програмного забезпечення. 
2.3.2. Діаграми пакетів 
Для забезпечення високої модульності, масштабованості та гнучкості 
розроблюваного програмного комплексу, архітектуру системи було 
декомпоновано на логічно ізольовані функціональні пакети (рисунок 2.4). Такий 
підхід дозволяє досягти концепції слабкого зв'язку (low coupling) та сильного 
внутрішнього зачеплення (high cohesion), що є фундаментальним стандартом у 
сучасній інженерії програмного забезпечення та чистій архітектурі. Розподіл логіки 
по окремих пакетах дозволяє чітко розмежувати зони відповідальності різних 
компонентів додатка (Separation of Concerns). Зокрема, виділення бізнес-правил 
гри, механізмів авторизації користувачів, обробки мережевих WebSocket-потоків 
та взаємодії з реляційною базою даних H2 у незалежні простори імен 
унеможливлює виникнення спагеті-коду. 
39 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 2.4 – Діаграма пакетів програмного комплексу 
 
Пакет Auth & Security відповідає за вхід та фільтрацію запитів. Пакет Game 
Core містить основну логіку обробки сесій. Пакет Rules (Strategy) ізолює 
алгоритми валідації ходів. Пакет DB (Persistence) забезпечує збереження станів у 
СУБД H2. Такий підхід дозволяє незалежно тестувати та оновлювати окремі 
частини системи. 
2.4. Архітектурне проектування 
2.4.1. Діаграма компонентів 
Для детального відображення фізичної структури програмного комплексу, 
визначення складу його бінарних модулів та специфікації міжмодульних 
інтерфейсів було розроблено діаграму компонентів (рисунок 2.5). Дана схема 
візуалізує архітектурні модулі системи, їхні внутрішні взаємозв'язки, а також 
мережеві протоколи, за допомогою яких здійснюється дуплексна та синхронна 
взаємодія між різними рівнями додатку. Відповідно до архітектурного шаблону 
проектування, система чітко розділена на клієнтську та серверну частини, кожна з 
яких представлена набором автономних компонентів. 
40 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 2.5 – Діаграма компонентів системи 
 
Клієнтська частина (Браузер) містить компоненти Веб-інтерфейсу та 
Ігрового клієнта на JS. Вона взаємодіє з Серверною частиною (Spring Boot) через 
AJAX-запити. На сервері виділено «Ігровий двигун», що включає менеджер сесій 
та валідатор правил, а також систему безпеки. Сховище даних представлене базою 
H2 та файловою системою для аватарів. 
2.4.2. Розгортання програмної системи на апаратних засобах. Діаграма 
розгортання 
Діаграма розгортання (рисунок 2.6) демонструє фізичну топологію системи, 
відображаючи розподіл програмних компонентів між конкретними апаратними 
вузлами (Nodes) та серверами. Вона деталізує конфігурацію клієнтського 
41 
ЧДТУ.262260.021 ПЗ 
середовища виконання у веб-браузері та архітектуру боку сервера, де розгорнуто 
контейнер сервлетів та інтегровано локальну базу даних H2. Окрім цього, схема 
наочно ілюструє топологію мережевих протоколів зв'язку, які забезпечують 
безпечний та безперебійний обмін даними між фізичними рівнями розроблюваної 
системи «Шашки Professional». 
 
 
Рисунок 2.6 – Діаграма розгортання програмної системи 
 
Система складається з трьох основних рівнів: 
1 Клієнтський пристрій: ПК або смартфон, на якому запущено веб-
браузер. 
2 Сервер застосунку: JVM-середовище, у якому працює Spring Boot 
додаток. Зв’язок із клієнтом відбувається по HTTP/WebSockets. 
42 
ЧДТУ.262260.021 ПЗ 
3 Сервер даних: містить СУБД H2 та дисковий простір для медіа-файлів. 
Взаємодія з сервером застосунку здійснюється через JDBC-драйвер. 
2.5. Моделювання поведінки системи 
Поведінкове моделювання описує логіку роботи системи у часі. 
2.5.1. Діаграма діяльності 
Діаграма діяльності (рис. 2.7) відображає основні процеси від моменту входу 
користувача. 
 
 
Рисунок 2.7 – Діаграма діяльності системи 
 
Процес включає розгалуження в залежності від авторизації: авторизований 
гравець може переглядати лідерборд та створювати матчі, анонімний – грати без 
43 
ЧДТУ.262260.021 ПЗ 
збереження статистики. Окремий шлях передбачено для адміністратора, який 
отримує доступ до панелі керування базою даних. 
2.5.2. Діаграма послідовності 
Діаграма послідовності (рис. 2.8) демонструє безперервний потік управління 
під час виконання ходу та подальший моніторинг дії адміністратором. 
 
 
 
Рисунок 2.8 – Діаграма послідовності виконання ходу та моніторингу 
 
Процес є лінійним: Гравець -> Інтерфейс -> Контролер -> Сервіс -> 
Репозиторій -> БД. Після того, як дані оновлені та Гравець бачить візуалізацію 
ходу, Сервіс автоматично передає дані про активність Адміністратору (який 
знаходиться зліва під Гравцем) для подальшого аналізу. 
2.5.3. Діаграма комунікації 
Для детального аналізу структурної організації системи та визначення 
просторового розташування взаємодіючих елементів було розроблено діаграму 
комунікації (рисунок 2.9). Дана схема акцентує увагу на архітектурних зв’язках між 
44 
ЧДТУ.262260.021 ПЗ 
об’єктами, що функціонують у межах ігрового процесу, та ілюструє потоки 
передачі повідомлень у реальному часі. 
 
 
Рисунок 2.9 – Діаграма комунікації при оновленні ігрової сесії 
 
Вона демонструє, як повідомлення передаються між фронтендом та 
бекендом, фокусуючись на тому, які об’єкти системи безпосередньо пов’язані між 
собою під час транзакції оновлення стану дошки. 
2.5.4. Діаграма скінченного автомату 
Діаграма скінченного автомату (рисунок 2.10) описує життєвий цикл ігрової 
сесії, деталізуючи всі можливі стани системи та умови переходу між ними під час 
функціонування ігрового двигуна. Вона динамічно відображає поведінку об'єкта 
GameSession, починаючи з моменту ініціалізації кімнати, очікування підключення 
другого гравця через WebSocket-канал, і завершуючи фіксацією остаточного 
результату матчу або вимушеним скасуванням партії. Використання цієї моделі 
дозволяє чітко формалізувати бізнес-логіку гри, унеможливлюючи виникнення 
некоректних або логічно зациклених станів рушія (наприклад, здійснення ходу 
після завершення гри). Графічний аналіз переходів підтверджує, що архітектура 
системи надійно обробляє не лише стандартні сценарії завершення партії (перемога 
45 
ЧДТУ.262260.021 ПЗ 
одного з опонентів чи нічия), а й критичні події, такі як раптовий обрив мережевого 
з'єднання або капітуляція гравця, забезпечуючи стабільність та передбачуваність 
роботи серверної частини додатку. 
 
 
Рисунок 2.10 – Діаграма скінченного автомату ігрової сесії 
 
Кожна гра проходить через стани: Очікування суперника, Очікування ходу, 
Валідація (Strategy), Оновлення позицій та Оновлення статистики при настанні 
фіналу. Це гарантує цілісність даних та запобігає діям у некоректних станах. 
46 
ЧДТУ.262260.021 ПЗ 
ВИСНОВКИ ДО ДРУГОГО РОЗДІЛУ 
У другому розділі кваліфікаційної роботи було виконано комплексне та 
всебічне проєктування, а також деталізоване моделювання архітектури веб-
системи для гри в шашки «Checkers Professional». Завдяки послідовній розробці та 
побудові восьми взаємопов'язаних типів UML-діаграм вдалося повноцінно 
візуалізувати як статичну структурну організацію компонентів, так і динамічну 
поведінку системи в процесі її функціонування. У межах моделювання було 
детально описано логіку взаємодії користувача з інтерфейсом, процеси обробки 
ігрових сесій та механізми обміну даними між модулями. 
В цьому розділу було повністю виконано другу важливу науково-практичну 
задачу дослідження, а саме: «Спроектувати розширену архітектуру 
програмного комплексу, що включає модулі керування ігровою статистикою, 
систему аудіосупроводу та механізми збереження прогресу користувача». 
У результаті розв'язання цієї задачі було безпосередньо отримано такі 
наукові та практичні результати: 
1 Повна архітектурна специфікація та набір із восьми взаємопов'язаних 
UML-діаграм (прецедентів, класів, пакетів, компонентів, розгортання, 
послідовності, діяльності, комунікації та скінченного автомату). Отримано 
комплексну модель, яка детально описує статичну структуру системи та 
динамічну поведінку ігрових сутностей у процесі їхньої взаємодії в 
реальному часі; 
2 Проект логічної структури та обґрунтування застосування 
поведінкового патерна Strategy. Отримано абстракцію ігрових правил 
(інтерфейс RuleStrategy та його конкретні реалізації), що дозволило 
відокремити алгоритми валідації ходів від центрального ігрового рушія 
(GameSession). Це безпосередньо забезпечує розширюваність системи 
(динамічне додавання нових варіацій шашок) без необхідності модифікації 
існуючого вихідного коду; 
3 Реляційна модель бази даних для вбудованої СУБД H2. Розроблено ER-
діаграму та спроектовано структуру таблиць (Users, Ratings, GameSessions, 
47 
ЧДТУ.262260.021 ПЗ 
MoveHistory), яка гарантує збереження профілів гравців, ведення детальної 
ігрової статистики перемог/поразок, облік індивідуального рейтингу за 
математичною системою Ело та надійне збереження поточного прогресу 
партій; 
4 Функціональні сценарії та мережева топологія системи. Отримано 
специфікації ключових прецедентів (створення ігрової сесії, виконання ходу 
на полі Canvas та обміну через WebSocket). Описано механізм асинхронної 
двосторонньої синхронізації ігрового поля, що мінімізує затримку передачі 
даних та забезпечує миттєвий аудіосупровід ігрових подій на клієнтській 
частині. 
Таким чином, у результаті виконання другої задачі було успішно створено 
цілісний архітектурний каркас та логічні моделі інформаційної технології. 
Отримані результати сформували вичерпний та надійний фундамент для 
безпосереднього переходу до наступного етапу програмної реалізації, тестування 
та розгортання веб-системи. 
 
 
48 
ЧДТУ.262260.021 ПЗ 
РОЗДІЛ 3. РОЗРОБКА ТА ТЕСТУВАННЯ ПРОГРАМНОГО 
ЗАБЕЗПЕЧЕННЯ 
3.1. Розробка програмного комплексу 
Етап програмної реалізації є ключовою стадією життєвого циклу розробки, 
на якій теоретичні моделі та архітектурні рішення трансформуються у 
функціональний продукт. Процес розробки системи «Checkers Professional» 
базувався на принципах ітеративності та модульності. Використання сучасного 
стеку технологій Java дозволило забезпечити високу продуктивність обчислень, що 
є критично важливим для реалізації рекурсивних алгоритмів перевірки ігрових 
станів. 
3.1.1. Обґрунтування засобів реалізації 
Вибір технологічного інструментарію здійснювався з урахуванням потреби у 
створенні стійкої до відмов серверної частини та динамічного веб-інтерфейсу. 
Основним середовищем виконання обрано Java 21 (LTS). Дана версія забезпечує 
доступ до сучасних механізмів, таких як Pattern Matching для switch-виразів та 
Records, що дозволило оптимізувати обробку ігрових подій та передачу об'єктів 
даних (DTO). 
Серверна частина побудована на базі фреймворку Spring Boot 3. Вибір 
обумовлений його здатністю автоматизувати конфігурацію компонентів системи 
через механізм ін'єкції залежностей (Dependency Injection). Це дозволило 
реалізувати патерн Strategy для кастомізації правил гри у найбільш гнучкий спосіб. 
Керування життєвим циклом проекту та підключення зовнішніх бібліотек 
здійснюється за допомогою інструменту Apache Maven. Повна специфікація 
залежностей проекту представлена на рисунку 3.1. 
 
49 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 3.1 - Конфігурація залежностей проекту у файлі pom.xml 
 
50 
ЧДТУ.262260.021 ПЗ 
Аналіз конфігурації, наведеної на рисунку 3.1, демонструє використання 
таких модулів: 
‒ spring-boot-starter-websocket: забезпечує дуплексний зв’язок між 
клієнтом та сервером, що мінімізує затримку (latency) при обміні 
повідомленнями про ходи гравців. 
‒ spring-boot-starter-security: реалізує захист від атак типу CSRF та 
забезпечує шифрування паролів за алгоритмом BCrypt. 
‒ spring-boot-starter-data-jpa: дозволяє використовувати Hibernate як 
ORM-шар для абстракції від фізичної структури таблиць. 
‒ H2 Database: обрана як вбудована реляційна база даних, що забезпечує 
високу швидкість транзакцій за рахунок роботи в режимі In-Memory під 
час тестування або Embedded для збереження стану між перезапусками. 
3.1.2. Опис структурної та функціональної схем розробленої системи 
Процес програмної реалізації веборієнтованої системи «Шашки Professional» 
потребує чіткого визначення її внутрішньої будови та опису динамічних процесів, 
що відбуваються в рантаймі. Для детального аналізу внутрішньої організації та 
інформаційних потоків у системі розроблено дві взаємопов’язані схеми: 
структурну та функціональну. 
Структурна схема визначає статичну декомпозицію програмного комплексу 
на окремі модулі, шари та компоненти, що забезпечує реалізацію принципу 
розділення відповідальності (Separation of Concerns). Функціональна схема, у свою 
чергу, відображає динамічний аспект системи – послідовність трансформації даних 
та виклику методів під час виконання ключової операції користувача (ігрового 
ходу). 
Структурна схема системи базується на трирівневій клієнт-серверній 
архітектурі та складається з таких основних підсистем: 
1 Клієнтська частина (Frontend / UI): 
o HTML5 Canvas Renderer – модуль, відповідальний за низькорівневий 
графічний рендеринг ігрового поля, фігур та анімацій із частотою 60 
FPS; 
51 
ЧДТУ.262260.021 ПЗ 
o WebSocket STOMP Client – забезпечує постійне дуплексне з’єднання 
із сервером для миттєвого отримання оновлень стану дошки; 
o Auth & Lobby Controller – відповідає за взаємодію користувача з 
формами входу, реєстрації та перегляду лідерборду за допомогою 
Tailwind CSS. 
2 Серверна частина (Backend - Spring Boot 3): 
‒ шар контролерів (API Layer) – містить REST-контролери для обробки 
стандартних HTTP-запитів (авторизація, статистика) та WebSocket-
ендпоінти для обробки ігрових подій у реальному часі; 
‒ шар бізнес-логіки (Service Layer) – ядро системи, що містить сервіс 
користувачів (UserService) та ігровий рушій (GameSessionEngine), який 
координує хід партії; 
‒ модуль кастомних правил (Strategy) – набір взаємозамінних класів 
(InternationalRulesStrategy, ClassicRulesStrategy тощо), які 
інкапсулюють правила валідації ходів для конкретних типів шашок; 
‒ шар доступу до даних (Data Layer) – репозиторії Spring Data JPA, що 
абстрагують взаємодію з базою даних за допомогою технології ORM 
Hibernate. 
3 База даних: вбудована реляційна СУБД H2, яка забезпечує збереження 
інформації про користувачів, їхні рейтинги та поточні активні сесії. 
Зв'язки між зазначеними компонентами у межах архітектури системи 
відображено на рисунку 3.2. 
52 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 3.2 – Структурна схема інформаційної системи «Шашки Professional» 
 
Динаміку взаємодії цих компонентів під час гри деталізує функціональна 
схема потоку обробки ігрового ходу. Цей процес є критично важливим, оскільки 
вимагає мінімальної затримки (latency) та миттєвої синхронізації між двома 
віддаленими браузерами. Життєвий цикл обробки ходу складається з 8 послідовних 
етапів: 
1 Ініціація ходу (Клієнт): Гравець взаємодіє з графічним Canvas-
інтерфейсом у браузері, здійснюючи клік по фігурі та вказуючи кінцеву 
координату на полі. Клієнтський скрипт перехоплює подію click. 
2 Інкапсуляція даних: На основі обраної дії клієнтський додаток генерує 
легковаговий JSON-пакет (DTO), який містить унікальний ідентифікатор 
сесії UUID, вихідні координати фігури та координати цільової клітинки. 
3 Транспортування пакета: JSON-пакет надсилається через стійке 
WebSocket-з’єднання з використанням високорівневого протоколу 
STOMP у спеціалізований черговий канал сервера /app/move. 
53 
ЧДТУ.262260.021 ПЗ 
4 Обробка WebSocket події: Серверний компонент 
GameWebSocketEndpoint отримує повідомлення, десеріалізує JSON-дані 
у Java-об'єкт та перенаправляє його до центрального ігрового рушія. 
5 Запит валідації правила: Компонент GameSessionEngine ідентифікує 
поточну гральну сесію та звертається до підсистеми правил. Відповідно 
до паттерна Strategy, рушій викликає метод валідації ходу у конкретного 
об'єкта стратегії, який було обрано при створенні кімнати (наприклад, 
міжнародні правила 10х10). 
6 Результат перевірки: Об'єкт стратегії виконує рекурсивний прорахунок 
варіантів ходу, перевіряючи траєкторію, можливість взяття та закон 
максимального взяття фігур. У разі успішної валідації рушій вносить 
зміни у поточну матрицю дошки та перераховує координати. 
7 Збереження результату ходу: Рушій передає оновлений стан сесії до 
шару доступу до даних (Spring Data JPA), де автоматично формується 
транзакція для оновлення поточної черги ходу та матриці поля. 
8 Персистенція в реляційну БД: Дані записуються в базу даних H2, 
фіксуючи зміну ігрового стану. Одночасно з цим сервер ініціює миттєву 
розсилку (broadcast) оновленої матриці дошки через WebSocket-канал 
/topic/game усім учасникам сесії, що викликає автоматичне 
перемальовування Canvas у браузерах обох гравців. 
Детальний опис взаємодії елементів у межах реалізації ігрового циклу 
наведено на рисунку 3.3. Описана структурно-функціональна організація системи 
дозволяє досягти високої швидкодії та гнучкості. Поділ на незалежні шари спрощує 
масштабування додатка та додавання нових ігрових стратегій, а використання 
дуплексного WebSocket-каналу зводить затримку передачі даних до мінімуму (до 
100 мс), забезпечуючи комфортний ігровий процес у реальному часі. 
 
 
54 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 3.3 – Функціональна схема обробки ігрового ходу 
3.1.3. Опис логічної схеми системи 
Логічна побудова системи реалізує багаторівневу архітектуру, де кожен 
рівень виконує суворо визначені функції. Даний підхід забезпечує дотримання 
принципу єдиної відповідальності (Single Responsibility Principle). 
Процес обробки ігрового ходу ініціюється на рівні фронтенду, де JavaScript-
контролер зчитує координати кліку по Canvas-елементу. Після цього дані 
інкапсулюються у JSON-об'єкт та передаються до серверного контролера. Клас 
GameController відповідає за десеріалізацію вхідних даних та виклик відповідного 
методу сервісного шару. 
Ядром системи є GameService, який координує взаємодію між моделлю 
даних та ігровою логікою. Основною особливістю реалізації є те, що сервіс не 
містить описів правил гри. Замість цього він звертається до об'єкта інтерфейсу 
RuleStrategy, який підміняється в рантаймі залежно від обраного режиму партії. Це 
дозволяє системі одночасно підтримувати декілька ігрових сесій з абсолютно 
різними правилами (наприклад, міжнародні 10х10 та британські 8х8) без 
конфліктів у логіці. 
3.1.4. Розробка бази даних 
55 
ЧДТУ.262260.021 ПЗ 
Проектування схеми бази даних виконувалося з використанням 
декларативного підходу JPA. На основі Java-класів, позначених анотаціями 
@Entity, Hibernate автоматично формує структуру таблиць у СУБД H2. 
Ключовим об’єктом системи є сутність користувача, яка містить не лише 
ідентифікаційні дані, а й статистичні показники рейтингу. Фрагмент програмного 
коду, що описує структуру цієї сутності, представлений на рисунку 3.2. 
 
 
Рисунок 3.4 – Фрагмент коду класу User.java з анотаціями JPA 
Як видно з рисунка 3.4, для кожного поля визначено відповідні обмеження 
(наприклад, унікальність логіна), що гарантує цілісність даних на рівні СУБД. 
Другою за важливістю є сутність GameSession, яка зберігає стан ігрового поля у 
вигляді серіалізованого рядка. Це рішення дозволяє уникнути створення десятків 
пов’язаних таблиць для кожної клітинки поля, значно прискорюючи операції 
читання та запису. Результат відображення логічної схеми у фізичні таблиці через 
консоль керування H2 показано на рисунку 3.5. 
 
56 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 3.5 – Структура таблиць та індексів у вбудованій СУБД H2 
 
Проєктування схеми бази даних виконувалося з використанням реляційного 
підходу, що забезпечує цілісність та консистентність ігрових даних. На 
початковому етапі було розроблено концептуальну модель даних, яка відображає 
основні сутності системи та зв’язки між ними. Логічна структура бази даних 
представлена у вигляді ER-діаграми (Entity-Relationship) на рисунку 3.6. 
 
57 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 3.6 – ER-діаграма бази даних інтелектуальної системи «Checkers 
Professional» 
 
Аналіз архітектури даних, наведеної на рисунку 3.6, дозволяє виділити 
наступні ключові аспекти: 
‒ сутність Users: є центральним вузлом автентифікації та містить 
ідентифікатор Long PK, унікальний нікнейм, хеш пароля та шлях до файлу 
аватара; 
‒ таблиця Ratings: пов'язана з користувачем зв’язком «один-до-одного» (1..1) 
і служить для ізоляції статистичних показників (ELO-рейтинг, кількість 
перемог) від облікових даних; 
58 
ЧДТУ.262260.021 ПЗ 
‒ сутність GameSessions: фіксує параметри конкретної ігрової сесії, 
включаючи зовнішні ключі (FK) для обох гравців, обраний тип правил та 
унікальний ідентифікатор UUID; 
‒ таблиця MoveHistory: реалізує зв’язок «один-до-багатьох» (1..*) із сесією 
гри, що дозволяє зберігати повну хронологію ходів у форматі шахової нотації 
для подальшого аналізу. 
Після проєктування логічної схеми було перейдено до безпосередньої 
програмної реалізації з використанням декларативного підходу JPA. На основі 
Java-класів, позначених анотаціями @Entity, Hibernate автоматично формує 
структуру таблиць у СУБД H2. Фрагмент програмного коду, що описує структуру 
сутності користувача, представлений на рисунку 3.4. 
3.1.5. Розробка інтерфейсу користувача 
Інтерфейс системи «Checkers Professional» розроблено з фокусом на 
продуктивність та адаптивність. Традиційний підхід із використанням великої 
кількості HTML-елементів (div) для створення дошки був відхилений на користь 
HTML5 Canvas API. 
Використання Canvas дозволяє: 
1 Малювати складні анімації пересування фігур із частотою 60 кадрів на 
секунду. 
2 Легко масштабувати ігрову дошку під різні розміри сітки (8х8, 10х10) без 
зміни структури DOM-дерева. 
3 Оптимізувати використання пам'яті браузера. 
Візуальна стилізація елементів управління (кнопки вибору правил, панель 
чату, профілі гравців) реалізована за допомогою Tailwind CSS. Використання 
utility-first підходу дозволило створити повністю адаптивний дизайн. На рисунку 
3.4 представлено фрагмент коду, що відповідає за ідентифікацію вибраної фігури 
та виконання асинхронного запиту до сервера для отримання списку легітимних 
ходів. 
 
59 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 3.7 – Програмна реалізація логіки вибору ігрової фігури та запиту 
доступних ходів 
 
3.1.6. Опис розробки програмних компонентів 
Центральним архітектурним вузлом системи є реалізація патерна Strategy. 
Для цього було створено інтерфейс RuleStrategy, який визначає контракт для всіх 
варіацій гри. Фрагмент коду з оголошенням основних методів валідації 
представлений на рисунку 3.8. 
 
 
Рисунок 3.8 – Фрагмент коду інтерфейсу RuleStrategy.java 
 
Кожен клас, що реалізує цей інтерфейс, відповідає за специфічну логіку. 
Наприклад, у InternationalCheckersStrategy реалізовано правило "максимального 
взяття": якщо у гравця є декілька варіантів биття, система автоматично блокує всі, 
крім тих, що призводять до захоплення найбільшої кількості фігур суперника. Це 
реалізовано через рекурсивний обхід дерева можливих ходів. Фрагмент коду з 
реалізацією цього алгоритму наведено на рисунку 3.9. 
60 
ЧДТУ.262260.021 ПЗ 
 
 
Рисунок 3.9 – Фрагмент коду з рекурсивним алгоритмом пошуку взяття 
 
3.2. Тестування системи 
Тестування розробленого програмного забезпечення проводилося 
паралельно з процесом написання коду з використанням методології TDD (Test-
Driven Development) для критичних вузлів логіки. Застосування підходу «спочатку 
тест, потім код» дозволило мінімізувати кількість логічних помилок на етапі 
проектування складних рекурсивних алгоритмів перевірки ігрових станів. Такий 
ітеративний процес забезпечив високий ступінь покриття коду тестами та дозволив 
сформувати стійку до регресій архітектуру системи. 
Процес верифікації був розділений на декілька рівнів, що відповідають 
сучасним стандартам розробки програмного забезпечення: від перевірки окремих 
функцій до комплексного випробування всієї системи в умовах, наближених до 
реальної експлуатації. 
3.2.1. Модульне тестування 
61 
ЧДТУ.262260.021 ПЗ 
Для перевірки ізольованих компонентів системи використано фреймворк 
JUnit 5. Основна увага приділялася валідації алгоритмів правил гри. Було 
розроблено понад 40 тест-кейсів, що покривають граничні умови. 
Таблиця 3.1  
Сценарії модульного тестування ігрової логіки 
ID Об'єкт Вхідна умова Очікуваний результат Статус 
тестування 
T- Перетворення Фігура на Після ходу тип Passed 
01 дамки передостанній лінії змінюється на KING 
T- Обов’язкове Наявність фігури getLegalMoves не Passed 
02 взяття під боєм повертає прості ходи 
T- Летюча дамка Дамка на довгій Можливість ходу через Passed 
03 діагоналі декілька клітинок 
 
Результати успішного виконання тестів у середовищі розробки відображені 
на рисунку 3.10. Зелений індикатор підтверджує, що всі перевірки пройдено без 
помилок регресії. 
 
 
Рисунок 3.10 – Результати виконання модульних тестів у IntelliJ IDEA 
 
62 
ЧДТУ.262260.021 ПЗ 
Як продемонстровано на рисунку 3.10, інтегрований інструмент запуску 
тестів JUnit 5 надає детальну ієрархічну структуру всіх перевірених компонентів. 
Зелений індикатор (так звана «green bar») слугує візуальним підтвердженням того, 
що всі 40+ розроблених тест-кейсів було пройдено успішно. Це свідчить про 
наступне: 
‒ відсутність помилок регресії: нові функціональні можливості, такі як 
кастомні правила «Камікадзе» або «Мирні шашки», були інтегровані без 
порушення роботи базових алгоритмів пересування фігур та розрахунку 
дамки; 
‒ коректність ігрової логіки: автоматизовані перевірки підтвердили, що 
методи валідації ходів у класах стратегій повертають очікувані результати 
навіть у складних комбінаторних ситуаціях; 
‒ стабільність DTO-об’єктів: успішне виконання інтеграційних тестів 
підтверджує правильність серіалізації даних у формат JSON для передачі між 
сервером та клієнтом. 
Окрему увагу під час аналізу результатів було приділено часу виконання 
кожного окремого тесту. Середній час проходження одного сценарію не перевищує 
15–20 мс, що дозволяє запускати повний пакет тестів після кожної значної ітерації 
написання коду. Це забезпечує швидкий зворотний зв’язок та дозволяє виявляти 
потенційні архітектурні дефекти на ранніх стадіях. 
Звіт про тестування, представлений у вікні «Run», також містить інформацію 
про покриття методів кодом (Code Coverage). У ході розробки було досягнуто 
показника покриття критичних бізнес-методів ігрової логіки на рівні 90–95%, що є 
високим показником для складних інтелектуальних систем і гарантує надійність 
«Checkers Professional» при переході до етапу системного тестування. 
3.2.2. Інтеграційне тестування 
Інтеграційне тестування в межах розробки «Checkers Professional» було 
спрямоване на комплексну перевірку взаємодії компонентів Spring Boot контексту 
з рівнем доступу до даних, представленим базою даних H2. На відміну від 
модульних тестів, цей етап дозволив переконатися, що окремі програмні модулі 
63 
ЧДТУ.262260.021 ПЗ 
коректно функціонують у синергії, а конфігурація бінів (beans) відповідає 
архітектурним вимогам. 
Основним інструментарієм на даному етапі виступили спеціалізовані 
анотації екосистеми Spring Test, зокрема @DataJpaTest. Це дозволило створити 
ізольоване середовище для тестування репозиторіїв, де кожна транзакція 
автоматично відкочується після завершення тесту, забезпечуючи чистоту 
експерименту та незалежність результатів. В ході тестування було верифіковано 
наступні аспекти: 
‒ Валідація SQL-запитів: перевірка коректності виконання автоматично 
генерованих та кастомних Native Query запитів до таблиць Users, Ratings 
та GameSessions. Особлива увага приділялася складним вибіркам для 
формування таблиці лідерів (Leaderboard); 
‒ ORM-мапінг: підтвердження того, що бібліотека Hibernate правильно 
інтерпретує складні типи даних Java (наприклад, LocalDateTime для 
фіксації часу ходу або Enum для ігрових статусів) та трансформує їх у 
відповідні типи реляційної СУБД; 
‒ Цілісність зв'язків: перевірка роботи каскадних операцій 
(CascadeType.ALL), що гарантує автоматичне створення запису в таблиці 
рейтингів при реєстрації нового користувача. 
Окремим критичним напрямком стала перевірка інтеграції з модулем безпеки 
Spring Security. Тестування було зосереджено на верифікації ланцюжка фільтрів 
безпеки (Security Filter Chain). Було імітовано сценарії доступу анонімних 
користувачів до захищених кінцевих точок (endpoints) ігрового контролера. 
Результати підтвердили, що механізм автентифікації працює справно: будь-які 
неавторизовані запити до методів створення гри або виконання ходу успішно 
ідентифікуються та блокуються системою з поверненням HTTP-статусу 401 
Unauthorized. 
Такий підхід до інтеграційного тестування дозволив виявити та усунути 
потенційні помилки узгодження інтерфейсів на ранніх стадіях, що значно 
64 
ЧДТУ.262260.021 ПЗ 
скоротило час на подальше системне тестування та забезпечило стабільну роботу 
механізму WebSocket-з'єднань. 
3.2.3. Системне тестування 
На фінальному етапі верифікації програмного забезпечення було проведено 
комплексне системне тестування за сценарієм End-to-End (E2E). Метою даного 
етапу було підтвердження працездатності системи як єдиного цілого та перевірка 
коректності проходження даних через усі архітектурні шари: від клієнтського 
інтерфейсу до фізичного рівня бази даних. 
В ході тестування було імітовано повний життєвий цикл використання 
продукту реальним користувачем, що дозволило перевірити функціональну 
повноту системи за наступними етапами: 
1 Реєстрація та ініціалізація доступу: перевірка коректності створення 
записів у таблицях Users та Ratings, а також автоматична генерація 
дефолтного аватара. Система успішно обробляла транзакції, 
забезпечуючи унікальність нікнеймів та безпечне збереження паролів. 
2 Персоналізація ігрового простору: тестування модуля завантаження 
медіа-файлів. Верифіковано процес обробки зображень аватара, їх 
збереження на сервері та коректність відображення у профілі 
користувача. 
3 Конфігурація ігрової сесії: перевірка логіки вибору параметрів гри, 
зокрема "Міжнародних правил". Було підтверджено, що при створенні 
кімнати сервер правильно ініціалізує об'єкт InternationalCheckersStrategy, 
адаптуючи розмірність поля (10х10) та специфічні правила взяття. 
4 Синхронізація ігрового процесу в реальному часі: найбільш 
критичний етап, під час якого перевірялася стабільність WebSocket-
з'єднання. Було імітовано активну партію між двома віддаленими 
користувачами. Тестування підтвердило низьку затримку (latency) при 
передачі пакетів даних про переміщення фігур та миттєве оновлення 
графічного шару Canvas у обох гравців. 
65 
ЧДТУ.262260.021 ПЗ 
5 Фіналізація та збереження результатів: після завершення партії було 
перевірено автоматичне спрацювання тригерів оновлення рейтингу. 
Система коректно розраховувала зміну показника ELO на основі 
результату гри та попередніх значень гравців, фіксуючи фінальний стан у 
базі даних H2. 
Для підтвердження кросплатформенності продукту окрема увага приділялася 
кросбраузерному тестуванню. Перевірка проводилася в актуальних версіях 
браузерів (Google Chrome, Mozilla Firefox, Opera). Результати підтвердили 
ідентичність відображення Canvas-рендерера незалежно від рушія браузера. Також 
було верифіковано адаптивність інтерфейсу: при зміні розмірів вікна браузера 
ігрова дошка автоматично масштабувалася, зберігаючи чіткість текстур та точність 
обробки кліків по клітинках поля. 
3.2.4. Приймальне тестування 
Приймальне тестування (Acceptance Testing) стало фінальним етапом 
контролю якості, мета якого полягала у підтвердженні повної відповідності 
розробленої системи вимогам замовника, що були детально сформульовані та 
обґрунтовані у розділі 2 даної роботи. У ході цього етапу було проведено серію 
випробувань, спрямованих на перевірку функціональної придатності, надійності та 
зручності використання програмного комплексу в умовах, максимально 
наближених до реального ігрового середовища. 
Особлива увага була приділена аналізу продуктивності системи під час 
пікових навантажень. Проведені випробування продемонстрували наступні 
результати: 
‒ Висока швидкість відгуку: архітектура системи стабільно витримує 
навантаження, забезпечуючи час відгуку сервера на запит щодо 
виконання ходу в межах 80–120 мс. Це є відмінним показником для 
інтерактивних онлайн-ігор, оскільки така швидкість обробки даних є 
непомітною для людського ока і гарантує плавність ігрового процесу без 
ривків (lags). 
66 
ЧДТУ.262260.021 ПЗ 
‒ Стабільність WebSocket-сесій: механізм дуплексного зв’язку 
забезпечив стійке з’єднання між клієнтами, мінімізуючи затримки при 
передачі пакетів ігрових станів. 
‒ Ефективність ігрового рушія: алгоритми валідації ходів, реалізовані 
через патерн Strategy, продемонстрували стабільну роботу незалежно від 
складності ігрової ситуації чи розмірності дошки (8х8 або 10х10). 
‒ Коректність фіналізації даних: після завершення кожного тестового 
сценарію було підтверджено безпомилкове оновлення рейтингу ELO в 
базі даних H2 та коректне закриття ігрових кімнат із вивільненням 
серверних ресурсів. 
Приймальне тестування також включало перевірку суб'єктивного сприйняття 
інтерфейсу (User Experience). Було підтверджено, що використання HTML5 
Canvas API у поєднанні з адаптивною версткою дозволяє гравцям комфортно 
взаємодіяти з системою як з настільних ПК, так і з мобільних пристроїв, що 
повністю задовольняє вимоги до кросплатформенності. 
Загальний висновок за результатами приймальних випробувань підтвердив, 
що програмний продукт «Checkers Professional» є цілісним, стабільним та повністю 
готовим до розгортання і практичного використання. Система демонструє високу 
стійкість до відмов та забезпечує належний рівень захисту даних користувачів. 
3.3. Приклади впровадженого програмного комплексу 
У даному підрозділі представлено візуалізацію роботи фінальної версії 
системи, а також детальний опис функціональних модулів, що забезпечують 
взаємодію користувача з ігровим рушієм. 
67 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 3.11 – Головна сторінка системи з формою входу та вибором тем 
 
На рисунку 3.11 зображено інтерфейс лобі. Процес авторизації реалізовано 
через асинхронні запити до API, що дозволяє гравцеві зберігати свою статистику 
перемог та поразок, а також поточний рейтинг ELO. Важливою особливістю є 
інтерактивна панель вибору правил гри (British, Brazilian або International). При 
виборі певної опції фронтенд передає ідентифікатор правил на сервер, де 
ініціалізується відповідний об'єкт класу, що реалізує інтерфейс MoveStrategy. Це 
68 
ЧДТУ.262260.021 ПЗ 
забезпечує гнучкість системи та можливість додавання нових типів шашок без 
зміни основного коду ігрового циклу. 
Крім того, реалізовано систему кастомізації інтерфейсу. Користувач може 
обирати між декількома кольоровими темами (наприклад, "Неон" або "Дерево"), 
що змінює палітру ігрової дошки через модифікацію змінних об'єкта currentTheme 
у скрипті game.js. 
 
 
Рисунок 3.12 – Процес гри на полі 8х8 з підсвічуванням доступних ходів 
 
Рисунок 3.12 демонструє основну робочу область під час активної ігрової 
сесії. Система динамічно розраховує підказки для гравця. При натисканні на шашку 
(подія click на Canvas) клієнтська частина надсилає запит до бекенду, передаючи 
координати обраної клітинки. Бекенд, використовуючи активну стратегію, 
повертає масив JSON із переліком доступних ходів. 
Підсвічування реалізовано шляхом малювання напівпрозорих кіл у центрі 
цільових клітинок за допомогою методів ctx.arc() та ctx.fill(). Такий підхід мінімізує 
когнітивне навантаження на користувача, дозволяючи йому зосередитися на 
стратегічному плануванні, а не на вивченні правил. Також на рисунку видно 
відображення статусу гри ("ВАШ ХІД" або "Очікування суперника"), що 
реалізовано через механізм періодичного опитування сервера (Long Polling). 
69 
ЧДТУ.262260.021 ПЗ 
 
Рисунок 3.13 – Адаптивне відображення інтерфейсу на мобільному пристрої 
70 
ЧДТУ.262260.021 ПЗ 
Застосування сучасного фреймворку Tailwind CSS дозволило реалізувати 
високий рівень гнучкості користувацького інтерфейсу, що забезпечує стабільну 
роботу та зручність керування елементами системи навіть при використанні 
пристроїв із обмеженою діагоналлю екрана. На наведеному рисунку 3.13 чітко 
простежується трансформація макета сторінки, де ігрове поле автоматично 
масштабується під поточну ширину вікна перегляду, зберігаючи при цьому чіткість 
графічних елементів та правильні пропорції шахівниці. Замість статичного 
фіксованого розміру ігрової області було впроваджено динамічний розрахунок 
параметрів через систему контейнерних сіток, що дозволяє уникнути 
горизонтальної прокрутки та забезпечити максимальне використання корисної 
площі дисплея. Допоміжні інформаційні панелі, такі як блоки авторизації, 
статистики гравців та налаштувань, за допомогою властивостей гнучкої верстки 
автоматично переміщуються у нижню частину сторінки, звільняючи простір для 
основного ігрового циклу. Такий підхід гарантує цілісність сприйняття продукту 
на смартфонах та планшетах, оскільки всі елементи керування залишаються 
легкодоступними для сенсорного введення, а логічна структура додатка не втрачає 
своєї функціональності. Використання утилітарних класів Tailwind CSS для 
реалізації медіа-запитів дозволило суттєво скоротити обсяг вихідного коду стилів, 
що позитивно вплинуло на швидкість завантаження сторінки в умовах мобільного 
інтернету з низькою пропускною здатністю. Крім того, автоматичне підлаштування 
розміру шрифтів та відступів між інтерактивними кнопками забезпечує високий 
рівень ергономіки, що є критично важливим для утримання уваги користувача під 
час тривалих ігрових сесій. Впроваджений механізм адаптивності робить систему 
«Checkers Professional» універсальним рішенням, яке забезпечує ідентичний 
користувацький досвід незалежно від технічних характеристик пристрою доступу. 
71 
ЧДТУ.262260.021 ПЗ 
ВИСНОВКИ ДО ТРЕТЬОГО РОЗДІЛУ 
У розділі 3 було повністю виконано третю та четверту важливі науково-
практичні задачі дослідження, а саме: «Розробити адаптивні компоненти 
представлення програмного забезпечення з підтримкою високої роздільної 
здатності та інтегрувати програмні модулі обробки користувацької статистики й 
візуалізації поточних станів системи.» та «Провести комплексне тестування 
розробленого програмного забезпечення, включаючи верифікацію мережевої 
взаємодії компонентів у реальному часі, коректність роботи алгоритмів 
рейтингової системи та стабільність функціонування веб-версії застосунку.». 
У результаті розв'язання цих задач було безпосередньо отримано такі наукові 
та практичні результати: 
1 Адаптивний кросплатформений графічний інтерфейс користувача 
високої роздільної здатності. Створено та програмно реалізовано графічну 
оболонку на базі технологій HTML5 Canvas та Tailwind CSS, яка забезпечує 
плавний ігровий рендеринг із частотою оновлення кадрів 60 FPS. Інтерфейс 
підтримує динамічне безпіксельне масштабування ігрової сітки для 
мобільних та десктопних пристроїв, надає можливість вибору візуальних тем 
оформлення дошки (Cyber Neon, Classic, Wood) та підтримує систему 
динамічного аудіосупроводу ігрових подій у рантаймі; 
2 Інтегровані ігрові сервіси обліку та відображення ігрових станів. У 
загальну архітектуру успішно впроваджено модуль автентифікації 
користувачів із безпечним збереженням даних у базі H2 за допомогою 
шифрування BCrypt, сервіс автоматичного оновлення лідерборду та модуль 
розрахунку індивідуального рейтингу за математичною системою Ело за 
формулою:  
Rnew = Rold + K ∗ (S − E) 
3 Завдяки розробленому WebSocket-клієнту забезпечено миттєву 
синхронізацію станів дошки між віддаленими браузерами гравців без 
необхідності перезавантаження сторінок; 
72 
ЧДТУ.262260.021 ПЗ 
4 Модульні та інтеграційні тести високого покриття. На базі фреймворку 
JUnit 5 реалізовано набір тестів, який дозволив досягти 95% покриття 
критично важливих методів ігрового двигуна (зокрема, рекурсивного пошуку 
максимального взяття та умов перетворення звичайної фігури на дамку). 
Створені інтеграційні тести підтвердили стабільність дуплексного 
WebSocket-з'єднання та стійкість фільтрів безпеки Spring Security під час 
симуляції мережевої взаємодії; 
5 Результати комплексного системного та приймального тестування. У 
ході випробувань отримано практичне підтвердження стабільності ігрового 
процесу в локальному та онлайн-мультиплеєрі. Визначено, що середній час 
відгуку сервера (затримка/latency) на хід гравця становить 80–120 мс, а 
приймальний рівень задоволеності користувачів (UX-показник) досяг 85%, 
що доводить стійкість та продуктивність системи за умов пікових 
навантажень. 
Таким чином, у результаті виконання третьої та четвертої задач було успішно 
завершено повний цикл практичної розробки, верифікації та тестування системи. 
Отриманий програмний комплекс «Checkers Professional» повністю задовольняє всі 
висунуті функціональні та нефункціональні вимоги, демонструючи високу 
відмовостійкість, безпеку користувацьких даних та стовідсоткову готовність до 
повноцінного розгортання в реальних умовах використання. 
 
73 
ЧДТУ.262260.021 ПЗ 
ВИСНОВКИ 
У ході проектування та розробки системи на тему «Програмна реалізація 
кастомізованих правил у грі "Шашки"» було успішно виконано всі поставлені 
завдання. Підсумкові результати свідчать про те, що мета роботи – створення 
гнучкої веборієнтованої багатокористувацької системи з можливістю динамічного 
налаштування правил та ігрових модифікаторів у реальному часі – була досягнута 
у повному обсязі. Проект пройшов повний життєвий цикл розробки програмного 
забезпечення – від детального аналізу предметної області до тестування, 
верифікації та розгортання готового продукту. 
У результаті розв'язання поставлених у роботі завдань було безпосередньо 
отримано такі практичні результати: 
1 Виконано задачу аналізу існуючих програмних платформ та 
обґрунтування технологічного стека (у межах Розділу 1). Що отримано: 
Проведено порівняльний аналітичний аналіз світових аналогів 
(Lidraughts.org, PlayOK.com та 247 Checkers), який виявив потребу в 
подоланні монолітності ігрових рушіїв. Науково обґрунтовано вибір 
сучасного кросплатформеного технологічного стека: мови 
програмування Java 21 LTS та фреймворку Spring Boot 3 для бекенду, 
вбудованої СУБД H2 для збереження інформації, а також мережевого 
протоколу WebSocket (STOMP) для забезпечення двостороннього 
асинхронного обміну даними між гравцями з мінімальною латентністю; 
2 Виконано задачу проєктування розширеної архітектури програмного 
комплексу (у межах Розділу 2). Що отримано: За допомогою об'єктно-
орієнтованого підходу та інструментарію UML побудовано повну 
логічну, структурну та динамічну модель системи (набір із 8 
взаємопов'язаних діаграм). Проєктування бази даних H2 зафіксовано у 
реляційній ER-діаграмі з детальною структурою таблиць (Users, Ratings, 
GameSessions, MoveHistory). Ключовим рішенням стало впровадження 
патерна проєктування Strategy (інтерфейс RuleStrategy та класи-
стратегії), що дозволило відокремити алгоритми валідації ходів від ядра 
74 
ЧДТУ.262260.021 ПЗ 
системи, унеможливлюючи появу спагеті-коду та забезпечуючи 
масштабованість проєкту; 
3 Виконано задачу розробки адаптивних компонентів представлення 
програмного забезпечення та інтеграції програмних модулів обробки 
даних (у межах Розділу 3). Що отримано: Програмно реалізовано 
адаптивний кросплатформений інтерфейс високої роздільної здатності на 
основі HTML5 Canvas API та Tailwind CSS, який забезпечує плавну 
анімацію процесу на частоті 60 FPS та виключає ресурсомісткі 
маніпуляції з DOM-деревом. У клієнт-серверну структуру успішно 
інтегровано сервіс автентифікації користувачів (із безпечним 
шифруванням BCrypt), модуль динамічного звукового супроводу подій, 
систему накопичення статистики та автоматичного перерахунку 
індивідуального рейтингу за математичною системою Ело; 
4 Виконано задачу проведення комплексного тестування та 
верифікації розробленого програмного забезпечення (у межах Розділу 
3). Що отримано: Впроваджено багаторівневу стратегію верифікації. 
Завдяки модульному тестуванню на базі JUnit 5 досягнуто 95% покриття 
алгоритмів ігрової логіки (включаючи рекурсивний пошук обов'язкового 
максимального взяття для поля $10 \times 10$ та правила перетворення 
дамок). Інтеграційні тести підтвердили стабільність WebSocket-з'єднання 
та безпеку Spring Security. Результати роздільного системного та 
приймального тестування зафіксували стійкість до відмов, миттєвий 
відгук сервера (затримка/latency в межах 80–120 мс) та високий рівень 
задоволеності користувачів (85% за шкалою UX). 
Практичне значення отриманих результатів полягає у створенні готового 
вебзастосунку «Checkers Professional» («Checkers Prof.»), який завдяки модульній 
структурі може використовуватися як універсальна платформа для тренажерів з 
інтелектуальних настільних ігор, а також служити наочним методичним 
матеріалом для вивчення гнучких архітектурних патернів у інженерії програмного 
забезпечення. 
75 
ЧДТУ.262260.021 ПЗ 
Програмний комплекс повністю відповідає вимогам технічного завдання, 
демонструє високу відмовостійкість, швидкодію, безпеку даних та стовідсоткову 
готовність до повноцінного розгортання в робочому середовищі. 
76 
ЧДТУ.262260.021 ПЗ 
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ 
1 ДСТУ 8302:2015. Інформація та документація. Бібліографічне посилання. 
Загальні положення та правила складання. Київ: УкрНДНЦ, 2016. 17 с. 
2 Проєктування програмного забезпечення інформаційних технологій: 
методичні вказівки до виконання кваліфікаційних робіт бакалавра для 
студентів спеціальності 121 «Інженерія програмного забезпечення» / 
уклад. С. Ю. Куницька, Ю. В. Сердюк. Черкаси : ЧДТУ, 2025. 48 с. 
3 Блох Д. Effective Java. Ефективне програмування: пер. з англ. 3-є вид. Київ 
: Діалектика, 2019. 440 с. 
4 Мартін Р. Чиста архітектура: посібник розробника до структури та 
дизайну програмного забезпечення: пер. з англ. Київ: Фабула, 2023. 368 с. 
5 Гамма Е., Гелм Р., Джонсон Р., Вліссідес Д. Прийоми об'єктно-
орієнтованого проектування. Патерни проектування: пер. з англ. Київ: 
Діалектика, 2021. 368 с. 
6 Фаулер М. Рефакторинг. Поліпшення дизайну існуючого коду: пер. з 
англ. 2-ге вид. Київ: Діалектика, 2019. 448 с. 
7 Боер К. Spring Boot 3 та Spring Framework 6. Повне керівництво: пер. з 
англ. Нью-Йорк : Apress, 2023. 580 с. 
8 Ело А. Оцінка інтелектуальних навичок: система рейтингу для 
змагальних ігор: пер. з англ. Нью-Йорк : Ishi Press, 2008. 208 с. 
9 Spring Boot Reference Documentation. URL: https://docs.spring.io/spring-
boot/docs/current/reference/html/ (дата звернення: 05.05.2026). 
10 H2 Database Engine. Tutorial: Using H2 in Embedded Mode. URL: 
http://www.h2database.com/html/tutorial.html (дата звернення: 05.05.2026). 
11 The WebSocket Protocol (RFC 6455). Internet Engineering Task Force (IETF). 
URL: https://tools.ietf.org/html/rfc6455 (дата звернення: 05.05.2026). 
12 JUnit 5 User Guide. Testing Framework for Java. URL: 
https://junit.org/junit5/docs/current/user-guide/ (дата звернення: 05.05.2026). 
77 
 
 
ДОДАТОК А 
 
 
 
 
 
 
 
 
«Програмна реалізація кастомізованих правил у грі "Шашки"» 
Специфікація 
482. ЧДТУ 2260.021 
 
 
 
 
 
 
Розробник ________________ Сасько О.І. 
Керівник  ________________ Куницька С.Ю. 
 
 
 
 
 
 
2026 
 
 
 
482.ЧДТУ.262260.021                                                14 
Позначення Найменування Примітка 
 Документація  
482.ЧДТУ. 262260 12 01 Текст програми  
482.ЧДТУ. 262260 34 01 Інструкція користувачеві  
482.ЧДТУ 262260 90 01 Графічні матеріали  
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
 
  
79 
 
 
 
ДОДАТОК Б 
 
 
 
 
 
 
 
 
«Програмна реалізація кастомізованих правил у грі "Шашки"» 
Лістинг програми 
482.ЧДТУ. 2260 12 01 
Листів 56 
 
 
 
 
 
 
Розробник ________________ О.І. Сасько 
 
 
 
 
 
 
2026 
 
 
482.ЧДТУ.262260 12 01                                               14 
Лістинг Б.1 - Файл CheckersWebApplication.java 
package org.chdtu; 
 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; 
 
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class }) 
public class CheckersWebApplication { 
 
    public static void main(String[] args) { 
        SpringApplication.run(CheckersWebApplication.class, args); 
 
        System.out.println("========================================"); 
        System.out.println("Сервер Шашок запущено!"); 
        System.out.println("Адреса: http://localhost:8080"); 
        System.out.println("База даних H2: http://localhost:8080/h2-console"); 
        System.out.println("БЛОКУВАННЯ SECURITY ВИМКНЕНО ДЛЯ ТЕСТІВ"); 
        System.out.println("========================================"); 
    } 
} 
Лістинг Б.2 - Файл SecurityConfig.java 
package org.chdtu.config; 
 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; // Додай цей 
імпорт 
import org.springframework.security.crypto.password.PasswordEncoder; // І цей 
import org.springframework.security.web.SecurityFilterChain; 
 
@Configuration 
@EnableWebSecurity 
public class SecurityConfig { 
 
    @Bean 
    public PasswordEncoder passwordEncoder() { 
        return new BCryptPasswordEncoder(); 
    } 
 
    @Bean 
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 
        http 
81 
 
482.ЧДТУ.262260 12 01                                               15 
                .csrf(csrf -> csrf.disable()) 
                .authorizeHttpRequests(auth -> auth 
                        .anyRequest().permitAll() 
                ) 
                .headers(headers -> headers 
                        .frameOptions(frame -> frame.disable()) 
                        .contentSecurityPolicy(csp -> csp 
                                .policyDirectives("script-src 'self' 'unsafe-inline' 
'unsafe-eval'; object-src 'self';") 
                        ) 
                ); 
 
        return http.build(); 
    } 
} 
Лістинг Б.3 - Файл WebConfig.java 
package org.chdtu.config; 
 
import org.springframework.context.annotation.Configuration; 
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 
 
@Configuration 
public class WebConfig implements WebMvcConfigurer { 
    @Override 
    public void addResourceHandlers(ResourceHandlerRegistry registry) { 
        registry.addResourceHandler("/uploads/**") 
                .addResourceLocations("file:src/main/resources/static/uploads/"); 
    } 
} 
Лістинг Б.4 - Файл WebSocketConfig.java 
package org.chdtu.config; 
 
import org.springframework.context.annotation.Configuration; 
import org.springframework.messaging.simp.config.MessageBrokerRegistry; 
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; 
import org.springframework.web.socket.config.annotation.StompEndpointRegistry; 
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; 
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; 
 
@Configuration 
@EnableWebSocketMessageBroker 
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { 
 
    @Override 
82 
 
482.ЧДТУ.262260 12 01                                               16 
    public void configureMessageBroker(MessageBrokerRegistry config) { 
        config.enableSimpleBroker("/topic", "/queue"); 
        config.setApplicationDestinationPrefixes("/app"); 
    } 
 
    @Override 
    public void registerStompEndpoints(StompEndpointRegistry registry) { 
        registry.addEndpoint("/ws") 
                .setAllowedOriginPatterns("*") 
                .addInterceptors(new HttpSessionHandshakeInterceptor()) // ВАЖЛИВО: 
зберігає дані сесії 
                .withSockJS(); 
    } 
} 
Лістинг Б.5 - Файл AuthController.java 
package org.chdtu.controller; 
 
import org.chdtu.service.AuthService; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.web.bind.annotation.*; 
 
@RestController 
@RequestMapping("/api/auth") 
public class AuthController { 
 
    @Autowired 
    private AuthService authService; 
 
    @PostMapping("/register") 
    public String register(@RequestParam String username, @RequestParam String password) { 
        return authService.registerUser(username, password); 
    } 
} 
Лістинг Б.6 - Файл AuthController.java 
package org.chdtu.controller; 
 
import org.chdtu.service.AuthService; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.web.bind.annotation.*; 
 
@RestController 
@RequestMapping("/api/auth") 
public class AuthController { 
 
    @Autowired 
83 
 
482.ЧДТУ.262260 12 01                                               17 
    private AuthService authService; 
 
    @PostMapping("/register") 
    public String register(@RequestParam String username, @RequestParam String password) { 
        return authService.registerUser(username, password); 
    } 
} 
Лістинг Б.7 - Файл GameController.java 
package org.chdtu.controller; 
 
import jakarta.persistence.EntityManager; 
import jakarta.persistence.PersistenceContext; 
import org.chdtu.entity.GameSession; 
import org.chdtu.model.CheckersData; 
import org.chdtu.model.CheckersMove; 
import org.chdtu.repository.GameSessionRepository; 
import org.chdtu.rules.*; 
import org.chdtu.service.UserService; 
import org.chdtu.service.GameService; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.http.CacheControl; 
import org.springframework.http.ResponseEntity; 
import org.springframework.transaction.annotation.Transactional; 
import org.springframework.web.bind.annotation.*; 
 
import java.util.*; 
 
@RestController 
@RequestMapping("/api/game") 
public class GameController { 
 
    @Autowired 
    private UserService userService; 
 
    @Autowired 
    private GameSessionRepository gameSessionRepository; 
 
    @Autowired 
    private GameService gameService; // Інжектуємо наш сервіс із динамічною логікою розміру 
поля 
 
    @PersistenceContext 
    private EntityManager entityManager; 
 
    private final String[] modifiers = { 
            "Втомлена дамка", "Мирні шашки", "Тил під захистом", 
84 
 
482.ЧДТУ.262260 12 01                                               18 
            "Важкі кроки", "Камікадзе", "Обмежений вибір", "Швидка коронація" 
    }; 
 
    /** 
     * Створення нової гри з урахуванням обраної стратегії правил (8х8 або 10х10) 
     */ 
    @PostMapping("/new") 
    @Transactional 
    public ResponseEntity<?> createNewGame(@RequestParam String rules, @RequestParam Long 
redId) { 
        // 1. Делегуємо створення сесії сервісу, який правильно розраховує boardSize 
        GameSession session = gameService.createNewGame(rules, redId); 
 
        // 2. Додаємо випадковий ігровий модифікатор 
        session.setCurrentPlayer(CheckersData.RED); 
        session.setModifier(modifiers[new Random().nextInt(modifiers.length)]); 
 
        // Візуально синхронізуємо зміни та зберігаємо в базу H2 
        GameSession saved = gameSessionRepository.saveAndFlush(session); 
 
        // 3. Відновлюємо правильну матрицю (8х8 або 10х10) з рядка бази для відповіді 
клієнту 
        int[][] boardMatrix = gameService.deserializeBoard(saved.getBoardState()); 
 
        Map<String, Object> response = new HashMap<>(); 
        response.put("gameId", saved.getId()); 
        response.put("modifier", saved.getModifier()); 
        response.put("board", boardMatrix); // Фронтенд отримає правильну довжину масиву 
        return ResponseEntity.ok(response); 
    } 
 
    /** 
     * Оновлено: приєднання другого гравця тепер повністю проходить через GameService, 
     * що гарантує цілісність даних та коректний стан дошки для Чорних. 
     */ 
    @PostMapping("/join") 
    @Transactional 
    public ResponseEntity<?> joinGame(@RequestParam Long blackId) { 
        // Шукаємо першу активну сесію, де ще немає другого гравця 
        Optional<GameSession> sessionOpt = 
gameSessionRepository.findFirstByActiveTrueAndBlackPlayerIdIsNull(); 
 
        if (sessionOpt.isPresent()) { 
            GameSession session = sessionOpt.get(); 
 
            // Викликаємо оновлений бізнес-метод сервісу 
            GameSession updatedSession = gameService.joinGame(session.getId(), blackId); 
85 
 
482.ЧДТУ.262260 12 01                                               19 
 
            Map<String, Object> response = new HashMap<>(); 
            response.put("gameId", updatedSession.getId()); 
            response.put("status", "JOINED"); 
            return ResponseEntity.ok(response); 
        } else { 
            return ResponseEntity.badRequest().body("Немає доступних ігор для приєднання"); 
        } 
    } 
 
    @GetMapping("/moves") 
    @Transactional(readOnly = true) 
    public ResponseEntity<?> getAvailableMoves(@RequestParam Long gameId, @RequestParam int 
row, @RequestParam int col) { 
        entityManager.clear(); // Очищуємо кеш перед читанням для мультиплеєра 
        Optional<GameSession> sessionOpt = gameSessionRepository.findById(gameId); 
        if (sessionOpt.isEmpty()) return ResponseEntity.notFound().build(); 
 
        GameSession session = sessionOpt.get(); 
 
        // Створюємо динамічний об'єкт CheckersData на основі розміру збереженої плати 
        int[][] currentBoard = gameService.deserializeBoard(session.getBoardState()); 
        CheckersData board = new CheckersData(currentBoard.length); 
        board.setBoard(currentBoard); 
        board.setRuleStrategy(getStrategy(session.getRuleType())); 
 
        int piece = board.pieceAt(row, col); 
        int currentPlayer = session.getCurrentPlayer(); 
 
        if (piece == CheckersData.EMPTY || 
                (currentPlayer == CheckersData.RED && piece > 2) || 
                (currentPlayer == CheckersData.BLACK && piece <= 2)) { 
            return ResponseEntity.ok(Collections.emptyList()); 
        } 
 
        CheckersMove[] allLegalMoves = board.getLegalMoves(currentPlayer); 
        List<CheckersMove> filteredMoves = new ArrayList<>(); 
        if (allLegalMoves != null) { 
            for (CheckersMove m : allLegalMoves) { 
                if (m.fromRow == row && m.fromCol == col) filteredMoves.add(m); 
            } 
        } 
        return ResponseEntity.ok(filteredMoves); 
    } 
 
    /** 
     * Повернення поточного статусу та актуальної матриці поля без кешування браузером 
86 
 
482.ЧДТУ.262260 12 01                                               20 
     */ 
    @GetMapping("/status/{gameId}") 
    @Transactional(readOnly = true) 
    public ResponseEntity<?> getGameStatus(@PathVariable Long gameId) { 
        entityManager.clear(); // Ключовий момент для синхронізації ходів двох гравців 
        Optional<GameSession> sessionOpt = gameSessionRepository.findById(gameId); 
        if (sessionOpt.isEmpty()) return ResponseEntity.notFound().build(); 
 
        GameSession session = sessionOpt.get(); 
 
        // Замість нью-хардкоду дістаємо чисту матрицю безпосередньо з десеріалізатора 
сервісу 
        int[][] boardMatrix = gameService.deserializeBoard(session.getBoardState()); 
 
        Map<String, Object> response = new HashMap<>(); 
        response.put("board", boardMatrix); // Поле віддасться як 10х10 або 8х8 
        response.put("currentPlayer", session.getCurrentPlayer()); 
        response.put("modifier", session.getModifier()); 
        response.put("blackPlayerId", session.getBlackPlayerId()); 
        response.put("active", session.isActive()); 
 
        return ResponseEntity.ok() 
                .cacheControl(CacheControl.noCache().mustRevalidate()) 
                .body(response); 
    } 
 
    @PostMapping("/move/{gameId}") 
    @Transactional 
    public ResponseEntity<?> makeMove(@PathVariable Long gameId, @RequestBody CheckersMove 
move) { 
        Optional<GameSession> sessionOpt = gameSessionRepository.findById(gameId); 
        if (sessionOpt.isEmpty()) return ResponseEntity.notFound().build(); 
 
        GameSession session = sessionOpt.get(); 
        if (!session.isActive()) return ResponseEntity.badRequest().body("Гра вже 
завершена"); 
 
        // Відновлюємо правильний розмір об'єкта логіки 
        int[][] currentBoard = gameService.deserializeBoard(session.getBoardState()); 
        CheckersData board = new CheckersData(currentBoard.length); 
        board.setBoard(currentBoard); 
        board.setRuleStrategy(getStrategy(session.getRuleType())); 
 
        int currentPlayer = session.getCurrentPlayer(); 
        CheckersMove[] legalMoves = board.getLegalMoves(currentPlayer); 
 
        boolean isLegal = false; 
87 
 
482.ЧДТУ.262260 12 01                                               21 
        if (legalMoves != null) { 
            for (CheckersMove lm : legalMoves) { 
                if (lm.fromRow == move.fromRow && lm.fromCol == move.fromCol && 
                        lm.toRow == move.toRow && lm.toCol == move.toCol) { 
                    isLegal = true; 
                    break; 
                } 
            } 
        } 
 
        if (!isLegal) return ResponseEntity.badRequest().body("Хід заборонено!"); 
 
        board.makeMove(move); 
        processModifiersAfterMove(board, move, session.getModifier(), currentPlayer); 
 
        // Оновлено серіалізацію: тепер використовуємо уніфікований метод serializeBoard з 
нашого GameService 
        String serializedStr = serializeCustomBoard(board.getBoard()); 
 
        if (move.isJump() && !"Втомлена дамка".equals(session.getModifier())) { 
            CheckersMove[] nextJumps = board.getLegalJumpsFrom(currentPlayer, move.toRow, 
move.toCol); 
            if (nextJumps != null && nextJumps.length > 0) { 
                session.setBoardState(serializedStr); 
                gameSessionRepository.saveAndFlush(session); 
 
                Map<String, Object> jumpRes = new HashMap<>(); 
                jumpRes.put("board", board.getBoard()); 
                jumpRes.put("status", "CONTINUE_JUMP"); 
                return ResponseEntity.ok(jumpRes); 
            } 
        } 
 
        session.setCurrentPlayer(currentPlayer == CheckersData.RED ? CheckersData.BLACK : 
CheckersData.RED); 
 
        if (board.getLegalMoves(session.getCurrentPlayer()) == null) { 
            String winnerColor = (session.getCurrentPlayer() == CheckersData.RED) ? "BLACK" 
: "RED"; 
            updateFinalStatistics(session, winnerColor); 
            session.setActive(false); 
        } 
 
        session.setBoardState(serializedStr); 
        gameSessionRepository.saveAndFlush(session); 
 
        return ResponseEntity.ok(Map.of("board", board.getBoard(), "status", "SUCCESS")); 
88 
 
482.ЧДТУ.262260 12 01                                               22 
    } 
 
    private void updateFinalStatistics(GameSession session, String winnerColor) { 
        if (winnerColor.equals("RED")) { 
            userService.updateStats(session.getRedPlayerId(), session.getBlackPlayerId()); 
        } else { 
            userService.updateStats(session.getBlackPlayerId(), session.getRedPlayerId()); 
        } 
    } 
 
    /** 
     * Безпечне визначення стратегії правил з ігноруванням регістру рядка (Case-
Insensitive) 
     */ 
    private RuleStrategy getStrategy(String type) { 
        if (type == null) return new BritishRuleStrategy(); 
        if (type.equalsIgnoreCase("International")) return new InternationalRuleStrategy(); 
        if (type.equalsIgnoreCase("Brazilian")) return new BrazilianRuleStrategy(); 
        return new BritishRuleStrategy(); 
    } 
 
    private void processModifiersAfterMove(CheckersData board, CheckersMove move, String 
mod, int player) { 
        if ("Камікадзе".equals(mod) && move.isJump()) { 
            board.setPieceAt(move.toRow, move.toCol, CheckersData.EMPTY); 
        } 
    } 
 
    /** 
     * Утилітарний метод для поточної серіалізації ходів всередині контролера, 
     * який повністю дублює логіку роздільників нашого GameService (";" та ","). 
     */ 
    private String serializeCustomBoard(int[][] board) { 
        StringBuilder sb = new StringBuilder(); 
        for (int i = 0; i < board.length; i++) { 
            for (int j = 0; j < board[i].length; j++) { 
                sb.append(board[i][j]); 
                if (j < board[i].length - 1) sb.append(","); 
            } 
            if (i < board.length - 1) sb.append(";"); 
        } 
        return sb.toString(); 
    } 
} 
Лістинг Б.8 - Файл UserController.java 
89 
 
482.ЧДТУ.262260 12 01                                               23 
package org.chdtu.controller; 
 
import org.chdtu.entity.User; 
import org.chdtu.repository.UserRepository; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.http.ResponseEntity; 
import org.springframework.web.bind.annotation.*; 
import org.springframework.web.multipart.MultipartFile; 
 
import java.io.File; 
import java.io.IOException; 
import java.nio.file.Files; 
import java.nio.file.Path; 
import java.nio.file.Paths; 
import java.util.List; 
import java.util.Map; 
import java.util.HashMap; 
 
@RestController 
@RequestMapping("/api/users") 
public class UserController { 
 
    @Autowired 
    private UserRepository userRepository; 
 
    // Отримання даних за ID 
    @GetMapping("/{id}") 
    public ResponseEntity<User> getUserInfo(@PathVariable Long id) { 
        return userRepository.findById(id) 
                .map(ResponseEntity::ok) 
                .orElse(ResponseEntity.notFound().build()); 
    } 
 
    // Пошук за іменем для авторизації/реєстрації 
    @GetMapping("/search") 
    public ResponseEntity<User> findByName(@RequestParam String name) { 
        return userRepository.findByUsername(name) 
                .map(ResponseEntity::ok) 
                .orElse(ResponseEntity.notFound().build()); 
    } 
 
    // ВИПРАВЛЕНО: Ендпоїнт тепер називається /leaderboard, як у game.js 
    @GetMapping("/leaderboard") 
    public List<User> getLeaderboard() { 
        return userRepository.findTop10ByOrderByRatingDesc(); 
    } 
 
90 
 
482.ЧДТУ.262260 12 01                                               24 
    // НОВИЙ МЕТОД: Завантаження аватара 
    @PostMapping("/{id}/upload-avatar") 
    public ResponseEntity<?> uploadAvatar(@PathVariable Long id, @RequestParam("file") 
MultipartFile file) { 
        return userRepository.findById(id).map(user -> { 
            try { 
                // Шлях до папки (відносно кореня проекту) 
                String uploadDir = "src/main/resources/static/uploads/avatars/"; 
                File dir = new File(uploadDir); 
                if (!dir.exists()) dir.mkdirs(); 
 
                // Ім'я файлу: ID_avatar.jpg (завжди переписуємо старий для економії місця) 
                String fileName = id + "_avatar.jpg"; 
                Path path = Paths.get(uploadDir + fileName); 
                Files.write(path, file.getBytes()); 
 
                // Шлях для фронтенду 
                user.setAvatarPath("/uploads/avatars/" + fileName); 
                userRepository.save(user); 
 
                Map<String, String> response = new HashMap<>(); 
                response.put("avatarUrl", user.getAvatarPath()); 
                return ResponseEntity.ok(response); 
            } catch (IOException e) { 
                return ResponseEntity.status(500).body("Помилка завантаження: " + 
e.getMessage()); 
            } 
        }).orElse(ResponseEntity.notFound().build()); 
    } 
 
    // НОВИЙ МЕТОД: Збереження улюбленої теми 
    @PostMapping("/{id}/theme") 
    public ResponseEntity<?> saveTheme(@PathVariable Long id, @RequestParam String theme) { 
        return userRepository.findById(id).map(user -> { 
            user.setFavoriteTheme(theme); 
            userRepository.save(user); 
            return ResponseEntity.ok("Тему збережено"); 
        }).orElse(ResponseEntity.notFound().build()); 
    } 
} 
Лістинг Б.9 - Файл GameRecord.java 
package org.chdtu.entity; 
 
import jakarta.persistence.*; 
import lombok.Data; 
import java.time.LocalDateTime; 
91 
 
482.ЧДТУ.262260 12 01                                               25 
 
@Entity 
@Table(name = "game_records") 
@Data 
public class GameRecord { 
 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private Long id; 
 
    @ManyToOne 
    @JoinColumn(name = "player_red_id") 
    private User playerRed; 
 
    @ManyToOne 
    @JoinColumn(name = "player_black_id") 
    private User playerBlack; 
 
    private String rulesType; // International, Brazilian, British 
 
    @Column(columnDefinition = "TEXT") 
    private String moveHistory; // Зберігаємо ходи як JSON-рядок або текст для аналізу 
 
    private LocalDateTime playedAt = LocalDateTime.now(); 
 
    private String result; // "RED_WON", "BLACK_WON", "DRAW" 
} 
Лістинг Б.10 - Файл User.java 
package org.chdtu.entity; 
 
import jakarta.persistence.*; 
import lombok.Data; 
import lombok.NoArgsConstructor; 
import lombok.AllArgsConstructor; 
 
@Entity 
@Table(name = "users") 
@Data 
@NoArgsConstructor 
@AllArgsConstructor 
public class User { 
 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private Long id; 
 
92 
 
482.ЧДТУ.262260 12 01                                               26 
    @Column(unique = true, nullable = false) 
    private String username; 
 
    @Column(nullable = false) 
    private String password; 
 
    private int rating = 1200; 
 
    private int wins = 0; 
    private int losses = 0; 
    private int draws = 0; 
 
    @Column(name = "avatar_path") 
    private String avatarPath; 
 
    @Column(name = "favorite_theme") 
    private String favoriteTheme = "classic"; 
 
    // --- МЕТОДИ СТАТИСТИКИ --- 
 
    public void addWin() { 
        this.wins++; 
        this.rating += 25; 
    } 
 
    public void addLoss() { 
        this.losses++; 
        // Рейтинг не може бути менше 0 
        this.rating = Math.max(0, this.rating - 20); 
    } 
 
    public void addDraw() { 
        this.draws++; 
        this.rating += 5; 
    } 
} 
Лістинг Б.11 - Файл WebSocketEventListener.java 
package org.chdtu.listener; 
 
import org.chdtu.service.GameService; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.event.EventListener; 
import org.springframework.messaging.simp.stomp.StompHeaderAccessor; 
import org.springframework.stereotype.Component; 
import org.springframework.web.socket.messaging.SessionDisconnectEvent; 
 
93 
 
482.ЧДТУ.262260 12 01                                               27 
@Component 
public class WebSocketEventListener { 
 
    @Autowired 
    private GameService gameService; 
 
    @EventListener 
    public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) { 
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage()); 
 
        // Дістаємо дані, які ми поклали в сесію при підключенні 
        String username = (String) headerAccessor.getSessionAttributes().get("username"); 
        String gameId = (String) headerAccessor.getSessionAttributes().get("gameId"); 
 
        if (username != null && gameId != null) { 
            // Викликаємо метод обробки технічної поразки 
            gameService.processTechnicalLoss(gameId, username); 
        } 
    } 
} 
Лістинг Б.12 - Файл CheckersData.java 
package org.chdtu.model; 
 
import org.chdtu.rules.RuleStrategy; 
import org.chdtu.model.CheckersMove; 
import java.io.Serializable; 
import java.util.Arrays; 
 
public class CheckersData implements Serializable { 
 
    public static final int EMPTY = 0; 
    public static final int RED = 1; 
    public static final int RED_KING = 2; 
    public static final int BLACK = 3; 
    public static final int BLACK_KING = 4; 
 
    private int[][] board; 
    private RuleStrategy ruleStrategy; 
 
    public CheckersData() { 
        this.board = new int[8][8]; 
        setUpGame(); 
    } 
 
    public CheckersData(int size) { 
        this.board = new int[size][size]; 
94 
 
482.ЧДТУ.262260 12 01                                               28 
        setUpGame(); 
    } 
 
    public void clearBoard() { 
        for (int[] row : board) { 
            Arrays.fill(row, EMPTY); 
        } 
    } 
 
    public void setBoard(int[][] board) { 
        this.board = board; 
    } 
 
    public void setRuleStrategy(RuleStrategy strategy) { 
        this.ruleStrategy = strategy; 
        int size = strategy.getBoardSize(); 
        if (board.length != size) { 
            board = new int[size][size]; 
            setUpGame(); 
        } 
    } 
 
    public void setPieceAt(int row, int col, int piece) { 
        if (row >= 0 && row < board.length && col >= 0 && col < board[0].length) { 
            board[row][col] = piece; 
        } 
    } 
 
    public int pieceAt(int row, int col) { 
        if (row >= 0 && row < board.length && col >= 0 && col < board[0].length) { 
            return board[row][col]; 
        } 
        return EMPTY; 
    } 
 
    public String getBoardAsDataString() { 
        StringBuilder sb = new StringBuilder(); 
        int rows = board.length; 
        int cols = board[0].length; 
        sb.append(rows).append(":").append(cols).append("|"); 
        for (int r = 0; r < rows; r++) { 
            for (int c = 0; c < cols; c++) { 
                sb.append(board[r][c]).append(","); 
            } 
        } 
        return sb.toString(); 
    } 
95 
 
482.ЧДТУ.262260 12 01                                               29 
 
    public void loadFromDataString(String data) { 
        if (data == null || !data.contains("|")) return; 
        String[] mainParts = data.split("\\|"); 
        String[] dimensions = mainParts[0].split(":"); 
        int rows = Integer.parseInt(dimensions[0]); 
        int cols = Integer.parseInt(dimensions[1]); 
 
        this.board = new int[rows][cols]; 
        String[] cells = mainParts[1].split(","); 
        int k = 0; 
        for (int r = 0; r < rows; r++) { 
            for (int c = 0; c < cols; c++) { 
                if (k < cells.length) { 
                    board[r][c] = Integer.parseInt(cells[k++]); 
                } 
            } 
        } 
    } 
 
 
    public void setUpGame() { 
        clearBoard(); 
        int rows = board.length; 
        int cols = board[0].length; 
        for (int row = 0; row < rows; row++) { 
            for (int col = 0; col < cols; col++) { 
                if (row % 2 != col % 2) { 
                    if (row < (rows / 2) - 1) board[row][col] = BLACK; 
                    else if (row > (rows / 2)) board[row][col] = RED; 
                } 
            } 
        } 
    } 
 
    public int[][] getBoard() { 
        return board; 
    } 
 
    public void makeMove(CheckersMove move) { 
        if (ruleStrategy != null) { 
            ruleStrategy.makeMove(this, move); 
            return; 
        } 
 
        int piece = board[move.fromRow][move.fromCol]; 
        board[move.toRow][move.toCol] = piece; 
96 
 
482.ЧДТУ.262260 12 01                                               30 
        board[move.fromRow][move.fromCol] = EMPTY; 
 
        if (Math.abs(move.fromRow - move.toRow) >= 2) { 
            int stepR = (move.toRow > move.fromRow) ? 1 : -1; 
            int stepC = (move.toCol > move.fromCol) ? 1 : -1; 
            int r = move.fromRow + stepR; 
            int c = move.fromCol + stepC; 
            while (r != move.toRow) { 
                board[r][c] = EMPTY; 
                r += stepR; c += stepC; 
            } 
        } 
 
        if (piece == RED && move.toRow == 0) board[move.toRow][move.toCol] = RED_KING; 
        if (piece == BLACK && move.toRow == board.length - 1) board[move.toRow][move.toCol] 
= BLACK_KING; 
    } 
 
    public CheckersMove[] getLegalMoves(int player) { 
        if (ruleStrategy == null) return null; 
        return ruleStrategy.getLegalMoves(this, player); 
    } 
 
    public CheckersMove[] getLegalMovesForPiece(int player, int row, int col) { 
        if (ruleStrategy == null) return null; 
        return ruleStrategy.getLegalMovesForPiece(this, player, row, col); 
    } 
 
    public CheckersMove[] getLegalJumpsFrom(int player, int row, int col) { 
        if (ruleStrategy == null) return null; 
        return ruleStrategy.getLegalJumpsFrom(this, player, row, col); 
    } 
} 
Лістинг Б.13 - Файл CheckersMove.java 
package org.chdtu.model; 
 
public class CheckersMove { 
    public int fromRow, fromCol; 
    public int toRow, toCol; 
 
    public CheckersMove(int fromRow, int fromCol, int toRow, int toCol) { 
        this.fromRow = fromRow; 
        this.fromCol = fromCol; 
        this.toRow = toRow; 
        this.toCol = toCol; 
    } 
97 
 
482.ЧДТУ.262260 12 01                                               31 
 
    public boolean isJump() { 
        return Math.abs(fromRow - toRow) == 2; 
    } 
} 
Лістинг Б.14 - Файл GameSessionRepository.java 
package org.chdtu.repository; 
 
import org.chdtu.entity.GameSession; 
import org.springframework.data.jpa.repository.JpaRepository; 
import org.springframework.stereotype.Repository; 
import java.util.Optional; 
 
@Repository 
public interface GameSessionRepository extends JpaRepository<GameSession, Long> { 
    Optional<GameSession> findFirstByActiveTrueAndBlackPlayerIdIsNull(); 
} 
 
Лістинг Б.15 - Файл UserRepository.java 
package org.chdtu.repository; 
 
import org.chdtu.entity.User; 
import org.springframework.data.jpa.repository.JpaRepository; 
import org.springframework.stereotype.Repository; 
import java.util.Optional; 
import java.util.List; 
 
@Repository 
public interface UserRepository extends JpaRepository<User, Long> { 
    Optional<User> findByUsername(String username); 
    List<User> findTop10ByOrderByRatingDesc(); 
    boolean existsByUsername(String username); 
} 
Лістинг Б.16 - Файл AbstractRuleStrategy.java 
package org.chdtu.rules; 
 
import java.util.ArrayList; 
import org.chdtu.model.CheckersData; 
import org.chdtu.model.CheckersMove; 
 
public abstract class AbstractRuleStrategy implements RuleStrategy { 
    protected boolean mandatoryCapture = true; 
    protected boolean flyingKings = false; 
    protected boolean menCanCaptureBackward = false; 
    protected int boardSize = 8; 
98 
 
482.ЧДТУ.262260 12 01                                               32 
 
    @Override 
    public int getBoardSize() { 
        return boardSize; 
    } 
 
    public void setUpGame(CheckersData data) { 
        int[][] board = data.getBoard(); 
        for (int row = 0; row < boardSize; row++) { 
            for (int col = 0; col < boardSize; col++) { 
                if (row % 2 == col % 2) { 
                    if (row < boardSize/2 - 1) 
                        board[row][col] = CheckersData.BLACK; 
                    else if (row > boardSize/2) 
                        board[row][col] = CheckersData.RED; 
                    else 
                        board[row][col] = CheckersData.EMPTY; 
                } else { 
                    board[row][col] = CheckersData.EMPTY; 
                } 
            } 
        } 
    } 
 
    public void makeMove(CheckersData data, CheckersMove move) { 
        int[][] board = data.getBoard(); 
        int fromRow = move.fromRow, fromCol = move.fromCol, toRow = move.toRow, toCol = 
move.toCol; 
        board[toRow][toCol] = board[fromRow][fromCol]; 
        board[fromRow][fromCol] = CheckersData.EMPTY; 
 
        if (Math.abs(fromRow - toRow) >= 2) { 
            int jumpRow = (fromRow + toRow) / 2; 
            int jumpCol = (fromCol + toCol) / 2; 
            board[jumpRow][jumpCol] = CheckersData.EMPTY; 
        } 
 
        if (toRow == 0 && board[toRow][toCol] == CheckersData.RED) 
            board[toRow][toCol] = CheckersData.RED_KING; 
        if (toRow == boardSize - 1 && board[toRow][toCol] == CheckersData.BLACK) 
            board[toRow][toCol] = CheckersData.BLACK_KING; 
    } 
 
    public CheckersMove[] getLegalMoves(CheckersData data, int player) { 
        if (player != CheckersData.RED && player != CheckersData.BLACK) 
            return null; 
 
99 
 
482.ЧДТУ.262260 12 01                                               33 
        int playerKing = (player == CheckersData.RED) ? CheckersData.RED_KING : 
CheckersData.BLACK_KING; 
        ArrayList<CheckersMove> moves = new ArrayList<>(); 
        for (int row = 0; row < boardSize; row++) { 
            for (int col = 0; col < boardSize; col++) { 
                int piece = data.pieceAt(row, col); 
                if (piece == player || piece == playerKing) { 
                    CheckersMove[] newMoves = getLegalMovesForPiece(data, player, row, 
col); 
                    if (newMoves != null) { 
                        for (CheckersMove m : newMoves) 
                            moves.add(m); 
                    } 
                } 
            } 
        } 
 
        if (moves.size() == 0) 
            return null; 
 
        if (mandatoryCapture) { 
            boolean hasJump = false; 
            for (CheckersMove m : moves) { 
                if (m.isJump()) { hasJump = true; break; } 
            } 
            if (hasJump) { 
                ArrayList<CheckersMove> filtered = new ArrayList<>(); 
                for (CheckersMove m : moves) if (m.isJump()) filtered.add(m); 
                moves = filtered; 
            } 
        } 
 
        CheckersMove[] arr = new CheckersMove[moves.size()]; 
        moves.toArray(arr); 
        return arr; 
    } 
 
    public CheckersMove[] getLegalMovesForPiece(CheckersData data, int player, int row, int 
col) { 
        ArrayList<CheckersMove> moves = new ArrayList<>(); 
        int piece = data.pieceAt(row, col); 
        int playerKing = (player == CheckersData.RED) ? CheckersData.RED_KING : 
CheckersData.BLACK_KING; 
        if (piece != player && piece != playerKing) 
            return null; 
 
        if (canJump(data, player, row, col, row + 1, col + 1, row + 2, col + 2)) 
100 
 
482.ЧДТУ.262260 12 01                                               34 
            moves.add(new CheckersMove(row, col, row + 2, col + 2)); 
        if (canJump(data, player, row, col, row - 1, col + 1, row - 2, col + 2)) 
            moves.add(new CheckersMove(row, col, row - 2, col + 2)); 
        if (canJump(data, player, row, col, row + 1, col - 1, row + 2, col - 2)) 
            moves.add(new CheckersMove(row, col, row + 2, col - 2)); 
        if (canJump(data, player, row, col, row - 1, col - 1, row - 2, col - 2)) 
            moves.add(new CheckersMove(row, col, row - 2, col - 2)); 
 
        if (moves.size() == 0) { 
            boolean isKing = (piece == CheckersData.RED_KING || piece == 
CheckersData.BLACK_KING); 
            int[][] board = data.getBoard(); 
            int size = board.length; 
            if (isKing && flyingKings) { 
                int[] dr = {1, 1, -1, -1}; 
                int[] dc = {1, -1, 1, -1}; 
                for (int d = 0; d < 4; d++) { 
                    int r = row + dr[d]; 
                    int c = col + dc[d]; 
                    while (r >= 0 && r < size && c >= 0 && c < size && board[r][c] == 
CheckersData.EMPTY) { 
                        moves.add(new CheckersMove(row, col, r, c)); 
                        r += dr[d]; 
                        c += dc[d]; 
                    } 
                } 
            } else { 
                if (canMove(data, player, row, col, row + 1, col + 1)) 
                    moves.add(new CheckersMove(row, col, row + 1, col + 1)); 
                if (canMove(data, player, row, col, row - 1, col + 1)) 
                    moves.add(new CheckersMove(row, col, row - 1, col + 1)); 
                if (canMove(data, player, row, col, row + 1, col - 1)) 
                    moves.add(new CheckersMove(row, col, row + 1, col - 1)); 
                if (canMove(data, player, row, col, row - 1, col - 1)) 
                    moves.add(new CheckersMove(row, col, row - 1, col - 1)); 
            } 
        } 
 
        if (moves.size() == 0) 
            return null; 
        CheckersMove[] arr = new CheckersMove[moves.size()]; 
        moves.toArray(arr); 
        return arr; 
    } 
 
    public CheckersMove[] getLegalJumpsFrom(CheckersData data, int player, int row, int 
col) { 
101 
 
482.ЧДТУ.262260 12 01                                               35 
        ArrayList<CheckersMove> moves = new ArrayList<>(); 
        if (canJump(data, player, row, col, row + 1, col + 1, row + 2, col + 2)) 
            moves.add(new CheckersMove(row, col, row + 2, col + 2)); 
        if (canJump(data, player, row, col, row - 1, col + 1, row - 2, col + 2)) 
            moves.add(new CheckersMove(row, col, row - 2, col + 2)); 
        if (canJump(data, player, row, col, row + 1, col - 1, row + 2, col - 2)) 
            moves.add(new CheckersMove(row, col, row + 2, col - 2)); 
        if (canJump(data, player, row, col, row - 1, col - 1, row - 2, col - 2)) 
            moves.add(new CheckersMove(row, col, row - 2, col - 2)); 
 
        if (moves.size() == 0) 
            return null; 
        CheckersMove[] arr = new CheckersMove[moves.size()]; 
        moves.toArray(arr); 
        return arr; 
    } 
 
    protected boolean canJump(CheckersData data, int player, int r1, int c1, int r2, int 
c2, int r3, int c3) { 
        int[][] board = data.getBoard(); 
        if (r3 < 0 || r3 >= boardSize || c3 < 0 || c3 >= boardSize) 
            return false; 
        if (board[r3][c3] != CheckersData.EMPTY) 
            return false; 
        int piece = board[r1][c1]; 
        int mid = board[r2][c2]; 
        if (player == CheckersData.RED) { 
            if (piece == CheckersData.RED && r3 > r1 && !menCanCaptureBackward) { 
                System.out.println("canJump: RED man cannot jump backward from 
("+r1+","+c1+") to ("+r3+","+c3+")"); 
                return false; // red men can't capture backward 
            } 
            if (mid != CheckersData.BLACK && mid != CheckersData.BLACK_KING) { 
                System.out.println("canJump: RED jump mid is not enemy at ("+r2+","+c2+") 
val="+mid); 
                return false; 
            } 
            System.out.println("canJump: RED jump allowed from ("+r1+","+c1+") to 
("+r3+","+c3+")"); 
            return true; 
        } else { 
            if (piece == CheckersData.BLACK && r3 < r1 && !menCanCaptureBackward) { 
                System.out.println("canJump: BLACK man cannot jump backward from 
("+r1+","+c1+") to ("+r3+","+c3+")"); 
                return false; // black men can't capture backward 
            } 
            if (mid != CheckersData.RED && mid != CheckersData.RED_KING) { 
102 
 
482.ЧДТУ.262260 12 01                                               36 
                System.out.println("canJump: BLACK jump mid is not enemy at ("+r2+","+c2+") 
val="+mid); 
                return false; 
            } 
            System.out.println("canJump: BLACK jump allowed from ("+r1+","+c1+") to 
("+r3+","+c3+")"); 
            return true; 
        } 
    } 
 
    protected boolean canMove(CheckersData data, int player, int r1, int c1, int r2, int 
c2) { 
        int[][] board = data.getBoard(); 
        if (r2 < 0 || r2 >= boardSize || c2 < 0 || c2 >= boardSize) 
            return false; 
        if (board[r2][c2] != CheckersData.EMPTY) 
            return false; 
        int piece = board[r1][c1]; 
        if (piece == CheckersData.RED) { 
            if (r2 >= r1) { 
                System.out.println("canMove: RED man cannot move from ("+r1+","+c1+") to 
("+r2+","+c2+")"); 
                return false; 
            } 
            System.out.println("canMove: RED man can move from ("+r1+","+c1+") to 
("+r2+","+c2+")"); 
            return true; 
        } else if (piece == CheckersData.BLACK) { 
            if (r2 <= r1) { 
                System.out.println("canMove: BLACK man cannot move from ("+r1+","+c1+") to 
("+r2+","+c2+")"); 
                return false; 
            } 
            System.out.println("canMove: BLACK man can move from ("+r1+","+c1+") to 
("+r2+","+c2+")"); 
            return true; 
        } 
        return true; 
    } 
} 
Лістинг Б.17 - Файл BrazilianRuleStrategy.java 
package org.chdtu.rules; 
 
public class BrazilianRuleStrategy extends InternationalRuleStrategy { 
    public BrazilianRuleStrategy() { 
        // Brazilian: majority capture similar to international but on 8x8 board 
103 
 
482.ЧДТУ.262260 12 01                                               37 
        this.mandatoryCapture = true; 
        this.flyingKings = true; 
        this.menCanCaptureBackward = true; 
        this.boardSize = 8; 
    } 
} 
Лістинг Б.18 - Файл BritishRuleStrategy.java 
package org.chdtu.rules; 
 
public class BritishRuleStrategy extends AbstractRuleStrategy { 
    public BritishRuleStrategy() { 
        // Standard English draughts: men move forward only, must capture, no flying kings. 
        this.mandatoryCapture = true; 
        this.flyingKings = false; 
        this.menCanCaptureBackward = false; 
        this.boardSize = 8; 
    } 
} 
Лістинг Б.19 - Файл InternationalRuleStrategy.java 
package org.chdtu.rules; 
 
import java.util.ArrayList; 
import org.chdtu.model.CheckersData; 
import org.chdtu.model.CheckersMove; 
 
public class InternationalRuleStrategy extends AbstractRuleStrategy { 
    public InternationalRuleStrategy() { 
        // International draughts (simplified): mandatory capture, flying kings, men can 
capture backward 
        this.mandatoryCapture = true; 
        this.flyingKings = true; 
        this.menCanCaptureBackward = true; 
        this.boardSize = 10; // international uses 10x10; CheckersData currently uses 8x8 
but architecture supports size 
    } 
 
    @Override 
    public void setUpGame(CheckersData data) { 
        if (data.getBoard().length != boardSize) { 
            this.boardSize = data.getBoard().length; 
        } 
        super.setUpGame(data); 
    } 
 
    @Override 
104 
 
482.ЧДТУ.262260 12 01                                               38 
    public CheckersMove[] getLegalMoves(CheckersData data, int player) { 
        int globalMax = 0; 
        int size = data.getBoard().length; 
        for (int r = 0; r < size; r++) { 
            for (int c = 0; c < size; c++) { 
                int piece = data.pieceAt(r, c); 
                if (piece == player || piece == (player == CheckersData.RED ? 
CheckersData.RED_KING : CheckersData.BLACK_KING)) { 
                    int max = maxCapturesFrom(data.getBoard(), player, r, c, piece == 
CheckersData.RED_KING || piece == CheckersData.BLACK_KING); 
                    if (max > globalMax) 
                        globalMax = max; 
                } 
            } 
        } 
        System.out.println("International.getLegalMoves: globalMax="+globalMax+" 
player="+player); 
 
        ArrayList<CheckersMove> moves = new ArrayList<>(); 
        for (int r = 0; r < size; r++) { 
            for (int c = 0; c < size; c++) { 
                int piece = data.pieceAt(r, c); 
                if (piece == player || piece == (player == CheckersData.RED ? 
CheckersData.RED_KING : CheckersData.BLACK_KING)) { 
                    if (globalMax > 0) { 
                        ArrayList<CheckersMove> initials = 
initialJumpsLeadingToMax(data.getBoard(), player, r, c, piece, globalMax); 
                        moves.addAll(initials); 
                    } else { 
                        CheckersMove[] normal = super.getLegalMovesForPiece(data, player, 
r, c); 
                        if (normal != null) { 
                            for (CheckersMove m : normal) 
                                moves.add(m); 
                        } 
                    } 
                } 
            } 
        } 
 
        if (moves.size() == 0) 
            return null; 
        CheckersMove[] arr = new CheckersMove[moves.size()]; 
        moves.toArray(arr); 
        return arr; 
    } 
 
105 
 
482.ЧДТУ.262260 12 01                                               39 
    @Override 
    public CheckersMove[] getLegalMovesForPiece(CheckersData data, int player, int row, int 
col) { 
        int globalMax = 0; 
        int size = data.getBoard().length; 
        for (int r = 0; r < size; r++) { 
            for (int c = 0; c < size; c++) { 
                int piece = data.pieceAt(r, c); 
                if (piece == player || piece == (player == CheckersData.RED ? 
CheckersData.RED_KING : CheckersData.BLACK_KING)) { 
                    int max = maxCapturesFrom(data.getBoard(), player, r, c, piece == 
CheckersData.RED_KING || piece == CheckersData.BLACK_KING); 
                    if (max > globalMax) 
                        globalMax = max; 
                } 
            } 
        } 
        System.out.println("International.getLegalMovesForPiece: globalMax="+globalMax+" 
at("+row+","+col+") player="+player); 
 
        ArrayList<CheckersMove> moves = new ArrayList<>(); 
        int piece = data.pieceAt(row, col); 
        if (piece != player && piece != (player == CheckersData.RED ? CheckersData.RED_KING 
: CheckersData.BLACK_KING)) 
            return null; 
 
        if (globalMax > 0) { 
            moves.addAll(initialJumpsLeadingToMax(data.getBoard(), player, row, col, piece, 
globalMax)); 
        } else { 
            CheckersMove[] normal = super.getLegalMovesForPiece(data, player, row, col); 
            if (normal != null) { 
                for (CheckersMove m : normal) 
                    moves.add(m); 
            } 
        } 
 
        if (moves.size() == 0) 
            return null; 
        CheckersMove[] arr = new CheckersMove[moves.size()]; 
        moves.toArray(arr); 
        return arr; 
    } 
 
    @Override 
    public CheckersMove[] getLegalJumpsFrom(CheckersData data, int player, int row, int 
col) { 
106 
 
482.ЧДТУ.262260 12 01                                               40 
        ArrayList<CheckersMove> moves = new ArrayList<>(); 
        int piece = data.pieceAt(row, col); 
        boolean isKing = piece == CheckersData.RED_KING || piece == 
CheckersData.BLACK_KING; 
        if (isKing && flyingKings) { 
            int size = data.getBoard().length; 
            int[][] board = data.getBoard(); 
            int[] dr = {1,1,-1,-1}; 
            int[] dc = {1,-1,1,-1}; 
            for (int d=0; d<4; d++){ 
                int r = row + dr[d], c = col + dc[d]; 
                while (r >=0 && r < size && c>=0 && c<size && board[r][c] == 
CheckersData.EMPTY){ r += dr[d]; c += dc[d]; } 
                if (r >=0 && r < size && c>=0 && c<size){ 
                    int midR = r, midC = c; 
                    int val = board[midR][midC]; 
                    if ((player==CheckersData.RED && (val==CheckersData.BLACK || 
val==CheckersData.BLACK_KING)) || 
                            (player==CheckersData.BLACK && (val==CheckersData.RED || 
val==CheckersData.RED_KING))) { 
                        int lr = midR + dr[d], lc = midC + dc[d]; 
                        while (lr>=0 && lr<size && lc>=0 && lc<size){ 
                            if (board[lr][lc] == CheckersData.EMPTY){ 
                                moves.add(new CheckersMove(row,col,lr,lc)); 
                            } else break; 
                            lr += dr[d]; lc += dc[d]; 
                        } 
                    } 
                } 
            } 
        } else { 
            if (canJump(data, player, row, col, row + 1, col + 1, row + 2, col + 2)) 
                moves.add(new CheckersMove(row, col, row + 2, col + 2)); 
            if (canJump(data, player, row, col, row - 1, col + 1, row - 2, col + 2)) 
                moves.add(new CheckersMove(row, col, row - 2, col + 2)); 
            if (canJump(data, player, row, col, row + 1, col - 1, row + 2, col - 2)) 
                moves.add(new CheckersMove(row, col, row + 2, col - 2)); 
            if (canJump(data, player, row, col, row - 1, col - 1, row - 2, col - 2)) 
                moves.add(new CheckersMove(row, col, row - 2, col - 2)); 
        } 
 
        if (moves.size() == 0) 
            return null; 
        CheckersMove[] arr = new CheckersMove[moves.size()]; 
        moves.toArray(arr); 
        return arr; 
    } 
107 
 
482.ЧДТУ.262260 12 01                                               41 
 
    @Override 
    public void makeMove(CheckersData data, CheckersMove move) { 
        int fromRow = move.fromRow, fromCol = move.fromCol, toRow = move.toRow, toCol = 
move.toCol; 
        int[][] board = data.getBoard(); 
        int moving = board[fromRow][fromCol]; 
        board[toRow][toCol] = moving; 
        board[fromRow][fromCol] = CheckersData.EMPTY; 
        if (Math.abs(fromRow - toRow) >= 2) { 
            int capR = (fromRow + toRow)/2; 
            int capC = (fromCol + toCol)/2; 
            if (flyingKings && (moving == CheckersData.RED_KING || moving == 
CheckersData.BLACK_KING) && Math.abs(fromRow-toRow) > 2) { 
                int dr = Integer.signum(toRow - fromRow); 
                int dc = Integer.signum(toCol - fromCol); 
                int r = fromRow + dr, c = fromCol + dc; 
                while (r != toRow && c != toCol) { 
                    if (board[r][c] != CheckersData.EMPTY && board[r][c] != moving) { 
                        board[r][c] = CheckersData.EMPTY; 
                        break; 
                    } 
                    r += dr; c += dc; 
                } 
            } else { 
                board[capR][capC] = CheckersData.EMPTY; 
            } 
        } 
 
        boolean becameKing = false; 
        if (toRow == 0 && board[toRow][toCol] == CheckersData.RED) 
            becameKing = true; 
        if (toRow == board.length-1 && board[toRow][toCol] == CheckersData.BLACK) 
            becameKing = true; 
 
        if (becameKing) { 
            CheckersMove[] cont = getLegalJumpsFrom(data, 
moving==CheckersData.RED?CheckersData.RED:CheckersData.BLACK, toRow, toCol); 
            if (cont == null) { 
                if (board[toRow][toCol] == CheckersData.RED) 
                    board[toRow][toCol] = CheckersData.RED_KING; 
                if (board[toRow][toCol] == CheckersData.BLACK) 
                    board[toRow][toCol] = CheckersData.BLACK_KING; 
            } 
        } 
    } 
 
108 
 
482.ЧДТУ.262260 12 01                                               42 
    private int maxCapturesFrom(int[][] boardState, int player, int r, int c, boolean 
isKing) { 
        int size = boardState.length; 
        int piece = boardState[r][c]; 
        int best = 0; 
        if (piece == CheckersData.EMPTY) return 0; 
 
        if (isKing && flyingKings) { 
            int[] dr = {1,1,-1,-1}; 
            int[] dc = {1,-1,1,-1}; 
            for (int d=0; d<4; d++){ 
                int rr = r + dr[d], cc = c + dc[d]; 
                while (rr>=0 && rr<size && cc>=0 && cc<size && 
boardState[rr][cc]==CheckersData.EMPTY){ rr+=dr[d]; cc+=dc[d]; } 
                if (rr>=0 && rr<size && cc>=0 && cc<size){ 
                    int val = boardState[rr][cc]; 
                    if ((player==CheckersData.RED && (val==CheckersData.BLACK || 
val==CheckersData.BLACK_KING)) || (player==CheckersData.BLACK && (val==CheckersData.RED || 
val==CheckersData.RED_KING))){ 
                        int lr = rr + dr[d], lc = cc + dc[d]; 
                        while (lr>=0 && lr<size && lc>=0 && lc<size){ 
                            if (boardState[lr][lc]==CheckersData.EMPTY){ 
                                int[][] copy = copyBoard(boardState); 
                                copy[lr][lc] = copy[r][c]; 
                                copy[r][c] = CheckersData.EMPTY; 
                                copy[rr][cc] = CheckersData.EMPTY; 
                                int resc = 1 + maxCapturesFrom(copy, player, lr, lc, true); 
                                if (resc > best) best = resc; 
                            } else break; 
                            lr += dr[d]; lc += dc[d]; 
                        } 
                    } 
                } 
            } 
        } else { 
            int[][] deltas = {{1,1},{-1,1},{1,-1},{-1,-1}}; 
            for (int[] d : deltas) { 
                int r2 = r + d[0], c2 = c + d[1], r3 = r + 2*d[0], c3 = c + 2*d[1]; 
                if (r3<0 || r3>=size || c3<0 || c3>=size) continue; 
                int mid = boardState[r2][c2]; 
                if (boardState[r3][c3] != CheckersData.EMPTY) continue; 
                if (player==CheckersData.RED) { 
                    if ((boardState[r][c]==CheckersData.RED && r3>r && 
!menCanCaptureBackward)) continue; 
                    if (!(mid==CheckersData.BLACK || mid==CheckersData.BLACK_KING)) 
continue; 
                } else { 
109 
 
482.ЧДТУ.262260 12 01                                               43 
                    if ((boardState[r][c]==CheckersData.BLACK && r3<r && 
!menCanCaptureBackward)) continue; 
                    if (!(mid==CheckersData.RED || mid==CheckersData.RED_KING)) continue; 
                } 
                int[][] copy = copyBoard(boardState); 
                copy[r3][c3] = copy[r][c]; 
                copy[r][c] = CheckersData.EMPTY; 
                copy[r2][c2] = CheckersData.EMPTY; 
                boolean promoted = false; 
                if (copy[r3][c3]==CheckersData.RED && r3==0) promoted = true; 
                if (copy[r3][c3]==CheckersData.BLACK && r3==size-1) promoted = true; 
                boolean nextIsKing = isKing || promoted; 
                if (promoted) nextIsKing = isKing; 
                int resc = 1 + maxCapturesFrom(copy, player, r3, c3, nextIsKing); 
                if (resc > best) best = resc; 
            } 
        } 
        return best; 
    } 
 
    private ArrayList<CheckersMove> initialJumpsLeadingToMax(int[][] boardState, int 
player, int r, int c, int piece, int targetMax) { 
        ArrayList<CheckersMove> res = new ArrayList<>(); 
        int size = boardState.length; 
        boolean isKing = piece==CheckersData.RED_KING || piece==CheckersData.BLACK_KING; 
        if (isKing && flyingKings) { 
            int[] dr = {1,1,-1,-1}; 
            int[] dc = {1,-1,1,-1}; 
            for (int d=0; d<4; d++){ 
                int rr = r + dr[d], cc = c + dc[d]; 
                while (rr>=0 && rr<size && cc>=0 && cc<size && 
boardState[rr][cc]==CheckersData.EMPTY){ rr+=dr[d]; cc+=dc[d]; } 
                if (rr>=0 && rr<size && cc>=0 && cc<size){ 
                    int val = boardState[rr][cc]; 
                    if ((player==CheckersData.RED && (val==CheckersData.BLACK || 
val==CheckersData.BLACK_KING)) || 
                            (player==CheckersData.BLACK && (val==CheckersData.RED || 
val==CheckersData.RED_KING))){ 
                        int lr = rr + dr[d], lc = cc + dc[d]; 
                        while (lr>=0 && lr<size && lc>=0 && lc<size){ 
                            if (boardState[lr][lc] == CheckersData.EMPTY){ 
                                int[][] copy = copyBoard(boardState); 
                                copy[lr][lc] = copy[r][c]; 
                                copy[r][c] = CheckersData.EMPTY; 
                                copy[rr][cc] = CheckersData.EMPTY; 
                                int got = 1 + maxCapturesFrom(copy, player, lr, lc, true); 
                                if (got == targetMax) res.add(new CheckersMove(r,c,lr,lc)); 
110 
 
482.ЧДТУ.262260 12 01                                               44 
                            } else break; 
                            lr += dr[d]; lc += dc[d]; 
                        } 
                    } 
                } 
            } 
        } else { 
            int[][] deltas = {{1,1},{-1,1},{1,-1},{-1,-1}}; 
            for (int[] d : deltas) { 
                int r2 = r + d[0], c2 = c + d[1], r3 = r + 2*d[0], c3 = c + 2*d[1]; 
                if (r3<0 || r3>=size || c3<0 || c3>=size) continue; 
                int mid = boardState[r2][c2]; 
                if (boardState[r3][c3] != CheckersData.EMPTY) continue; 
                if (player==CheckersData.RED) { 
                    if ((boardState[r][c]==CheckersData.RED && r3>r && 
!menCanCaptureBackward)) continue; 
                    if (!(mid==CheckersData.BLACK || mid==CheckersData.BLACK_KING)) 
continue; 
                } else { 
                    if ((boardState[r][c]==CheckersData.BLACK && r3<r && 
!menCanCaptureBackward)) continue; 
                    if (!(mid==CheckersData.RED || mid==CheckersData.RED_KING)) continue; 
                } 
                int[][] copy = copyBoard(boardState); 
                copy[r3][c3] = copy[r][c]; 
                copy[r][c] = CheckersData.EMPTY; 
                copy[r2][c2] = CheckersData.EMPTY; 
                boolean promoted = false; 
                if (copy[r3][c3]==CheckersData.RED && r3==0) promoted = true; 
                if (copy[r3][c3]==CheckersData.BLACK && r3==size-1) promoted = true; 
                boolean nextIsKing = isKing || promoted; 
                if (promoted) nextIsKing = isKing; 
                int got = 1 + maxCapturesFrom(copy, player, r3, c3, nextIsKing); 
                if (got == targetMax) res.add(new CheckersMove(r,c,r3,c3)); 
            } 
        } 
        return res; 
    } 
 
    private int[][] copyBoard(int[][] src){ 
        int n = src.length; 
        int[][] dst = new int[n][n]; 
        for (int i=0;i<n;i++) System.arraycopy(src[i],0,dst[i],0,n); 
        return dst; 
    } 
} 
111 
 
482.ЧДТУ.262260 12 01                                               45 
Лістинг Б.20 - Файл RuleStrategy.java 
package org.chdtu.rules; 
 
import org.chdtu.model.CheckersData; 
import org.chdtu.model.CheckersMove; 
 
public interface RuleStrategy { 
    void setUpGame(CheckersData data); 
    void makeMove(CheckersData data, CheckersMove move); 
    CheckersMove[] getLegalMoves(CheckersData data, int player); 
    CheckersMove[] getLegalMovesForPiece(CheckersData data, int player, int row, int col); 
    CheckersMove[] getLegalJumpsFrom(CheckersData data, int player, int row, int col); 
    int getBoardSize(); 
} 
Лістинг Б.21 - Файл AuthService.java 
package org.chdtu.service; 
 
import org.chdtu.entity.User; 
import org.chdtu.repository.UserRepository; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.security.crypto.password.PasswordEncoder; 
import org.springframework.stereotype.Service; 
 
import java.util.Optional; 
 
@Service 
public class AuthService { 
 
    @Autowired 
    private UserRepository userRepository; 
 
    @Autowired 
    private PasswordEncoder passwordEncoder; 
 
    public String registerUser(String username, String password) { 
        if (userRepository.findByUsername(username).isPresent()) { 
            return "Користувач з таким іменем вже існує!"; 
        } 
 
        User newUser = new User(); 
        newUser.setUsername(username); 
        // Хешуємо пароль перед збереженням у БД H2 
        newUser.setPassword(passwordEncoder.encode(password)); 
        newUser.setRating(1200); // Початковий рейтинг 
 
112 
 
482.ЧДТУ.262260 12 01                                               46 
        userRepository.save(newUser); 
        return "Реєстрація успішна!"; 
    } 
} 
Лістинг Б.22 - Файл GameService.java 
package org.chdtu.service; 
 
import org.chdtu.entity.GameSession; 
import org.chdtu.model.CheckersData; 
import org.chdtu.repository.GameSessionRepository; 
import org.chdtu.rules.RuleStrategy; 
import org.chdtu.rules.BritishRuleStrategy; 
import org.chdtu.rules.InternationalRuleStrategy; 
import org.chdtu.rules.BrazilianRuleStrategy; // Додали імпорт бразильської стратегії 
import org.springframework.messaging.simp.SimpMessagingTemplate; 
import org.springframework.stereotype.Service; 
import org.springframework.transaction.annotation.Transactional; 
 
import java.util.Optional; 
 
@Service 
public class GameService { 
 
    private final GameSessionRepository gameSessionRepository; 
    private final SimpMessagingTemplate messagingTemplate; 
 
    // Впровадження залежностей через конструктор для чистоти архітектури 
    public GameService(GameSessionRepository gameSessionRepository, 
                       SimpMessagingTemplate messagingTemplate) { 
        this.gameSessionRepository = gameSessionRepository; 
        this.messagingTemplate = messagingTemplate; 
    } 
 
    /** 
     * Створення нової ігрової сесії з динамічним визначенням розміру поля 
     */ 
    @Transactional 
    public GameSession createNewGame(String rulesType, Long redPlayerId) { 
        GameSession session = new GameSession(); 
        session.setRuleType(rulesType); 
        session.setRedPlayerId(redPlayerId); 
        session.setActive(true); 
 
        // 1. Визначаємо стратегію за допомогою розширеної фабричної логіки 
        RuleStrategy strategy; 
        if (rulesType != null && rulesType.equalsIgnoreCase("International")) { 
113 
 
482.ЧДТУ.262260 12 01                                               47 
            strategy = new InternationalRuleStrategy(); 
        } else if (rulesType != null && rulesType.equalsIgnoreCase("Brazilian")) { 
            strategy = new BrazilianRuleStrategy(); // Підтримка бразильських шашок (8х8 з 
правилами дамок від 10х10) 
        } else { 
            strategy = new BritishRuleStrategy(); // Дефолтні правила (8х8) 
        } 
 
        // 2. Отримуємо коректний розмір дошки безпосередньо зі стратегії (8 або 10) 
        int size = strategy.getBoardSize(); 
 
        // 3. Ініціалізуємо об'єкт моделі CheckersData під правильний розмір 
        CheckersData data = new CheckersData(size); 
 
        // 4. Стратегія розставляє фігури у матриці 
        strategy.setUpGame(data); 
 
        // 5. Конвертуємо двовимірний масив у рядок для збереження в базу даних 
        String serializedBoard = serializeBoard(data.getBoard()); 
        session.setBoardState(serializedBoard); 
 
        // Записуємо випадковий модифікатор, якщо система підтримує кастомізацію 
        session.setModifier("Класика"); 
 
        return gameSessionRepository.save(session); 
    } 
 
    /** 
     * Обробка підключення другого гравця (чорні фігури) до існуючої гри 
     */ 
    @Transactional 
    public GameSession joinGame(Long gameId, Long blackPlayerId) { 
        Optional<GameSession> sessionOpt = gameSessionRepository.findById(gameId); 
        if (sessionOpt.isPresent()) { 
            GameSession session = sessionOpt.get(); 
            if (session.getBlackPlayerId() == null) { 
                session.setBlackPlayerId(blackPlayerId); 
                return gameSessionRepository.save(session); 
            } 
        } 
        throw new IllegalStateException("Гра недоступна для підключення або вже 
заповнена"); 
    } 
 
    /** 
     * Обробка технічної поразки при відключенні одного з користувачів 
     */ 
114 
 
482.ЧДТУ.262260 12 01                                               48 
    @Transactional 
    public void processTechnicalLoss(String gameId, String disconnectedUserId) { 
        Optional<GameSession> sessionOpt = 
gameSessionRepository.findById(Long.parseLong(gameId)); 
 
        if (sessionOpt.isPresent()) { 
            GameSession session = sessionOpt.get(); 
 
            if (session.isActive()) { 
                Long disconnectedId = Long.parseLong(disconnectedUserId); 
                Long winnerId = session.getRedPlayerId().equals(disconnectedId) 
                        ? session.getBlackPlayerId() 
                        : session.getRedPlayerId(); 
 
                session.setActive(false); 
                gameSessionRepository.save(session); 
 
                String destination = "/topic/game/" + gameId; 
                messagingTemplate.convertAndSend(destination, 
                        "SYSTEM: Гравець з ID " + disconnectedUserId + " залишив гру. 
Технічна поразка."); 
 
                System.out.println("Гру " + gameId + " завершено. Переміг гравець за 
технічною поразкою: " + winnerId); 
            } 
        } 
    } 
 
    // --- Допоміжні методи для серіалізації/десеріалізації матриці дошки --- 
 
    /** 
     * Перетворення двовимірного масиву int[][] у рядок для збереження в текстове поле БД 
     */ 
    private String serializeBoard(int[][] board) { 
        StringBuilder sb = new StringBuilder(); 
        for (int i = 0; i < board.length; i++) { 
            for (int j = 0; j < board[i].length; j++) { 
                sb.append(board[i][j]); 
                if (j < board[i].length - 1) sb.append(","); 
            } 
            if (i < board.length - 1) sb.append(";"); 
        } 
        return sb.toString(); 
    } 
 
    /** 
     * Зворотне відновлення рядка з бази даних у двовимірний масив (матрицю). 
115 
 
482.ЧДТУ.262260 12 01                                               49 
     * Захищене від помилок та динамічно визначає розміри стовпців. 
     */ 
    public int[][] deserializeBoard(String boardStr) { 
        if (boardStr == null || boardStr.trim().isEmpty()) { 
            return new int[8][8]; // Безпечний дефолт 
        } 
 
        // Захист на випадок, якщо в базі затесався лівий префікс "8:8|" з логів дебагу 
        if (boardStr.contains("|")) { 
            boardStr = boardStr.substring(boardStr.indexOf("|") + 1); 
        } 
 
        try { 
            String[] rows = boardStr.split(";"); 
            int rowCount = rows.length; 
 
            // Динамічно визначаємо кількість колонок по першому рядку 
            String[] firstColTokens = rows[0].split(","); 
            int colCount = firstColTokens.length; 
 
            int[][] board = new int[rowCount][colCount]; 
 
            for (int i = 0; i < rowCount; i++) { 
                String[] cols = rows[i].split(","); 
                for (int j = 0; j < colCount; j++) { 
                    if (j < cols.length) { 
                        // Очищаємо від можливих пробілів чи лівих символів перед парсингом 
                        String cellValue = cols[j].replaceAll("[^0-9\\-]", "").trim(); 
                        if (!cellValue.isEmpty()) { 
                            board[i][j] = Integer.parseInt(cellValue); 
                        } else { 
                            board[i][j] = 0; 
                        } 
                    } 
                } 
            } 
            return board; 
 
        } catch (Exception e) { 
            System.err.println("Помилка десеріалізації дошки! Текст в БД: " + boardStr); 
            e.printStackTrace(); 
            return new int[8][8]; // Гарантує, що гра не впаде з 500, а намалює чисту дошку 
        } 
    } 
Лістинг Б.23 - Файл UserService.java 
116 
 
482.ЧДТУ.262260 12 01                                               50 
package org.chdtu.service; 
 
import org.chdtu.entity.User; 
import org.chdtu.repository.UserRepository; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Service; 
import org.springframework.transaction.annotation.Transactional; 
 
@Service 
public class UserService { 
 
    @Autowired 
    private UserRepository userRepository; 
 
    @Transactional 
    public void updateStats(Long winnerId, Long loserId) { 
        if (winnerId != null) { 
            userRepository.findById(winnerId).ifPresent(u -> { 
                u.addWin(); 
                userRepository.save(u); 
            }); 
        } 
        if (loserId != null) { 
            userRepository.findById(loserId).ifPresent(u -> { 
                u.addLoss(); 
                userRepository.save(u); 
            }); 
        } 
    } 
} 
Лістинг Б.24 - Файл index.html 
<!DOCTYPE html> 
<html lang="uk"> 
<head> 
    <meta charset="UTF-8"> 
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
    <title>Checkers Web Professional | Diplom Project</title> 
    <style> 
        :root { 
            --bg-main: #0f0f1b; 
            --bg-sidebar: #1a1a2e; 
            --bg-panel: #16213e; 
            --accent: #e94560; 
            --highlight: #00fff2; 
        } 
 
117 
 
482.ЧДТУ.262260 12 01                                               51 
        * { box-sizing: border-box; } 
 
        body { 
            background-color: #0f3460; 
            color: #e2e2e2; 
            font-family: 'Segoe UI', sans-serif; 
            display: flex; 
            margin: 0; 
            min-height: 100vh; 
            overflow-x: hidden; 
        } 
 
        /* ДЕСКТОПНИЙ РЕЖИМ */ 
        #sidebar { 
            width: 320px; 
            padding: 20px; 
            background: var(--bg-sidebar); 
            border-right: 2px solid var(--accent); 
            height: 100vh; 
            overflow-y: auto; 
            flex-shrink: 0; 
            display: flex; 
            flex-direction: column; 
        } 
 
        #game-area { 
            flex-grow: 1; 
            display: flex; 
            align-items: center; 
            justify-content: center; 
            background: var(--bg-main); 
            padding: 20px; 
        } 
 
        .panel { 
            margin-bottom: 15px; 
            padding: 15px; 
            background: var(--bg-panel); 
            border-radius: 8px; 
            border-left: 4px solid var(--accent); 
            box-shadow: 0 4px 10px rgba(0,0,0,0.3); 
        } 
 
        canvas { 
            border: 5px solid var(--accent); 
            background-color: #333; 
            box-shadow: 0 0 30px rgba(233, 69, 96, 0.3); 
118 
 
482.ЧДТУ.262260 12 01                                               52 
            max-width: 100%; 
            height: auto; 
            cursor: pointer; 
        } 
 
  
        @media screen and (max-width: 1100px) { 
            body { flex-direction: column; overflow-y: auto; } 
 
            #game-area { 
                order: -1; /* Дошка зверху */ 
                width: 100%; 
                min-height: auto; 
            } 
 
            #sidebar { 
                width: 100%; 
                height: auto; 
                border-right: none; 
                border-top: 2px solid var(--accent); 
            } 
 
            canvas { width: 95vw; } 
        } 
 
        /* Елементи керування */ 
        h3 { margin: 0 0 10px 0; color: var(--accent); text-transform: uppercase; font-
size: 1.1em; } 
        button { 
            background: var(--accent); color: white; border: none; padding: 12px; 
            cursor: pointer; border-radius: 4px; width: 100%; font-weight: bold; 
            margin-bottom: 8px; transition: 0.2s; 
        } 
        button:hover { background: #ff4d6d; transform: translateY(-1px); } 
        .btn-join { background: #2ecc71; } 
        .btn-join:hover { background: #27ae60; } 
 
        select, input { 
            width: 100%; padding: 10px; margin: 5px 0 10px 0; border-radius: 4px; 
            background: #0f3460; color: white; border: 1px solid #1a1a2e; 
        } 
 
        .profile-container { display: flex; align-items: center; gap: 12px; } 
        #user-avatar { width: 50px; height: 50px; border-radius: 50%; border: 2px solid 
var(--highlight); } 
        .rating-badge { font-size: 1.2em; font-weight: bold; color: var(--highlight); } 
 
119 
 
482.ЧДТУ.262260 12 01                                               53 
        #tooltip { display: none; position: absolute; background: #1a1a2e; color: #bdc3c7; 
padding: 10px; border: 1px solid var(--accent); border-radius: 6px; z-index: 100; } 
    </style> 
</head> 
<body> 
 
<div id="sidebar"> 
    <div class="panel" id="auth-section"> 
        <h3>Вхід в систему</h3> 
        <input type="text" id="reg-username" placeholder="Ваш нікнейм"> 
        <input type="password" id="reg-password" placeholder="Пароль"> 
        <button onclick="handleRegister()">УВІЙТИ / СТВОРИТИ</button> 
        <p id="reg-message" style="font-size: 11px; color: var(--highlight); text-align: 
center; margin: 0;"></p> 
    </div> 
 
    <div class="panel"> 
        <h3>Мій Профіль</h3> 
        <div class="profile-container"> 
            <img id="user-avatar" src="https://ui-
avatars.com/api/?name=User&background=random"> 
            <div> 
                <div style="display:flex; align-items:center;"> 
                    <div id="player-color-dot" style="width:10px; height:10px; border-
radius:50%; margin-right:5px; background:#555;"></div> 
                    <strong id="player-name">Гість</strong> 
                </div> 
                <span id="player-rating" class="rating-badge">1200 ELO</span> 
            </div> 
        </div> 
        <div style="display:grid; grid-template-columns: 1fr 1fr; font-size: 12px; margin-
top: 10px;"> 
            <div>Виграші: <span id="player-wins" style="color:#2ecc71">0</span></div> 
            <div>Програші: <span id="player-losses" style="color:#e94560">0</span></div> 
        </div> 
    </div> 
 
    <div class="panel"> 
        <h3>  Топ Гравців</h3> 
        <table style="width:100%; font-size:12px; border-collapse:collapse;"> 
            <tbody id="leaderboard-body"></tbody> 
        </table> 
    </div> 
 
    <div class="panel"> 
        <h3>Налаштування</h3> 
120 
 
482.ЧДТУ.262260 12 01                                               54 
        <div style="display:flex; justify-content:space-between; align-items:center; 
margin-bottom:8px;"> 
            <span style="font-size:13px;">Музика:</span> 
            <button id="music-toggle" onclick="toggleMusic()" style="width:60px; 
padding:5px; margin:0;">ВКЛ</button> 
        </div> 
        <select id="theme-select" onchange="updateFavoriteTheme(this.value)"> 
            <option value="classic">Класична тема</option> 
            <option value="cyber">Неонова тема</option> 
            <option value="wood">Дерев'яна тема</option> </select> 
    </div> 
 
    <div class="panel" style="border-bottom:none;"> 
        <h3>Гра</h3> 
        <select id="rule-select"> 
            <option value="British">British (8x8)</option> 
            <option value="International">International (10x10)</option> 
            <option value="Brazilian">Brazilian (8x8)</option> </select> 
        <button onclick="startNewGame()">СТВОРИТИ (RED)</button> 
        <button onclick="joinGame()" class="btn-join">ПРИЄДНАТИСЯ (BLACK)</button> 
        <div id="special-condition" style="background:var(--accent); padding:8px; margin-
top:10px; font-weight:bold; border-radius:4px; text-align:center; display:none; font-
size:13px;"></div> 
        <div id="status-message" style="margin-top:10px; font-size:13px; text-align:center; 
color:var(--highlight);">Авторизуйтесь для гри</div> 
    </div> 
 
    <div style="font-size: 10px; color: #444; text-align: center; margin-top: auto; 
padding: 10px 0;">Diplom Project © 2026 | Oleg's Engine</div> 
</div> 
 
<div id="game-area"> 
    <div id="game-container" style="width:100%; display:flex; justify-content:center;"> 
        <canvas id="checkersCanvas"></canvas> 
    </div> 
</div> 
 
<div id="tooltip"></div> 
 
<script src="js/game.js"></script> 
</body> 
</html> 
 
Лістинг Б.25 - Файл style.css 
:root { 
    --bg-main: #0f0f1b; 
121 
 
482.ЧДТУ.262260 12 01                                               55 
    --bg-sidebar: #1a1a2e; 
    --bg-panel: #16213e; 
    --accent: #e94560; 
    --highlight: #00fff2; 
    --text: #e2e2e2; 
} 
 
* { box-sizing: border-box; } 
 
body { 
    background-color: #0f3460 !important; 
    color: var(--text) !important; 
    font-family: 'Segoe UI', Tahoma, sans-serif !important; 
    display: flex; 
    margin: 0; 
    min-height: 100vh; 
    overflow-x: hidden; 
} 
 
/* ДЕСКТОП: Бокова панель зліва */ 
#sidebar { 
    width: 320px; 
    padding: 20px; 
    background: var(--bg-sidebar); 
    border-right: 2px solid var(--accent); 
    height: 100vh; 
    overflow-y: auto; 
    flex-shrink: 0; 
    display: flex; 
    flex-direction: column; 
} 
 
#game-area { 
    flex-grow: 1; 
    display: flex; 
    align-items: center; 
    justify-content: center; 
    background: var(--bg-main); 
} 
 
/* Панелі (картки) */ 
.panel { 
    margin-bottom: 15px; 
    padding: 15px; 
    background: var(--bg-panel); 
    border-radius: 8px; 
    border-left: 4px solid var(--accent); 
122 
 
482.ЧДТУ.262260 12 01                                               56 
    box-shadow: 0 4px 10px rgba(0,0,0,0.3); 
} 
 
h3 { 
    margin-top: 0; 
    color: var(--accent); 
    text-transform: uppercase; 
    font-size: 1.1em; 
    letter-spacing: 1px; 
} 
 
/* КАНВАС */ 
canvas { 
    border: 5px solid var(--accent); 
    background-color: #333; 
    box-shadow: 0 0 30px rgba(233, 69, 96, 0.3); 
    max-width: 95%; 
    height: auto; 
} 
 
 
@media screen and (max-width: 1100px) { 
    body { 
        flex-direction: column !important; 
        overflow-y: auto !important; 
    } 
 
    #game-area { 
        order: -1 !important; /* Дошка стає ПЕРШОЮ зверху */ 
        width: 100% !important; 
        padding: 20px 0; 
        min-height: auto; 
    } 
 
    #sidebar { 
        width: 100% !important; 
        height: auto !important; 
        border-right: none; 
        border-top: 2px solid var(--accent); 
    } 
 
    canvas { 
        width: 95vw !important; 
    } 
} 
 
/* Красиві кнопки та інпути */ 
123 
 
482.ЧДТУ.262260 12 01                                               57 
button { 
    background: var(--accent); 
    color: white; 
    border: none; 
    padding: 12px; 
    cursor: pointer; 
    border-radius: 4px; 
    width: 100%; 
    font-weight: bold; 
    transition: 0.3s; 
    margin-bottom: 5px; 
} 
 
button:hover { 
    background: #ff4d6d; 
    transform: translateY(-2px); 
} 
 
select, input { 
    width: 100%; 
    padding: 10px; 
    margin: 5px 0; 
    border-radius: 4px; 
    background: #0f3460; 
    color: white; 
    border: 1px solid #1a1a2e; 
} 
 
.rating-badge { font-size: 1.2em; font-weight: bold; color: var(--highlight); } 
Лістинг Б.26 - Файл game.js 
// --- 1. Елементи інтерфейсу та Аудіо --- 
const canvas = document.getElementById('checkersCanvas'); 
const ctx = canvas.getContext('2d'); 
const statusMsg = document.getElementById('status-message'); 
const condBox = document.getElementById('special-condition'); 
const tooltip = document.getElementById('tooltip'); 
 
const bgMusic = new Audio('music/bgm.mp3'); 
bgMusic.loop = true; 
bgMusic.volume = 0.4; 
 
// --- 2. Стан гри --- 
let board = []; 
let lastBoardStr = ""; 
let selectedCell = null; 
let availableMoves = []; 
124 
 
482.ЧДТУ.262260 12 01                                               58 
let squareSize = 75; 
 
window.currentUserId = null; 
window.gameId = null; 
let myPlayerType = 0; // 1 - RED, 3 - BLACK 
let isPolling = false; 
 
let currentTheme = { dark: "#16213e", light: "#0f3460" }; 
let hintsEnabled = true; 
 
const modInfo = { 
    "Втомлена дамка": "Дамка втрачає сили: вона не може робити серію стрибків за один 
хід.", 
    "Мирні шашки": "У цій грі бити фігури суперника не обов'язково.", 
    "Тил під захистом": "Прості шашки бояться відступати: хід назад суворо заборонено.", 
    "Важкі кроки": "Дамка стає важкою: вона може пересуватися лише на одну сусідню 
клітинку.", 
    "Камікадзе": "Після успішного взяття фігури суперника, ваша шашка також зникає.", 
    "Обмежений вибір": "Сервер залишає лише один випадковий варіант ходу.", 
    "Швидка коронація": "Шашка стає дамкою вже на передостанньому ряду.", 
    "Класика": "Звичайна гра без додаткових модифікаторів." 
}; 
 
// --- 3. Ініціалізація та Адаптивність --- 
document.addEventListener("DOMContentLoaded", () => { 
    if (!canvas) return; 
    applyForceLayout(); // Примусове очищення позиціонування 
    resizeCanvas(); 
    initTooltips(); 
    refreshLeaderboard(); 
}); 
 
// Метод для повного виправлення "наповзання" елементів на мобільних пристроях 
function applyForceLayout() { 
    const isMobile = window.innerWidth <= 1100; 
    const uiLayer = document.getElementById('ui-layer'); 
    const sidebar = document.getElementById('sidebar'); 
    const container = document.getElementById('game-container'); 
 
    if (isMobile && uiLayer && sidebar && container) { 
        // Примусово скидаємо inline-стилі, що ламають верстку 
        [uiLayer, sidebar, container].forEach(el => { 
            el.style.setProperty('position', 'static', 'important'); 
            el.style.setProperty('left', 'auto', 'important'); 
            el.style.setProperty('top', 'auto', 'important'); 
            el.style.setProperty('width', '100%', 'important'); 
        }); 
125 
 
482.ЧДТУ.262260 12 01                                               59 
 
        // Гарантуємо правильний порядок: Гра зверху 
        container.style.setProperty('order', '-1', 'important'); 
    } 
} 
 
function resizeCanvas() { 
    const container = document.getElementById('game-container'); 
    if (!container) return; 
 
    const padding = 20; 
    const availableWidth = container.clientWidth - padding; 
    const size = Math.min(availableWidth, 600); // Максимум 600px для десктопа 
 
    canvas.width = size; 
    canvas.height = size; 
 
    // Динамічний розрахунок розміру клітинки залежно від поточної розмірності масиву дошки 
    if (board && board.length > 0) { 
        squareSize = canvas.width / board.length; 
    } else { 
        squareSize = canvas.width / 8; 
    } 
    draw(); 
} 
 
window.addEventListener('resize', () => { 
    applyForceLayout(); 
    resizeCanvas(); 
}); 
 
// --- 4. Мережева логіка (Polling) --- 
function startPolling() { 
    if (isPolling) return; 
    isPolling = true; 
 
    const pollInterval = setInterval(async () => { 
        if (!window.gameId) return; 
 
        try { 
            const response = await 
fetch(`/api/game/status/${window.gameId}?t=${Date.now()}`); 
            if (response.ok) { 
                const data = await response.json(); 
                const currentStr = JSON.stringify(data.board); 
 
                if (lastBoardStr !== currentStr) { 
126 
 
482.ЧДТУ.262260 12 01                                               60 
                    board = data.board; 
                    lastBoardStr = currentStr; 
                    // Перераховуємо розмір клітинки, оскільки сервер міг повернути дошку 
10х10 
                    squareSize = canvas.width / board.length; 
                    draw(); 
                } 
 
                if (data.active === false) { 
                    clearInterval(pollInterval); 
                    isPolling = false; 
                    statusMsg.innerHTML = `<b style="color:gold">ГРУ ЗАВЕРШЕНО</b>`; 
                    draw(); 
                    setTimeout(() => { 
                        alert("Партія закінчена! Рейтинг оновлено."); 
                        if (window.currentUserId) updateUIStatistics(window.currentUserId); 
                        refreshLeaderboard(); 
                    }, 500); 
                    return; 
                } 
 
                const turnColor = data.currentPlayer === 1 ? "Червоних" : "Чорних"; 
                if (data.currentPlayer === myPlayerType) { 
                    statusMsg.innerHTML = `<b style="color:#00fff2">ВАШ ХІД 
(${turnColor})</b>`; 
                } else { 
                    statusMsg.innerHTML = `Очікування суперника (${turnColor})...`; 
                } 
            } 
        } catch (e) { console.error("Polling error:", e); } 
    }, 1000); 
} 
 
// --- 5. Обробка кліків (з динамічним масштабуванням 8х8 та 10х10) --- 
canvas.addEventListener('click', async (e) => { 
    if (!window.gameId || !board.length) return; 
 
    const rect = canvas.getBoundingClientRect(); 
    const scaleX = canvas.width / rect.width; // Коефіцієнт масштабування для респонсиву 
    const scaleY = canvas.height / rect.height; 
 
    // Розрахунок поточної клітинки базується суворо на фактичній довжині масиву (8 або 10) 
    const currentBoardSize = board.length; 
    const c = Math.floor(((e.clientX - rect.left) * scaleX) / (canvas.width / 
currentBoardSize)); 
    const r = Math.floor(((e.clientY - rect.top) * scaleY) / (canvas.height / 
currentBoardSize)); 
127 
 
482.ЧДТУ.262260 12 01                                               61 
 
    // Валідація виходу за межі масиву (про всяк випадок при швидких кліках на мобільних) 
    if (r < 0 || r >= currentBoardSize || c < 0 || c >= currentBoardSize) return; 
 
    if (selectedCell && selectedCell.r === r && selectedCell.c === c) { 
        selectedCell = null; availableMoves = []; draw(); return; 
    } 
 
    if (!selectedCell) { 
        const piece = board[r][c]; 
        const isMyPiece = (myPlayerType === 1 && (piece === 1 || piece === 2)) || 
            (myPlayerType === 3 && (piece === 3 || piece === 4)); 
 
        if (isMyPiece) { 
            selectedCell = { r, c }; 
            try { 
                const response = await 
fetch(`/api/game/moves?gameId=${window.gameId}&row=${r}&col=${c}&t=${Date.now()}`); 
                if (response.ok) availableMoves = await response.json(); 
            } catch (err) { console.error(err); } 
            draw(); 
        } 
    } else { 
        const move = { fromRow: selectedCell.r, fromCol: selectedCell.c, toRow: r, toCol: c 
}; 
 
        if (!availableMoves.some(m => m.toRow === r && m.toCol === c)) { 
            selectedCell = null; availableMoves = []; draw(); return; 
        } 
 
        try { 
            const response = await fetch(`/api/game/move/${window.gameId}`, { 
                method: 'POST', 
                headers: { 'Content-Type': 'application/json' }, 
                body: JSON.stringify(move) 
            }); 
 
            if (response.ok) { 
                const data = await response.json(); 
                board = data.board; 
                lastBoardStr = JSON.stringify(board); 
                availableMoves = []; 
                selectedCell = null; 
 
                if (data.status === "CONTINUE_JUMP") { 
                    selectedCell = { r: move.toRow, c: move.toCol }; 
                    statusMsg.innerText = "Бийте далі!"; 
128 
 
482.ЧДТУ.262260 12 01                                               62 
                    const jumpRes = await 
fetch(`/api/game/moves?gameId=${window.gameId}&row=${r}&col=${c}&t=${Date.now()}`); 
                    if (jumpRes.ok) availableMoves = await jumpRes.json(); 
                } 
                draw(); 
            } else { 
                statusMsg.innerText = await response.text(); 
                selectedCell = null; availableMoves = []; draw(); 
            } 
        } catch (err) { console.error(err); } 
    } 
}); 
 
// --- 6. Візуалізація (Rendering) --- 
function draw() { 
    if (!board || board.length === 0) return; 
    const size = board.length; 
    squareSize = canvas.width / size; // Динамічне перевизначення кроку сітки (для 8х8 це 
1/8, для 10х10 це 1/10) 
    ctx.clearRect(0, 0, canvas.width, canvas.height); 
 
    for (let r = 0; r < size; r++) { 
        for (let c = 0; c < size; c++) { 
            // Малювання клітинок 
            ctx.fillStyle = (r % 2 === c % 2) ? currentTheme.light : currentTheme.dark; 
            ctx.fillRect(c * squareSize, r * squareSize, squareSize, squareSize); 
 
            // Підказки ходів 
            if (hintsEnabled && availableMoves.some(m => m.toRow === r && m.toCol === c)) { 
                ctx.beginPath(); 
                ctx.arc(c * squareSize + squareSize / 2, r * squareSize + squareSize / 2, 
squareSize * 0.15, 0, Math.PI * 2); 
                ctx.fillStyle = "rgba(0, 255, 242, 0.5)"; 
                ctx.fill(); 
            } 
 
            // Виділена клітинка 
            if (selectedCell && selectedCell.r === r && selectedCell.c === c) { 
                ctx.strokeStyle = "#00fff2"; 
                ctx.lineWidth = Math.max(2, squareSize * 0.05); 
                ctx.strokeRect(c * squareSize + 2, r * squareSize + 2, squareSize - 4, 
squareSize - 4); 
            } 
 
            // Малювання фігур 
            const p = board[r][c]; 
            if (p > 0) { 
129 
 
482.ЧДТУ.262260 12 01                                               63 
                const cx = c * squareSize + squareSize / 2; 
                const cy = r * squareSize + squareSize / 2; 
                ctx.beginPath(); 
                ctx.arc(cx, cy, squareSize * 0.38, 0, Math.PI * 2); 
                ctx.fillStyle = (p <= 2) ? "#e74c3c" : "#222"; 
                ctx.fill(); 
                ctx.strokeStyle = "white"; 
                ctx.lineWidth = Math.max(1, squareSize * 0.03); 
                ctx.stroke(); 
 
                if (p === 2 || p === 4) { // Дамки 
                    ctx.fillStyle = "gold"; 
                    ctx.font = `bold ${Math.floor(squareSize * 0.4)}px Arial`; 
                    ctx.textAlign = "center"; 
                    ctx.fillText("K", cx, cy + (squareSize * 0.12)); 
                } 
            } 
        } 
    } 
} 
 
// --- 7. Керування профілем та Статистика --- 
window.handleRegister = async function() { 
    const user = document.getElementById('reg-username').value; 
    const pass = document.getElementById('reg-password').value; 
    const msgArea = document.getElementById('reg-message'); 
 
    if (!user || !pass) { 
        if(msgArea) msgArea.innerText = "Заповніть усі поля!"; 
        return; 
    } 
 
    const formData = new FormData(); 
    formData.append('username', user); 
    formData.append('password', pass); 
 
    try { 
        if(msgArea) msgArea.innerText = "Обробка..."; 
        const response = await fetch('/api/auth/register', { method: 'POST', body: formData 
}); 
 
        if (response.ok) { 
            const userRes = await fetch(`/api/users/search?name=${user}&t=${Date.now()}`); 
            const userData = await userRes.json(); 
 
            window.currentUserId = userData.id; 
            updateUIStatistics(userData.id); 
130 
 
482.ЧДТУ.262260 12 01                                               64 
            refreshLeaderboard(); 
 
            document.getElementById('auth-section').style.display = "none"; 
            if(msgArea) msgArea.innerText = "Успішно!"; 
        } else { 
            if(msgArea) msgArea.innerText = "Помилка (вже існує?)"; 
        } 
    } catch (e) { 
        console.error(e); 
        if(msgArea) msgArea.innerText = "Сервер недоступний"; 
    } 
}; 
 
async function updateUIStatistics(userId) { 
    if (!userId) return; 
    try { 
        const response = await fetch(`/api/users/${userId}?t=${Date.now()}`); 
        const user = await response.json(); 
        document.getElementById('player-name').innerText = user.username; 
        document.getElementById('player-wins').innerText = user.wins; 
        document.getElementById('player-losses').innerText = user.losses; 
        document.getElementById('player-rating').innerText = user.rating + " ELO"; 
    } catch (e) {} 
} 
 
async function refreshLeaderboard() { 
    try { 
        const response = await fetch('/api/users/leaderboard?t=' + Date.now()); 
        if (!response.ok) return; 
        const topPlayers = await response.json(); 
        const tbody = document.getElementById('leaderboard-body'); 
        if (!tbody) return; 
 
        tbody.innerHTML = ""; 
        topPlayers.forEach((user, index) => { 
            const isMe = user.id === window.currentUserId; 
            const avatar = user.avatarPath || `https://ui-
avatars.com/api/?name=${user.username}&background=random`; 
            tbody.innerHTML += ` 
                <tr style="${isMe ? 'background: rgba(0,255,242,0.1); color: #00fff2; font-
weight: bold;' : ''}"> 
                    <td style="padding: 5px;">${index + 1}</td> 
                    <td> 
                        <img src="${avatar}" class="mini-avatar" style="width:20px; 
height:20px; border-radius:50%; vertical-align:middle; margin-right:5px;"> 
                        ${user.username} 
                    </td> 
131 
 
482.ЧДТУ.262260 12 01                                               65 
                    <td style="text-align:right; padding-right:5px;">${user.rating}</td> 
                </tr>`; 
        }); 
    } catch (e) { console.error("Leaderboard error:", e); } 
} 
 
// --- 8. Керування іграми --- 
window.startNewGame = async function() { 
    if (!window.currentUserId) { alert("Увійдіть в систему!"); return; } 
    const rules = document.getElementById('rule-select').value; 
 
    try { 
        const response = await 
fetch(`/api/game/new?rules=${rules}&redId=${window.currentUserId}`, { method: 'POST' }); 
        const data = await response.json(); 
 
        window.gameId = data.gameId; 
        myPlayerType = 1; 
        board = data.board; 
        lastBoardStr = JSON.stringify(board); 
 
        document.getElementById('player-color-dot').style.background = "#e74c3c"; 
        condBox.innerText = "Умова: " + data.modifier; 
        condBox.style.display = 'block'; 
 
        // Перераховуємо розмір під отриману від сервера дошку (наприклад, 10 для 
International) 
        squareSize = canvas.width / board.length; 
 
        startPolling(); 
        draw(); 
    } catch (e) { alert("Помилка створення гри"); } 
}; 
 
window.joinGame = async function() { 
    if (!window.currentUserId) { alert("Увійдіть в систему!"); return; } 
 
    try { 
        const response = await fetch(`/api/game/join?blackId=${window.currentUserId}`, { 
method: 'POST' }); 
        if (response.ok) { 
            const data = await response.json(); 
            window.gameId = data.gameId; 
            myPlayerType = 3; 
 
            document.getElementById('player-color-dot').style.background = "#222"; 
 
132 
 
482.ЧДТУ.262260 12 01                                               66 
            const statusRes = await 
fetch(`/api/game/status/${window.gameId}?t=${Date.now()}`); 
            const statusData = await statusRes.json(); 
            board = statusData.board; 
            lastBoardStr = JSON.stringify(board); 
            condBox.innerText = "Умова: " + statusData.modifier; 
            condBox.style.display = 'block'; 
 
            // Перераховуємо розмір під отриману від сервера дошку 
            squareSize = canvas.width / board.length; 
 
            startPolling(); 
            draw(); 
        } else { 
            alert(await response.text()); 
        } 
    } catch (e) { alert("Немає доступних ігор"); } 
}; 
 
// --- 9. Налаштування та Інтерфейс --- 
function changeTheme() { 
    const theme = document.getElementById('theme-select').value; 
    switch(theme) { 
        case 'cyber': currentTheme = { dark: "#16213e", light: "#0f3460" }; break; 
        case 'wood': currentTheme = { dark: "#5d4037", light: "#d7ccc8" }; break; 
        default: currentTheme = { dark: "#7f8c8d", light: "#ecf0f1" }; 
    } 
    draw(); 
} 
 
window.updateFavoriteTheme = async function(themeValue) { 
    changeTheme(); 
    if (window.currentUserId) { 
        try { 
            await fetch(`/api/users/${window.currentUserId}/theme?theme=${themeValue}`, { 
method: 'POST' }); 
        } catch (e) {} 
    } 
}; 
 
window.toggleMusic = function() { 
    const musicBtn = document.getElementById('music-toggle'); 
    if (bgMusic.paused) { 
        bgMusic.play().catch(()=>{}); 
        musicBtn.innerText = "ВКЛ"; 
        musicBtn.style.background = "#2ecc71"; 
    } else { 
133 
 
482.ЧДТУ.262260 12 01                                               67 
        bgMusic.pause(); 
        musicBtn.innerText = "ВИКЛ"; 
        musicBtn.style.background = "#e74c3c"; 
    } 
}; 
 
window.toggleHints = function() { 
    hintsEnabled = document.getElementById('show-hints').checked; 
    if (!hintsEnabled) availableMoves = []; 
    draw(); 
}; 
 
function initTooltips() { 
    if (!condBox) return; 
    condBox.addEventListener('mouseenter', (e) => { 
        const currentMod = condBox.innerText.replace("Умова: ", ""); 
        if (modInfo[currentMod]) { 
            tooltip.innerText = modInfo[currentMod]; 
            tooltip.style.display = 'block'; 
        } 
    }); 
    condBox.addEventListener('mousemove', (e) => { 
        tooltip.style.left = (e.pageX + 15) + 'px'; 
        tooltip.style.top = (e.pageY + 15) + 'px'; 
    }); 
    condBox.addEventListener('mouseleave', () => tooltip.style.display = 'none'); 
} 
134 
 
 
 
ДОДАТОК В 
 
 
 
 
 
 
 
 
«Програмна реалізація кастомізованих правил у грі "Шашки"» 
Інструкція користувачева 
482.ЧДТУ. 2260 34 01 
Листів 8 
 
 
 
 
 
 
Розробник ________________ О.І. Сасько 
 
 
 
 
 
 
 
2026 
 
 
482.ЧДТУ.262260 34 01                                                13 
 
Коли користувач запускає веборієнтований програмний комплекс, перед ним 
відкривається головне вікно програми. Для початку повноцінної ігрової сесії, 
збереження особистої статистики та участі у рейтинговій таблиці лідерів, 
користувач повинен пройти швидку процедуру авторизації. 
Реєстрація та вхід об’єднані в одну зручну форму: у лівій панелі «Вхід в систему» 
необхідно ввести унікальний нікнейм та пароль, після чого натиснути кнопку 
«УВІЙТИ / СТВОРИТИ». 
 
 
Рисунок В.1 – Головне меню 
 
Після успішного підтвердження даних сервером інтерфейс динамічно 
оновлюється. У панелі «Мій Профіль» замість стандартного статусу гостя 
відображається зареєстроване ім'я користувача (наприклад, "Олег"), генерується 
персональний аватар на основі перших літер нікнейму, а також підтягуються 
індивідуальні статистичні дані з бази даних H2: кількість перемог, поразок та 
стартовий рейтинг за системою Ело (1200 ELO). 
136 
482.ЧДТУ.262260 34 01                                                14 
 
Рисунок В.2 – Вхід в акаунт 
 
Програмний комплекс підтримує індивідуальне налаштування візуального 
оформлення під потреби користувача. У розділі «Налаштування» гравець може 
увімкнути чи вимкнути фонову музику і звукові ефекти, а також обрати одну з 
трьох графічних тем для оформлення ігрової дошки: 
• Класична тема – стриманий монохромний інтерфейс; 
• Неонова тема – яскраве кіберпанк-оформлення з блакитною та рожевою 
підсвіткою; 
• Дерев'яна тема – традиційна коричнева текстура шахового поля. 
 
Рисунок В.3 – Вибір теми 
137 
482.ЧДТУ.262260 34 01                                                15 
 
Завдяки реалізації патерна проєктування Strategy, вебдодаток дозволяє грати за 
різними правилами. Перед ініціалізацією сесії в меню «Гра» користувач обирає 
один із трьох доступних режимів у списку, що випадає: 
1. British (8x8) – класична англійська версія гри з кроками простих фігур лише 
вперед; 
2. International (10x10) – міжнародні шашки з розширеним полем і 
специфічним правилом максимального взяття; 
3. Brazilian (8x8) – бразильські шашки (міжнародні правила, адаптовані під 
стандартне поле 8х8). 
 
Рисунок В.4 – Вибір правил шашок 
 
При виборі опції «International (10x10)» та натисканні кнопки «СТВОРИТИ 
(RED)», серверне ядро Spring Boot ініціалізує нову ігрову сесію з розширеною 
матрицею поля. Клієнтська логіка автоматично перераховує геометричні розміри 
елементів на Canvas-конвеєрі під формат 10х10 клітинок та розставляє по 20 фігур 
для кожного суперника. 
Додатково система генерує випадковий ігровий модифікатор (наприклад, "Важкі 
кроки", "Тил під захистом"), що додає унікальні додаткові умови до поточної 
партії. 
138 
482.ЧДТУ.262260 34 01                                                16 
 
 
Рисунок В.5 – Початок гри та генерація кастомних правил 
 
Завдяки інтегрованій дуплексній технології WebSocket-з'єднання, інші користувачі 
мережі можуть миттєво долучитися до створеної активної гри. Для приєднання до 
відкритої сесії другий гравець натискає зелену кнопку «ПРИЄДНАТИСЯ 
(BLACK)». 
Сервер автоматично синхронізує стан поля між двома віддаленими браузерами, 
відображає актуальний ігровий процес та передає чергу ходу Чорним. 
 
 
139 
482.ЧДТУ.262260 34 01                                                17 
 
Рисунок В.6 – Передача ходу іншому гравцю 
 
Взаємодія з полем відбувається за допомогою кліків мишкою (або тач-подій на 
мобільних пристроях). При виборі фігури система підсвічує доступні варіанти 
ходів відповідно до обраної стратегії правил. Гра триває до повної капітуляції 
одного з опонентів. 
У момент фіксації перемоги система блокує подальшу активність ігрового поля та 
виводить офіційне модальне вікно про завершення партії та оновлення рейтингу. 
140 
482.ЧДТУ.262260 34 01                                                18 
 
Рисунок В.7 – Завершення гри з спливаючим про це повідомленням 
 
Після завершення гри та натискання кнопки «ОК» користувачі повертаються до 
головного меню. Серверний модуль розрахунку за формулою Ело автоматично 
перераховує рейтинги гравців: переможець отримує додаткові бали (рейтинг Олег 
підвищився з 1250 до 1275 ELO), а статистика виграшів моментально оновлюється 
в базі даних H2 та синхронізується в загальній таблиці топ-гравців. 
141 
482.ЧДТУ.262260 34 01                                                19 
 
Рисунок В.8 – Успішне автоматичне оновлення рейтингів 
142 
 
ДОДАТОК Г 
 
 
 
 
 
 
 
 
«Програмна реалізація кастомізованих правил у грі "Шашки"» 
Графічні матеріали  
482.ЧДТУ. 2260 90 01 
Листів 13 
 
 
 
 
 
 
Розробник ________________ О.І. Сасько 
 
 
 
 
 
 
2026 
 
 
 482.ЧДТУ 262260 90 01 12 
 
Рисунок Г.1 – Тема роботи   
 
Рисунок Г.2 – Мета та основні завдання 
144 
 
 482.ЧДТУ 262260 90 01 13 
 
Рисунок Г.3 – Аналіз тех.інформації 
 
Рисунок Г.4 – Актуальність задачі 
145 
 
 482.ЧДТУ 262260 90 01 14 
 
Рисунок Г.5 – Аналіз програмних засобів 
 
Рисунок Г.6 – Аналіз способів рішень 
146 
 
 482.ЧДТУ 262260 90 01 15 
 
Рисунок Г.7 – Постановка задачі 
 
Рисунок Г.8 – Аналіз вимог 
147 
 
 482.ЧДТУ 262260 90 01 16 
 
Рисунок Г.9 – Діаграма прецедентів 
 
Рисунок Г.10 – Логічна структура: класи 
148 
 
 482.ЧДТУ 262260 90 01 17 
 
Рисунок Г.11 – Архітектурне рішення 
 
Рисунок Г.12 – Моделювання поведінки 
149 
 
 482.ЧДТУ 262260 90 01 18 
 
Рисунок Г.13 – Комунікація та стан 
 
Рисунок Г.14 – Вибір засобів реалізації 
150 
 
 482.ЧДТУ 262260 90 01 19 
 
Рисунок Г.15 – Структурна та функціональна схеми 
 
Рисунок Г.16 – Логічна схеми 
151 
 
 482.ЧДТУ 262260 90 01 20 
 
Рисунок Г.17 – Структура бази даних 
 
Рисунок Г.18 – Інтерфейс користувача 
152 
 
 482.ЧДТУ 262260 90 01 21 
 
Рисунок Г.19 – Модульне та інтеграційне тестування 
 
Рисунок Г.20 – Системне та приймальне тестування 
153 
 
 482.ЧДТУ 262260 90 01 22 
 
Рисунок Г.21 – Впровадження: мультиплеєр 
 
Рисунок Г.22 – Впровадження: рейтинг 
154 
 
 482.ЧДТУ 262260 90 01 23 
 
Рисунок Г.23 – Висновки до роботи 
 
155