Приручаем ZoG (Часть 4: Осторожно — мины!)

в 5:09, , рубрики: dsl, game development, Zillions of Games 2, ZRF, декларативное программирование, игры, Программирование, шахматы, метки: , , , , ,

Приручаем ZoG (Часть 4: Осторожно — мины!)Сегодня я хочу продолжить рассказ о возможностях языка описания игр ZRF, используемого Zillions of Games. В предыдущих статьях цикла я показал как описываются ходы фигур, но есть еще одна важная разновидность хода, оставшаяся не рассмотренной. Помимо перемещения фигур по доске (возможно со взятием фигур противника), игрок (если ему это разрешено), может добавлять новые фигуры на поле. Эта разновидность хода называется сбросом (drops).
Кроме того, в сегодняшней статье, я расскажу о том, как в ZoG осуществляется генерация случайных ходов. Этот функционал необходим, например, при реализации игр, использующих броски игровых костей, для выполнения ходов, таких как Ludo или Chaturanga.

В качестве примера, я предлагаю, взяв за основу классические Шахматы, реализовать игру по мотивам одной из миссий сюжетной кампании Battle vs Chess. Большинство миссий в кампании играются по измененным правилам. Миссии различаются по сложности, в некоторых, для победы, достаточно провести одну из пешек в ферзи, в других — поставить мат за ограниченное число ходов. Я предлагаю рассмотреть четвертую миссию кампании Хаоса под названием «Точка невозврата».

В этой миссии часть центральных полей доски «заминирована». Фигура, остановившаяся на таком поле, исчезает (вместе с миной). При этом, «мины» видимы только игроку, играющему за Черных. Компьютер, играющий за Белых, «думает», что играет по обычным правилам, что позволяет заманивать его фигуры на «заминированные» позиции. Надо сказать, что подобный подход используется в большинстве из миссий кампании. Один из игроков в них обладает неполной информацией. В некоторых случаях (как например в рассматриваемой нами миссии) это является своеобразной «форой», позволяющей даже не очень сильному шахматисту выиграть у довольно сильного шахматного «движка» Fritz, используемого Battle vs Chess.

Разумеется, в описанном виде, реализовать игру в ZoG не удастся. В силу своей универсальности, игровое ядро ZoG будет учитывать наличие мин при выполнении ходов. Ход на «заминированную» клетку очевидно не выгоден, поскольку попросту приводит к потере фигуры. Попробуем изменить правила таким образом, чтобы сделать подрыв «мин» выгодным, даже с учетом потери своей фигуры.

Пусть при взрыве теряется не только фигура, вставшая на заминированную клетку, но и все фигуры, оказавшиеся рядом с ней. Также, чтобы сделать игру более комбинационной, будем взрывать мины, оказавшиеся рядом с исходной, убирая с поля фигуры, оказавшиеся и вблизи них тоже. Далее цепочку взрывов распространять не будем, поскольку это связано с некоторыми техническими сложностями. Кроме того, возможный подрыв цепочкой «мин» большей части фигур, расположенных на доске, привнесет в игру слишком большой элемент случайности и может сделать её не интересной.

Начнем разработку, взяв за основу описание традиционных Шахмат (Chess.zrf), входящее в комплект поставки ZoG. Из нее мы возьмем описание доски и ходов шахматных фигур. Подходящий графический ресурс «бомбы» можно взять, например, из игры Bombalot.

Начнем с описания взрыва бомбы

(define bomb-capture
  (if (not (piece? Bomb $1))
     (capture $1)
  )
)

(define check-bomb-direction
  mark
  $1
  (if (piece? Bomb)
     (bomb-capture n) (bomb-capture nw)
     (bomb-capture s) (bomb-capture sw)
     (bomb-capture e) (bomb-capture ne)
     (bomb-capture w) (bomb-capture se)
  )
  capture
  back
)

(define check-bomb
  (if (piece? Bomb)
     (check-bomb-direction n) (check-bomb-direction nw)
     (check-bomb-direction s) (check-bomb-direction sw)
     (check-bomb-direction e) (check-bomb-direction ne)
     (check-bomb-direction w) (check-bomb-direction se)
     capture
  )
)

Здесь все нам знакомо. Если фигура встала на бомбу, взрываем все вокруг. Если вблизи оказалась другая бомба, взрываем и ее тоже (ее соседок не трогаем). Вот как это используется:

(define leap1 
  ($1 
  (verify not-friend?) 
+ (check-bomb)
  add
  )
 )

Просто добавляем наш макрос перед завершением каждого хода. Теперь, запретим королям завершать игру суицидом:

+(define check_safe
+ (verify (not (piece? Bomb) ) )
+)

(define king-shift   
  ($1    
+ (check_safe) 
  (verify not-friend?) 
  (set-attribute never-moved? false) 
  add
  ) 
)

Здесь макрос check-bomb можно не применять, поскольку мы специально проверяем, что не встаем на бомбу.

Расставим бомбы

+(define drop-bomb
+ ( (verify (and empty? (empty? n) (empty? s) (empty? w) (empty? e) ) )
+   add
+ )
+)

(game
   (title "Chess")
   ...
-  (players White Black)
+  (players White Black ?Init)
-  (turn-order White Black)
+  (turn-order ?Init ?Init White Black)
   (board (Board-Definitions))
   (board-setup
+     (?Init
+        (Bomb off 8)
+     )
      (White
         (Pawn a2 b2 c2 d2 e2 f2 g2 h2)
         (Knight b1 g1)
         (Bishop c1 f1)
         (Rook a1 h1)
         (Queen d1)
         (King e1)
      )
      (Black
         (Pawn a7 b7 c7 d7 e7 f7 g7 h7)
         (Knight b8 g8)
         (Bishop c8 f8)
         (Rook a8 h8)
         (Queen d8)
         (King e8)
      )
   )

+  (piece
+     (name Bomb)
+     (image ?Init "imagesBombalotBlackBomb.bmp" "imagesBombalotBlackBomb.bmp")
+     (drops
+       (drop-bomb)
+     )
+  )
   ...
)

Можно заметить, что мы добавили третьего игрока, по имени ?Init. Вопросительный знак в начале имени игрока означает, что он будет делать случайные ходы (в соответствии с тем что ему разрешено). Кроме того, этот игрок не будет отображаться в списке игроков (это означает, что сыграть за него будет невозможно). Новый игрок делает по два хода каждый раз перед ходом Белых. Какие ходы он может делать?

В board-setup ему выданы 8 бомб, которые он может выставлять на поле. Хочу обратить внимание на ключевое слово off в этом описании. Именно таким образом должны описываться фигуры, доступные для сброса на поле. Также описываются фигуры в играх начинающихся с пустой доски, например в реализации Крестиков-ноликов.

В описании фигуры Bomb разрешена всего одна разновидность хода — сброс фигуры на пустое поле доски, при условии того, что соседние поля по вертикали и горизонтали также пусты. Бомбы могу соседствовать с другими фигурами (в том числе бомбами) по диагонали, что, на мой взгляд, делает игру интереснее.

Примечание

Можно заметить, что графические ресурсы доски и всех фигур описываются дважды. Такой подход позволяет использовать несколько вариантов оформления, переключать которые можно в процессе игры. Поскольку наша бомба во всех вариантах будет выглядеть одинаково, просто указываем дважды путь к одному и тому же файлу.

Осталось внести пару почти косметических изменений. Добавим в описание игры включение следующих опций:

(option "pass turn" forced)
(option "animate captures" false)

Первая из них означает, что игрок (?Init) может пропускать ход, при условии того, что он не может его сделать в соответствии с правилами (если не включить эту опцию, игра может завершиться, если ?Init не найдет место для сброса бомбы). Вторая отключает анимацию взятия фигур (она выглядела слишком неестественно). Ознакомиться со списком всех доступных опций можно в документации к Zillions of Games.

Игра готова. Можно посмотреть, как все это выглядит:


В целом, можно сказать, что ZoG играет по новым правилам вполне адекватно, но иногда его ходы ставят меня в тупик. Например, он подрывает своих ферзей на одиноко стоящих бомбах. Объяснить это я не могу. Видимо ядро ZoG считает бомбу настолько опасной фигурой, что её «размен» на ферзя становится выгодным.

Я обнаружил и еще один интересный эффект, который можно пронаблюдать на следующем видео (белый король перенесен на третью горизонталь вручную):


По подсветке полей при ходе Короля заметно, что наличие бомб на соседних полях (напомню, что на само поле с бомбой Король ходить не может) не считается угрозой для Короля, при условии того, что их не могут «подорвать» вражеские фигуры. Как только на бомбы нападают, Король обязан от них отойти также, как если бы он уходил из под шаха. В общем то это вполне логичное поведение, если не рассматривать небольшой нюанс:

Приручаем ZoG (Часть 4: Осторожно — мины!)

Эта позиция считается матовой. Король не может съесть ферзя, так как не может подойти к бомбам, поскольку на них нападает ферзь. К сожалению, такое поведение исправить нельзя, поскольку требование ухода королем из под шаха «прибито гвоздями» к условию проигрыша:

(loss-condition (White Black) (checkmated King) )

Если разрешить есть королей и заменить условие проигрыша на потерю Короля:

(loss-condition (White Black) (pieces-remaining 0 King) )

… то ферзь будет благополучно съеден. Но я заметил, что в этом режиме, ZoG играет в эндшпиле существенно хуже. В частности, я так и не смог дождаться классического матом ладьей и королем одинокого короля. При использовании условия checkmated, мат ставится очень быстро.

Автор: GlukKazan

Источник

Поделиться

* - обязательные к заполнению поля