You could write a function to take chunks
vectors of size
items each from a given sequence and another one to drop those chunks from the front:
;; 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))
Then maybe add a function to do both (modelled after split-at
and split-with
):
(defn split-chunks [chunks size s]
[(take-chunks chunks size s)
(drop-chunks chunks size s)])
Assuming that each card is initially {:face-up false}
, you can use the following function to turn the last card on a stack:
(defn turn-last-card [stack]
(update-in stack [(dec (count stack)) :face-up] not))
Then a function to deal out the initial stacks / chunks from the the given deck:
(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)))])))
The return value is a doubleton vector whose first element is the remainder of the deck and whose second element is a vector of the initial stacks.
Then use this in a transaction to take the Ref into account:
(dosync (let [[new-deck stacks] (deal-initial-stacks @deck-ref)]
(ref-set deck-ref new-deck)
stacks))
Better yet, keep the whole state of the game in a single Ref or Atom and switch from ref-set
to alter
/ swap!
(I'll use a Ref for this example, omit the dosync
and switch alter
to swap!
to use an atom instead):
;; 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))))
Disclaimer: None of this has received the slightest amount of testing attention (though I think it should work fine, modulo any silly typos I might have missed). It's your exercise, though, so I think leaving the testing / polishing part to you is fine. :-)