Clojure 中纸牌游戏的状态

Posted

技术标签:

【中文标题】Clojure 中纸牌游戏的状态【英文标题】:State of a card game in Clojure 【发布时间】:2015-04-12 05:29:24 【问题描述】:

我对clojure 比较陌生,但掌握了主要的功能概念。我真正苦苦挣扎的是状态。

我正在编写一个简单的纸牌游戏应用程序,它已经到了我正在编写游戏引擎的地步。所以状态这个概念很快就给了我一记耳光。

我需要跟踪很多与游戏相关的事情:

甲板状态 点的状态 谁是经销商 ...等

我已经阅读了如何在 clojure、Refs、Atoms、Atoms 和线程本地变量中使用状态。但它们似乎都不适合我正在做的事情。

所以我的实际问题是:我使用什么 clojure 构造来维护 clojure 中单线程游戏引擎的状态?

【问题讨论】:

【参考方案1】:

函数式编程的一般原则是,您可以通过将全局状态替换为每个访问状态的函数添加的输入 arg 和输出 arg 来使代码更通用。

在这种情况下,这意味着为每一轮游戏提供一个 game 参数,并让每一轮游戏返回一个新的 game 以供后续使用。这有几个优点。没有突变,因此无需管理和协调突变。您的测试可以包括运行一轮游戏功能。如果您需要 AI,它可以在广度优先的基础上轻松运行游戏的许多分支回合,以测试可能的结果,而不会干扰实际游戏的状态。

大概的样子:

(def make-game
  [players]
  (let [[draw & deck] (shuffle cards)]
  :draw draw
   :deck deck
   :points (zipmap players (repeat 0))
   :dealer (first players))

(defn run-round
  [game]
  (let [points (update-points (:draw game) (:points game))
        [draw & deck] (:deck game)]
    (assoc game :deck deck :draw draw :points points)))

(defn winner?
  [game]
  (some #(> (val %) 42) (:points game)))

(defn -main
  (let [gameplay (take-while #(not (winner? %))
                             (iterate run-round (make-game)))]
    (:points (run-round (last gameplay)))))

这当然是一个非常微不足道的游戏,每个玩家的分数都是从抽到的牌中得出的。下一张牌将在每回合从洗好的牌组中抽出,直到我们的总分表示获胜。

【讨论】:

【参考方案2】:

听起来这个游戏的状态有几个组成部分,牌组、点数、庄家等,因此您可以选择将所有这些内容放入一张地图中,并将其存储在一种不协调的可变数据类型中(@ 987654321@, agent, var) 或单独存储它们并使用协调可变数据类型ref。既然你说游戏是单线程的,那么走不协调的路线可能会稍微容易一些,省去输入几次dosync这个词,尽管这两种方式的工作量差别不大。

(def state-of-game (atom :deck ...
                          :points
                          :dealer))   

【讨论】:

以上是关于Clojure 中纸牌游戏的状态的主要内容,如果未能解决你的问题,请参考以下文章

如何在 clojure 中映射很少使用的状态?

如何从 Clojure 调用 C++ 程序以使程序保持打开状态?

避免 Clojure 应用程序的 DAO 层中的全局状态

如何具体化 Prolog 的回溯状态以执行与 Clojure 中的“lazy seq”相同的任务?

庄晓丹:Java程序员眼中的Clojure

如何Clojure.Spec引用类型(如原子)?