practicalli / practicalli/clojure

Simple Finite State Machine - Rock Paper Scissors

Open
#56 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

design Finite State Machine
Dominant language
Makefile
Stars
117
Forks
36
PR merge metrics
No merged PRs in 30d

Description

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start from the rock-paper-scissors namespace and the rps entry point described in the issue, then review the provided rock-paper-scissors-tests examples. Implement the basic game behavior, and consider the extended finite-state rules only if the project scope requires them; done means all listed win, loss, and draw cases pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
clojure
Domain
content
Issue type
Feature
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.