Asi move-rps-game

Rock-Paper-Scissors PvP game on Aptos with commit-reveal pattern and ACSet-informed design

install
source · Clone the upstream repo
git clone https://github.com/plurigrid/asi
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/plurigrid/asi "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/move-rps-game" ~/.claude/skills/plurigrid-asi-move-rps-game && rm -rf "$T"
manifest: skills/move-rps-game/SKILL.md
source content

Move RPS Game Skill

"In the commit-reveal pattern, GF(3) determines victory: (p1 - p2 + 3) % 3"

Overview

Move RPS Game implements a provably fair Rock-Paper-Scissors game on Aptos blockchain using the commit-reveal pattern to prevent cheating.

GF(3) Role

AspectValue
Trit+1 (PLUS)
RoleGENERATOR
FunctionGenerates game states via Move contracts

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    RPS GAME STATE MACHINE                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  WAITING ──join──▶ COMMIT ──both──▶ REVEAL ──both──▶ COMPLETE   │
│     │                 │                 │                │       │
│   cancel           timeout           timeout          winner     │
│     │                 │                 │                │       │
│     ▼                 ▼                 ▼                ▼       │
│  [refund]         [forfeit]        [forfeit]         [payout]   │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

ACSet Schema

The game state follows a categorical schema:

@present SchRPS(FreeSchema) begin
  Player::Ob
  Game::Ob
  Move::Ob           # {Rock=0, Paper=1, Scissors=2}

  player1::Hom(Game, Player)
  player2::Hom(Game, Player)
  commit1::Hom(Game, Hash)
  commit2::Hom(Game, Hash)
  reveal1::Hom(Game, Move)
  reveal2::Hom(Game, Move)
  winner::Hom(Game, Player)

  BetType::AttrType
  bet::Attr(Game, BetType)
end

Winner Determination (GF(3))

Move values: Rock=0, Paper=1, Scissors=2

Winner formula: result = (move1 - move2 + 3) % 3
  0 = Draw
  1 = Player 1 wins
  2 = Player 2 wins

Cycle: Rock → Scissors → Paper → Rock
       (0)     (2)        (1)     (0)

Game Flow

1. Create Game

aptos move run \
  --function-id 'ADDR::game::create_game' \
  --args u64:10000000  # 0.1 APT bet

2. Join Game

aptos move run \
  --function-id 'ADDR::game::join_game' \
  --args address:GAME_ID

3. Commit Move

# Off-chain: compute hash
MOVE=0  # Rock
SALT=$(openssl rand -hex 32)
HASH=$(echo -n "${MOVE}${SALT}" | sha3sum -a 256)

aptos move run \
  --function-id 'ADDR::game::commit_move' \
  --args address:GAME_ID "vector<u8>:${HASH}"

4. Reveal Move

aptos move run \
  --function-id 'ADDR::game::reveal_move' \
  --args address:GAME_ID u8:0 "vector<u8>:${SALT}"

Security Model

Commit-Reveal Pattern

Phase 1 (Commit):
  P1: hash(move₁ || salt₁) → chain
  P2: hash(move₂ || salt₂) → chain

Phase 2 (Reveal):
  P1: (move₁, salt₁) → chain, verify hash
  P2: (move₂, salt₂) → chain, verify hash

Winner: GF(3) computation on revealed moves

Timeout Protection

PhaseTimeoutResolution
WAITINGNonePlayer 1 can cancel
COMMIT5 minNon-committer forfeits
REVEAL5 minNon-revealer forfeits

Why This Works

  1. No front-running: Moves hidden until both committed
  2. No backing out: Commitment is binding
  3. Provable fairness: Hash verification on-chain
  4. Timeout protection: Can't grief by not playing

Contract Functions

Entry Functions

FunctionDescription
create_game(bet)
Create game with APT bet
join_game(game_id)
Join and match bet
commit_move(game_id, hash)
Submit move commitment
reveal_move(game_id, move, salt)
Reveal move
claim_timeout(game_id)
Claim win on timeout
cancel_game()
Cancel if no opponent

View Functions

FunctionReturns
get_game_state(game_id)
(phase, pot, winner)
get_game_players(game_id)
(player1, player2)
is_game_joinable(game_id)
bool
get_commitment_status(game_id)
(p1_committed, p2_committed)
get_reveal_status(game_id)
(p1_revealed, p2_revealed)
compute_hash(move, salt)
commitment hash

Deployment

cd ~/.claude/skills/move-rps-game

# Initialize (testnet)
aptos init --network testnet

# Compile
aptos move compile --named-addresses rps_game=default

# Test
aptos move test --named-addresses rps_game=default

# Publish
aptos move publish --named-addresses rps_game=default

GF(3) Triads

move-rps-game (+1) ⊗ acsets-relational-thinking (0) ⊗ move-smith-fuzzer (-1) = 0 ✓
move-rps-game (+1) ⊗ aptos-agent (0) ⊗ clj-kondo-3color (-1) = 0 ✓
move-rps-game (+1) ⊗ gay-mcp (0) ⊗ three-match (-1) = 0 ✓

Frontend Integration

import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({ network: Network.TESTNET });
const aptos = new Aptos(config);

// Compute commitment client-side
function computeCommitment(move: number, salt: Uint8Array): Uint8Array {
  const data = new Uint8Array([move, ...salt]);
  return sha3_256(data);
}

// Create game
await aptos.transaction.build.simple({
  sender: player1.accountAddress,
  data: {
    function: `${moduleAddress}::game::create_game`,
    functionArguments: [100_000_000], // 1 APT
  },
});

Testing Locally

# Start local testnet
aptos node run-local-testnet --with-faucet

# Fund accounts
aptos account fund-with-faucet --account default --amount 100000000

# Run test game
aptos move run --function-id 'default::game::create_game' --args u64:10000000

Related Skills

  • aptos-agent
    (0) - Blockchain interaction
  • acsets-relational-thinking
    (0) - Categorical schema design
  • move-smith-fuzzer
    (-1) - Contract fuzzing
  • kolmogorov-codex-quest
    (0) - Quest contract reference

Skill Name: move-rps-game Type: Move Smart Contract / PvP Game Trit: +1 (PLUS - GENERATOR) GF(3): Generates game states, winner via modular arithmetic