I'm writing a console blackjack game.

To hold all the possible moves a player can make, I have a map that maps a keyboard key to a move keyword:

(def move-map {\s :stand
               \h :hit
               \p :split
               \d :double})

Splitting and doubling are only available options during the first move though, so I'd like to filter them out of the available moves before verifying the user's input if they aren't legal moves.

My current method is to indicate which are legal using a flag for doubling and one for splitting, then filtering the map if a flag isn't set:

(defn- filter-moves [moves can-split? can-double?]
  (let [r-split (if can-split? moves (filter #(not= :split (second %)) moves))
        r-doub (if can-double? r-split (filter #(not= :double (second %)) r-split))]
    (into {} r-doub)))

The problem is, this contains a fair amount of redundancy. It should only iterate the map once because filter is lazy, but I'm still not happy with this solution.


(filter-moves move-map true false)

=> {"s" :stand, "h" :hit, "p" :split}

Any improvement suggestions would be appreciated. I'm not against redesigning the function if it helps readability and maintainability.


1 Answer 1


You can iterate once by using remove in combination with a set of illegal moves:

(defn- filter-moves [moves can-split? can-double?]
  (let [illegal? (cond-> #{} 
                   (not can-split?) (conj :split)
                   (not can-double?) (conj :double))]
    (into {} (remove #(illegal? (second %)) moves))))

What's more, If I understand correctly that moves is the map mapping keys to moves. In my opinion, this map has no place in your core game logic: have your filter-moves function manipulate only sets of moves and keep any notion of user input at the edge of your system.

  • \$\begingroup\$ Cool. Never used cond-> before. And I know tying key bindings directly to the set of legal moves is a bad idea. It was kind of a hack to get a test up and running quickly, but ya, bad idea. Thanks. \$\endgroup\$ Commented Oct 7, 2016 at 14:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.