Skip to content

Commit

Permalink
Add presentation and org file with (labrepl) solution.
Browse files Browse the repository at this point in the history
  • Loading branch information
ck committed Jan 18, 2012
1 parent 7898f84 commit 6616eac
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
Binary file added OO-Clojure.pdf
Binary file not shown.
163 changes: 163 additions & 0 deletions 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

0 comments on commit 6616eac

Please sign in to comment.