Skip to content

Instantly share code, notes, and snippets.

@wtaysom
Created September 4, 2012 02:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wtaysom/3616016 to your computer and use it in GitHub Desktop.
Save wtaysom/3616016 to your computer and use it in GitHub Desktop.
Nim Minimal Viable Snippet in Clojure
;;; Player
(defn next [p]
(if (= p :P1) :P2 :P1))
;;; Game
(defn winner [game]
(if (= (:pile game) 0) (next (:player game)) nil))
(defn game-over [game]
(not= (winner game) nil))
(defn winner-is [game player]
(= (winner game) player))
;;; Move
(defn options [game]
(range 1 (+ (min (:pile game) 2) 1)))
(defn move [game move]
{:pile (- (:pile game) move)
:player (next (:player game))})
;;; Strategy
(defn take-more [game options]
(apply max options))
(defn take-less [game options]
(apply min options))
(def trivial-tactics
{:P1 take-more
:P2 take-less})
;;; Play
(defn take-turn [game strategy]
(move game (strategy game (options game))))
(defn until [test action initial]
(loop [v initial]
(if (test v) v (recur (action v)))))
(defn play [tactics game]
(let [take-turn- #(take-turn % (tactics (:player %)))]
(winner (until game-over take-turn- game))))
(defn you-play [player tactics game]
(printf " you %s\n"
(if (= (play tactics game) player) "win" "lose")))
;;; Example Games
(defn trivial-play [game]
(play trivial-tactics game))
(def g2 {:pile 2 :player :P1})
(def g3 {:pile 3 :player :P1})
(def g14 {:pile 14 :player :P1})
(trivial-play g14) ;=> :P1
;;; Human Player
; Since Light Table doesn't yet support Instarepl input,
; we define all the *human-moves* then form a *human-stream*
; from them.
(def *human-moves* '(2 1 1 1 1 1 1))
(defn join
([coll]
(join "\n" coll))
([sep coll]
(apply str (interpose sep coll))))
; This <http://www.learningclojure.com/2009/12/understanding-repl.html>
; makes it easy.
(def *human-stream* (java.io.PushbackReader.
(java.io.StringReader. (join *human-moves*))))
; Since Clojure "(catch RuntimeException e (.getMessage e))".
(defn maybe-read []
(try
(read *human-stream*)
(catch RuntimeException e e)))
; Using nested 'if instead of 'when since
; "Can only recur from tail position".
(defn consult-human [game options]
(loop []
(printf " in %s choose from %s \n" game options)
(let [choice (maybe-read)]
(if (instance? RuntimeException choice)
(do (printf " %s\n" (.getMessage choice))
(recur))
(if-not (some #{choice} options)
(do (printf " %s not in %s\n" choice options)
(recur))
(do (printf "okay %s\n" choice)
choice))))))
(def simple-tactics
{:P1 consult-human
:P2 take-more})
(defn simple-play [game]
(you-play :P1 simple-tactics game))
; try:
;(simple-play g14)
;;; Look Ahead Player
(defn find-choice [test game]
(first (filter #(test (move game %)) (options game))))
(defn look-ahead [game]
(or (find-choice #(winner-is % (:player game)) game)
(find-choice (comp not look-ahead) game)))
(defn look-ahead-or-give-up [game options]
(or (look-ahead game) (last options)))
(def hard-tactics
{:P1 consult-human
:P2 look-ahead-or-give-up})
(defn hard-play [game]
(you-play :P1 hard-tactics game))
; Try:
(hard-play g14)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment