diff --git a/OO-Clojure.pdf b/OO-Clojure.pdf new file mode 100644 index 0000000..d2d1779 Binary files /dev/null and b/OO-Clojure.pdf differ diff --git a/rock_paper_scissors.org b/rock_paper_scissors.org new file mode 100644 index 0000000..7e0a0e9 --- /dev/null +++ b/rock_paper_scissors.org @@ -0,0 +1,163 @@ +* Rock Paper Scissors + +** Setup + +First define the namespace and require the necessary libraries. + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (ns oo-clj.rock-paper-scissors) +#+end_src + +** The Rules + +The rules are pretty simple: Rock beats scissors, scissors beats paper, and paper beats rock. + +Write an idiomatic Clojure function `dominates` that returns the thing that dominates the argument passed in. + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (def dominates + {:rock :paper + :scissors :rock + :paper :scissors}) +#+end_src + +Create a `choices` set that contains the possible choices. +Don't Repeat Yourself. + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (def choices (set (keys dominates))) +#+end_src + +Create a `winner` function that takes two players' choices, +and returns the winner, or `nil` for a tie. + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (defn winner + [choice1 choice2] + (cond + (= choice1 choice2) nil + (= (dominates choice1) choice2) choice2 + :else choice1)) +#+end_src + +Create a `draw?` predicate that takes two players' choices +and returns true if they are a draw + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (defn draw? + [me you] + (= me you)) +#+end_src + +Create an `iwon?` predicate that takes two players' choices +and returns true if the first player won. + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (defn iwon? + [me you] + (= (winner me you) me)) +#+end_src + + +** The Players + +All the players will conform to a `Player` protocol. It should have two methods: + +1. `choose`: takes a player and returns that player's choice +2. `update-strategy`: takes a player, that player's last choice, and the other player's last choice, returning the `Player` for the next round. + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (defprotocol Player + (choose [this]) + (update-strategy [this me you])) +#+end_src + +The random player always picks at random, and never changes +strategy based on what the other player is doing: + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (defn random-choice [] + "Random choice" + (nth (vec choices) (rand-int (count choices)))) + + (defrecord Random [] + Player + (choose [_] (random-choice)) + (update-strategy [this me you] this)) +#+end_src + +The stubborn player is initialized with a choice and never +changes it: + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (defrecord Stubborn [choice] + Player + (choose [_] choice) + (update-strategy [this me you] this)) +#+end_src + +The mean player sticks with what worked last time, +or plays at random following a loss: + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (defrecord Mean [last-winner] + Player + (choose [_] (or last-winner (random-choice))) + (update-strategy [_ me you] (Mean. (when (iwon? me you) me)))) +#+end_src + +The copycat player picks the opponents last choice or plays at random: + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (defrecord CopycatPlayer [choice] + Player + (choose [_] + (if choice choice (random-choice))) + (update-strategy [_ player-last-choice opponent-last-choice] + (CopycatPlayer. opponent-last-choice))) +#+end_src + +The cycle player simply cycles through the available choices: + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (defrecord CyclePlayer [choices-seq] + Player + (choose [_] (first choices-seq)) + (update-strategy [_ _ _] (CyclePlayer. (rest choices-seq)))) +#+end_src + +** The Game + +To play, create a `game` function with three arguments: +two players and a number of rounds. + +It reads nicely as a loop with five arguments:" + +1. Player 1 +2. Player 2 +3. Player 1's current score +4. Player 2's current score +5. Number of rounds remaining + +Have the `game` return the two player's scores in a map. + +#+begin_src clojure :tangle src/oo_clj/rock_paper_scissors.clj + (defn game + [p1 p2 rounds] + (loop [p1 p1 + p2 p2 + p1-score 0 + p2-score 0 + rounds rounds] + (if (pos? rounds) + (let [p1-choice (choose p1) + p2-choice (choose p2) + result (winner p1-choice p2-choice)] + (recur + (update-strategy p1 p1-choice p2-choice) + (update-strategy p2 p2-choice p1-choice) + (+ p1-score (if (= result p1-choice) 1 0)) + (+ p2-score (if (= result p2-choice) 1 0)) + (dec rounds))) + {:p1 p1-score :p2 p2-score}))) +#+end_src