2.3 Rock, Paper, and Scissors
In this section, we’ll have Alice and Bob actually execute the game of Rock, Paper, Scissors!.
We have to decide how to represent the hands of the game.
A simple way is to represent them as the numbers 0
, 1
, and 2
, standing for Rock, Paper, and Scissors.
However, Reach does not support unsigned integers of exactly two bits, so it is better to represent them as the equivalence class of integers modulo three, so we won’t distinguish between 0
and 3
as Rock.
We’ll use a similar strategy for representing the three outcomes of the game: B wins, Draw, and A wins.
The first step is to change the Reach program to specify that Alice and Bob’s frontends can be interacted with to get the move that they will play, and later informed of the outcome of the game.
Lines 3 through 5 defines a participant interact interface that will be shared between the two players. In this case, it provides two methods:
getHand
, which returns a number; andseeOutcome
, which receives a number.Line 10 uses this interface for both participants. Because of this line,
interact
in the rest of the program will be bound to an object with methods corresponding to the these actions, which will connect to the frontend of the corresponding participant.
Before continuing with the Reach application, let’s move over to the JavaScript interface and implement these methods in our frontend.
.. // ...
14 const HAND = ['Rock', 'Paper', 'Scissors'];
15 const OUTCOME = ['Bob wins', 'Draw', 'Alice wins'];
16 const Player = (Who) => ({
17 getHand: () => {
18 const hand = Math.floor(Math.random() * 3);
19 console.log(`${Who} played ${HAND[hand]}`);
20 return hand;
21 },
22 seeOutcome: (outcome) => {
23 console.log(`${Who} saw outcome ${OUTCOME[outcome]}`);
24 },
25 });
26
27 await Promise.all([
28 backend.Alice(
29 ctcAlice,
30 Player('Alice'),
31 ),
32 backend.Bob(
33 ctcBob,
34 Player('Bob'),
35 ),
36 ]);
.. // ...
Lines 14 and 15 define arrays to hold the meaning of the hands and outcomes.
Line 16 defines a constructor for the
Player
implementation.Lines 17 through 21 define the
getHand
method.Lines 22 through 24 define the
seeOutcome
method.Finally, lines 30 and 34 instantiate the implementation once for Alice and once for Bob. These are the actual objects that will be bound to
interact
in the Reach program.
There should be nothing interesting or controversial about these implementations; that’s the point of Reach: we get to just write normal business logic without worrying about the details of the consensus network and decentralized application.
Let’s return to the Reach program and look inside of the body of the program for what actions Alice and Bob take.
In a real-life game of Rock, Paper, Scissors!, Alice and Bob simultaneously decide what hand they will play and reveal it at the same time. "Simultaneity" is a complex concept that is hard to realize in practice. For example, if you’ve ever player against a little kid, you may notice them trying to see what you’re going to choose and delaying until the last minute to show their hand so they will win. In a decentralized application, it is not possible to have simultaneity. Instead, we have to select a particular participant who will "go first". In this case, we’ll choose Alice.
Does Alice go first, or do we call the player that goes first "Alice"? This might seem like an unnecessary distinction to make, but it is a very subtle point about the way that Reach works. In our frontend, we explicitly ran
backend.Alice
andbackend.Bob
. When we did that, we were committing that particular JavaScript thread to be either Alice or Bob. In our game, whoever chose to run the Alice backend is the one that will go first. In other words, Alice goes first. This will be more obvious at the end of the tutorial when we’ll make the choice interactively about which role to play.
The game proceeds in three steps.
First, Alice’s backend interacts with her frontend, gets her hand, and publishes it.
Line 12 states that this block of code is something that only
A
(i.e., Alice) performs.That means that the variable,
handA
, bound on line 13 is known only to Alice.Line 13 binds that value to the result of interacting with Alice through the
getHand
method, which we wrote in JavaScript.Line 13 also declassifies the value, because in Reach, all information from frontends is secret until it is explicitly made public.
Line 14 has Alice join the application by publishing the value to the consensus network, so it can be used to evaluate the outcome of the game. Once this happens, the code is in a "consensus step" where all participants act together.
Line 15 commits the state of the consensus network and returns to "local step" where individual participants can act alone.
The next step is similar, in that Bob publishes his hand; however, we don’t immediately commit the state, instead we compute the outcome of the game.
Lines 17 through 19 match Alice’s similar local step and joining of the application through a consensus transfer publication.
But, line 21 computes the outcome of the game before committing. (
(handA + (4 - handB)) % 3
is a clever equation to compute the winner of a game of Rock, Paper, Scissors! using modular arithmetic. Consider whenhandA
is0
(i.e., Rock) andhandB
is2
(i.e., Scissors), then this equation is((handA + (4 - handB)) % 3) = ((0 + (4 - 2)) % 3) = ((0 + 2) % 3) = (2 % 3) = 2
, which is the last outcome, that is A wins, as we expect it to be.)
Finally, we use the each form to have each of the participants send the final outcome to their frontends.
Line 24 states that this is a local step that each of the participants performs.
At this point, we can run the program and see its output by running
$ ./reach run
Since the players act randomly, the results will be different every time. When I ran the program three times, this is the output I got:
$ ./reach run |
Alice played Scissors |
Bob played Paper |
Alice saw outcome Alice wins |
Bob saw outcome Alice wins |
|
$ ./reach run |
Alice played Scissors |
Bob played Paper |
Alice saw outcome Alice wins |
Bob saw outcome Alice wins |
|
$ ./reach run |
Alice played Paper |
Bob played Rock |
Alice saw outcome Alice wins |
Bob saw outcome Alice wins |
Alice is pretty good at Rock, Paper, Scissors!!
Consensus networks in general, and Reach specifically, guarantee that all participants agree on the outcome of their decentralized computation. Indeed, this is where the name consensus network comes from, as they enable these distributed, and untrusted, parties to come to a consensus, or agreement, about the intermediate states of a computation; and if they agree on the intermediate states, they will also agree on the output. That’s why every time you run ./reach run, both Alice and Bob will see the same outcome!
If your version isn’t working, look at the complete versions of tut-2/index.rsh and tut-2/index.mjs to make sure you copied everything down correctly!
In the next step, we’ll add some stakes to the game, because Alice needs to take her skills to the bank!
Check your understanding: Reach programs allows interaction with a user interface through which of the following methods
by forcing you to write a custom backend for the user interface that connects to the generated smart contract,
by allowing the frontends to provide values directly to the Reach application,
by allowing the Reach program to callback to the frontend via the interact object.
Answer:2 and 3; Reach programs specify a two-way interface between the frontend and the backend via the participant interact interface.
Check your understanding: How do participants in a Reach application share information with each other and find out what what others have shared?
Reach generates a smart contract, but you need to implement a process to scan the blockchain for events that corresponding to sharing;
The Reach primitive
publish
allows a participant to share information with all other participants, which happens automatically without the other parties needing to do anything special;The Reach primitive
publish
allows a participant to share information with all other participants, but they need to explicitly run the receive primitive to receive published information.Answer:2; the
publish
primitive does everything for you.