Хорошо, я не буду предоставлять вам полный код, но лучше с высокоуровневым дизайном в отношении функционального программирования и, в частности, циклического выполнения / рекурсии в Clojure.
Начнем с очень важной концепции в функциональном программировании - неизменность . Функциональные программы стараются избегать любых изменений состояния системы. Вместо того, чтобы изменять состояние, программы должны создавать новое состояние. Например, если у вас есть вектор v1 = [1, 2, 3, 4]
и вы хотите вставить 5 в его конец, вы не уничтожаете v1
, а скорее производите v2 = [1, 2, 3, 4, 5]
(v1
остается в памяти без изменений). См. этот вопрос для более подробной информации об неизменности в функциональном программировании.
Имея это, лучше всего создать специальную переменную state
, которая будет содержать общее состояние игры.
Следующее, что нужно рассмотреть, это looping . Опять же, в функциональном программировании концепция цикла почти заменяется на рекурсию . Циклы и рекурсия очень часто похожи - оба позволяют повторять некоторый код много раз, прежде чем он завершается. В большинстве императивных языков программирования (например, Python, Java) рекурсия приводит к росту стека, но в функциональных языках очень популярна концепция хвостовой рекурсии . Если вы не знакомы с этой концепцией, я настоятельно рекомендую вам изучить ее. Пока я только говорю, что хвостовая рекурсия может происходить только в хвостовой позиции (последняя инструкция в потоке управления), и она не приводит к росту стека и, таким образом, может использоваться как конструкция цикла.
В Clojure хвостовая рекурсия организована с ключевым словом recur
:
(defn play [state ...]
...
(recur new-state ...))
Здесь мы определяем функцию play
с параметром state
и рекурсивно вызываем ее с ключевым словом recur
в последней строке (мы можем также назвать ее как (play new-state ...)
, но в этом случае JVM не будет оптимизировать код быть хвостовой рекурсивной). new-state
определяется где-то в теле функции и обозначает именно то, что это означает - новое состояние игры.
Наконец, вы хотите сделать свою игру многопользовательской, то есть менять текущего игрока после каждой итерации. С помощью цикла / рекурсии этого легко добиться, просто поменяв местами игроков:
(defn play [state current-player next-player]
;; do play with current-player and compute new-state
(recur new-state next-player current-player))
Обратите внимание, что при повторном вызове игроки меняются местами, и, таким образом, следующий игрок становится текущим игроком, и наоборот при новом вызове play
.
Имея это, вы сможете перевести ваш код в новую версию.