npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fmg-rock-paper-scissors

v0.0.10

Published

Rock-paper-scissors as a force-move game

Downloads

13

Readme

Force-move Games: Rock Paper Scissors

This package contains a sample implementation of a rock-paper-scissors (RPS) game.

The package defines two objects:

  1. RockPaperScissorsState extends the core State contract, defining the attributes needed to describe a "move".
  2. RockPaperScissorsGame implementats the validTransition function, which outlines the rules that players have to follow during gameplay.

The real-world game involves two players simultaneously picking a move. In our force-move game implementation, we will use a commit-reveal strategy to simulate this, where one player commits to a value beforehand and then only reveals their choice once the other player's choice is known.

In our implementation, each round of the game will pass through four different stages: Resting, RoundProposed, RoundAccepted, and Reveal. We describe below how a client could control the flow of states that could be exchanged during a round of rock-paper-scissors played between Alice and Bob.

Alt text In each state aBal and bBal represent Alice and Bob's current balances in the game respectively. The dotted lines in the above figure represent the resolution of the game if it were to conclude from the given state -- that is, what the game rules considers a fair payout to each player.

The states we show in the diagram are slightly simplified. In particular, we omit the framework attributes implemented by the core state contract. The client's game library should automatically take care of these -- if they aren't updated correctly, the other player could enforce the game rules with the rules contained in the on-chain smart-contract.

The game requires some setup, as outlined in the simple adjudicator's readme. For simplicty we assume that we start in a position where Bob has just signed and sent a state state0A to Alice as message0B.

// In Alice's client, Alice holds message0B, which satisfies:
assert(message0B.signedBy(bobAddress));
assert(message0B.state.positionType == RPS.Resting);
assert(message0B.state.aBal == 5);
assert(message0B.state.bBal == 4);

It could be that Alice and Bob have just entered the game from the setup phase, which we'll cover later, or they might have just finished a previous round.

Alics kicks off a round by moving to a RoundProposed state. In doing this, she chooses a stake that they're each going to contribute and adjusts their totals accordingly. She also provides the preCommit, which she calculates by hashing her choice, rock, with a random string, xyz:

// In Alice's client:

let move0B = RPS.fromMessage(message0B);
let gameAttributes = {
  positionType: RPS.RoundProposed,
  aBal: 4,
  bBal: 3,
  stake: 1,
  preCommit: h('rock', 'xyz')
};
let state1A = RPS.Game({ ...move0B, ...gameAttributes});
let message1A = StateWalletA.sign(state1A);

// Alice ---> message1A ----> Bob

Bob then decides whether to accept the round or not. If he didn't want to accept, he would sign and send back the same Resting state (apart from an increased turnNum) as he sent in the beginning. If he does want to accept, he signs the RPS.RoundAccepted state, providing his choice -- in this case scissors:

// In Bob's client:
StateWalletB.receive(message1A);
let move1A = RPS.fromMessage(message1A);
let gameAttributes = {
  positionType: RPS.RoundAccepted,
  aBal: 4,
  bBal: 3,
  stake: 1,
  preCommit: h('rock', 'xyz'),
  bPlay: 'scissors'
};
let state2B = RPS.Game({ ...move1A, ...gameAttributes});
let message2B = StateWalletB.sign(state1B);

// b ---> message2B ----> a

Notice that, even though Alice appears to be winning at this point, if Bob were to force-move Alice into responding to message2B, and Alice fails to respond in time, Bob would take the stake. This is the only fair distribution, as from the world's point of view -- ie. anyone but Alice -- this case is indistinguishable from Alice deliberately stalling, knowing that she's lost.

The next step is for Alice to reveal her value. To do this she signs the RPS.Reveal state, which reveals her choice. She also provides the salt used in the pre-commit, so that Bob can verify that she hasn't changed her choice:


let move2B = RPS.fromMessage(message2B);
let gameAttributes = {
  positionType: RPS.Reveal,
  aBal: 4,
  bBal: 3,
  stake: 1,
  aPlay: 'rock',
  salt: 'xyz',
  preCommit: h('rock', 'xyz'),
  bPlay: 'scissors'
};
let state3A = RPS.Game({ ...move2B, ...gameAttributes});
let message3A = StateWalletA.sign(state3A);

// Alice ---> message3A ----> Bob

Bob then completes the round by signing the Resting state. In doing this he updates the totals to reflect the fact that Alice won the last round:

// In Bob's client:
StateWalletB.receive(message3A);
let move3A = RPS.fromMessage(message3A);
let gameAttributes = {
  positionType: RPS.Resting,
  aBal: 6,
  bBal: 3,
};
let state4B = RPS.Game({ ...move3A, gameAttributes});
let message4B = StateWalletB.sign(state3B);

// Bob ---> message4B ----> Alice

Now they are back in the Resting state, Alice is free to propose another round if she wishes.

Either play may communicate their wish to end the game at any point by moving to an RPS.Conclude state. This is discussed in detail in the (to-be-released) whitepaper.