НОУ ІНТУЇТ | лекція | Гра "Хрестики-нулики"

  1. Правила гри. Стратегія вибору ходу
  2. Створення ігрового поля
  3. Створення вікна установки параметрів
  4. Створення класу game
  5. Створення ігрового вікна
  6. Реалізація гри
  7. Реалізація вибору ходу
  8. Повідомлення про ходах гравців
  9. Реалізація ходу гри

Анотація: Реалізується варіант гри "Хрестики-нулики" на поле довільного розміру. Замість "хрестиків" та "налякав" використовуються темні і світлі камені. Камені ставляться на перетин ліній сітки (як в грі "Го"). Створюється додаток для гри користувача з комп'ютером. Користувач може вибрати розмір ігрового поля (кількість рядків і стовпців), колір своїх каменів і гравця, який буде ходити першим. Він може змінити розмір ігрового поля - збільшити його або зменшити, при цьому пропорційно зміниться розмір клітин і каменів. Стратегія комп'ютера реалізується за допомогою правил. Комп'ютер грає не гірше, ніж внічию. Він вміє ставити "вилки" і не дає їх ставити противнику. Користувач ходить за допомогою миші - вказує поле, в яке ставить свій камінь. Він може відкрити більше одного вікна, що містить ігрове поле, і грати в кілька ігор одночасно.

Правила гри. Стратегія вибору ходу

Реалізується такий варіант гри "Хрестики-нулики". Гра ведеться на прямокутному полі розміром m х n, де Реалізується такий варіант гри Хрестики-нулики і , Але місце для гри (ігровий ділянку) не повинно виходити за межі квадрата, що містить дев'ять клітин (3 х 3). На відміну від звичайної гри, ця ділянка визначається тільки в процесі гри. Виграє гравець, який поставив в ряд по вертикалі, по горизонталі або по діагоналі три своїх каменю. Якщо ділянку заповнений повністю, а жодному гравцю не вдалося поставити три камені в ряд, то визнається нічия.

Перший гравець першим ходом може поставити камінь в будь-яке поле. Гравці ходять по черзі. Вони можуть ходити тільки в доступні поля, так щоб в результаті всі камені виявилися на ділянці розміром 3 х 3. Наприклад, якщо перший гравець поставить камінь так, як показано на рис. 9.1 (a) Мал. 9.1 , То другий гравець може ходити в будь-яке поле, що знаходиться всередині виділеного квадрата, доступними для нього є 24 поля. Після ходу другого гравця, показаного на рис. 9.1 (b) Мал. 9.1 , Місце гри звужується, доступними для першого гравця залишаються 14 полів. Якщо перший гравець піде так, як показано на рис. 9.1 (c) Мал. 9.1 , То місце гри буде повністю визначено.


Мал.9.1.

Визначення ігрового ділянки

Неважко помітити, що у першого гравця більше шансів виграти. Проте правильна гра, т. Е. Гра, в якій обидва гравці ходять найкращим для себе чином, завжди завершиться внічию.

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

Нижче перераховуються правила, відповідно до яких ходить гравець-комп'ютер. Вони наводяться в тому ж порядку, в якому реалізуються в програмі. Формулювання правил - "декларативна", по можливості сама загальна, без уточнення всіх випадків (див. Програму). Правила реалізуються просто і в загальному вигляді за рахунок використання списків. Списки в мові Пролог - це зберігає (persistent) тип даних, тому їх зручно використовувати для прораховування позиції на крок чи на кілька кроків вперед.

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

Після першого ходу першого гравця, другий гравець має, щонайбільше, п'ять варіантів ходу, з точністю до симетрії (див. Рис. 9.1 (a) Мал. 9.1 , Де вони відзначені точками). У чотирьох з них він програє в грі з правильним гравцем-противником (див. Нижче правило 6). Залишається хід по діагоналі в сусіднє поле. У програмі таке поле вибирається випадковим чином.

2. Гравець ставить свій камінь третім в ряд і виграє.

3. Гравець ставить блок так, щоб противник не міг у відповідь поставити вилку або поставити камінь третім в ряд.

