Читати онлайн Підручник з Haskell calibre 0

починається з позиції, де всі фішки перемішані. Необхідно, переставляючи фішки, повернутися до

вихідне положення. Кожного разу ми рухаємо одну фішку на порожнє поле. У вихідному положенні фішки

йдуть по порядку.

Мал. 13.1: Випадковий та кінцевий стан гри цятки

Програма перемішуватиме фішки та відображатиме поле для гри. Вона питатиме наступний

хід та оновлювати поле після ходу. Якщо ми розставимо всі фішки по порядку, програма повідомить про це

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

почати все заново. Відомо, що з будь-якого становища можна розставити фішки по порядку. Тому наш

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

Малюнок рішення

Програма, яку ми хочемо написати, вестиме діалог з користувачем. Вона показує поле для

ігри та запитує наступний хід. Потім вона розпізнає хід і показує оновлене поле. І так далі.

Нам треба якось організувати цей діалог.

При цьому у програмі можна виділити дві незалежні частини. Одна відповідає за сам діалог. Вона прини-

має репліки користувача та відображає поле для гри. А інша частина відповідає за правила гри квача:

як ходи впливають на поле, яке становище є переможним, як перемішувати фішки.

У нас буде два окремі модулі: один для опису гри, назвемо йогоGame, а інший для опису гри

діалогу з користувачем. Ми назвемо йогоLoop (петля або цикл), оскільки діалог це зациклена проце-

дура отримання репліки та реакцію репліку.

Такий ось малюнок-орієнтир. Після цього можна приступати дореалізації. Але з чого розпочати?

Каркас. Типи та класи

У Haskell програми зазвичай починають будувати з каркасу – з типів та класів. Нам потрібно виділити ос-

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

У нашій задачі є поле з фішками та ходи. Ми робимо ходи та фішки рухаються. Поле – це матриця

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

осередку масиву зберігаються фішки. Фішки позначаються цілими числами:

type Pos

= (Int,Int )

type Label

= Int

type Board

= Array Pos Label

Порожню фішку ми також позначатимемо числом. Фізично коли ми ходимо, ми змінюємо становище

однієї фішки. Але в нашому описі ми міняємо місцями дві фішки, оскільки порожня фішка також обізнана.

чається номером. Коли ми ходимо, ми змінюємо положення порожньої фішки, одним ходом ми можемо змістити

її вгору, вниз, ліворуч чи праворуч. Введемо спеціальний тип для позначення ходів:

Data Move = Up Down Left Right

Для того щоб при кожному ході не шукати порожню клітину, збережемо її поточне положення. Тип

Game міститиме поточне положення порожньої клітини та положення фішок:

data Game = Game

:: Pos,

:: Board >

Ось і всі типи для опису гри. Збережемо їх у модуліGame. Тепер подумаємо про типи для діалогу

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

взаємодія із гравцем. Але що є каркасом для діалогу?

Якщо ми хочемо з кимось спілкуватися, необхідно щоб у нас був зспіврозмовником спільна мова, він і

буде каркасом для діалогу. Згадаймо, що ми очікуємо від користувача. Користувач може:

• Почати нову гру

Якщо користувач робить хід ми показуємо нове положення поля, якщо він починає нову гру ми

показуємо йому нову перемішану позицію, давайте у нас буде різний ступінь перемішаності фі-

гур. При перемішуванні ми стартуємо з переможного становища і починаємо випадково робити хо-

ди. Чим більше ходів ми зробимо, тим складніше буде зібрати гру. Тому користувач вказуватиме

кількість кроків для перемішування під час запиту нової гри. Якщо користувач попросить закінчити гру ми

попрощаємось і вийдемо з гри.

На основі цих міркувань вимальовується такий тип повідомлень:

Data Query = Quit NewGame Int Play Move

Значення типуQuery (запит) може бути константаQuit (вихід), запит нової гриNewGame з числом,