In Chapter 7 of our Networking with the Micro:bit book, we ask learners to program Rock-Paper-Scissors over the Micro:bit radio.
When my colleague @alistairbraden suggested to extend it to Rock-Paper-Scissors-Lizard-Spock, I have to admit I had not heard this version before.
All I had to do remember that episode of Big Bang Theory:
If you have not followed Sheldon’s explanation of the rules (even after the 2nd time), here they are:
Scissors cuts Paper
Paper covers Rock
Rock crushes Lizard
Lizard poisons Spock
Spock smashes Scissors
Scissors decapitate Lizard
Lizard eats Paper
Paper disproves Spock
Spock vaporizes Rock
(and as it always has) Rock crushes (or blunts) Scissors
So, how do we program this into a micro:bit? We will follow the same method as in Chapter 7 of Networking with the Micro:bit book.
To prepare for programming:
Assign numbers to Rock = 0, Paper = 1, Scissors = 2, Lizard = 3, and Spock = 4. (We will send these numbers over the radio to represent our hand selection.)
Create a table to help you play the game and display the result based on your and your opponent’s hand selection, and the rules of the game. If you win, display a happy face. If you lose, display a sad face. If you tie, display a surprised face.
Table of possible outcomes
Now we are ready for programming. We need to do the following:
Create images for Rock, Paper, Scissors, Lizard, and Spock.
Use button A to make a hand selection.
Use button B to send the selection over the radio.
When the selection is received over the radio, play the game, display the result, and reset the game.
We must also respect the following:
Only play the game when both parties selected a result and received it over the radio.
Once a selection is made, do not change it.
The code for the entire game is at the end of the blog post. In the following, I explain different parts of the code. I suggest looking at the full code first to have a general idea. Then, the explanations in the following sections will make more sense.
Rock, Paper, Scissors, Lizard and Spock images
There is already an icon image for scissors, so we can just use that. For others, I created what I thought a good representation, but I am sure there are more creative people out there who can come up with a better Lizard or Spock.
Making a hand selection
We will use button A to rotate over possible options and decide our hand selection. The default hand selection is rock (0). As we press button A, we will need to keep track of the next image to show. For that, we use the variable next_image. next_image is initially 0 and increments modulo 5. Our current hand selection is represented with the me variable.
Sending the selection over the radio
The only thing to be careful of when sending the me value is to send it once per each round the game. So, when button B is pressed, the selected variable is set to true. This variable is reset to false only once the game is played.
Receiving the selection over the radio
Next, we will receive the opponent’s hand selection over the radio.
To indicate that we received the opponent hand, we set the received variable to true. This variable is set to false at the beginning of each game round.
Playing the game
This is the heart of the game, and it codes the table of possible outcomes.
Once both selected and received variables are set to true, we are ready to play the game.
We start by displaying what we received from the opponent.
For the sake of simplicity, we are going to code each row of the table of outcomes as a separate if-block. We could also have written the entire table as a single logic statement.
If the opponent and me are the same, we display a surprised face. It is a tie.
Then, based on the table, we code when we display a happy face. I will explain just the first if-statement as the rest follows. In the first row of the outcomes table, it shows that when me is a rock and the opponent is either scissors or a lizard, me wins. So, replacing rock, scissors and lizard with their numerical values, the resulting if-statement is:
if me = 0 and (opponent = 2 or opponent = 3)
show happy face
After we coded each row like this and exhausted all the happy-face options, the final else-statement is for displaying the sad face.
Resetting the game for the next round
At the beginning of the game, or once the game is played, we should return the necessary variables to their correct states. So, we need an init function that resets the selected, received, next_image, and me variables. We also show a waiting image (which is the sleeping icon).
The complete code
Below is the complete code. Do not skip the next section where I explain how and why this code may get stuck. The culprit is radio packet loss!
Packet loss can stall the game for one or both players. The following diagram explains why by looking at how the selected and received variables evolve throughout the game. In the case where one player or both players’ packets (containing the hand selection) are lost, the received variable stays false not allowing the player(s) to play the game. This is shown by the red states in the following diagram.
If the game is stuck in one of these red states, this can be solved with a hard reset of the game. Or you may want to look at Chapters 8 and 9 of the Networking with the Micro:bit book to programmatically deal with packet errors.