Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
164 lines (126 sloc) 4.47 KB

Rock Paper Scissors

Setup

First define the namespace and require the necessary libraries.

(ns oo-clj.rock-paper-scissors)

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.

(def dominates
  {:rock     :paper
   :scissors :rock
   :paper    :scissors})

Create a `choices` set that contains the possible choices. Don’t Repeat Yourself.

(def choices (set (keys dominates)))

Create a `winner` function that takes two players’ choices, and returns the winner, or `nil` for a tie.

(defn winner
  [choice1 choice2]
  (cond
   (= choice1 choice2) nil
   (= (dominates choice1) choice2) choice2
   :else choice1))

Create a `draw?` predicate that takes two players’ choices and returns true if they are a draw

(defn draw?
  [me you]
  (= me you))

Create an `iwon?` predicate that takes two players’ choices and returns true if the first player won.

(defn iwon?
  [me you]
  (= (winner me you) me))

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.
(defprotocol Player
  (choose [this])
  (update-strategy [this me you]))

The random player always picks at random, and never changes strategy based on what the other player is doing:

(defn random-choice []
  "Random choice"
  (nth (vec choices) (rand-int (count choices))))

(defrecord Random []
  Player
  (choose [_] (random-choice))
  (update-strategy [this me you] this))

The stubborn player is initialized with a choice and never changes it:

(defrecord Stubborn [choice]
  Player
  (choose [_] choice)
  (update-strategy [this me you] this))

The mean player sticks with what worked last time, or plays at random following a loss:

(defrecord Mean [last-winner]
  Player
  (choose [_] (or last-winner (random-choice)))
  (update-strategy [_ me you] (Mean. (when (iwon? me you) me))))

The copycat player picks the opponents last choice or plays at random:

(defrecord CopycatPlayer [choice]
  Player
  (choose [_]
    (if choice choice (random-choice)))
  (update-strategy [_ player-last-choice opponent-last-choice]
    (CopycatPlayer. opponent-last-choice)))

The cycle player simply cycles through the available choices:

(defrecord CyclePlayer [choices-seq]
  Player
  (choose [_] (first choices-seq))
  (update-strategy [_ _ _] (CyclePlayer. (rest choices-seq))))

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.

(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})))