在Clojure交易卡片
我想写一个蜘蛛纸牌玩家作为学习Clojure的练习。我在想怎么发牌 在stackoverflow的帮助下,我从两个标准牌组中创建了一个由104张牌组成的洗牌序列。每张卡片都表示为一个在Clojure交易卡片,clojure,refs,Clojure,Refs,我想写一个蜘蛛纸牌玩家作为学习Clojure的练习。我在想怎么发牌 在stackoverflow的帮助下,我从两个标准牌组中创建了一个由104张牌组成的洗牌序列。每张卡片都表示为一个 (defstruct card :rank :suit :face-up) Spider的表格将表示为: (defstruct tableau :stacks :complete) 其中:stacks是一个卡片向量向量,其中4个包含5张正面朝下和1张正面朝上的卡片,其中6个包含4张正面朝下和1张正面朝上的卡片,
(defstruct card :rank :suit :face-up)
Spider的表格将表示为:
(defstruct tableau :stacks :complete)
其中:stacks是一个卡片向量向量,其中4个包含5张正面朝下和1张正面朝上的卡片,其中6个包含4张正面朝下和1张正面朝上的卡片,总共54张卡片;而:complete是一个(最初)已完成的ace king集合的空向量(例如,出于打印目的,表示为king hearts)。取消ALT组的其余部分应保存在ref中
(def deck (ref seq))
在游戏期间,画面可能包含,例如:
(struct-map tableau
:stacks [[AH 2C KS ...]
[6D QH JS ...]
...
]
:complete [KC KS])
其中“AH”是一张包含{:等级:王牌:套装:红心:面朝上假}等的卡片
如何编写一个函数来处理堆栈,然后将剩余部分保存在ref中?您可以编写一个函数,从给定序列中获取
块
大小的向量项,另一个函数从前面删除这些块:
;; note the built-in assumption that s contains enough items;
;; if it doesn't, one chunk less then requested will be produced
(defn take-chunks [chunks size s]
(map vec (partition size (take (* chunks size) s))))
;; as above, no effort is made to handle short sequences in some special way;
;; for a short input sequence, an empty output sequence will be returned
(defn drop-chunks [chunks size s]
(drop (* chunks size) s))
然后可能会添加一个函数来同时执行这两项操作(在和拆分后建模):
假设每个卡最初都是{:face up false}
,您可以使用以下函数打开堆栈上的最后一张卡:
(defn turn-last-card [stack]
(update-in stack [(dec (count stack)) :face-up] not))
然后是一个函数,用于处理给定数据组中的初始堆栈/数据块:
(defn deal-initial-stacks [deck]
(dosync
(let [[short-stacks remaining] (split-chunks 6 5 deck)
[long-stacks remaining] (split-chunks 4 6 remaining)]
[remaining
(vec (map turn-last-card
(concat short-stacks long-stacks)))])))
返回值是一个doubleton向量,其第一个元素是甲板的剩余部分,第二个元素是初始堆栈的向量
然后在事务中使用此选项来考虑Ref:
(dosync (let [[new-deck stacks] (deal-initial-stacks @deck-ref)]
(ref-set deck-ref new-deck)
stacks))
更好的是,将游戏的整个状态保持在单个Ref或Atom中,并从Ref set
切换到alter
/交换
(我将在本例中使用Ref,省略dosync
,并将alter
切换到swap!
以使用atom):
免责声明:所有这些都没有受到丝毫的测试关注(尽管我认为它应该工作得很好,排除了我可能错过的任何愚蠢的打字错误)。不过,这是你的练习,所以我认为将测试/抛光部分留给你是可以的。:-) 这是我在研究了上述答案后提出的一个解决方案。请注意,我仍在改进它,并欢迎提出改进建议,特别是使用更惯用的Clojure。还要注意的是,这些函数是在几个单独的文件中定义的,并且不一定按显示的顺序出现(如果这样做有区别的话)
(def套装[:梅花:钻石:红桃:黑桃])
(d)诉讼名称
{:梅花“C”:钻石“D”
:红桃“H”:黑桃“S”})
(def等级
(减少为(复制2
[:ace:2:3:4:5:6:7:8:9:10:jack:queen:king]))
(d)职级名称
{:A:两个“2”
:三“3”:四“4”
:五“5”:六“6”
:七“7”:八“8”
:九“9”:十“T”
:杰克“J”:女王“Q”
:king“K”})
(defn)卡名
[卡片展示正面朝下]
(让
[等级(等级名称(:等级卡))
套装(套装名称(:套装卡))
正面朝下(:正面朝下卡片)]
(如果
面朝下
(如果
面朝下
(.toLowerCase(str等级诉讼))
“XX”)
(str等级诉讼)
(见下文)
“退回4件诉讼:
如果西装数量=1::梅花:梅花:梅花
如果西装数量=2::梅花:钻石:梅花:钻石
如果西装数量=4::梅花:钻石:红桃:黑桃。“
[诉讼数目]
(取4(周期(取套数)))
(防御卡:等级:套装:正面朝下)
(非缓冲甲板
“创建一个非缓冲牌组,其中包含指定套牌数量的所有牌。”
[诉讼数目]
(用于
[等级-等级-诉讼(诉讼顺序-诉讼数量)]
(结构卡等级(正确)))
(defn洗牌组)
“创建一个洗牌牌组,其中包含指定套牌数量的所有牌。”
[诉讼数目]
(洗牌(未洗牌的套牌数量)))
(defn处理一个堆栈
“处理n张牌的堆栈,并返回包含新堆栈和牌组其余部分的向量。”
[n甲板]
(环路
[堆栈[]
当前n
休息甲板]
(如果(
(dosync (let [[new-deck stacks] (deal-initial-stacks @deck-ref)]
(ref-set deck-ref new-deck)
stacks))
;; the empty vector is for the stacks
(def game-state-ref (ref [(get-initial-deck) []]))
;; deal-initial-stacks only takes a deck as an argument,
;; but the fn passed to alter will receive a vector of [deck stacks];
;; the (% 0) bit extracts the first item of the vector,
;; that is, the deck; you could instead change the arguments
;; vector of deal-initial-stacks to [[deck _]] and pass the
;; modified deal-initial-stacks to alter without wrapping in a #(...)
(dosync (alter game-state-ref #(deal-initial-stacks (% 0))))
(def suits [:clubs :diamonds :hearts :spades])
(def suit-names
{:clubs "C" :diamonds "D"
:hearts "H" :spades "S"})
(def ranks
(reduce into (replicate 2
[:ace :two :three :four :five :six :seven :eight :nine :ten :jack :queen :king])))
(def rank-names
{:ace "A" :two "2"
:three "3" :four "4"
:five "5" :six "6"
:seven "7" :eight "8"
:nine "9" :ten "T"
:jack "J" :queen "Q"
:king "K"})
(defn card-name
[card show-face-down]
(let
[rank (rank-names (:rank card))
suit (suit-names (:suit card))
face-down (:face-down card)]
(if
face-down
(if
show-face-down
(.toLowerCase (str rank suit))
"XX")
(str rank suit))))
(defn suit-seq
"Return 4 suits:
if number-of-suits == 1: :clubs :clubs :clubs :clubs
if number-of-suits == 2: :clubs :diamonds :clubs :diamonds
if number-of-suits == 4: :clubs :diamonds :hearts :spades."
[number-of-suits]
(take 4 (cycle (take number-of-suits suits))))
(defstruct card :rank :suit :face-down)
(defn unshuffled-deck
"Create an unshuffled deck containing all cards from the number of suits specified."
[number-of-suits]
(for
[rank ranks suit (suit-seq number-of-suits)]
(struct card rank suit true)))
(defn shuffled-deck
"Create a shuffled deck containing all cards from the number of suits specified."
[number-of-suits]
(shuffle (unshuffled-deck number-of-suits)))
(defn deal-one-stack
"Deals a stack of n cards and returns a vector containing the new stack and the rest of the deck."
[n deck]
(loop
[stack []
current n
rest-deck deck]
(if (<= current 0)
(vector
(vec
(reverse
(conj
(rest stack)
(let
[{rank :rank suit :suit} (first stack)]
(struct card rank suit false)))))
rest-deck)
(recur (conj stack (first rest-deck)) (dec current) (rest rest-deck)))))
(def current-deck (ref (shuffled-deck 4)))
(defn deal-initial-tableau
"Deals the initial tableau and returns it. Sets the @deck to the remainder of the deck after dealing."
[]
(dosync
(loop
[stacks []
current 10
rest-deck @current-deck]
(if (<= current 0)
(let [t (struct tableau (reverse stacks) [])
r rest-deck]
(ref-set current-deck r)
t)
(let
[n (if (<= current 4) 6 5)
[s r] (deal-one-stack n rest-deck)]
(recur (vec (conj stacks s)) (dec current) r))))))
(defstruct tableau :stacks :complete)
(defn pretty-print-tableau
[tableau show-face-down]
(let
[{stacks :stacks complete :complete} tableau]
(apply str
(for
[row (range 0 6)]
(str
(apply str
(for
[stack stacks]
(let
[card (nth stack row nil)]
(str
(if
(nil? card)
" "
(card-name card show-face-down)) " "))))
\newline)))))