На рис. 9.2 (a) Мал. 9.2 наведено приклад позиції, в якій застосовується правило 3. Ходить гравець темними. Він повинен поставити камінь в поле, що знаходиться безпосередньо зліва чи справа від двох світлих каменів, в іншому випадку він негайно програє. Якщо гравець поставить камінь зліва від каменів супротивника, то той зможе відразу поставити вилку і виграти. Тому гравець повинен ходити так, як показано на рис. 9.2 (b) Мал. 9.2 .


Мал.9.2.

Блок із запобіганням вилки, ходить гравець темними

4. Якщо ділянку гри повністю визначений, то гравець ставить блок.

5. Гравець ставить виделку.

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

На рис. 9.3 (a) - 9.6 (a) Мал. 9.3 Мал. 9.6 наведені приклади позицій, в яких для вибору ходу гравця застосовується правило 6. Ходить гравець темними. Правильні ходи показані на рис. 9.3 (b) - 9.6 (b) Мал. 9.3 Мал. 9.6 . У кожному разі противник змушений ставити блок, після чого гравець ставить вилку (рис. 9.3 (c) - 9.6 (с) Мал. 9.3 Мал. 9.6 ).


Мал.9.3.

Примус до блоку з подальшою виделкою. позиція 1
Мал. 9.4. Примус до блоку з подальшою виделкою. позиція 2
Мал. 9.5. Примус до блоку з подальшою виделкою. позиція 3
Мал. 9.6. Примус до блоку з подальшою виделкою. позиція 4

7. Якщо ділянку гри повністю визначений, то робиться хід в центральне поле.

На рис. 9.7 (a) Мал. 9.7 наведено приклад позиції, в якій гравець темними ходить в центральне поле ігрового ділянки (рис. 9.7 (b)) Мал. 9.7 . Неважко помітити, що якщо він зробить будь-який інший хід, то противник зможе поставити йому вилку.


Мал.9.7.

Хід в центральне поле

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

На рис. 9.8 (a) Мал. 9.8 наведено приклад позиції, в якій гравець темними вибирає хід за правилом 8 (рис. 9.8 (b)) Мал. 9.8 . Якщо він піде в кутове поле ігрового ділянки, то противник зможе поставити йому вилку.


Мал.9.8.

Запобігання вилки противника

9. Гравець займає поле, ходом в яке противник поставив би йому вилку.

10. Гравець робить випадковий хід в доступне поле (останні два правила програмою фактично не використовуються).

Створення ігрового поля

Створимо проект tictactoe (MDI). Після запуску програми має відкритися вікно установки параметрів, в якому користувач зможе вказати розміри поля, гравця, який буде ходити першим, і колір каменів.

Створення вікна установки параметрів

За допомогою діалогового вікна Create Project Item створимо діалогове вікно settingsDialog.


Мал.9.9.

Діалогове вікно установки параметрів

Розмістимо в ньому такі елементи управління (рис. 9.9 Мал. 9.9 ):

  • написи (Static Text) "Ваше ім'я", "Кількість рядків", "Кількість стовпців", "Ходить першим";
  • поле редагування (Edit Control):
    • Name: name_ctl; Text: Петя (введіть своє ім'я);
  • призначені для користувача елементи управління (Custom Control; Class: integerControl):
    • Name: row_ctl; Text: 15; Maximum: 30; Minimum: 3;
    • Name: column_ctl; Text: 15; Maximum: 30; Minimum: 3;
  • прапорець (Check Box):
    • Text: комп'ютер;
  • груповий блок (Group Box):
    • Text: Колір ваших каменів;
  • в груповому блоці потрібно розмістити два перемикача (Radio Button):
    • Name: dark_ctl; Text: темний; RadioState: checked;
    • Name: light_ctl; Text: світлий.

Тепер слід включити пункт меню File -> New, додати обробник подій вибору даної команди меню (див. П. 1.1 "Основні елементи графічного інтерфейсу користувача" ) І визначити його наступним чином:

clauses onFileNew (_Source, _MenuTag): - _ = settingsDialog :: display (This).

Для того щоб вікно установки параметрів відкривалося відразу при запуску програми, змінимо також визначення предиката onShow в імплементації класу taskWindow так, як показано нижче:

clauses onShow (_, _CreationData): - _MessageForm = messageForm :: display (This), _ = settingsDialog :: display (This).

Створення класу game

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

constants compPlayer: positive = 0. humanPlayer: positive = 1. winner: positive = 1. draw: positive = 0. domains sq = sq (integer Row, integer Column, state). % Поле state = darkStone; lightStone; empty. % Стан поля properties m: integer. % Число рядків n: integer. % Число стовпців isComputerFirst: boolean. % Комп. ходить першим isDarkStone: boolean. % Користей. відіграє темними Лістинг 9.1. Оголошення параметрів в інтерфейсі game

У декларації класу game оголосимо конструктор і властивість.

constructors new: (integer M, integer N, boolean CompFirst, boolean DarkSt). properties game: game. Лістинг 9.2. Оголошення властивості в декларації класу game

Нижче наведено визначення оголошених властивостей і конструкторів в імплементації класу game.

facts m: integer: = 15. n: integer: = 15. isComputerFirst: boolean. isDarkStone: boolean. clauses new (M, N, IsComputerFirst, IsDarkStone): - m: = M, n: = N, isComputerFirst: = IsComputerFirst, isDarkStone: = IsDarkStone. class facts game: game: = erroneous. Лістинг 9.3. Визначення властивостей в імплементації класу game

Створення ігрового вікна

За допомогою діалогового вікна Create Project Item створимо поле для гри (Control) і назвемо його gameControl. Закриємо редактор вікна gameControl.

Далі створимо форму (Form) gameForm. Розмістимо в поле форми призначений для користувача елемент управління (CustomControl; Class: gameControl), для всіх якорів прив'язки вкажемо значення True (рис. 9.10 Мал. 9.10 ).


Мал.9.10.

Форма для гри

Потім за допомогою діалогового вікна Create Project Item створимо клітку (Draw Control) під назвою cellControl. Додамо в інтерфейс cellControl оголошення властивостей і предиката setCellState.

properties gameCtl: gameControl. i: integer. j: integer. state: game :: state. predicates setCellState: (game :: state). Лістинг 9.4. Оголошення властивостей в інтерфейсі cellControl

У імплементацію класу cellControl додамо код, наведений нижче.

constants bgColor: color = color_BlanchedAlmond. % Колір фону lineColor: color = color_BurlyWood. % Колір ліній darkBrushColor: color = color_Sienna. % Колір каменю darkBorderColor: color = color_SaddleBrown. % Межа каменю lightBrushColor: color = color_LightGray. lightBorderColor: color = color_DarkGray. facts gameCtl: gameControl: = erroneous. i: integer: = 0. j: integer: = 0. state: state: = empty. clauses setCellState (State): - state: = State, invalidate (). Лістинг 9.5. Визначення властивостей в імплементації класу cellControl

Далі додамо в інтерфейс gameControl оголошення властивостей, що зберігають покажчики на об'єкти класів game і gameForm.

properties game: game. gameFrm: gameForm. Лістинг 9.6. Оголошення властивостей в інтерфейсі gameControl

Змінимо розділ open імплементації класу gameControl так, як показано нижче:

open core, vpiDomains, cellControl, game

В імплементації класу gameControl слід визначити оголошені вище властивості, а також предикати, які створюють ігрове поле.

facts gameFrm: gameForm: = erroneous. game: game: = erroneous. facts cell: (integer Row, integer Column, cellControl). predicates createControls: (). clauses createControls (): - foreach I = std :: cIterate (game: m), J = std :: cIterate (game: n) do Cell = cellControl :: new (This), Cell: i: = I, Cell: j: = J, Cell: gameCtl: = This, Cell: show (), assert (cell (I, J, Cell)) end foreach. predicates setControls: (). clauses setControls (): - getSize (Width, Height), Wc = Width div game: n, Hc = Height div game: m, foreach cell (I, J, Cell) do Cell: setSize (Wc, Hc), Cell: setPosition (Wc * J, Hc * I), Cell: invalidate () end foreach. Лістинг 9.7. Створення картатого поля

У редакторі вікна gameControl додамо обробники подій ShowListener і SizeListener. Визначення предикатів наведено нижче.

clauses onShow (_Source, _Data): - createControls (), setControls (). Лістинг 9.8. Визначення предиката onShow clauses onSize (_Source): - setControls (). Лістинг 9.9. Визначення предиката onSize

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

У східних іграх камені часто ставляться на перетин ліній сітки.

У редакторі вікна cellControl додамо обробник подій PaintResponder. Визначення предиката onPaint наведено нижче. Спочатку закрашивается фон клітини, потім проводяться фрагменти ліній сітки - горизонтальний відрізок і вертикальний відрізок. Відрізки проходять через центр клітини. Правила обчислення координат кінців відрізків визначаються тим, чи лежить клітина на кордоні ігрового поля.

clauses onPaint (_Source, Rectangle, GDI): - rct (L, T, R, B) = Rectangle,% фон GDI: setPen (pen (1, ps_Solid, bgColor)), GDI: setBrush (brush (pat_Solid, bgColor) ), GDI: drawRect (Rectangle),% координати центру клітини X = (L + R) div 2, Y = (T + B) div 2,% кінці горизонтального відрізка HorPnt1 = pnt (endPntCoord (j, 0, X, L ), Y), HorPnt2 = pnt (endPntCoord (j, gameCtl: game: n - 1, X, R), Y),% кінці вертикального відрізка VertPnt1 = pnt (X, endPntCoord (i, 0, Y, T)) , VertPnt2 = pnt (X, endPntCoord (i, gameCtl: game: m - 1, Y, B)),% лінії сітки GDI: setPen (pen (1, ps_Solid, lineColor)), GDI: drawLine (HorPnt1, HorPnt2) , GDI: drawLine (VertPnt1, VertPnt2),% камінь if state <> empty then Dx = (R - L) div 8, Dy = (B - T) div 8,% розміри відступу stoneColor (state, BorderColor, BrushColor), GDI: setPen (pen (3, ps_Solid, BorderColor)), GDI: setBrush (brush (pat_Solid, BrushColor)), GDI: drawEllipse (rct (L + Dx, T + D y, R - Dx, B - Dy)) end if. predicates stoneColor: (state, color Border [out], color Brush [out]). endPntCoord: (integer J, integer J, integer X, integer L) -> integer. clauses stoneColor (darkStone, darkBorderColor, darkBrushColor): -!. stoneColor (_, lightBorderColor, lightBrushColor). endPntCoord (J, J, X, _) = X: -!. endPntCoord (_, _, _, L) = L. Лістинг 9.10. Визначення предиката onPaint

В інтерфейсі gameForm оголосимо властивість для зберігання імені гравця-користувача.

properties name: string. Лістинг 9.11. Оголошення властивості в інтерфейсі gameForm

Змінимо розділ open імплементації класу gameForm наступним чином:

open core, vpiDomains, game

Далі слід змінити визначення конструктора new / 1 і визначити оголошене властивість.

clauses new (Parent): - formWindow :: new (Parent), generatedInitialize (),% gameControl_ctl: gameFrm: = This, gameControl_ctl: game: = game :: game. facts name: string: = "гравець". Лістинг 9.12. Зміна визначення конструктора

Тепер, нарешті, ми зможемо відкрити форму. Форма gameForm відкривається при натисканні на кнопку Ok вікна settingsDialog. Відкриємо редактор вікна settingsDialog, додамо обробник події натискання на цю кнопку і визначимо його так, як показано нижче.

clauses onOkClick (_Source) = button :: defaultAction: - Name = string :: trim (name_ctl: getText ()), IsDarkStone = toBoolean (radioButton :: checked = dark_ctl: getRadioState ()), Game = game :: new (row_ctl : getInteger (), column_ctl: getInteger (), checkBox_ctl: getChecked (), IsDarkStone), game :: game: = Game, Form = gameForm :: new (getParent ()), if Name <> "" then Form: name : = Name end if, Form: show (). Лістинг 9.13. Установка параметрів гри

Після натискання на кнопку Ok закривається вікно settingsDialog і відкривається вікно gameForm.

Реалізація гри

Правила ходу гравця-комп'ютера реалізуються в класі game. Позиція представляється упорядкованим списком полів, в яких стоять камені. Поле описується парою індексів і кольором каменя.

Реалізація вибору ходу

В інтерфейсі game оголосимо предикати gameOver / 2 і move / 3. Предикат gameOver перевіряє, чи виконано умову закінчення гри. Предикат move повертає індекси поля, в яке гравець-комп'ютер ставить свій камінь.

predicates gameOver: (sq * Position, state Stone) -> positive Result determ. predicates move: (sq * Position, state CompStone, state HumanStone) -> tuple {integer Row, integer Column}. Лістинг 9.14. Оголошення предикатів в інтерфейфсе game

У декларації класу game оголосимо предикати insert / 3 і isCorrect / 3. Перший предикат додає поле, в яке зроблений хід, в поточну позицію і повертає нову позицію. Другий предикат перевіряє, чи є хід в вказане поле допустимим.

predicates insert: (tuple {integer, integer}, state Stone, sq * Position) -> sq *. predicates isCorrect: (integer Row, integer Column, sq * Position) determ. Лістинг 9.15. Оголошення предикатів в декларації класу game

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

Список полів, на яких стоять камені, впорядковується (див. Визначення предиката insert). Поле допустимо в даній позиції, якщо різниця між індексами, як по рядках, так і по стовпцях, що не перевищує двох (див. Визначення предиката isCorrect).

% Вставка поля в позицію clauses insert (tuple (I, J), Stone, Pos) = list :: sort ([sq (I, J, Stone) | Pos]). % Перевірка допустимості поля clauses isCorrect (I, J, Position): - maxdist (I, J, Position, 2). class predicates maxdist: (integer I, integer J, sq *, integer R) determ. maxdist: (tuple {integer, integer}, tuple {integer, integer} *, integer R) determ. dist: (tuple {integer, integer}, tuple {integer, integer}, integer) determ. dist: (integer, integer) -> integer. clauses maxdist (I, J, L, R): - list :: all (L, {(sq (I1, J1, _)): - dist (tuple (I, J), tuple (I1, J1), R )}). maxdist (Sq, ML, R): - list :: all (ML, {(Sq1): - dist (Sq, Sq1, R)}). dist (tuple (I1, J1), tuple (I2, J2), R): - math :: max (dist (I1, I2), dist (J1, J2)) <= R. dist (I1, I2) = math :: abs (I1 - I2). Лістинг 9.16. Оновлення позиції. Перевірка допустимості ходу

Предикат all / 2 правдивий, якщо зазначена умова виконується для кожного елемента списку. Предикат maxdist правдивий, якщо відстань від заданого поля до інших полів не перевищує R. Відстань визначається як максимальна абсолютна величина різниці між номерами рядків і стовпців.

Гра закінчена, якщо в поточній позиції є три камені однакового кольору в ряд, або на дошці стоять дев'ять каменів (див. Визначення предиката gameOver).

clauses gameOver (Position, S) = winner: -% три каменю S в ряд existsFullRow (Position, S),!. gameOver (Position, _) = draw: -% нічия 9 = list :: length (Position). class predicates existsFullRow: (sq * Position, state Stone) determ. subset: (positive, sq *, state Stone) -> sq * nondeterm. isFullRow: (sq *) determ. clauses existsFullRow (Position, S): - Row = subset (3, Position, S), isFullRow (Row),!. subset (0, _, _) = []: -!. subset (N, [sq (I, J, S) | L], S) = [sq (I, J, S) | subset (N - 1, L, S)]. subset (N, [_ | L], S) = subset (N, L, S). isFullRow ([sq (I, J, _), sq (I, J + 1, _), sq (I, J + 2, _)]): -!. isFullRow ([sq (I, J, _), sq (I + 1, J, _), sq (I + 2, J, _)]): -!. isFullRow ([sq (I, J, _), sq (I + 1, J + 1, _), sq (I + 2, J + 2, _)]): -!. isFullRow ([sq (I, J, _), sq (I + 1, J - 1, _), sq (I + 2, J - 2, _)]). Лістинг 9.17. Умова закінчення гри

Першим аргументом предиката move / 3 є поточна позиція - список полів, в яких стоять камені, другим аргументом - колір каменю комп'ютера і третім - колір каменю його супротивника. Останнім аргументом предиката move / 4 є список допустимих полів, т. Е. Полів, в які гравець-комп'ютер може зробити хід.

clauses move (Pos, S, S1) = move (Pos, S, S1, getMoves (Pos)). predicates getMoves: (sq *) -> tuple {integer, integer} *. clauses getMoves (Position) = getMoves (Position, ML): - [sq (I, J, _) | _] = Position,!, ML = [tuple (I1, J1) || I1 = std :: fromTo (math :: max (0, I-2), math :: min (I + 2, m-1)), J1 = std :: fromTo (math :: max (0, J- 2), math :: min (J + 2, n-1)), not ((I1 = I, J1 = J))]. getMoves (_) = []. predicates getMoves: (sq *, tuple {integer, integer} *) -> tuple {integer, integer} * MoveList. clauses getMoves (Position, ML) = list :: filter (ML, {(tuple (I, J)): - not (sq (I, J, _) in Position), isCorrect (I, J, Position)}) . Лістинг 9.18. Реалізація ходу гравця. Обчислення допустимих ходів

Нижче наводиться реалізація правил ходу гравця-комп'ютера.

predicates move: (sq *, state, state, tuple {integer, integer} * MoveList) -> tuple {integer, integer} Move. clauses% Останній Хід move (_, _, _, [Move]) = Move: - !. % Хід по діагоналі від первого каменя первого гравця move ([Sq], _, _, ML) = Move: - MoveList = [M || M = diagMove (Sq, ML)], Move = randomMove (MoveList),!. % Три в ряд - виграш move (Position, S, _, ML) = Move: - Move = block (ML, Position, S),!. % Блок, что не приводити до вилки або прямому програшу move (Position, S, S1, ML) = Move: - Move = block (ML, Position, S1), nextPosition (ML, Position, S, Move, Position2, ML2) , not ((nextPosition (ML2, Position2, S1, _, Position3, ML3), isFork (ML3, Position3, S1))), not (_ = block (ML2, Position2, S1)),!. % Блок, если поле визначили move (Position, _S, S1, ML) = Move: - isGamePlaceDefined (Position, ML), Move = block (ML, Position, S1),!. % Вилка move (Position, S, _S1, ML) = Move: - nextPosition (ML, Position, S, Move, Position1, ML1), isFork (ML1, Position1, S),!. % Змушуємо ставити блок, щоб поставити вилку move (Position, S, S1, ML) = Move: - nextPosition (ML, Position, S, Move, Position1, ML1),% Хід комп Move1 = block (ML1, Position1, S) ,% Хід людини not ((nextPosition (ML1, Position1, S1, Move2, Pos0, ML0), Move2 <> Move1, not (_ = block (ML0, Pos0, S)))), nextPosition (ML1, Position1, S1 , Move1, Position2, ML2), nextPosition (ML2, Position2, S, _, Position3, ML3),% хід комп isFork (ML3, Position3, S), not (_ = block (ML3, Position3, S1)),! . % Хід в центр, якщо місце визначено move (Position, _, _, ML) = Move: - Move = centerMove (Position, ML),!. % Запобігання можливої ​​вилки move (Position, S, S1, ML) = Move: - nextPosition (ML, Position, S, Move, Position1, ML1), isGamePlaceDefined (Position1, ML1), not ((nextPosition (ML1, Position1, S1 , _, Position2, ML2), not (_ = block (ML2, Position2, S)), isFork (ML2, Position2, S1))), not (_ = centerMove (Position1, ML1)),!. % Не даємо ставити вилку - НЕ ісп. move (Position, _S, S1, ML) = Move: - nextPosition (ML, Position, S1, Move, Position1, ML1), isFork (ML1, Position1, S1),!. % Випадковий хід - НЕ ісп. move (_Position, _S, _S1, ML) = randomMove (ML). Лістинг 9.19. Правила ходу гравця-комп'ютера

Нижче визначаються допоміжні предикати

predicates% вибір ходу і визначення позиції nextPosition: (tuple {integer, integer} *, sq *, state, tuple {integer, integer}, sq *) nondeterm (i, i, i, o, o) determ (i, i , i, i, o). clauses nextPosition (ML, Pos, S, Move, insert (Move, S, Pos)): - Move in ML. predicates% вибір ходу, позиції і допустимих ходів nextPosition: (tuple {integer, integer} *, sq *, state, tuple {integer, integer}, sq *, tuple {integer, integer} *) nondeterm (i, i, i , o, o, o) determ (i, i, i, i, o, o). clauses nextPosition (ML, Pos, S, Move, Pos1, getMoves (Pos1, ML)): - nextPosition (ML, Pos, S, Move, Pos1). predicates% блок block: (tuple {integer, integer} *, sq *, state) -> tuple {integer, integer} nondeterm. clauses block (ML, Position, S) = Move: - nextPosition (ML, Position, S, Move, Position1), existsFullRow (Position1, S). predicates% перевірка, поставлена ​​чи вилка isFork: (tuple {integer, integer} *, sq *, state) determ. clauses isFork (ML, Position, S): - isGamePlaceDefined (Position, ML), Move1 = block (ML, Position, S), Move2 = block (ML, Position, S), Move1 <> Move2,!. predicates% чи визначено ділянку гри isGamePlaceDefined: (sq *, tuple {integer, integer} *) determ. clauses isGamePlaceDefined (Position, ML): - 9 = list :: length (Position) + list :: length (ML). predicates% хід в центр centerMove: (sq *, tuple {integer, integer} *) -> tuple {integer, integer} determ. clauses centerMove (Position, ML) = tuple (I, J): - isGamePlaceDefined (Position, ML), tuple (I, J) in ML, maxdist (I, J, Position, 1), maxdist (tuple (I, J ), ML, 1),!. predicates% хід по діагоналі diagMove: (sq, tuple {integer, integer} *) -> tuple {integer, integer} nondeterm. clauses diagMove (sq (I, J, _), ML) = tuple (I1, J1): - tuple (I1, J1) in ML, 1 = dist (I, I1), 1 = dist (J, J1). class predicates% випадковий хід randomMove: (tuple {integer, integer} *) -> tuple {integer, integer}. clauses randomMove (ML) = list :: nth (math :: random (list :: length (ML)), ML). Лістинг 9.20. Блок, вилка, центральний і діагональний ходи

Повідомлення про ходах гравців

Повідомлення про хід гравця і про результат гри виводяться в рядку заголовка вікна gameForm. Оголосимо відповідні предикати в інтерфейсі gameForm.

predicates moveMessage: (positive Player). gameOverMessage: (positive Result, positive Player). Лістинг 9.21. Оголошення предикатів в інтерфейфсе gameForm

Нижче наведено визначення цих предикатів в імплементації класу gameForm.

clauses moveMessage (Player): - setText (string :: format ( "Ходить%", playerName (Player))). clauses gameOverMessage (winner, Player): -!, setText (string :: format ( "Переможець:%!", playerName (Player))). gameOverMessage (_Result, _Player): - setText ( "Нічия!"). predicates playerName: (positive Player) -> string Name. clauses playerName (humanPlayer) = name: -!. playerName (_) = "комп'ютер". Лістинг 9.21. Визначення в імплементації класу gameForm

Реалізація ходу гри

Ходом гри управляє клас gameContol. Оголосимо в інтерфейсі gameControl необхідні властивості і предикати. Предикат startGame обробляє початок гри. Предикат isCorrect / 2 перевіряє, чи є допустимим хід користувача. Предикат humanMove / 2 обробляє хід користувача.

properties isGameOver: boolean. predicates startGame: (). isCorrect: (cellControl) determ. humanMove: (cellControl). Лістинг 9.22. Оголошення предикатів в інтерфейфсе gameControl

Визначення властивостей і предикатів в імплементації класу gameControl наведено нижче.

facts isGameOver: boolean: = false. humanStone: state: = darkStone. compStone: state: = lightStone. currentPosition: sq *: = []. clauses startGame (): - false = game: isDarkStone, humanStone: = lightStone, compStone: = darkStone, fail. startGame (): - true = game: isComputerFirst,!, gameFrm: moveMessage (compPlayer), Move = firstMove (), showMove (Move). startGame (): - gameFrm: moveMessage (humanPlayer). clauses isCorrect (Cell): - isCorrect (Cell: i, Cell: j, currentPosition). clauses humanMove (Cell): -% обробка ходу користувача Cell: setCellState (humanStone), updatePosition (tuple (Cell: i, Cell: j), humanStone), move (compPlayer). % Перехід ходу до комп'ютера predicates% перший хід у випадкове поле firstMove: () -> tuple {integer, integer}. clauses firstMove () = tuple (F (game: m), F (game: n)): - F = {(N) = math :: random (N - 2 * D) + D: - D = if N> 4 then 2 else 1 end if}. predicates updatePosition: (tuple {integer, integer}, state Stone). clauses updatePosition (Move, Stone): - currentPosition: = insert (Move, Stone, currentPosition). predicates move: (positive Player). makeMove: (positive Player). clauses move (Player): - gameOver (currentPosition, nextPlayer (Player)),!. move (Player): - gameFrm: moveMessage (Player), makeMove (Player). makeMove (compPlayer): -!, Move = game: move (currentPosition, compStone, humanStone), showMove (Move). makeMove (_). predicates gameOver: (sq *, positive) determ. clauses gameOver (Position, Player): - Result = game: gameOver (Position, stone (Player)), isGameOver: = true, gameFrm: gameOverMessage (Result, Player). predicates stone: (positive Player) -> state. nextPlayer: (positive Player) -> positive NextPlayer. clauses stone (compPlayer) = compStone: -!. stone (_) = humanStone. nextPlayer (compPlayer) = humanPlayer: -!. nextPlayer (_) = compPlayer. predicates showMove: (tuple {integer, integer}). clauses showMove (Move): - getCell (Move): setCellState (compStone), updatePosition (Move, compStone), move (humanPlayer). % Перехід ходу до користувача predicates getCell: (tuple {integer, integer}) -> cellControl. clauses getCell (tuple (I, J)) = Cell: - cell (I, J, Cell),!. getCell (_) = _: - exception :: raise_error (). Лістинг 9.23. Визначення в імплементації класу gameControl

Користувач ходить за допомогою миші. У редакторі вікна cellControl додамо обробник подій MouseDownListener. Нижче наведено його визначення.

clauses onMouseDown (_Source, _Point, _ShiftControlAlt, _Button): - empty = state, false = gameCtl: isGameOver, gameCtl: isCorrect (This),!, gameCtl: humanMove (This). onMouseDown (_Source, _Point, _ShiftControlAlt, _Button). Лістинг 9.24. Реалізація ходу користувача

Залишається відкрити редактор форми gameForm і додати обробник подій ShowListener. Нижче наведено його визначення.

clauses onShow (_Source, _Data): - gameControl_ctl: startGame (). Лістинг 9.25. початок гри

вправи

9.1. Напишіть реалізацію гри "Чотири в ряд", використовуючи для ходу гравця-комп'ютера правила і оцінну функцію.

9.2. Напишіть реалізацію гри "П'ять в ряд", використовуючи для ходу гравця-комп'ютера правила і оцінну функцію.

9.3. Реалізуйте звичайну гру "Хрестики-нулики" 3 х 3, в якій гравець-комп'ютер грає на різних рівнях складності.

9.4. Напишіть самообучающуюся програму гри в "Хрестики-нулики".

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

Или позвоните нам по телефонам: (048) 823-25-64

Организация (обязательно) *

Адрес доставки

Объем

Как с вами связаться:

Имя

Телефон (обязательно) *

Мобильный телефон

Ваш E-Mail

Дополнительная информация: