Skip to content
This repository


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…


Cannot retrieve contributors at this time

file 164 lines (126 sloc) 4.582 kb

Rock Paper Scissors


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]
   (= 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 []
  (choose [_] (random-choice))
  (update-strategy [this me you] this))

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

(defrecord Stubborn [choice]
  (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]
  (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]
  (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]
  (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)]
        (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})))
Something went wrong with that request. Please try again.