Skip to content

Simple Finite State Machine - Rock Paper Scissors #56

Open
@practicalli-johnny

Description

@practicalli-johnny

Solving the "Rock Paper Scisors" game with a very simple state machine, defined as a clojure hash-map. Challenge taken from Codewars
https://www.codewars.com/kata/rock-paper-scissors/

Instructions

Let's play! You have to return which player won! In case of a draw return Draw!.

Examples:

(rps "scissors" "paper") 
;; => "Player 1 won!"
(rps "scissors" "rock")
;; => "Player 2 won!"
(rps "paper" "paper") 
;; => "Draw!"

The winner can be decided using a simple cond function (rather than nested if functions).

The draw state is simple, just compare if the strings for player one and player two are equal.

Defining when rock paper and scissors wins is done by defining which choices each of them beat. In the basic game, each choice will only beat one other choice and will only loose to one other choice.

Defining a simple state machine is easy, using a Clojure hash-map. For each entry in the hash-map, the choice is the key and the choice that it beats is the value.

So if Player1 chooses rock, we look up rock in the hash-map and get the value that is the thing that it beats. Compare the value retrieved from the hash-map with the value of player 2, if the values are equal then player 1 has won.

(ns rock-paper-scissors)

(def winner {"rock"     "scissors"
            "scissors" "paper"
            "paper"    "rock"})

(defn rps [p1 p2]
  (cond
  (= p1 p2) "Draw!"
  (= (get winner p1) p2) "Player 1 won!"
  (= (get winner p2) p1) "Player 2 won!"))

We could remove (= (get winner p2) p1) "Player 2 won!" and just put a default result of "Player 2 won!", because if its not a draw and player1 did not win, then player 2 must have won.

Sample Tests from Codewars

(ns rock-paper-scissors-tests
  (:require [clojure.test :refer :all]
            [rock-paper-scissors :refer [rps]]))

(deftest rps-tests
  (testing "player 1 win"
    (are [p1 p2] (= "Player 1 won!" (rps p1 p2))
      "rock" "scissors"
      "scissors" "paper"
      "paper" "rock"))
  (testing "player 2 win"
    (are [p1 p2] (= "Player 2 won!" (rps p1 p2))
      "scissors" "rock"
      "paper" "scissors"
      "rock" "paper"))
  (testing "draw"
    (are [p1 p2] (= "Draw!" (rps p1 p2))
      "rock" "rock"
      "scissors" "scissors"
      "paper" "paper"))
)

Rock Paper Scissors Spock

https://en.wikipedia.org/wiki/Rock_paper_scissors

Finite State machine for the game
https://en.wikipedia.org/wiki/Rock_paper_scissors#/media/File:Rock_Paper_Scissors_Lizard_Spock_en.svg

(def rules-finite-state-machine
  {"rock"     {"lizard" "scissors"}
   "paper"    {"spock" "rock"}
   "scissors" {"paper" "lizard"}
   "lizard"   {"spock" "paper"}
   "spock"    {"scissors" "rock"}})

Related articles

http://blog.cognitect.com/blog/2017/5/22/restate-your-ui-using-state-machines-to-simplify-user-interface-development

Foundation: using a map as a translation between two alphabets, eg. in the Clacks example. The map is used to move from a character in one alphabet state to a character in another alphabet state.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions