Comments 12
Что-то мне кажется с декларативностью у Вас не очень получилось…
Да, описание ходов, само по себе, довольно императивно, но здесь я отталкивался от существующих аналогов. И в ZoG и в Ludi и в Axiom оно примерно такое же, но только без any и инвариантов. Чего то более декларативного, в этой области, я не видел. Если что-то придумаете или найдёте, сообщите мне. Меня этот вопрос очень интересует.
Было бы неплохо, если бы сначала Вы где-нибудь объяснили начальный код («Очень простые шашки»), а то не всё понятно. :)
как-то так:
(pre ; Предварительные действия (для любого из определённых (двух) ходов
(check is-friend?) ; На текущей клетке дружественная фигура?
(take) ; Взять её в руку
(log position) ; Отметить начальную позицию (для нотации хода)
(let captured 0) ; Определяем переменную - счётчик взятых фигур
)
(post ; Завершающие действия для любого из ходов
(check (<= max-captured captured)) ; Взято фигур не меньше глобального счётчика?
(set! max-captured captured) ; Обновить глобальный счётчик
(log " - " position) ; Отметить конечную позицию (для нотации хода)
(drop) ; Поместить фигуру на доску
)
(move ; Тихий ход
(check (any nw ne)) ; Двигаемся на северо-запад или северо-восток (если возможно)
(check is-empty?) ; Позиция пуста?
)
(move ; Бой фигур
(while true ; Повторять в цикле
(let dir (any nw ne sw se)) ; Выбираем одно из 4 направлений
(check dir) ; Двигаемся в выбранном направлении (если это возможно)
(check is-enemy?) ; Вражеская фигура?
(capture) ; Берём её (можно ещё и отметить промежуточную позицию командой log)
(inc! captured) ; Увеличиваем счётчик взятых фигур
(check dir) ; Продолжаем двигаться в том же направлении (если это возможно)
(check is-empty?) ; Пустая клетка?
(end-move) ; В этом месте ход может быть завершён
)
)
Чтобы перейти к декларативному стилю, надо, в частности, while, скажем, менять на пару «состояние — действие». Например, я бы попробовал как-то так:
1. задан список текущих позиций шашек А.
2. если нет активной шашки, и есть шашки, у которых есть доступные ходы -> выбрать (any) активную шашку (из тех, что с доступными ходами)
3. если нет активной шашки, и нет шашек, у которых есть доступные ходы -> конец игры
4. если есть активная шашка и у неё есть варианты ходов-> сделать (any) возможный ход
5. если есть активная шашка без вариантов хода -> снять с неё метку активности.
Этот список прогоняется до тех пока не сработает пункт 3.
Это навскидку :)
1. задан список текущих позиций шашек А.
2. если нет активной шашки, и есть шашки, у которых есть доступные ходы -> выбрать (any) активную шашку (из тех, что с доступными ходами)
3. если нет активной шашки, и нет шашек, у которых есть доступные ходы -> конец игры
4. если есть активная шашка и у неё есть варианты ходов-> сделать (any) возможный ход
5. если есть активная шашка без вариантов хода -> снять с неё метку активности.
Этот список прогоняется до тех пока не сработает пункт 3.
Это навскидку :)
while здесь не перебирает шашки на доске. Это цикл последовательных шагов в цепочечном взятии. Надеюсь, комментарии выше немного прояснили этот момент. Это моя промашка. Я настолько часто писал ZRF код, что посчитал всё это самоочевидным.
Фишка в том, что в при декларативном подходе не должно быть циклов. Должны быть рекурсии.
Моя цель — не декларативный подход как таковой, а простой и удобный DSL для описания настольных игр. Настолько простой, чтобы им мог пользоваться человек, слабо знакомый с программированием. Рекурсии, замыкания и анонимные функции в эту концепцию не очень укладываются (ну или это я не знаю как их туда уложить).
Вы заметили, что никто не комментирует, кроме меня?
Возможно, это потому, что сквозь Ваш текст вообще крайне сложно прорваться (во всяком случае, для меня — точно ;))…
Объясните, пожалуйста, почему в этом фрагменте:
написано:
а не, скажем:
Возможно, это потому, что сквозь Ваш текст вообще крайне сложно прорваться (во всяком случае, для меня — точно ;))…
Объясните, пожалуйста, почему в этом фрагменте:
(game
(name chess-game)
(loss
(exists?
(any King)
(if is-friend?
(check is-attacked?)
)
(check no-moves?)
)
)
)
написано:
(if is-friend?
(check is-attacked?)
)
(check no-moves?)
а не, скажем:
(check is-friend?)
(check is-attacked?)
(check no-moves?)
Это очень правильный вопрос. Дело в том, что предикат is-attacked? крайне дорогой в вычислительном отношении. По хорошему, чтобы проверить атакуется ли фигура на поле, необходимо выполнить просмотр ещё на один ход вперёд и найти ходы, атакующие фигуру на поле. Именно фигуру, а не пустое поле, поскольку в таких играх, как Ультима, тип атаки может зависеть от типа атакуемой фигуры (Хамелеон бьёт фигуры их атаками). В общем, хотелось ограничить количество обращений к столь дорогому предикату.
Пожалуй, в отношении традиционных Шахмат, этот код не вполне верен. Он будет фиксировать проигрыш и в случае пата (есть варианты шахмат, в которых пат считается поражением). Для Шахмат более правильно будет:
Но и это не совсем правильно, поскольку не рассматриваются игры, в которых может быть несколько дружественных королей (например "Шахматы Тамерлана"). В таких играх, мат ставить необходимо последнему оставшемуся королю (остальных нужно просто есть). И для них придётся сочинять гораздо более сложную проверку. В общем, это очень правильный и интересный вопрос, но это, совершенно точно, не вопрос текущей итерации, до него я ещё нескоро доберусь.
Что касается сложности текста — ничего не могу поделать. Текст сложен. Тема сложная. Я старался изложить всё как можно проще, но не мне судить, насколько хорошо это удалось.
Пожалуй, в отношении традиционных Шахмат, этот код не вполне верен. Он будет фиксировать проигрыш и в случае пата (есть варианты шахмат, в которых пат считается поражением). Для Шахмат более правильно будет:
(loss
(check no-moves?)
(check (exists?
(any King)
(if is-friend?
(check is-attacked?)
)
))
)
Но и это не совсем правильно, поскольку не рассматриваются игры, в которых может быть несколько дружественных королей (например "Шахматы Тамерлана"). В таких играх, мат ставить необходимо последнему оставшемуся королю (остальных нужно просто есть). И для них придётся сочинять гораздо более сложную проверку. В общем, это очень правильный и интересный вопрос, но это, совершенно точно, не вопрос текущей итерации, до него я ещё нескоро доберусь.
Что касается сложности текста — ничего не могу поделать. Текст сложен. Тема сложная. Я старался изложить всё как можно проще, но не мне судить, насколько хорошо это удалось.
Кстати, по поводу декларативности. Если будет время, не поленитесь прочитать этот документ. Собственно, весь текст читать не обязательно, в конце есть примеры кода. Так вот, в этой статье, предлагается гораздо более декларативный подход чем то, до чего додумался я. К сожалению, он недостаточно универсален и на игры накладываются серьёзные ограничения. Если бы удалось его как-то улучшить, решение было бы весьма интересно (просто я никак не могу придумать, как это сделать).
Sign up to leave a comment.
Dagaz: На полпути