You are on page 1of 54

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ

ЧЕРКАСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ


ІМЕНІ БОГДАНА ХМЕЛЬНИЦЬКОГО
Факультет обчислювальної техніки, інтелектуальних та управляючих систем
Кафедра програмного забезпечення автоматизованих систем
Спеціальність 121 – Інженерія програмного забезпечення
(Програмна інженерія)

До захисту допускаю
Завідувачка кафедри ПЗАС
О.О. Супруненко
(ініціали та прізвище)

_______________________
(підпис)

«___» __________2022 р.

КВАЛІФІКАЦІЙНА РОБОТА
освітнього ступеня БАКАЛАВР

МОДИФІКАЦІЯ ГРИ «ЯПОНСЬКИЙ КРОСВОРД» З


МОЖЛИВІСТЮ АВТОМАТИЧНОГО РОЗВ'ЯЗУВАННЯ ТА
ДОДАТКОВИМИ ЗАСОБАМИ ОЦІНЮВАННЯ ГРИ
КОРИСТУВАЧА

Студент групи КС-18 Весельський Олексій Сергійович


(шифр групи) (прізвище, ім’я, по батькові) (підпис)

Науковий керівник викладач Порубльов І.М.


(науковий ступінь та вчене звання, прізвище, ініціали) (підпис)

Черкаси – 2022
2
Зміст
ВСТУП.......................................................................................................................... 4
РОЗДІЛ 1. ОГЛЯД ТА АНАЛІЗ СПОСОБІВ РЕАЛІЗАЦІЇ МОДИФІКАЦІЇ ГРИ
«ЯПОНСЬКИЙ КРОСВОРД» .................................................................................... 6
1.1 Огляд предметної області японських кросвордів .......................................... 6
1.2 Актуальність розробки гри «Японський кросворд» та її модифікації......... 7
1.3 Огляд програм-аналогів гри «Японський кросворд» .................................... 7
1.4 Огляд методів та технологій для розробки модифікації гри «Японський
кросворд» та способів реалізації автоматичного розв’язування ...................... 11
1.5 Постановка завдання на розробку модифікації гри «Японський
кросворд» ............................................................................................................... 12
Висновок до розділу 1 ........................................................................................... 13
РОЗДІЛ 2. АНАЛІЗ ВИМОГ І ПРОЕКТУВАННЯ МОДИФІКАЦІЇ ГРИ
«ЯПОНСЬКИЙ КРОСВОРД» .................................................................................. 14
2.1 Формування вимог до модифікації гри «Японський кросворд» та їх
аналіз ....................................................................................................................... 14
2.2 Попереднє проектування архітектури модифікації гри «Японський
кросворд» ............................................................................................................... 18
2.3 Детальне проектування модифікації гри «Японський кросворд» .............. 21
2.3.1 Проектування пакету «Розв’язування кросворду» ......................... 21

2.3.2 Проектування пакету «Створення кросворду» ............................... 23

2.3.3 Проектування бази даних модифікації гри «Японський


кросворд»...................................................................................................... 24

Висновок до розділу 2 ........................................................................................... 25


РОЗДІЛ 3. РОЗРОБКА І ТЕСТУВАННЯ МОДИФІКАЦІЇ ГРИ «ЯПОНСЬКИЙ
КРОСВОРД» .............................................................................................................. 26
3.1 Вибір мови програмування та середовища для розробки модифікації гри
«Японський кросворд» ......................................................................................... 26
3
3.2 Уточнення архітектури та структурна схема реалізації модифікації гри
«Японський кросворд» ......................................................................................... 26
3.3 Розробка компонентів модифікації гри «Японський кросворд» ................ 28
3.3.1 Розробка компоненту «Розв’язування кросворду» ......................... 28

3.3.2 Розробка компоненту «Автоматичне розв’язування» .................... 31

3.3.3 Інтеграція компонентів «Розв’язування кросворду» та


«Автоматичне розв’язування» ................................................................... 33

3.3.4 Реалізація компоненту «Створення кросворду» та його інтеграція з


«Автоматичним розв’язуванням» .............................................................. 35

3.3.5 Реалізація компоненту «Таблиця рекордів» .................................... 38

3.3.6 Загальна діаграма класів модифікації гри «Японський


кросворд»...................................................................................................... 39

3.4 Тестування модифікації гри «Японський кросворд» .................................. 40


Висновок до розділу 3 ........................................................................................... 40
ВИСНОВКИ ............................................................................................................... 41
СПИСОК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ .......................................................... 43
ДОДАТОК А – PRODUCT BACKLOG МОДИФІКАЦІЇ ГРИ «ЯПОНСЬКИЙ
КРОСВОРД» .............................................................................................................. 44
ДОДАТОК Б – БЛОК-СХЕМА ЗАПОВНЕННЯ КАРТИ ШЛЯХІВ В
ANALYZELINE ......................................................................................................... 47
ДОДАТОК В – БЛОК-СХЕМА АЛГОРИТМУ ВИЗНАЧЕННЯ ТИПУ
КРОСВОРДУ НА ОСНОВІ ІАЛ (ІТЕРАЦІЙНОГО АНАЛІЗУ ЛІНІЙ) .............. 48
ДОДАТОК Г – ЛІСТИНГИ КОДУ МОДИФІКАЦІЇ ГРИ «ЯПОНСЬКИЙ
КРОСВОРД» .............................................................................................................. 49
ДОДАТОК Ґ – РЕЗУЛЬТАТИ ТЕСТУВАННЯ МОДИФІКАЦІЇ ГРИ
«ЯПОНСЬКИЙ КРОСВОРД» .................................................................................. 50
ДОДАТОК Д – ВИХІДНИЙ КОД МОДИФІКАЦІЇ ГРИ «ЯПОНСЬКИЙ
КРОСВОРД» .............................................................................................................. 54
4
ВСТУП

Актуальність теми:
Розв’язування японських кросвордів дозволяє покращити пам’ять, увагу та
логічне мислення. Реалізація ж японських кросвордів у вигляді комп’ютерної
програми зробить розв’язування більш зручним та дозволить урізноманітнити
процес додаванням рівнів складності, штрафів, таймера і так далі. Введення
додаткових засобів оцінювання гри користувача якраз і є одним із таких
вдосконалень, яке спонукатиме користувача поліпшувати свої навички,
шукаючи розв’язок логічним шляхом.
Мета:
Програмно реалізувати середовище для розв’язування японських
кросвордів, алгоритм для автоматичного розв’язування кросвордів та поєднати
їх.
Завдання:
Написати програму, яка надає середовище для розв’язування японських
кросвордів. Реалізувати алгоритм для автоматичного розв’язування кросвордів
та на його основі розробити модуль, здатний перевіряти, чи є хід користувача
логічно вирахуваним, чи зробленим навмання, та інтегрувати даний модуль в
середовище для розв’язування.
Задачі:
1. Сформувати та проаналізувати вимоги до модифікації гри «Японський
кросворд».
2. Провести архітектурне планування модифікації гри «Японський
кросворд».
3. Створити базу даних для кросвордів.
4. Реалізувати алгоритм автоматичного розв’язування японського
кросворду.
5. Реалізувати рівні складності на основі алгоритму автоматичного
розв’язування.
5
6. Реалізувати можливість створювати кросворди.
7. Реалізувати авторизацію з різними ролями користувачів.
8. Реалізувати користувацький інтерфейс модифікації гри «Японський
кросворд».
9. Провести тестування модифікації гри «Японський кросворд».
10. Написати пояснювальну записку, описавши процес розробки, написати
доповідь і створити презентацію.
Об’єктом розробки є програмна реалізація японських кросвордів та
урізноманітнення процесу їх розв’язування.
Предметом розробки є реалізація середовища для розв’язування
японських кросвордів та інтеграція до середовища алгоритму автоматичного
розв’язування з метою реалізації додаткових засобів оцінювання гри.
Апробація роботи
За темою даної кваліфікаційної роботи була підготовлена доповідь на
XXIV Всеукраїнську наукову конференцію молодих учених «Актуальні
проблеми природничих і гуманітарних наук у дослідженнях молодих учених
«Родзинка - 2022»», м. Черкаси, 27-28 квітня 2022 року. Тези доповіді
опубліковані в збірнику матеріалів конференції [1].
Структура та обсяг роботи
Кваліфікаційна робота складається зі вступу, трьох розділів, висновків,
списку використаних джерел (8 найменувань), 5 додатків. Загальний обсяг
роботи становить 43 сторінок основного тексту, 17 рисунків та 2 таблиць.
6
РОЗДІЛ 1. ОГЛЯД ТА АНАЛІЗ СПОСОБІВ РЕАЛІЗАЦІЇ
МОДИФІКАЦІЇ ГРИ «ЯПОНСЬКИЙ КРОСВОРД»

1.1 Огляд предметної області японських кросвордів

Японський кросворд – логічна головоломка, розв’язком якої є прямокутна


таблиця, кожна з клітинок якої або зафарбована, або незафарбована [2]. Ліворуч
від рядків таблиці та над її стовпчиками розташовані числа, які кодують
зображення. Оскільки в даному випадку стовпчики і рядки є повністю
рівноправними, називатимемо їх лініями (тут і далі використовуємо
термінологію, наведену в [2]). Кількість чисел, розташованих перед лінією,
показує, скільки блоків (блок – послідовність зафарбованих клітинок в лінії,
розташованих підряд) є в цій лінії, а самі числа – довжини відповідних їм блоків.
Між двома блоками обов’язково повинна бути хоча б одна незафарбована
клітинка.
В процесі розв’язування користувач зафарбовує частину клітинок таблиці
(кожне зафарбовування клітинки називається ходом). Японський кросворд
вважається розв’язаним, якщо наведена комбінація зафарбованих клітинок
задовольняє вимогам всіх ліній. Кросворд може мати один розв’язок – тоді він
задовольняє вимоги єдиності розв’язку – або мати більше одного розв’язку.
Існують як кросворди, розв’язок яких можливо знайти логічним шляхом, так і
кросворди, розв’язок яких знаходиться лише з використанням перебору.
Якщо розв’язувати кросворд логічним шляхом (за умови, якщо даний
кросворд розв’язний без перебору), то в кожен момент часу протягом
розв’язування існуватиме деяка множина правильних ходів, які можуть бути
вирахувані логічно. Назвемо такі ходи гарантованими, в той час як правильні
ходи, що на поточному етапі можуть бути лише випадково вгадані, назвемо
ризикованими.
7
1.2 Актуальність розробки гри «Японський кросворд» та її
модифікації

Японські кросворди є вельми розповсюдженими головоломками,


розв’язуванням яких займається близько 500 млн людей у світі [3]. Їх
розв’язування розвиває увагу, пам’ять, логічне мислення та в цілому покращує
розумові здібності, а тому є корисним для будь-якої людини будь-якого віку.
Створення відеогри, що дозволяє розв’язувати японські кросворди,
зробить процес розв’язування набагато зручнішим, ніж на папері: не потрібно
окремо шукати самі кросворди, не потрібно витрачатися на їх купівлю або друк,
можна скільки завгодно перетворювати зафарбовану клітинку в незафарбовану і
навпаки. Також це дозволить внести різноманіття в процес розв’язування у
вигляді різних рівнів складності, де, наприклад, можна отримувати підказки, або
введена система штрафів за помилки. Також така програма може надавати зручні
інструменти для того, щоб створювати власні кросворди і ділитися ними з
іншими.
Модифікація гри, що полягає у введенні додаткових засобів оцінювання
гри користувача, також покликана внести додаткове різноманіття в процес
розв’язування. Реалізація алгоритму автоматичного розв’язування японського
кросворду дозволить ввести систему штрафів за ходи, зроблені навмання, що
стимулюватиме гравців користуватися при розв’язуванні лише логікою та
вдосконалювати свої навички.

1.3 Огляд програм-аналогів гри «Японський кросворд»

Перша програма-аналог – Nonogram game [4] з Microsoft Store (рис. 1.1).


8

Рис. 1.1 – Зовнішній вигляд зразку-аналогу «Nonogram game»

Розглянемо переваги та недоліки даного варіанта. Переваги:


- приємний та сучасний стиль інтерфейсу;
- анімації.
Недоліки:
- занизька складність (навіть ті кросворди, що позиціонуються як складні,
є досить простими);
- розв’язки кросвордів являють собою довільний набір клітинок (було б
цікавіше, якби вони складалися в якесь осмислене зображення).
Перейдемо до другого зразка – додаток Nonogram.com [5] з Play Market
(рис. 1.2).

Рис. 1.2 – Зовнішній вигляд зразку-аналогу «Nonogram.com»


9

Переваги:
- комбінації клітинок розв’язку є осмисленими зображеннями (хоч і не в
звичайному чорно-білому стані);
- при заповненні всіх клітинок лінії ті, що залишились, автоматично
позначаються як незаповнені.
Недоліки:
- кросворди ідуть підряд один за одним – немає можливості обирати
довільний;
- аналогічно з попереднім зразком занизька складність (втім, в даному
зразку це навряд чи можна виправити, оскільки це мобільний додаток).
Перейдемо до третього зразка – онлайн-ресурс nonogramsonline.com [6]
(рис. 1.3).

Рис. 1.3 – Зовнішній вигляд зразку-аналогу «nonogramsonline.com»

Переваги:
- наявні кросворди різної складності;
- є таймер;
- швидка реакція на дії користувача;
10
- простий і зрозумілий інтерфейс.
Недоліки:
- після заповнення лінії порожні клітинки не позначаються автоматично;
- немає можливості обирати довільний кросворд;
- не надто презентабельний дизайн;
- фіксовані розміри кросвордів;
- немає можливості ввімкнення режиму з штрафами за помилки.
Перейдемо до четвертого зразка – онлайн-ресурс onlinenonograms.com [7]
(рис. 1.4).

Рис. 1.4 – Зовнішній вигляд зразку-аналогу «onlinenonograms.com»

Переваги:
- комбінації клітинок розв’язку є осмисленими зображеннями;
- є можливість обирати довільний кросворд;
- є можливість збереження та завантаження поточного прогресу;
- є гнучкі налаштування;
- представлені кросворди різних складностей, в тому числі дійсно складні.
Недоліки:
- відмічання незафарбованих клітинок після заповнення лінії відбувається
з затримкою, що не надто зручно;
- відсутній таймер;
11
- немає можливості ввімкнення режиму з штрафами за помилки.
Найбільш підходящим видається четвертий зразок, оскільки він надає
широкий функціонал і передбачає у користувачів більш високий рівень навичок
розв’язування (а саме таке спрямування і має бути у розроблюваного продукту,
адже не надто просунутим користувачам навряд чи будуть цікаві та потрібні
удосконалення, які планується реалізувати), хоча і кросворди для новачків теж
надає. Втім, скоріш за все, варто ще сильніше знизити поріг входження, додавши
ще менші кросворди, навіть якщо в них доведеться пожертвувати ідеєю
осмислених розв’язків. Наприклад, у третьому зразку наряду з великими
кросвордами є також і зовсім невеличкі, розміром 5х5 клітинок. Також з третього
зразку варто взяти таймер, оскільки він може бути досить важливим аспектом
для збільшення азартності процесу. З першого та другого зразку варто взяти
модель, що передбачає штрафи за помилки. Втім, не варто обмежуватись
простою наявністю трьох життів, після втрати яких зараховується поразка.
Краще натомість зробити цю систему більш гнучкою, ввівши різні рівні
складності.

1.4 Огляд методів та технологій для розробки модифікації гри


«Японський кросворд» та способів реалізації автоматичного розв’язування

Розглянувши програми-аналоги, бачимо, що вони реалізують різні методи


реалізації: мобільний додаток, веб-додаток, ПК-програма. Варіант з мобільним
додатком на основі аналізу зразків-аналогів видається не надто вдалим, адже на
маленькому екрані смартфона буде неможливо зручно відобразити велике поле
кросворду. Обмежувати ж розміри кросвордів розмірами екрану недоцільно в
тому числі з огляду на особливості теми.
Тому зробимо висновок про доцільність розробки для ПК. Також варто
зважити на те, що розроблювана програма передбачає використання алгоритму
автоматичного розв’язування, який на деяких вхідних даних може вимагати
значних ресурсів для своєї роботи. Тому, в даному випадку, обираючи між
12
браузерною та окремою програмою, варто все ж схилитися до другого з позиції
кращої продуктивності.
Розглянемо особливості зразків-аналогів окрім платформи розробки, на які
варто звернути увагу. З третього зразку варто перейняти можливість вільного
вибору кросворду для розв’язування, таймер, розв’язки кросвордів у вигляді
осмислених зображень. Також варто звернути увагу на дизайн першого зразку.
Представлені зразки також дещо відрізняються в суті гри: в перших двох
зразках є обмеження на кількість неправильних ходів та робиться акцент саме на
безпомилкову гру, в той час як в третьому зразку ніяких обмежень нема і
розв’язувати можна скільки завгодно. Обидва варіанти є цікавими, тому варто
реалізувати їх обидва, надавши користувачу можливість обирати режим гри.
Продовжуючи ідею надавання користувачу можливості вибору, важливо також
додати кросворди різних складностей, в тому числі і надпрості, представлені в
перших двох зразках.
Для реалізації автоматичного розв’язування японського кросворду
застосуємо алгоритм, описаний в [2], розділ 15. Даний алгоритм заснований на
ітераційному аналізі ліній (скорочено, ІАЛ) кросворду. Аналіз лінії має на меті
з’ясувати стан якомога більшої кількості її клітинок на основі наявної на даний
момент інформації та виконується за допомогою скінченного автомату. Лінії
продовжують аналізуватися доти, доки знаходяться нові клітинки, стан яких
вдається з’ясувати. Даний алгоритм здатен працювати швидко на кросвордах
розмірами до 100х100 і дозволяє визначити стани всіх клітинок, які можуть бути
визначені без використання перебору. Він також може бути застосований і для
пришвидшення перебору (якщо він виявиться потрібним) при обробці решти
клітинок.

1.5 Постановка завдання на розробку модифікації гри «Японський


кросворд»

Таким чином, в ході виконання даної роботи потрібно розробити програму,


яка:
13
- реалізує середовище для розв’язування японських кросвордів з
записуваннями та зчитуваннями прогресу розв’язання і можливістю скасування
попереднього ходу;
- здатна автоматично розв’язувати кросворд;
- за допомогою алгоритму автоматичного розв’язання може перевіряти
єдиність розв’язку, виконувати перевірку ходу користувача на
гарантованість/ризикованість, надавати підказки і на основі цього вводить різні
рівні складності;
- дозволяє створювати картинки для японського кросворду, в разі
неєдиності – надає обмежені засоби автоматизованого пошуку недалекої
картинки, з єдиним розв’язком;
- дозволяє розв’язувати кросворд на час та має таблицю рекордів з
найкращими результатами, з врахуванням часу розв’язання та кількості
набраних балів;
- підтримує авторизацію з різними ролями користувачів;
- зберігає базу кросвордів, базу користувачів, базу рекордів.

