-
-
Save thickey/5969833 to your computer and use it in GitHub Desktop.
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
;; An implementation of the rock-paper-scissors variant - | |
;; rock-paper-scissors-lizard-spock - based on Alex Miller's core.async | |
;; implementation. | |
;; - http://tech.puredanger.com/2013/07/10/rps-core-async/ | |
;; - https://gist.github.com/puredanger/5965883 | |
;; - https://github.com/relevance/labrepl/blob/master/src/solutions/rock_paper_scissors.clj | |
;; - http://www.imdb.com/title/tt1256039/quotes?item=qt0493730 | |
(require 'clojure.core.async :refer :all) | |
(def rules | |
[[:scissors :cuts :paper] | |
[:paper :covers :rock] | |
[:rock :crushes :lizard] | |
[:lizard :poisons :spock] | |
[:spock :smashes :scissors] | |
[:scissors :decapitates :lizard] | |
[:lizard :eats :paper] | |
[:paper :disproves :spock] | |
[:spock :vaporizes :rock] | |
[:rock :crushes :scissors]]) | |
(def moves (seq (set (map first rules)))) | |
(def dominates | |
^{:doc "Includes two mappings: a set of moves that each moves beats, and the | |
descriptive reason why one move beats another (for reporting)."} | |
(reduce (fn [m [i beat u]] | |
(-> m | |
(update-in [:beats i] (fn [s] (set (conj s u)))) | |
(assoc-in [:reasons i u] beat))) | |
{} | |
rules)) | |
(defn rand-player | |
"Create a named player and return a channel to report moves." | |
[name] | |
(let [out (chan)] | |
(go (while true (>! out [name (rand-nth moves)]))) | |
out)) | |
(defn winner | |
"Based on two moves, return the name of the winner." | |
[p1 p2] | |
(let [[name1 move1] p1 | |
[name2 move2] p2] | |
(cond | |
(= move1 move2) ["no one"] | |
(= move2 (-> dominates :beats move1 move2)) p1 | |
:else p2))) | |
(defn judge | |
"Given two channels on which players report moves, create and return an | |
output channel to report the results of each match as [move1 move2 winner]." | |
[c1 c2] | |
(let [out (chan)] | |
(go | |
(while true | |
(let [m1 (<! c1) | |
m2 (<! c2)] | |
(>! out [m1 m2 (winner m1 m2)])))) | |
out)) | |
(defn init | |
"Create 2 players (by default Alice and Bob) and return an output channel of match results." | |
([] (init "Alice" "Bob")) | |
([n1 n2] (judge (rand-player n1) (rand-player n2)))) | |
(defn report | |
"Report results of a match to the console." | |
[p1 p2 winner] | |
(let [[name1 move1] p1 | |
[name2 move2] p2 | |
[wname wmove] winner] | |
(println name1 "throws" move1) | |
(println name2 "throws" move2) | |
(if wmove | |
(let [[lname lmove] (if (= p1 winner) p2 p1)] | |
(println wmove (get-in dominates [:reasons wmove lmove]) lmove) | |
(println wname "wins!")) | |
(println "No one wins!")))) | |
(defn play | |
"Play by taking a match reporting channel and reporting the results of the latest match." | |
[out-chan] | |
(apply report (<!! out-chan))) | |
(defn play-many | |
"Play n matches from out-chan and report a summary of the results." | |
[out-chan n] | |
(loop [remaining n | |
results {}] | |
(if (zero? remaining) | |
results | |
(let [[_ _ [winner]] (<!! out-chan)] | |
(recur (dec remaining) | |
(merge-with + results {winner 1})))))) | |
(comment | |
(def game (init)) | |
(play game) | |
(time (play-many game 10000))) | |
;; user> (play game) | |
;; Alice throws :scissors | |
;; Bob throws :lizard | |
;; :scissors :decapitates :lizard | |
;; Alice wins! | |
;; nil | |
;; user> (play game) | |
;; Alice throws :rock | |
;; Bob throws :spock | |
;; :spock :vaporizes :rock | |
;; Bob wins! | |
;; nil |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment