Files
crypto_clash/docs/data-structure-diagram.md

15 KiB

Game Contract Data Structure Diagram

Overview

This diagram illustrates how the Game contract manages multiple concurrent Rock-Paper-Scissors games using mappings, arrays, and structs.

Data Structure Visualization

┌─────────────────────────────────────────────────────────────────────────────┐
│                          GAME CONTRACT STATE                                │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│  1. games (mapping: uint => GameState)                                      │
│     Maps game ID to the complete game state                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   Game ID                      Game State                                   │
│   ┌───────┐                   ┌──────────────────────────────────────┐      │
│   │   1   │    ───────────→   │ GameState {                          │      │
│   └───────┘                   │   gameId: 1                          │      │
│                               │   isActive: true                     │      │
│                               │   firstReveal: 0                     │      │
│                               │   initialBet: 0.01 ETH               │      │
│                               │   outcome: None                      │      │
│                               │   playerA: Player {                  │      │
│                               │     addr: 0xABC...123                │      │
│                               │     bet: 0.01 ETH                    │      │
│                               │     encrMove: 0x4f2a...              │      │
│                               │     move: None                       │      │
│                               │     nickname: "Alice"                │      │
│                               │   }                                  │      │
│                               │   playerB: Player {                  │      │
│                               │     addr: 0xDEF...456                │      │
│                               │     bet: 0.01 ETH                    │      │
│                               │     encrMove: 0x8b3c...              │      │
│                               │     move: None                       │      │
│                               │     nickname: "Bob"                  │      │
│                               │   }                                  │      │
│                               │ }                                    │      │
│                               └──────────────────────────────────────┘      │
│                                                                             │
│   ┌───────┐                   ┌──────────────────────────────────────┐      │
│   │   2   │    ───────────→   │ GameState {                          │      │
│   └───────┘                   │   gameId: 2                          │      │
│                               │   isActive: true                     │      │
│                               │   playerA: { addr: 0xGHI...789 }     │      │
│                               │   playerB: { addr: 0x000...000 }     │      │
│                               │   (waiting for playerB)              │      │
│                               │   ...                                │      │
│                               │ }                                    │      │
│                               └──────────────────────────────────────┘      │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│  2. gameIds (array: uint[])                                                 │
│     Tracks all game IDs ever created (for enumeration)                      │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   Index:     0       1       2       3       4                              │
│            ┌───┐   ┌───┐   ┌───┐   ┌───┐   ┌───┐                            │
│   Value:   │ 1 │   │ 2 │   │ 3 │   │ 4 │   │ 5 │  ...                       │
│            └───┘   └───┘   └───┘   └───┘   └───┘                            │
│                                                                             │
│   Entries are never removed (enables iteration over all games)              │
│   Use isActive flag to distinguish active from completed games              │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│  3. nextGameId (uint counter)                                               │
│     Auto-incrementing counter for unique game IDs                           │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   Current Value: 6  ──→  Next game created will have ID = 6                 │
│                                                                             │
│   Increments with each new game: createNewGame()                            │
│   Starts at 1 and never resets (prevents ID collisions)                     │
└─────────────────────────────────────────────────────────────────────────────┘

Flow Diagram: Player Lifecycle