Висновок до розділу 1

В першому розділі був здійснений огляд предметної області японських


кросвордів та розглянута актуальність розробки і вдосконалення програми, що
дозволяє їх розв’язувати. Також був виконаний аналіз програм-аналогів, в ході
якого були виявлені позитивні особливості, які варто перейняти, та негативні,
яких варто уникнути, та поставлене завдання на розробку.
14
РОЗДІЛ 2. АНАЛІЗ ВИМОГ І ПРОЕКТУВАННЯ
МОДИФІКАЦІЇ ГРИ «ЯПОНСЬКИЙ КРОСВОРД»

2.1 Формування вимог до модифікації гри «Японський кросворд» та їх


аналіз

Щоб сформувати вимоги до розроблюваного програмного продукту,


спершу визначимо, які дії користувач повинен мати змогу виконувати за
допомогою даного продукту. Для цього використаємо користувацькі історії:
1. Користувач може змінювати рівень складності (з підказкою, звичайний,
з штрафами за помилку, з обривом гри в разі помилки).
2. Користувач може скористатись підказкою, якщо вибрано відповідний
рівень складності.
3. Користувач може розв’язувати кросворд.
4. Користувач може скасовувати попередню дію.
5. Користувач може зберігати поточний прогрес гри.
6. Користувач може завантажити раніше збережену гру.
7. Користувач може зареєструвати власний обліковий запис.
8. Користувач може ввійти в свій обліковий запис.
9. Користувач може переглядати таблицю рекордів.
10. Користувач може внести свої дані в таблицю рекордів.
11. Користувач може обрати розміри поля для створення власного
кросворду.
12. Користувач може обрати клітинки, які будуть зафарбованими в
створюваному кросворді.
13. Користувач може перевірити створений кросворд на єдиність
розв’язку.
14. Користувач може отримати підказку з модифікації створеного
кросворду для одержання варіанту з єдиним розв’язком.
15. Користувач може додати створений кросворд в базу даних.
15
16. Адміністратор може переглядати запропоновані користувачами
кросворди.
17. Адміністратор може допустити або відхилити запропонований
користувачем кросворд.
В наведеному списку деякі крім користувача вводиться також термін
«Адміністратор». Оскільки деякі функції (користувацькі історії 16 та 17) не
повинні бути доступними будь-якому користувачу, вводиться роль
адміністратора з розширеними правами.
Тепер сформуємо product backlog на основі наведених користувацьких
історій. Для цього користувацькі історії спершу потрібно формалізувати,
сформувавши з них конкретні функціональні вимоги. Також для кожної вимоги
опишемо, як може бути продемонстроване її виконання, та дамо оцінку
важливості та складності реалізації вимоги. Всі вищеописані дані включимо в
таблицю в Додатку А.
Крім функціональних вимог, необхідно сформувати також
нефункціональні вимоги:
1. Програма повинна зберігати кросворди та таблицю рекордів в базі даних,
що розміщується на окремому від користувачів сервері.
2. Програма повинна містити модуль, здатний автоматично розв’язувати
кросворд.
3. Використовуючи модуль, означений в попередньому пункті, програма
повинна перевіряти, чи є черговий хід користувача обгрунтованим з точки зору
алгоритму розв’язку.
4. Кнопки інтерфейсу повинні бути розташовані зручно для користувача.
5. Меню вибору рівня складності повинне містити вичерпні описи рівнів
складності.
6. В разі відмови користувачу в додаванні кросворду через невідповідність
вимогам єдиності розв’язку програма повинна доступно інформувати про дану
причину.
16
7. Якщо користувач встановив рекорд, програма повинна автоматично
вносити запис до таблиці рекордів.
8. Програма повинна реагувати на клік користувача на клітинку кросворду
з затримкою, непомітною для людського ока.
9. Операції, пов’язані зі зверненням до бази даних, повинні створювати для
користувача затримку не більшу, ніж 500 мс.
Повернемося до функціональних вимог та відобразимо їх на Use-case
діаграмі для подальшого аналізу. Наведемо дві діаграми: для адміністратора
(рис. 2.1) та для користувача (рис. 2.2).

Рис. 2.1 – Use-case діаграма для адміністратора

Дана Use-case діаграма показує можливість адміністратора приймати або


відхиляти запропонований користувачем кросворд, при чому дана можливість є
розширенням можливості переглянути згаданий кросворд.
17

Рис. 2.2 – Use-case діаграма для користувача

Дана Use-case діаграма відображає наступні особливості структури


способів використання:
-додавання запису в таблицю рекордів можливе лише за умови що рекорд
був встановлений;
-можливість додавати запис в таблицю рекордів розширює варіант
використання з переглядом таблиці;
-при створенні кросворду є можливість отримати підказку з його
модифікації (в разі якщо його початковий варіант не відповідає вимогам єдиності
розв’язку);
-додати кросворд до бази можна лише після того, як він буде схвалений
адміністратором;
-при виборі відповідного рівня складності можна використати підказку;
18
-якщо поточний прогрес був збережений, його можна згодом завантажити.
Проаналізувавши діаграму, в тому числі на предмет можливого протиріччя
або перекриття різних вимог, можемо бачити, що вимоги щодо збереження та
завантаження даних з диску, імовірно, перекриваються, адже збереження і
завантаження є досить близькими операціями. Використаємо діаграму
послідовності для аналізу даної ситуації (рис. 2.3).

Рис. 2.3 – Аналіз збереження та завантаження даних на діаграмі послідовності

На даній діаграмі бачимо, що завантаження даних може відбутися лише


після їх збереження. Тобто, завантажити дані, які перед цим не були збережені,
неможливо. Щоб уникнути пов’язаних з цим можливих помилок, в ході розробки
ПЗ варто передбачити перевірку наявності збережених записів при спробі їх
завантаження.

2.2 Попереднє проектування архітектури модифікації гри «Японський


кросворд»

Для початку попереднього проектування архітектури спершу сформуємо


групи функціональних вимог, об’єднавши між собою ті, що є близькими.
Отримаємо наступні групи:
1. Розв’язування кросворду:
19
- Розв’язування кросворду;
- Вибір рівня складності;
- Можливість скористатися підказкою.
2. Дії прогресом розв’язання:
- Скасування попередньої дії;
- Збереження поточного прогресу;
- Завантаження поточного прогресу.
3. Авторизація користувачів:
- Реєстрація облікового запису;
- Авторизація зареєстрованого користувача.
4. Таблиця рекордів:
- Запис у таблицю рекордів.
5. Створення кросворду:
- Створення кросворду;
- Перевірка створеного кросворду на єдиність розв’язку;
- Підказка з модифікації створеного кросворду для одержання варіанту з
єдиним розв’язком.
6. База кросвордів:
- Додавання кросворду в базу;
- Надання доступу або відмова в доступі до кросворду.
Тепер, спираючись на сформовані групи, потрібно вибрати архітектурний
шаблон. Для даного програмного продукту непоганим варіантом є шаблон
клієнт-сервер, адже на сервер можна винести таблицю рекордів, авторизацію та
базу доступних кросвордів, а решту залишити на клієнті. Такий розподіл буде
зрозумілим і ефективним, тому зупинимося на цьому шаблоні.
Наступним кроком має стати формування попередньої архітектури ПЗ.
Зобразимо її на діаграмі пакетів (рис. 2.4), а в якості пакетів візьмемо
вищеописані групи вимог.
20

Рис. 2.4 – Попередня архітектура на діаграмі пакетів

На даній діаграмі пакетів відображена попередня архітектура майбутнього


ПЗ за архітектурним шаблоном клієнт-сервер. Розглянемо, які пакети
розташовані на сервері, а які - на клієнті, та обгрунтування такого вибору.
Сервер: авторизація користувача, додавання кросворду в базу, таблиця
рекордів. Виконання даних функцій на сервері дозволить всім користувачам в
будь-який момент отримувати до них доступ, будучи до того ж впевненими, що
отримувані дані є актуальними.
Клієнт: створення кросворду, розв’язування кросворду, дії з прогресом
розв’язування. Дані функції ПЗ стосуються лише конкретного користувача, тому
розташування їх на сервері розцінюємо як недоцільне. Крім того, розташувавши
їх на клієнтській частині, можна буде надати користувачу можливість
користуватися суттєвою частиною функцій програми і без виходу в мережу.
Зв’язок між клієнтом і сервером представлений декількома зв’язками між
пакетами, що знаходяться відповідно на боці клієнта та сервера. Зокрема, сервер
21
та клієнт взаємодіють при завантаженні кросворду для розв’язування, додаванні
нового кросворду, взаємодії з таблицею рекордів та авторизації (є необхідною
для створення кросворду, запису рекорду в таблицю рекордів та перевірки
кросворду адміністратором).
Також на діаграмі показано зв’язок між таблицею рекордів та
авторизацією, який позначає необхідність авторизації для внесення запису до
таблиці. Зв’язок між базою рекордів та авторизацією показує можливість
використання облікового запису адміністратора для допуску або відхилення
запропонованих користувачами кросвордів.

2.3 Детальне проектування модифікації гри «Японський кросворд»

Розглянемо деякі основні пакети з вищенаведеної попередньої архітектури


більш детально.

2.3.1 Проектування пакету «Розв’язування кросворду»


Розглянемо пакет «Розв’язування кросворду» на діаграмі діяльності (рис.
2.5).
22

Рис. 2.5 – Фрагмент алгоритму автоматичного розв’язку кросворду на діаграмі


діяльності

Наведена діаграма показує частину алгоритму автоматичного розв’язку


кросворду. Даний алгоритм є важливою частиною описуваного пакету, адже
автоматичне розв’язування необхідне для надання підказок (при обраному рівню
складності, який передбачає підказки) та реалізації додаткових засобів
оцінювання гри користувача (що є ключовою особливістю розроблюваного ПЗ).
Частина алгоритму, що відображена на даній діаграмі діяльності, показує в
23
загальному суть розв’язування: проходити по кросворду в одному вимірі (по
стовпчиках або по рядках) і визначати стан клітинок, де це можливо (через
виклик AnalyzeLine), після чого переходити на протилежний вимір, і виконувати
такий самий аналіз, використовуючи нові дані. Кожна лінія аналізується лише в
тому випадку, якщо в ході останнього аналізу протилежного виміру в ній
оновився стан хоча б однієї клітинки. Проходи по вимірах рядків та стовпчиків
представлені на діаграмі двома внутрішніми циклами, а зовнішній цикл
відображає те, що дані дії треба виконувати доти, доки при поточному проході
вдається визначити стан хоча б однієї клітинки.

2.3.2 Проектування пакету «Створення кросворду»


Розглянемо пакет «Створення кросворду» на діаграмі послідовності (рис.
2.6).

Рис. 2.6 – Робота пакету «Створення кросворду» на діаграмі послідовності


24
На діаграмі згідно з архітектурним шаблоном відображена серверна та
клієнтська частини програми і користувач. Бачимо, що користувач ініціює
початок роботи пакету натисканням кнопки в вікні програми, після чого
здійснюється запит на сервер щодо авторизації користувача. Подальша взаємодія
відбувається лише в разі якщо користувач є авторизованим. Програма
відображає вікно для створення кросворду, в якому користувач обирає комірки,
що у створюваному кросворді мають бути зафарбованими. Отримавши
заповнену таким чином таблицю, клієнтський додаток перевіряє майбутній
кросворд на єдиність розв’язку і в разі невідповідності пропонує виправлений
варіант користувачу. Дочекавшись від останнього коректного кросворду, клієнт
відправляє його на сервер для перевірки, а після одержання відповіді показує її
користувачу. Подібне проектне рішення сповна відповідає заявленим вимогам
до ПЗ, що розробляється.

2.3.3 Проектування бази даних модифікації гри «Японський кросворд»


Спершу визначимо, які сутності має містити база даних. Згідно з
поставленим завданням, вона повинна зберігати кросворди та таблицю рекордів.
Також для реалізації авторизації необхідно зберігати дані користувачів. Таким
чином, доцільно виділити такі сутності:
- Кросворд;
- Користувач;
- Рекорд.
Оглянемо, в яких зв’язках між собою перебувають дані сутності:
- Користувач встановлює Рекорд;
- Користувач створює Кросворд;
- Рекорд встановлюється при розв’язуванні Кросворду.
Всі три зв’язки мають тип Один до Багатьох.
Відобразимо вищеописані характеристики бази даних на діаграмі сутність-
зв’язок (рис. 2.7).
25

Рис. 2.7 – Модель бази даних на діаграмі сутність-зв’язок

Розглянемо атрибути, якими володіють сутності бази даних.


- Кросворд: id, опис блоків, користувач-автор, позначення того, чи є
кросворд перевіреним, та того, чи розв’язується він без використання перебору;
- Користувач: id, логін, хеш-представлення паролю, позначення того, чи є
користувач адміністратором;
- Рекорд: id, користувач, що встановив рекорд, кросворд, на якому рекорд
був встановлений, складність, на якій проходила гра, кінцевий рахунок, час гри.

Висновок до розділу 2

В другому розділі були сформовані користувацькі історії, після чого на їх


основі були отримані функціональні вимоги, описані в таблиці product backlog.
На основі вимог були створені та проаналізовані Use-case діаграми та сформовані
пакети для побудови попередньої архітектури. Після вибору архітектурного
шаблону була розроблена попередня архітектура на діаграмі пакетів. Також були
проаналізовані на окремих діаграмах основні пакети з даної архітектури.
26
РОЗДІЛ 3. РОЗРОБКА І ТЕСТУВАННЯ МОДИФІКАЦІЇ ГРИ
«ЯПОНСЬКИЙ КРОСВОРД»

3.1 Вибір мови програмування та середовища для розробки


модифікації гри «Японський кросворд»

Як було визначено в розділі 1, розроблюване ПЗ буде призначатися для


використання на ПК. Відштовхуючись від цього, доцільно буде використати в
розробці мову C#, що займає лідируючі позиції в рейтингах мов програмування
для ПК-розробки [8]. Зручність розробки за допомогою C# досягається в тому
числі завдяки інструменту Windows Forms. Він дозволяє створювати
користувацькі інтерфейси у вікнах та надає широкі можливості для роботи з
елементами управління.
Для створення бази даних використаємо PostgreSQL – сучасну і потужну
систему управління базами даних, яку нескладно підключити до проекту,
написаного на C#. Для підключення використаємо NuGet-пакет npgsql.

3.2 Уточнення архітектури та структурна схема реалізації модифікації


гри «Японський кросворд»

В якості основи для побудови схеми уточненої архітектури (рис. 3.1)


розроблюваного ПЗ використаємо попередню архітектуру, описану в розділі 2.
27

Рис. 3.1 – Уточнена архітектура на діаграмі компонентів

Ключова деталь, що відрізняє попередню архітектуру від уточненої


полягає у винесенні Автоматичного розв’язування в окремий компонент. Раніше
функціонал даного компоненту передбачалося включати в компонент
Розв’язування кросворду. Втім, в ході детального проектування пакетів
попередньої архітектури було з’ясовано, що компонент Створення кросворду
теж потребує використання даного функціоналу, зокрема, для перевірки
створеного кросворду на єдиність розв’язку. Також в ході аналізу, зокрема,
пакету Розв’язування кросворду на діаграмі діяльності було виявлено, що
реалізація автоматичного розв’язування є складнішою, ніж передбачалося на
початку проектування. Це слугує додатковим аргументом на користь винесення
даного функціоналу в окремий компонент.
Таким чином, на діаграмі компонентів бачимо новий компонент
Автоматичне розв’язування та два зв’язки з його участю: Розв’язування
кросворду використовує Автоматичне розв’язування та Створення кросворду
28
використовує Автоматичне розв’язування. Решта компонентів та зв’язків
відповідають таким в попередній архітектурі.

3.3 Розробка компонентів модифікації гри «Японський кросворд»

Розглянемо процес реалізації компонентів, описаних в попередньому


підрозділі.

3.3.1 Розробка компоненту «Розв’язування кросворду»


Компонент «Розв’язування кросворду» відповідає в першу чергу за
надання користувачу можливості розв’язувати японський кросворд та
отримувати вердикт про правильність або неправильність запропонованого
розв’язку. Щоб це реалізувати, потрібно організувати представлення кросворду
в програмі. Відобразимо це представлення на локальній діаграмі класів (рис. 3.2).

Рис. 3.2 – Діаграма класів для компоненту «Розв’язування кросворду»


29

Розглянувши наведену діаграму, бачимо, що за власне представлення


кросворду відповідає клас Nonogram. Він має поля rowC і colC, які зберігають
відповідно кількість рядків і стовпців кросворду. Двовимірний масив correctPict
призначений для зберігання картинки, що є розв’язком кросворду. Масив pict
теж використовується в процесі розв’язку (в ньому виконується формування
розв’язку). Масив initialPict використовується при формуванні варіантів
модифікації створюваного кросоврду. Їх використання буде детальніше показано
в наступних підрозділах.
Значення nType вибирається з перелічення NonogramType. Згідно з
зазначеним в першому розділі роботи, розглядаємо три типи японських
кросвордів: такий, що може бути повністю розв’язаний алгоритмічно (OnlyILL),
такий, що потребує використання перебору (NeedBacktracking) і такий, що має
кілька розв’язків (FewSolutions).
Приватне поле blocksDescriptions використовується для зберігання
короткого представлення описів блоків кросворду (у вигляді одновимірного
масиву). Дане представлення використовується для зберігання кросворду у базі
даних. При отриманні ж кросворду з бази клієнтська програма «розбирає»
blocksDescriptions та формує більш зручне для подальшої роботи представлення,
що зберігається в полі lines. Дане поле являє собою зубчатий масив з двох рядків.
Один рядок відповідає лініям-рядкам, інший – лініям-стовпцям. Кожна лінія є
об’єктом класу Line.
Клас Line, що описує одну лінію кросворду, містить єдине поле – масив
довжин блоків. Елементи в масиві розташовуються в порядку, що відповідає
порядку розташування відповідних їм блоків у кросворді, а за допомогою
властивостей можна організувати зручне отримання кількості блоків, що буде
корисно при автоматичному розв’язуванні.
Поле id зберігає номер кросворду в базі даних, а blackCount – сумарну
кількість зафарбованих клітинок. Це значення використовується в реалізації
рахунку гри.
30
Продовжуючи аналізувати діаграму класів, бачимо, що примірник класу
Nonogram є полем класу NonogramSession. Даний клас є одним із основних в
розроблюваному ПЗ і відображає поточну сесію роботи з конкретним
кросвордом. Така робота може полягати в створенні кросворду або його
розв’язуванні і в залежності від цього будуть потрібні різні методи, що
використовують алгоритм автоматичного розв’язування. Також ці методи будуть
відрізнятися в залежності від типу кросворду. NonogramSession містить метод
SolveEntire(), який повністю розв’язує кросворд, прив’язаний до об’єкту.
Оскільки виконується огляд реалізації саме розв’язування кросворду, то на
діаграмі наведений лише один нащадок NonogramSession –
NonogramToSolveSession. Він містить методи CheckByLines(int[,]), CheckCell(int,
int) та ChangeCell(int, int, int). CheckCell дозволяє перевірити, чи є конкретна
клітинка зафарбованою (за умови, що в pict вже наявний правильний розв’язок).
CheckByLines перевіряє правильність картинки-розв’язку, що передається в
якості аргументу, звіряючи її з описами блоків в полі lines. ChangeCell
викликається при зміні значення клітинки в ході розв’язування (впливає на масив
pict) та повертає статус результату. Даний метод перевизначатиметься в
нащадків класу для реалізації різних дій в залежності від рівня складності.
Також NonogramToSolveSession містить додаткові поля. Score і SolTime
позначають рахунок та час розв’язання. IsFromLocal позначає, чи є дана сесія
розв’язанням кросворду, завантаженого з локального сховища. User позначає
користувача, що виконує розв’язання (логін, id та логічна змінна на позначення
того, чи є користувач адміністратором).
Таким чином, при завантаженні користувачем кросворду з бази даних
створюватиметься примірник класу Nonogram, в який заноситимуться дані про
кросворд. При виборі рівня складності створюватиметься відповідний йому
нащадок класу NonogramToSolveSession. Коли користувач завершить
розв’язування і ініціалізує перевірку, побудована ним картинка буде передана в
метод CheckByLines (для звичайного рівня складності, який не використовує
автоматичне розв’язування), який визначить, чи є розв’язок правильним.
31
3.3.2 Розробка компоненту «Автоматичне розв’язування»
Компонент «Автоматичне розв’язування» є важливою частиною
розроблюваного ПЗ, адже саме він дозволяє реалізувати зазначені в темі засоби
додаткового оцінювання гри користувача. В ході розробки автоматичного
розв’язання візьмемо за основу алгоритмічний спосіб, описаний в [2], розділ 15,
реалізувавши його мовою C# в окремому статичному класі AutoSolve, різні
методи якого будуть використовуватися іншими модулями програми.
Алгоритм був частково розглянутий в розділі 2 при детальному
проектуванні, де була продемонстрована суть ітераційності аналізу ліній. Тепер
розглянемо принцип виконання власне аналізу лінії, який виконує метод
AnalyzeLine. Аналіз лінії проходить в три кроки:
1. Побудова і заповнення таблиці переходів автомату.
2. Побудова і заповнення карти шляхів.
3. Уточнення станів клітинок за допомогою зворотного ходу.
Ключовим тут є другий етап, тому розглянемо його розробку детальніше,
попередньо представивши на блок-схемі (Додаток Б).
Карта шляхів призначена для зберігання інформації про всі можливі
способи розташування блоків на лінії. Можливість існування різних способів
породжена наявністю клітинок з невизначеним станом (2) і зазначеною раніше
необхідністю обробляти обидва варіанти визначення таких клітинок. Карта являє
собою двовимірний масив, де стовпці відповідають станам автомату. Рядки ж, як
бачимо на блок-схемі, відповідають значенню freeZeros + 1. freeZeros відображає
кількість незамальованих клітинок, які можуть бути розміщені довільним чином
(спираючись на термінологію з [2], назвемо їх вільними нулями). Це всі такі
клітинки, окрім тих, що обов’язково повинні відділяти один від одного блоки.
Таким чином, рядки карти шляхів (масиву map) відповідають кількості вільних
нулів, що вже були використані на поточному кроці. Щодо клітинки (вхідного
символу), що оброблюється на поточному кроці, то її відповідність – діагональ
масиву. Тобто, при прочитанні першого символу відбудеться перехід в map[0,1]
32
або в map[1,0]; при прочитанні другого символу – map[0,2], map[2,0], map[1,1]; і
так далі.
Перейдемо до безпосереднього опису заповнення map. За допомогою
циклу відбувається прохід по всіх клітинках лінії. Всередині однієї ітерації
спершу визначається мінімальний та максимальний за номером стани, в яких
може опинитися автомат при прочитанні поточного символу (jMin та jMax).
Мінімальним станом є нульовий (доки не скінчаться вільні нулі, що дозволяють
в ньому залишатися). Максимальним – i-ий (доки не будуть пройдені всі стани).
Після визначення jMin і jMax запускається внутрішній цикл по j, який
перебирає для j всі значення він jMin до jMax. Всередині ітерації по j знаходяться
два вкладених умовних оператори. Розглянемо спершу внутрішній оператор
першого зовнішнього оператора. Він перевіряє, чи можливо при прочитанні
поточного символу лишитися в поточному стані. Для цього мають виконатися
три умови: елемент map, що знаходиться над тим, що розглядається, досяжний;
зчитуваний символ не є одиницею; в поточному стані є петля по символу 0. Якщо
всі умови виконані, то в поточний елемент додається значення 4 на позначення
того, що в нього можна потрапити через читання вільного нуля. Зовнішня умова
даного умовного оператора (i > j) перевіряє, чи не знаходиться поточна точка в
першому рядку map. Якщо це так, то потрапляння в неї згори неможливе.
Розглянемо другий умовний оператор (j > 0). Суть його приблизно така ж,
але з стовпчиком. Він позначає неможливість потрапити в поточну точку шляхом
зміни стану у випадку, якщо поточна точка знаходиться в нульовому стані.
Всередині даного оператора перевіряються два варіанти: зміна стану автомату по
символу 1 і по 0. Для 1 виконується перевірка, чи є в попередньому стані перехід
в поточний по 1 і чи не є зчитуваний символ нулем. Для 0 перевіряється перехід
з попереднього стану по 0 і чи не є зчитуваний символ одиницею. В першому
випадку до значення поточного елементу додається 1, в другому – 2.
33
3.3.3 Інтеграція компонентів «Розв’язування кросворду» та
«Автоматичне розв’язування»
Об’єднання компонентів «Розв’язування кросворду» та «Автоматичне
розв’язування», описаних в попередніх підрозділах, дозволяє реалізувати
ключову особливість розроблюваного ПЗ – додаткові засоби контролю гри
користувача. Для цього введемо декілька рівнів складності. На різних рівнях при
розв’язуванні буде по-різному використовуватися алгоритм автоматичного
розв’язування. Використаємо діаграму класів для розгляду цих відмінностей
(рис. 3.3).

Рис. 3.3 – Діаграма нащадків класу NonogramToSolveSession

Батьківський клас NonogramToSolveSession використовується на рівні


складності Звичайний. В цьому випадку алгоритм автоматичного розв’язання не
34
застосовується. Клас-нащадок NTSSWithHints призначений для позначення сесії
розв’язання, в ході якої можна використовувати підказки. Це реалізовано на рівні
складності З підказками.
Використання підказки передбачає зафарбовування однієї клітинки по
натисканню відповідної кнопки і виконується за допомогою методу OpenCell().
При цьому програма намагається відкрити таку клітинку, яка може бути
вирахуваною з описів блоків та уже відкритих клітинок. Для цього виконується
прохід по всіх лініях ігрового поля з метою знаходження такої клітинки. В ході
проходу для кожної лінії викликається модифікований метод AnalyzeLine, який
виконує аналіз до знаходження першої клітинки, уточнений стан якої рівний 1.
Ця клітинка повертається в OpenCell та до його викликача для подальшої
обробки. Щоб уникнути одноманітності підказок, прохід лініями починається з
випадкової лінії. Якщо ж даний кросворд нерозв’язний без використання
перебору, то замість вищеописаного способу обирається випадкова клітинка із
зафарбованих в correctPict.
Подальша обробка здійснюється за допомогою перевизначеного методу
ChangeCell(). В батьківському класі даний метод просто записує нове значення в
масив pict, але в NTSSWithHints даний масив використовується для визначення
клітинок для підказок, тому в ньому не має бути хибно вказаних клітинок. Щоб
уникнути цього, в перевизначеному методі здійснюється перевірка правильності
ходу через CheckCell. Якщо хід неправильний, то значення не заноситься в pict.
Замість цього використовується поле failedCells, реалізоване структурою
SortedSet. Туди заноситься номер хибно зафарбованої клітинки на ігровому полі.
Таким чином масив pict залишається правильним, але інформація про дію
користувача зберігається. Якщо ж хід був правильним, значення заноситься в pict
з подальшим викликом AnalyzeLine для рядка і стовпчика, в яких знаходиться
клітинка, та запам’ятовуванням в pict нових нульових значень, які вдасться
уточнити. Це потрібно для того, щоб при наданні подальших підказок
враховувалася неможливість знаходження блоків впритул та обнулення решти
клітинок лінії при заповненні всіх її блоків.
35
Також в перевизначеному ChangeCell() виконуються додаткові дії при
знятті позначення зафарбованої клітинки. Якщо це була одна з помилково
зафарбованих клітинок, що зберігаються в failedCells, виконується її вилучення
зі структури.
Останній метод класу – перевизначений CheckByLines(). Він використовує
результат свого предка, але вводить додаткову перевірку через failedCells.
Розв’язок вважається правильним лише в тому разі, якщо на момент перевірки
failedCells порожній (тобто, неправильних ходів або взагалі не було, або вони всі
були виправлені).
Розглянемо другий клас-нащадок – NTSSWithChecks. Він призначений для
позначення сесії розв’язання, в ході якої виконуються перевірки гарантованості
ходу користувача. Це реалізовано на рівні складності З перевірками.
Даний клас перевизначає лише метод ChangeCell(). Він повертає різні
значення в залежності від результатів перевірок (0 – гарантований хід, 1 –
ризикований хід, 2 – неправильний хід). Неправильний хід визначається через
CheckCell(), а вибір між гарантованим і ризикованим здійснюється за допомогою
AnalyzeLine(). Даний метод запускається для рядку і стовпця, в яких знаходиться
клацнута клітинка. Якщо хоча б в одному з цих запусків вдалося уточнити
клітинку як зафарбовану, хід вважається гарантованим, інакше – ризикованим.
Після цього важливо аналогічно з NTSSWithHints запам’ятати в pict нові нульові
клітинки, які вдалося знайти, а також виконати ще один аналіз лінії, протилежної
до тої, за якою вдалося уточнити стан. Це потрібно для того, що уточнити на ній
нулі вже беручи до уваги нову клітинку.

3.3.4 Реалізація компоненту «Створення кросворду» та його інтеграція


з «Автоматичним розв’язуванням»
Компонент «Створення кросворду» дозволяє користувачу запропонувати
зображення, яке має бути конвертоване в новий японський кросворд та додане в
базу даних. При такій конвертації може вийти кросворд, який має більше одного
розв’язку, тобто, його розв’язком є не тільки те зображення, яке запропонував
36
користувач. Щоб уникнути додавання такого кросворду в базу, потрібно
визначати кількість розв’язків. Це визначення теж виконується за допомогою
ІАЛ (ітераційного аналізу ліній, описаного в розд. 1.4).
Розглянемо, як це робиться, на блок-схемі (Додаток В).
Для цього виклик методу IterateLineLook(), що вміщує функціонал,
описаний при детальному проектуванні, виконується в рекурсивному методі
Try(). Після виконання ІАЛ отриманий результат переглядається на предмет
наявності клітинок, стан яких не був визначений (цикл while на блок-схемі).
Перегляд виконується через прохід по змінних i (номер рядку) та j (номер
стовпця). Цикл завершує роботу або коли i досягає кількості рядків кросворду,
або коли знаходиться клітинка з невизначеним станом. В другому випадку в
результаті виходу з циклу в змінних i та j залишаються збереженими координати
клітинки. Тобто, якщо після виконання циклу змінна i не менша за кількість
рядків (умова i>=nonogram.RowCount виконується), то кросворд розв’язано
повністю і потрібно повернути управління викликачу. В протилежному випадку
існує принаймні одна невизначена клітинка з координатами i, j. ЇЇ наявність може
свідчити як про те, що кросворд має кілька розв’язків, так і про те, що розв’язок
один, але не може бути знайдений лише за допомогою ІАЛ (потребує перебору).
Щоб визначити, який з цих варіантів актуальний для конкретного кросворду,
виконаємо цей перебір.
Візьмемо першу клітинку, стан якої не визначений, і припустимо, що вона
незафарбована (nonogram.Picture[i,j]=0). Виконаємо в такому припущенні
рекурсивний виклик Try(). Після цього припустимо, що клітинка зафарбована, і
зробимо те саме. Варто зазначити, що перед першим викликом необхідно
зберегти копію Picture, щоб здійснювати другий виклик з аналогічного
початкового стану, адже в ході роботи Try() Picture буде змінений. До того ж,
якщо всередині виклику знову знайдуться клітинки з невизначеним станом,
будуть здійснені наступні рекурсивні виклики з необхідністю збереження
масиву. Щоб обробити цю ситуацію, створимо стек copies, в який будемо
додавати копії Picture перед першим викликом і витягувати перед другим. Таким
37
чином копії, збережені на вищих рівнях рекурсії, будуть взяті лише після того,
як будуть вилучені копії з нижчих рівнів.
Коли клітинок з невизначеним станом зрештою не лишиться, запишемо в
логічній змінній solutionFound, що розв’язок був знайдений. Таким чином, якщо
при знаходженні розв’язку бачимо, що він не перший, можна зазначати тип
кросворду як FewSolutions (має кілька розв’язків) і завершувати.
Також, як уже згадувалося, за допомогою даного алгоритму можна
визначати, чи розв’язується кросворд без використання перебору. Цю
інформацію теж корисно зберігати, зокрема тому що в залежності від цього по
різному мають працювати рівні складності. Висновок про нерозв’язність
кросворду без перебору виконується можна робити у разі знаходження клітинки
з невизначеним станом після першого запуску ІАЛ в першому виклиці Try(). Це,
як зазначалось раніше, визначається за значенням змінної i. Тому при входженні
в гілку, відповідну ситуації з наявністю невизначеної клітинки, можемо відразу
зазначити тип кросворду як NeedBacktracking, тобто, такий, що без перебору
бути розв’язаним не може (якщо раніше вважалося, що може). В подальшому тип
може бути змінений на FewSolutions, що описувалося раніше.
Якщо розв’язок не один, можна спробувати модифікувати запропоноване
користувачем зображення так, щоб з нього можна було отримати коректний
кросворд. Для цього при здійсненні автоматичного розв’язування не будемо
виходити з рекурсії при знаходженні другого розв’язку, а пройдемося по всіх
(при виклику методу передамо collect=true, що використовується в двох останніх
умовах на блок-схемі). В ході цього запам’ятовуватимемо в cellsForMod
клітинки, які на різних рівнях рекурсії виявлялися невизначеними. Розглянемо
подальші дії з ними на лістингу 1, Додаток Г. Бачимо, що спершу оголошується
масив newPict, в який копіюються значення InitialPict початкового кросворду.
Потім значення клітинки, записаної в cellToMod, змінюється на протилежне,
після чого на основі newPict створюється новий кросворд та знаходиться його
тип. Якщо тип кросворду знову рівний FewSolutions, даний кросворд
вилучається з modVariants. Аналогічні дії виконуються для всіх клітинок з
38
cellToMod. Якщо так і не вдається знайти кросворд з єдиним розв’язком, можна
спробувати змінювати стани цих клітинок в комбінації. Якщо вдасться одержати
зображення, з яких можна утворити кросворд з єдиним розв’язком, виведемо їх
в окремому вікні та надамо користувачу можливість обрати (якщо зображень
декілька).

3.3.5 Реалізація компоненту «Таблиця рекордів»


Компонент «Таблиця рекордів» відповідає за відображення користувачу
даних з таблиці з рекордами, що зберігається на базі даних. В даній таблиці
зберігається по одному рекорду для кожного користувача. Рекорди є окремими
для різних кросвордів та для різних рівнів складності в одному кросворді.
Щоб уникнути зайвої плутанини, надаватимемо можливість перегляду
рекордів по кросворду, що в даний момент завантажений на основну форму. В
цьому випадку ставатиме активною кнопка меню Рекорди. При відкритті таблиці
рекордів в окремому вікні з бази даних отримуватимуться рекорди за всіма
рівнями складності та надаватиметься можливість перемикати рівень складності
через comboBox. Перемикання призводитиме до миттєвого відображення
таблиці рекордів для обраного рівня складності.
Розглянемо запит, за допомогою якого одержуються рекорди (Додаток Г,
лістинг 2).
В таблиці records зберігається id користувача, в той час як виводити
потрібно username. Для цього виконаємо left join таблиці users до records по умові
records.user_id = users.id. В select використовуватимемо поля users.username,
records.score, records.s_time, тобто логін користувача, рахунок, час розв’язання.
Також додамо умову where, в якій вибиратимемо лише записи по поточному
кросворду (records.nonogram_id = nonId, яке отримується з основного вікна) та
по конкретному рівню складності. Виконаємо три таких запити та збережемо
результати в масиві DataTable d. Елементи масиву будемо встановлювати в
якості джерела даних (DataSource) елемента управління DataGridView, на якому
і будуть виводитись рекорди.
39
3.3.6 Загальна діаграма класів модифікації гри «Японський кросворд»
Наведемо діаграму з усіма розробленими класами на рис. 3.4.

Рис. 3.4 – Загальна діаграма класів

Оскільки деякі фрагменти даної діаграми вже були описані в попередніх


підрозділах, розглянемо лише ті, які ще не згадувалися.
Так, окрім класу NonogramToSolveSession нащадками NonogramSession є
також NonogramToAddSession та NonogramToVerifySession.
NonogramToAddSession використовується при створенні нового кросворду. Він
перевизначає метод SolveEntire, надаючи можливість обирати кросворд, який
потрібно розв’язати (необхідно для знаходження типу варіантів модифікації
кросворду). Метод BuildModVariants() виконує операції, описані в підрозділі
40
3.3.4: формує варіанти модифікації, змінюючи стани клітинок, які були
невизначеними. Варіанти кросвордів, що проходять за вимогами, зберігаються в
modVariants. Зрештою, метод ModifyNonogram(), який викликається, коли
користувач робить вибір на користь одного з варіантів, замінює початковий
кросворд на обраний варіант.
NonogramToVerifySession використовується при перевірці кросворду
адміністратором. Його відмінність від батьківського класу полягає в викликові
SolveEntire() в конструкторі, адже в базі даних зберігаються лише описи блоків,
в той час як для винесення вердикту адміністратор має бачити зображення-
відповідь.

3.4 Тестування модифікації гри «Японський кросворд»

Щоб визначити, чи відповідає розроблений програмний продукт заданим


на початку вимогам, виконаємо його тестування. Щоб охопити відразу всі
вимоги та аспекти продукту, виконаємо тестування вручну. Застосуємо
тестування чорного ящика, щоб змоделювати процес роботи використання
програми реальним користувачем. Результати тестування наведемо в Додатку Ґ.
Оскільки наведені тести цілком покривають задані для розроблюваного ПЗ
вимоги та були вдало пройдені, можемо зробити висновок, що ПЗ відповідає
сформованим на початку вимогам та готове до впровадження.

Висновок до розділу 3

В третьому розділі був виконаний вибір мови і середовища для розробки


модифікації гри «Японський кросворд». Було проведене уточнення архітектури,
в ході якого «Автоматичне розв’язування» було винесене в окремий компонент.
Були описані всі суттєві компоненти розробленого програмного забезпечення та
зв’язки між ними. Також було проведене тестування, за результатами якого було
визначена готовність ПЗ до впровадження.
41
ВИСНОВКИ

Отже, був розроблений програмний продукт «Модифікація гри


«Японський кросворд» з можливістю автоматичного розв'язування та
додатковими засобами оцінювання гри користувача». Даний продукт включає
середовище для розв’язування японських кросвордів та модуль, здатний
автоматично їх розв’язувати. Інтеграція цих двох компонентів відбувається
зокрема через систему рівнів складності. Зокрема, реалізований рівень «З
перевірками» використовує алгоритм автоматичного розв’язку для перевірки
того, чи був хід користувача гарантованим, та по-різному нараховує бали в
залежності від результатів перевірки. Рівень «З підказками» використовує
модуль автоматичного розв’язання для надання в якості підказок лише тих
клітинок, що можуть бути вирахувані логічно на поточному кроці. Також ПЗ
надає можливість користувачу пропонувати власні зображення для кросвордів, а
модуль автоматичного розв’язування здатен визначати їх тип (розв’язується без
перебору / не розв’язується без перебору / має кілька розв’язків), зокрема, з
метою відсіювання кросвордів з неєдиним розв’язком. В разі неєдиності
розв’язку ПЗ також надає обмежені засоби для автоматизованого пошуку
схожого зображення, яке може бути конвертоване в коректний кросворд.
Реалізована також авторизація, яка дозволяє пропонувати кросворди лише
зареєстрованим користувачам, а адміністратору – підтверджувати або відхиляти
їх.
Також розроблений продукт підтримує таблицю рекордів по кросвордах і
рівнях складності в них, дозволяє зберігати на локальному диску і завантажувати
поточний прогрес.
Розроблене ПЗ відповідає поставленим на початку розробки завданням та
вимогам.
Ключовим напрямом подальшого розвитку даного ПЗ може стати
реалізація можливості розв’язувати багатокольорові кросворди. Також на
багатоколірні кросворди може бути узагальнений алгоритм ітераційного аналізу
42
ліній, а в результаті перенесений весь наявний для чорно-білих кросвордів
функціонал. Також може бути додана більша кількість рівнів складності, зокрема
і таких, які реалізують інші способи використання алгоритму автоматичного
розв’язку. Ще одним напрямком розвитку може стати удосконалення модуля, що
пропонує варіанти модифікації запропонованого користувачем зображення, з
метою збільшити кількість випадків, в яких дані варіанти вдається знайти.
43
СПИСОК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ

1. Весельський О.С. Модифікація гри "Японський кросворд" з можливістю


автоматичного розв'язування та додатковими засобами оцінювання гри
користувача // Актуальні проблеми природничих і гуманітарних наук у
дослідженнях молодих учених «Родзинка – 2022» / XXIV Всеукраїнська наукова
конференція молодих учених. [Текст] – Черкаси : ЧНУ ім. Б. Хмельницького,
2022. – С. 669-670.
2. Порублев И.Н., Ставровский А.Б. Алгоритмы и программы. Решение
олимпиадных задач. [Текст] – М.: ТОВ «И.Д. Вильямс», 2007. – 480 с.
3. Японське хобі. [Електронний документ]. Режим доступу:
https://web.archive.org/web/20140408230744/http://exo.at.ua/news/2009-10-02-
8040. Перевірено: 29.05.2022
4. Nonogram Game. [Електронний документ]. Режим доступу:
https://www.microsoft.com/store/productId/9NRTD31WRG3J. Перевірено:
20.04.2022
5. Nonogram.com. [Електронний документ]. Режим доступу:
https://play.google.com/store/apps/details?id=com.easybrain.nonogram. Перевірено:
6. Nonograms Online – Nonogram Puzzle. [Електронний документ]. Режим
доступу: https://nonogramsonline.com. Перевірено: 20.04.2022
7. Nonograms. [Електронний документ]. Режим доступу:
https://onlinenonograms.com/. Перевірено: 20.04.2022
8. Top 10 Programming Languages for Desktop Apps in 2021. [Електронний
документ]. Режим доступу: https://www.decipherzone.com/blog-detail/top-
programming-languages-for-desktop-apps-in-2021. Перевірено: 02.04.2022
44
ДОДАТОК А – PRODUCT BACKLOG МОДИФІКАЦІЇ ГРИ
«ЯПОНСЬКИЙ КРОСВОРД»

1. 2. Назва 3. 4. 5. Як продемонструвати 6.Примітки


№ Важливість Попередня
оцінка

1. Розв’язування 13 3 Запустити програму, -


кросворду розв’язати кросворд,
отримати повідомлення
про результати.

2. Створення 8 5 Натиснути кнопку -


кросворду додавання кросворду,
обрати клітинки, які
будуть зафарбовані,
переконатись, що
відбувся перехід до
перевірки на єдиність
розв’язку.

3. Вибір рівня 8 6 Обрати по черзі всі Всі рівні


складності рівні складності, доступні з
побачити відмінності в самого
одержаному початку гри
функціоналі. (якщо
дозволяє тип
кросворду).

4. Реєстрація 8 4 Ввести логін та пароль в -


облікового вікні реєстрації; ввести
запису ці ж самі дані у вікні
авторизації та
переконатись, що вхід в
систему був виконаний.

5. Авторизація 8 2 Ввести раніше -


зареєстрованого зареєстровані логін та
користувача пароль у вікні
авторизації та
переконатись, що вхід в
систему був виконаний.

6. Запис у таблицю 5 3 Натиснути кнопку -


рекордів відкриття таблиці
рекордів, переконатися,
що вона відображається
коректно; розв’язати
кросворд, перевірити,
що в таблиці рекордів
з’явився новий запис.
45

1. 2. 3. 4. 5. 6.

7. Перевірка 5 2 Послідовно створити -


створеного кросворд, що точно
кросворду на відповідає вимогам
єдиність єдиності розв’язку та
розв’язку кросворд, що їм точно не
відповідає; переконатись,
що в обох випадках був
отриманий відповідний
результат їх оцінки.

8. Додавання 5 2 Відправити кросворд на -


кросворду в перевірку та від імені
базу адміністратора прийняти
його; переконатись, що
кросворд з’явився у базі.

9. Надання 5 3 Від імені користувача -


доступу або відправити кросворд на
відмова в перевірку, перевірити що
доступі до він став доступним для
кросворду перевірки. Допустити
кросворд та
переконатись, що він
з’явився в базі;
заблокувати кросворд та
переконатись, що він не
додався до бази.

10. Можливість 3 5 Натиснути кнопку -


скористатися підказки, переконатись
підказкою що одна клітинка
зафарбувалася.

11. Спроба 3 4 Створити кросворд, що Відповідність


підказки з має кілька розв’язків; єдиності
модифікації переконатись, що був розв'язку
створеного отриманий коректний перевірити з
кросворду для модифікований варіант використання
одержання або повідомлення про м вимоги 7
варіанту з неможливість надання цієї таблиці.
єдиним такого варіанту для
розв’язком даного кросворду.

12. Збереження 3 1 Розв’язати частину -


поточного кросворду, натиснути
прогресу кнопку збереження;
переконатися що файл зі
станом поля зберігся на
комп’ютері.
46
1. 2. 3. 4. 5. 6.

13. Завантаження 3 1 Натиснути кнопку -


поточного завантаження, обрати
прогресу збережений раніше файл;
переконатись, що стан
поля відновився.

14. Скасування 1 2 Виконати дію, скасувати -


попередньої дії її, переконатися що
система повернулася в
стан до виконання дії.
47
ДОДАТОК Б – БЛОК-СХЕМА ЗАПОВНЕННЯ КАРТИ ШЛЯХІВ
В ANALYZELINE
48
ДОДАТОК В – БЛОК-СХЕМА АЛГОРИТМУ ВИЗНАЧЕННЯ
ТИПУ КРОСВОРДУ НА ОСНОВІ ІАЛ (ІТЕРАЦІЙНОГО
АНАЛІЗУ ЛІНІЙ)
49
ДОДАТОК Г – ЛІСТИНГИ КОДУ МОДИФІКАЦІЇ ГРИ
«ЯПОНСЬКИЙ КРОСВОРД»
1)
int[,] newPict = new int[nonogram.RowCount, nonogram.ColumnCount];
Array.Copy(nonogram.InitialPicture, newPict, nonogram.InitialPicture.Length);
newPict[cellToMod[i] / nonogram.ColumnCount, cellToMod[i] %
nonogram.ColumnCount] = newPict[cellToMod[i] / nonogram.ColumnCount, cellToMod[i]
% nonogram.ColumnCount] * (-1) + 1;
modVariants.Add(new Nonogram(newPict));
SolveEntire(modVariants[modVariants.Count - 1]);
if (modVariants[modVariants.Count - 1].Type == NonogramType.FewSolutions)
{
modVariants.Remove(modVariants.Last());
}

