2010-04-24 5 views
2

J'essaie d'écrire un lecteur Spider Solitaire comme un exercice dans l'apprentissage de Clojure. J'essaie de comprendre comment traiter les cartes.cartes de distribution dans Clojure

J'ai créé (avec l'aide de stackoverflow) une séquence mélangée de 104 cartes à partir de deux platines standard. Chaque carte est représentée comme une

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

Le tableau Spider sera représentée comme suit:

(defstruct tableau :stacks :complete) 

où: piles est un vecteur de vecteurs de cartes, dont 4 contenant 5 cartes face vers le bas et 1 face cachée, et 6 d'entre eux contiennent 4 cartes face cachée et 1 carte face visible, pour un total de 54 cartes, et: complete est un vecteur (initialement) vide d'ensembles complets d'as-roi (représentés par exemple, king-hearts, à des fins d'impression). Le reste de la plate-forme undealt doit être enregistré dans un ref

(def deck (ref seq)) 

Au cours du jeu, un tableau peut contenir, par exemple:

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

où « AH » est une carte contenant {: rang: ace: costume: coeurs: face visible false}, etc.

Comment puis-je écrire une fonction pour traiter les piles, puis enregistrer le reste dans le ref?

Répondre

0

Vous pouvez écrire une fonction pour prendre chunks vecteurs de size éléments chacun d'une séquence donnée et l'autre pour laisser tomber ces morceaux de l'avant:

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

ajouter ensuite peut-être une fonction de faire les deux (sur le modèle split-at et split-with):

(defn split-chunks [chunks size s] 
    [(take-chunks chunks size s) 
    (drop-chunks chunks size s)]) 

en supposant que chaque carte est d'abord {:face-up false}, vous pouvez utiliser la fonction suivante pour activer la dernière carte sur une pile:

(defn turn-last-card [stack] 
    (update-in stack [(dec (count stack)) :face-up] not)) 

Ensuite, une fonction pour traiter les piles initiales/morceaux du pont suivant:

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

La valeur de retour est un vecteur doubleton dont le premier élément est le reste de la plate-forme et dont la seconde element est un vecteur des piles initiales.

Ensuite, utilisez ceci dans une transaction pour prendre les Ref compte:

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

Mieux encore, garder tout l'état du jeu dans un seul Ref ou Atom et commutateur ref-set-alter/swap! (I « ll utiliser Aref pour cet exemple, omettre le dosync et passer alter-swap! d'utiliser un atome à la place):

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

Avertissement: Rien de tout cela a reçu la moindre quantité d'attention de test (bien que je pense que cela devrait fonctionner correctement, modulo toute fautes de frappe stupide que je pourrais avoir manqué).C'est votre exercice, cependant, donc je pense que laisser la partie test/polissage est bien. :-)

1

Voici une solution que j'ai trouvée après avoir étudié la réponse ci-dessus. Notez que je suis encore en train de l'affiner et je suis heureux de recevoir des suggestions d'amélioration, en particulier l'utilisation de Clojure plus idiomatique. Notez également que ces fonctions sont définies dans plusieurs fichiers distincts et n'apparaissent pas nécessairement dans l'ordre indiqué (si cela fait une différence).

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