┌──────────────────────────────────────────────────────────────────────────┐
│                         PLAYER GAME LIFECYCLE                            │
└──────────────────────────────────────────────────────────────────────────┘

    Player: 0xABC...123 (msg.sender)
         │
         │ register(gameId, {value: 0.01 ETH})
         ↓
    ┌─────────────────────────────────────────────────────┐
    │  1. Register with Game                              │
    │     - If gameId == 0: createNewGame()               │
    │     - Validate bet >= BET_MIN (0.01 ETH)            │
    │     - If playerA slot empty: become playerA         │
    │     - Else if playerB slot empty: become playerB    │
    │     - Else: revert "Game is full"                   │
    └─────────────────────────────────────────────────────┘
         │
         ↓
    ┌─────────────────────────────────────────────────────┐
    │  2. Commit Phase                                    │
    │     play(gameId, encrMove)                          │
    │     - Store encrypted move for player               │
    │     - Wait for both players to commit               │
    └─────────────────────────────────────────────────────┘
         │
         │ (once both players committed)
         ↓
    ┌─────────────────────────────────────────────────────┐
    │  3. Reveal Phase                                    │
    │     reveal(gameId, "move-password")                 │
    │     - Hash and verify move                          │
    │     - Store clear move                              │
    │     - Start firstReveal timer on first reveal       │
    │     - Auto-calculate outcome if both revealed       │
    └─────────────────────────────────────────────────────┘
         │
         │ (once both players revealed OR timeout reached)
         ↓
    ┌─────────────────────────────────────────────────────┐
    │  4. Game completion                                 │
    │     getOutcome(gameId)                              │
    │     - Verify reveal phase ended                     │
    │     - Mark game as inactive                         │
    │     - Transfer winnings (2x bet for winner,        │
    │       1x bet each for draw)                         │
    │     - Return outcome (PlayerA/PlayerB/Draw)         │
    └─────────────────────────────────────────────────────┘
         │
         ↓
    Player can register for new game

Relationship Diagram

                    ┌───────────────────────────────┐
                    │        Game IDs               │
                    │    (1, 2, 3, 4, 5...)         │
                    └──────────────┬────────────────┘
                                   │
                    ┌──────────────┴────────────────┐
                    │                               │
                games (mapping)              gameIds (array)
                    │                               │
                    ↓                               ↓
         ┌─────────────────────┐         ┌────────────────┐
         │   GameState Objects │         │  For iteration │
         │   - Player data     │         │  over all games│
         │   - Moves           │         │  (active/      │
         │   - Outcomes        │         │   inactive)    │
         │   - isActive flag   │         └────────────────┘
         │   - Timestamps      │
         └─────────────────────┘

Key Relationships

  1. gameIds array → games mapping:

    • gameIds maintains a list of all game IDs created
    • Each ID in gameIds[i] can be used to look up the game state: games[gameIds[i]]
    • Enables iteration over all games regardless of active status
  2. GameState structure:

    • Each game has exactly 2 players (playerA and playerB)
    • Players are stored as Player structs with address, bet amount, moves, and nickname
    • The isActive flag indicates if the game is ongoing or completed
    • outcome is calculated when both players reveal or after reveal timeout
  3. nextGameId counter:

    • Ensures unique game IDs across all games
    • Increments with each new game creation
    • Never resets, preventing ID collisions even in long-running contracts

Data Flow Example: Two Players Join Game

Step 1: Player A registers with gameId=0 (create new game)
    createNewGame() → gameId = 1, nextGameId = 2
    games[1] = {
        gameId: 1,
        isActive: true,
        playerA: { addr: PlayerA, bet: 0.01 ETH, encrMove: 0, move: None, nickname: "" },
        playerB: { addr: 0x0, bet: 0, encrMove: 0, move: None, nickname: "" },
        outcome: None,
        firstReveal: 0,
        initialBet: 0
    }
    gameIds = [1]

Step 2: Player B joins game 1
    games[1] = {
        gameId: 1,
        isActive: true,
        playerA: { addr: PlayerA, bet: 0.01 ETH, ... },
        playerB: { addr: PlayerB, bet: 0.01 ETH, ... },
        outcome: None,
        ...
    }
    gameIds = [1]  (unchanged - game ID already exists)

Step 3: Players commit moves
    game.playerA.encrMove = hash("1-password")
    game.playerB.encrMove = hash("2-password")

Step 4: Players reveal moves
    game.playerA.move = Rock (1)
    game.playerB.move = Paper (2)
    game.firstReveal = block.timestamp
    game.outcome = PlayerB (Paper beats Rock)

Step 5: Game completes via getOutcome()
    games[1].isActive = false
    Transfer 0.02 ETH to PlayerB
    gameIds = [1]  (unchanged - game ID remains for historical reference)