дилинговые карты в Clojure - PullRequest
       31

дилинговые карты в Clojure

3 голосов
/ 24 апреля 2010

Я пытаюсь написать игрока в пасьянс «Паук» в качестве упражнения для изучения Clojure. Я пытаюсь понять, как раздавать карты.

Я создал (с помощью stackoverflow) перетасованную последовательность из 104 карт из двух стандартных колод. Каждая карта представлена ​​как

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

Таблица для Spider будет представлена ​​следующим образом:

(defstruct tableau :stacks :complete)

где: стопки - это вектор векторов карт, 4 из которых содержат 5 карт лицевой стороной вниз и 1 карту лицевой стороной вверх, а 6 из которых содержат 4 карты лицевой стороной вниз и 1 карту лицевой стороной вверх, в общей сложности 54 карты, и : complete - это (изначально) пустой вектор завершенных наборов туза-короля (представленный, например, как король-сердце, для целей печати). Остальная часть колоды должна быть сохранена в ref

(def deck (ref seq))

Во время игры таблица может содержать, например:

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

где "AH" - карта, содержащая {: rank: ace: suit: hearts: face-up false} и т. Д.

Как я могу написать функцию для обработки стеков, а затем сохранить остаток в ссылке?

Ответы [ 2 ]

2 голосов
/ 26 апреля 2010

Вот решение, которое я нашел после изучения ответа выше. Обратите внимание, что я все еще совершенствуюсь и приветствую предложения по улучшению, в частности, использование более идиоматического 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)))))
0 голосов
/ 24 апреля 2010

Вы можете написать функцию, которая будет брать chunks векторов size элементов каждый из заданной последовательности и еще одну, чтобы отбрасывать эти фрагменты спереди:

;; 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-at и split-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)))])))

Возвращаемым значением является вектор дублета, первый элемент которого является остатком колоды, а вторым элементом является вектор начальных стеков.

Затем используйте это в транзакции для учета Ref:

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

Еще лучше, сохранить все состояние игры в одном Ref или Atom и переключиться с ref-set на alter / swap! (в этом примере я буду использовать Ref, пропустите dosync и переключите alter на swap!, чтобы вместо него использовать атом):

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

Отказ от ответственности: Ничто из этого не получило ни малейшего количества проверочного внимания (хотя я думаю, что оно должно работать нормально, по модулю любых глупых опечаток, которые я мог пропустить). Однако это ваше упражнение, поэтому я думаю, что оставить часть тестирования / полировки - это хорошо. : -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...