Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add presentation and org file with (labrepl) solution.
- Loading branch information
Showing
2 changed files
with
163 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |