Skip to content

Gaming

Rock-Paper-Scissors

The classic example of why commit-reveal exists -- simultaneous moves in a two-player game:

from commit_reveal import CommitRevealScheme


def play_rps():
    cr = CommitRevealScheme()

    # Both players commit simultaneously
    alice_commitment, alice_salt = cr.commit("rock")
    bob_commitment, bob_salt = cr.commit("scissors")

    # Exchange commitments (neither knows the other's move)
    # ...

    # Both reveal
    assert cr.reveal("rock", alice_salt, alice_commitment)
    assert cr.reveal("scissors", bob_salt, bob_commitment)

    # Alice wins!

Neither player can change their move after seeing the other's commitment.

Fair Card Game

Secure card distribution where no player can predict others' cards. Each player contributes a random seed; the combined seed shuffles the deck.

import random
from commit_reveal import CommitRevealScheme


class SecureCardGame:
    def __init__(self, players, deck_size=52):
        self.cr = CommitRevealScheme(use_zkp=True)
        self.players = players
        self.deck_size = deck_size
        self.player_seeds = {}
        self.shuffled_deck = None
        self.hands = {}

    def commit_seed(self, player_id, seed):
        """Each player commits to a random seed."""
        commitment, salt = self.cr.commit(str(seed))
        proof = self.cr.create_zkp_proof(str(seed), salt, commitment)

        self.player_seeds[player_id] = {
            "commitment": commitment,
            "salt": salt,
            "proof": proof,
            "revealed_seed": None,
        }

    def reveal_seed(self, player_id, seed):
        """Reveal seed and verify it matches the commitment."""
        seed_data = self.player_seeds[player_id]
        if self.cr.reveal(str(seed), seed_data["salt"], seed_data["commitment"]):
            seed_data["revealed_seed"] = seed
            return True
        return False

    def shuffle_and_deal(self, cards_per_player=5):
        """Combine all seeds to shuffle the deck deterministically."""
        # Combine all revealed seeds
        all_seeds = sorted(
            str(data["revealed_seed"])
            for data in self.player_seeds.values()
            if data["revealed_seed"] is not None
        )
        combined = "".join(all_seeds)

        # Hash to create final seed
        final_hash = self.cr._hash_func(combined.encode()).digest()
        final_seed = int.from_bytes(final_hash, "big") % (2**32)

        # Shuffle deck deterministically
        random.seed(final_seed)
        deck = list(range(1, self.deck_size + 1))
        random.shuffle(deck)
        self.shuffled_deck = deck

        # Deal
        idx = 0
        for player_id in self.players:
            self.hands[player_id] = deck[idx : idx + cards_per_player]
            idx += cards_per_player

    def verify_fairness(self):
        """Anyone can reproduce the shuffle from the revealed seeds."""
        all_seeds = sorted(
            str(data["revealed_seed"])
            for data in self.player_seeds.values()
            if data["revealed_seed"] is not None
        )
        combined = "".join(all_seeds)
        final_hash = self.cr._hash_func(combined.encode()).digest()
        final_seed = int.from_bytes(final_hash, "big") % (2**32)

        random.seed(final_seed)
        expected_deck = list(range(1, self.deck_size + 1))
        random.shuffle(expected_deck)

        return expected_deck == self.shuffled_deck

Running the Game

players = ["alice", "bob", "charlie", "dave"]
game = SecureCardGame(players)

seeds = {"alice": 12345, "bob": 67890, "charlie": 24680, "dave": 13579}

# Commit phase
for player_id, seed in seeds.items():
    game.commit_seed(player_id, seed)

# Reveal phase
for player_id, seed in seeds.items():
    game.reveal_seed(player_id, seed)

# Shuffle and deal
game.shuffle_and_deal(cards_per_player=5)

for player_id in players:
    print(f"{player_id}: {game.hands[player_id]}")

# Verify fairness
assert game.verify_fairness()

Why This Works

  • No single player controls the randomness (all seeds are combined)
  • Each player commits before any seeds are revealed
  • The shuffle is deterministic and reproducible from the revealed seeds
  • ZKP proofs ensure each player actually committed to a real seed