2)
d[0] = npg.Query("select users.username, records.score, records.s_time from
records" + " left join users on records.user_id = users.id where
records.nonogram_id=" + nonId + " and records.difficulty=0");
d[1] = npg.Query("select users.username, records.score, records.s_time from
records" + " left join users on records.user_id = users.id where
records.nonogram_id=" + nonId + " and records.difficulty=1");
d[2] = npg.Query("select users.username, records.score, records.s_time from
records" + " left join users on records.user_id = users.id where
records.nonogram_id=" + nonId + " and records.difficulty=2");
50
ДОДАТОК Ґ – РЕЗУЛЬТАТИ ТЕСТУВАННЯ МОДИФІКАЦІЇ
ГРИ «ЯПОНСЬКИЙ КРОСВОРД»
1. Тест 2. Очікуваний результат 3. Одержаний результат
Запустити програму Відкривається головне вікно Відкривається головне вікно
Оглянути меню Вгорі наявні вкладки Файл, Вгорі наявні вкладки Файл,
Розв’язати, Акаунт. Розв’язати, Акаунт.
Елементи управління в Елементи управління в
середині форми недоступні середині форми недоступні
Натиснути Акаунт, З’являється вікно для вводу З’являється вікно для вводу
Реєстрація логіну та пароля нового логіну та пароля нового
користувача користувача
Ввести логін та пароль, Вікно з логіном Вікно з логіном
натиснути ОК закривається; в основному закривається; в основному
вікні з’являється пункт вікні з’являється пункт
меню Створити, логін меню Створити, логін
користувача з’являється на користувача з’являється на
пункті Акаунт. пункті Акаунт.
Повторити два попередні З’являється також пункт З’являється також пункт
пункти для акаунту меню Адмін меню Адмін
адміністатора
Натиснути Розв’язати З’являється вікно для З’являється вікно для
завантаження кросворду завантаження кросворду
Ввести в текстове поле Id 3, Поточне вікно закривається; Поточне вікно закривається;
натиснути завантажити в основному вікні стає в основному вікні стає
активною перша група активною перша група
елементів управління, елементів управління,
з’являються описи блоків з’являються описи блоків
кросворду; вгорі з’являється кросворду; вгорі з’являється
пункт Рекорди пункт Рекорди
Обрати Тренувальний Перша група елементів Перша група елементів
режим, натиснути Старт управління стає неактивною; управління стає неактивною;
друга група елементів друга група елементів
управління стає активною управління стає активною
Зафарбувати кілька З’являється повідомлення З’являється повідомлення
клітинок, натиснути Готово про неправильність про неправильність
наданого розв’язку наданого розв’язку
Натиснути Стоп Перша група елементів Перша група елементів
управління стає активною; управління стає активною;
друга група елементів друга група елементів
управління стає неактивною; управління стає неактивною;
ігрове поле очищується ігрове поле очищується

Обрати Змагальний режим, Перша група елементів Перша група елементів


натиснути Старт управління стає неактивною; управління стає неактивною;
друга група елементів друга група елементів
управління стає активною; управління стає активною;
внизу з’являється поточний внизу з’являється поточний
рахунок та таймер рахунок та таймер
51
1. 2. 3.
Зафарбувати кілька З’являється повідомлення З’являється повідомлення
клітинок, натиснути Готово про неправильність про неправильність
наданого розв’язку; рахунок наданого розв’язку; рахунок
зменшується зменшується
Зупинити гру, запустити Окрім решти, в другій групі Окрім решти, в другій групі
заново обравши режим З елементів управління елементів управління
підказками з’являється кнопка Підказка з’являється кнопка Підказка
Натиснути кнопку Підказка Одна клітинка Одна клітинка
зафарбовується; рахунок зафарбовується; рахунок
зменшується зменшується
Зупинити гру, запустити Окрім решти, кнопка Окрім решти, кнопка
заново обравши режим З Пізказка зникає Пізказка зникає
перевірками
Клацнути клітинку в 6 рядку Рахунок збільшується Рахунок збільшується
Клацнути клітинку (8, 9) Рахунок не збільшується; Рахунок не збільшується;
з’являється повідомлення з’являється повідомлення
про ризикованість ходу про ризикованість ходу
Клацнути клітинку (0, 0) Рахунок зменшується; Рахунок зменшується;
з’являється повідомлення з’являється повідомлення
про помилковість ходу; про помилковість ходу;
клітинка не зафарбовується клітинка не зафарбовується
Зафарбувати всі клітинки в 6 З’являється вікно для вибору З’являється вікно для вибору
рядку, натиснути Файл, місця збереження місця збереження
Зберегти
Зберегти файл, натиснути З’являється вікно для вибору З’являється вікно для вибору
Стоп, натиснути Файл, файлу для завантаження файлу для завантаження
Завантажити
Обрати файл, збережений в Друга група елементів Друга група елементів
попередньому тесті управління стає активною; управління стає активною;
на ігровому полі на ігровому полі
з’являються клітинки, з’являються клітинки,
зафарбовані перед зафарбовані перед
збереженням; рахунок та збереженням; рахунок та
таймер не з’являються таймер не з’являються
Клацнути клітинку (5, 10) Повідомлення про Повідомлення про
ризикованість ходу не ризикованість ходу не
з’являється з’являється
Розв’язати кросворд до З’являється повідомлення З’являється повідомлення
кінця, натиснути Готово про правильність розв’язку; про правильність розв’язку;
всі елементи управління всі елементи управління
стають неактивними стають неактивними
Завантажити кросворд з бази З’являється повідомлення З’являється повідомлення
заново, повторити про правильність розв’язку; про правильність розв’язку;
розв’язання перша група елементів перша група елементів
управління стає активною; управління стає активною;
друга група елементів друга група елементів
управління стає неактивною управління стає неактивною
Завантажити кросворд з Id 7 Режим З перевірками Режим З перевірками
відсутній відсутній
52
1. 2. 3.
Натиснути кнопку Рекорди З’являється вікно таблиці З’являється вікно таблиці
рекордів рекордів
Обрати інший рівень Записи в таблиці Записи в таблиці
складності змінюються змінюються

Авторизуватися з акаунту, З’явився запис з З’явився запис з


який ще не розв’язував результатами розв’язування результатами розв’язування
кросворд 7; завантажити
кросворд 7; розв’язати;
відкрити таблицю рекордів
для відповідного рівня
складності
Розв’язати частину Логін користувача зникає, Логін користувача зникає,
кросворду, натиснути пункт меню Створити пункт меню Створити
Акаунт, Вийти зникає, елементи управління зникає, елементи управління
повертаються до стану при повертаються до стану при
запуску програми запуску програми
Зайти в акаунт, натиснути З’являється вікно для З’являється вікно для
Створити створення кросворду створення кросворду
Кілька разів змінити розміри Поле змінює розміри у Поле змінює розміри у
поля відповідності до введених відповідності до введених
значень значень
Зафарбувати кілька Поле очищується Поле очищується
клітинок, натиснути
Очистити
Створити кросворд з З’являється повідомлення З’являється повідомлення
неєдиним розв’язком, про неможливість додавання про неможливість додавання
натиснути Відправити даного кросворду та даного кросворду та
пропозиція спроби пропозиція спроби
модифікації модифікації
Натиснути Так З’являється вікно з З’являється вікно з
варіантами модифікації або варіантами модифікації або
повідомлення про повідомлення про
неможливість модифікації неможливість модифікації
Домогтися відкриття вікна з Зображення змінюється на Зображення змінюється на
кількома варіантами для наступне або попереднє при наступне або попереднє при
модифікації; поперемикати натисканні кнопки натисканні кнопки
варіанти
Обрати один з варіантів, Вікно з варіантами Вікно з варіантами
натиснути Обрати закривається, у вікні закривається, у вікні
створення з’являється створення з’являється
обраний варіант обраний варіант
Натиснути Відправити З’являється запит на З’являється запит на
підтвердження з підтвердження з
інформацією про тип інформацією про тип
кросворду кросворду
Натиснути Так З’являється повідомлення З’являється повідомлення
про відправку на перевірку про відправку на перевірку
та Id кросворду та та Id кросворду та
53
1. 2. 3.
Спробувати завантажити З’являється повідомлення З’являється повідомлення
новостворений кросворд по про непроходження про непроходження
Id перевірки адміністратора перевірки адміністратора
Авторизуватись акаунтом З’являється вікно з З’являється вікно з
адміністратора, натиснути створеним на попередньому створеним на попередньому
Адмін, Перевірка кросвордів кроці кросвордом та кроці кросвордом та
кнопками Підтвердити і кнопками Підтвердити і
Відхилити Відхилити
Натиснути Підтвердити Вікно закривається Вікно закривається
Натиснути Адмін, Перевірка З’являється повідомлення З’являється повідомлення
кросвордів про відсутність кросвордів про відсутність кросвордів
для перевірки для перевірки
Спробувати завантажити Кросворд завантажується Кросворд завантажується
новостворений кросворд по
Id
Повторити тести від З’являється повідомлення З’являється повідомлення
створення кросворду, але про відсутність такого про відсутність такого
натиснути Відхилити кросворду кросворду
54
ДОДАТОК Д – ВИХІДНИЙ КОД МОДИФІКАЦІЇ ГРИ
«ЯПОНСЬКИЙ КРОСВОРД»
Вихідний код програми розміщений в Git-репозиторії за адресою
https://github.com/TomQWERTY/Qualifying_work

You might also like