2010-04-24 89 views
2

我想寫一個蜘蛛紙牌選手作爲學習Clojure的練習。我想弄清楚如何處理這些卡片。Clojure交易卡

我已經創建(在stackoverflow的幫助下),從兩個標準套牌中洗牌104張牌。每個卡被表示爲

(defstruct card :rank :suit :face-up) 

用於蜘蛛,這個畫面將被表示爲如下:

(defstruct tableau :stacks :complete) 

其中:堆疊是卡矢量,其中4含有5張牌面向下和1的矢量牌面朝上,其中6個牌面朝下,1個牌面朝上,總共54張牌,並且:完成是一個(最初)完成的ace-king組的向量(例如,王心,用於印刷目的)。所述的undealt甲板的其餘部分應保存在一個裁判

(def deck (ref seq)) 

在遊戲過程中,一個畫面可以包含,例如:

(struct-map tableau 
    :stacks [[AH 2C KS ...] 
      [6D QH JS ...] 
      ... 
      ] 
    :complete [KC KS]) 

其中「AH」是含有{卡:排名: ace:suit:hearts:face-up false}等。

我該如何編寫一個函數來處理堆棧,然後將餘數保存在ref中?

回答

0

你可以寫一個函數採取的每size項目chunks向量從給定的順序,另外一個從前面丟棄這些大塊:

;; 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)) 

那麼也許增加一個功能,做後兩者(仿照split-atsplit-with):

(defn split-chunks [chunks size s] 
    [(take-chunks chunks size s) 
    (drop-chunks 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)))]))) 

返回值是一個雙張向量,其第一個元素是甲板的剩餘部分,並且其第二元素是初始堆棧的向量。

然後用這個在交易採取參考到:

(dosync (let [[new-deck stacks] (deal-initial-stacks @deck-ref)] 
      (ref-set deck-ref new-deck) 
      stacks)) 

更重要的是,保持遊戲的整體狀態在一個單一的參考或Atom和ref-set切換到alter/swap!(我「將使用的Ref對於這個例子,省略dosync並切換到alterswap!使用的原子代替):

;; 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)))) 

聲明:這一切都不已經接收儘管我認爲它應該可以正常工作,但我可能會錯過任何愚蠢的拼寫錯誤)。不過,這是你的練習,所以我認爲把測試/拋光部分留給你很好。 :-)

1

這是我在研究上面的答案後提出的解決方案。請注意,我仍然在對其進行改進,並歡迎有關改進的建議,特別是使用更多地道的Clojure。還要注意,這些函數是在幾個單獨的文件中定義的,並不一定以所示順序出現(如果這有所影響)。

(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)))))