Created
September 4, 2012 02:54
-
-
Save wtaysom/3616016 to your computer and use it in GitHub Desktop.
Nim Minimal Viable Snippet in Clojure
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; 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