Think-Data Challenge: Rock Paper Scissors

Challenge

Allow two players to play the game "rock paper scissors" via a web browser.

Goals

Practice looking at a problem from a pure data perspective. This series takes example problems and "models the data" to solve those problems.

Prerequisites

Basic knowledge of the key/value map and Array data structures.

I use key/value map as the common name to refer to an unordered set of key/value pairs which has many names depending on the language:

  • associative array
  • hash table
  • dictionary

Challenge Outline

First let's outline what exactly "rock paper scissors" is.

Basic Setup

  • Two players should be able to play.
  • Each player can decide three options to play: "rock", "paper", "scissors"
  • Each takes their turn at the same time based on a count of say 3 seconds.

Gameplay

The game is played between two people. A round consists of both players simultaneously "throwing" either a rock, paper, or scissors out into the playing field against the opponent's choice. After choices have been thrown the field is analyzed to determine the winner.

Rules

Wins and losses are calculated based on the following rule system:

rock breaks scissors
scissors cut paper
paper covers rock

Thinking in Data

In order to think in data we must first understand the role of data in a program.

Firstly any program built for users will always have a "platform" the program runs on. In this case we need it to run inside a web browser. The user(s) necessarily need to interact with the program relative to this platform. This is called the user interface or UI. Further, the program needs to contain the logic that drives the interactions, in this case the game's gameplay and rules that determine the outcome of the gameplay. Finally, the data is what drives the logic.

We think about programs in this way so we can separate the program into smaller, more maintainable components:

  • Platform
  • UI
  • Logic
  • Data

Platforms can change, say web-browser, iphone, ipad, android, etc. UI layers can change such as touch based (ipad) to click based (browser).

The logic concepts usually persist across programming langauges in the same way that data concepts and data structures are extremely applicable across multiple languages and platforms.

Therefore, thinking in data and modeling the data composes the fundemental building blocks of programs.

Let's think in data:

The two players can be considered data, let's call them player1 and player2. The game's logic needs to determine which player wins and loses for a given set of choices made. So really, the data driving which choice wins relative to another choice is the set of rules.

rock breaks scissors
scissors cut paper
paper covers rock

How would you model these game rules as data?

...Thinking Time...

What do the rules model?
Player choices and what other choices those choices beat. We can see there are only three unique choices "rock", "paper", "scissors". We can see the words "breaks", "cut", and "covers" are really just meanings for "WIN". So a rock "WINS" against scissors which implicitly means scissors "LOSES" against rock.

So we have 3 unique "choices" and 2 unique "outcomes" that are being modeled in this data. Additionally we can see that each line represents a "round" of play. Finally since in each round if you reverse the choices you see which choice "LOSES" against the other.

Therefore the data represents the win/loss outcomes between 2 choices when played against one another in a given "round" with 6 total possible round combinations.

Here's a revised mockup of the rules in a more explicit format:

Choices (pairs)          Outcome
----------------------------------------------
"rock" -> "scissors"    : "WIN"
"scissors" -> "rock"    : "LOSE"

"scissors" -> "paper"   : "WIN"
"paper" -> "scissors"   : "LOSE"

"paper" -> "rock"       : "WIN"
"rock" -> "paper"       : "LOSE"

How would you model this as data?

...Thinking Time...

Let's lean on our core data structures: key/value map and Array. We can say the data associates two "choices" with one "outcome". Assocations between two values are a very common use-case for key/value maps in that one value is the "key" and the other value is the "value" so a key/value map sounds like a good data structure to try.

How might you use a key/value map to model the assocations?

...Thinking Time...
var round = {
"rock" : "scissors"
}

This data structure represents the association of the two choices in the first round. However it does not show the outcome!

How can we include the outcome of this round?

...Thinking Time...

Well the previous data structure correctly associates "rock" with "scissors". We need to associate this pair with the outcome "WIN". If a key/value map works for one association, it can work for two!

var round = {
"rock" : {
  "scissors" : "WIN"
}
}

This works! We have modeled the first round in data. We can get the outcome by accessing the data-structure:

-> round["rock"]["scissors"];
-> "WIN"

Let's do the same thing for all rounds:

var rounds = {
"rock" : {
  "scissors" : "WIN"
}
,
"scissors" : {
  "rock" : "LOSE"
}
,
"scissors" : {
  "paper" : "WIN"
}
,
"paper" : {
  "scissors" : "LOSE"
}
,
"paper" : {
  "rock" : "WIN"
}
,
"rock" : {
  "paper" : "LOSE"
}
}

Wait, that's not exactly how key/value maps work! Notice I have duplicate keys for every choice. The last defined key/value definition will overwrite the earlier one so our data model will not be complete.

But our key/value map is better than that! We just need to merge the values onto a single key:

var rounds = {
  "rock" : {
      "scissors" : "WIN",
      "paper" : "LOSE"
  }
  ,
  "paper" : {
      "rock" : "WIN",
      "scissors" : "LOSE",
  }
  ,
  "scissors" : {
      "paper" : "WIN",
      "rock" : "LOSE",
  }
}

Our data-structure can now give us all outcomes for all choice pairs!

-> rounds["rock"]["scissors"];
-> "WIN"

-> rounds["scissors"]["rock"];
-> "LOSE"

-> rounds["paper"]["scissors"];
-> "LOSE"

Hmm.... that makes me wonder ...

-> rounds["scissors"]["scissors"];
-> undefined

There's no reason two players couldn't choose the same choice! We haven't modeled that round =(. In the scenario both players choose the same choice there is nneither a "WIN" nor "LOSE" event, so let's define a new event "TIE". We need to support every possible "TIE" scenario:

-> rounds["rock"]["rock"];
-> rounds["paper"]["paper"];
-> rounds["scissors"]["scissors"];

How would you model this new data?

...Thinking Time...
var rounds = {
  "rock" : {
      "scissors" : "WIN",
      "paper" : "LOSE",
      "rock" : "TIE"
  }
  ,
  "paper" : {
      "rock" : "WIN",
      "scissors" : "LOSE",
      "paper" : "TIE"
  }
  ,
  "scissors" : {
      "paper" : "WIN",
      "rock" : "LOSE",
      "scissors" : "TIE"
  }
}

Bam! We can now calculate every outcome for every round scenario:

-> rounds["rock"]["scissors"];
-> "WIN"

-> rounds["scissors"]["rock"];
-> "LOSE"

-> rounds["paper"]["scissors"];
-> "LOSE"

-> rounds["scissors"]["scissors"];
-> "TIE"

This is great! We have just modeled the game mechanics of "rock paper scissors" completely in data.

We can pragmatically use our data-model to give us outcomes to any round of "rock paper scissors".