fix game and add playbook

This commit is contained in:
SamKry
2025-12-16 12:25:44 +01:00
parent 773fc76e1c
commit 2711b6ab87
2 changed files with 751 additions and 348 deletions

View File

@@ -0,0 +1,528 @@
# Example Gameplay: Rock-Paper-Scissors Minus One
This document provides a **step-by-step playbook** for two players (Alice and Bob) to play the "Minus One" variant. Follow along to understand the exact order of operations and function calls.
## Quick Reference
### Move Encoding
- `1` = Rock
- `2` = Paper
- `3` = Scissors
### Game Flow
```
1. Registration (both players)
2. Initial Commit (both players)
3. First Reveal (both players)
4. Withdrawal Commit (both players)
5. Withdrawal Reveal (both players)
6. Get Outcome (either player)
```
### Hash Generation Tool
🔗 **https://emn178.github.io/online-tools/keccak_256.html**
- Input Type: **UTF-8**
- Output Type: **Hex**
- Format: `<move>-<password>` (e.g., `1-mypass` for Rock)
### Game Rules
1. Each player commits 2 **different** moves (e.g., Rock + Paper)
2. Both players reveal their 2 moves
3. Each player secretly withdraws 1 move (commits which index: 1 or 2)
4. Both players reveal which move they withdrew
5. The **remaining move** for each player battles to determine winner
---
## Example Game Scenario
**Players:**
- 👩 **Alice** (will choose Rock + Paper, keep Rock)
- 👨 **Bob** (will choose Rock + Scissors, keep Scissors)
**Expected Winner:** Alice (Rock beats Scissors)
**Minimum Bet:** 0.01 ETH (10000000000000000 wei)
---
## 🎮 PHASE 1: REGISTRATION
> **Goal:** Both players join the game with their nicknames and bets
### Step 1.1: Alice Registers (Player 1)
**Function:** `register(uint gameId, string memory name)`
**Alice's Input:**
```
gameId: 0 (creates new game)
name: "Alice"
value: 0.01 ETH
```
**Smart Contract Call:**
```javascript
register(0, "Alice");
// Send 0.01 ETH with transaction
```
**Returns:** `(1, 1)`
- Position: Player 1
- Game ID: 1
✅ Alice is now registered as Player 1 in Game 1
---
### Step 1.2: Bob Registers (Player 2)
**Function:** `register(uint gameId, string memory name)`
**Bob's Input:**
```
gameId: 1 (joins Alice's game)
name: "Bob"
value: 0.01 ETH (must match Alice's bet!)
```
**Smart Contract Call:**
```javascript
register(1, "Bob");
// Send 0.01 ETH with transaction
```
**Returns:** `(2, 1)`
- Position: Player 2
- Game ID: 1
✅ Bob is now registered as Player 2
**Game automatically advances to InitialCommit phase**
---
## 🎮 PHASE 2: INITIAL COMMIT
> **Goal:** Both players commit their 2 encrypted moves (hashes)
### Step 2.1: Alice Prepares Her Hashes (Off-chain)
**Alice's Strategy:**
- Move 1: Rock (1)
- Move 2: Paper (2)
**Generate Hash for Move 1 (Rock):**
1. Go to: https://emn178.github.io/online-tools/keccak_256.html
2. Input: `1-alicepass1` (UTF-8)
3. Output: `0x7364263d5fc729b4709129564a2c516f2eb40f55d8704860e46f597c91b6b264`
**Generate Hash for Move 2 (Paper):**
1. Input: `2-alicepass2` (UTF-8)
2. Output: `0xc9ad7a6c99b3f5af24991d9c66c0cc2b731869f4b7399653f0820f99e34d45ef`
💾 **Alice must save these:**
- Cleartext for reveal: `1-alicepass1` and `2-alicepass2`
- Hashes for commit: `0x7364...` and `0xc9ad...`
---
### Step 2.2: Alice Commits
**Function:** `commitInitialMoves(uint gameId, bytes32 hash1, bytes32 hash2)`
**Alice's Input:**
```
gameId: 1
hash1: 0x7364263d5fc729b4709129564a2c516f2eb40f55d8704860e46f597c91b6b264
hash2: 0xc9ad7a6c99b3f5af24991d9c66c0cc2b731869f4b7399653f0820f99e34d45ef
```
**Smart Contract Call:**
```javascript
commitInitialMoves(
1,
"0x7364263d5fc729b4709129564a2c516f2eb40f55d8704860e46f597c91b6b264",
"0xc9ad7a6c99b3f5af24991d9c66c0cc2b731869f4b7399653f0820f99e34d45ef"
);
```
**Returns:** `true`
✅ Alice has committed her two moves
---
### Step 2.3: Bob Prepares His Hashes (Off-chain)
**Bob's Strategy:**
- Move 1: Rock (1)
- Move 2: Scissors (3)
**Generate Hash for Move 1 (Rock):**
1. Go to: https://emn178.github.io/online-tools/keccak_256.html
2. Input: `1-bobsecret1` (UTF-8)
3. Output: `0x16864f12ec74b4fac1cd9fd5b0db1959e4df91cf55e9180cc13cdf20a134af16`
**Generate Hash for Move 2 (Scissors):**
1. Input: `3-bobsecret2` (UTF-8)
2. Output: `0x86d1def3d3f9bed5f2de22161040c10c75dcb52f263f3c3ec08cdc8ba10d2103`
💾 **Bob must save these:**
- Cleartext for reveal: `1-bobsecret1` and `3-bobsecret2`
- Hashes for commit: `0x1686...` and `0x86d1...`
---
### Step 2.4: Bob Commits
**Function:** `commitInitialMoves(uint gameId, bytes32 hash1, bytes32 hash2)`
**Bob's Input:**
```
gameId: 1
hash1: 0x16864f12ec74b4fac1cd9fd5b0db1959e4df91cf55e9180cc13cdf20a134af16
hash2: 0x86d1def3d3f9bed5f2de22161040c10c75dcb52f263f3c3ec08cdc8ba10d2103
```
**Smart Contract Call:**
```javascript
commitInitialMoves(
1,
"0x16864f12ec74b4fac1cd9fd5b0db1959e4df91cf55e9180cc13cdf20a134af16",
"0x86d1def3d3f9bed5f2de22161040c10c75dcb52f263f3c3ec08cdc8ba10d2103"
);
```
**Returns:** `true`
✅ Bob has committed his two moves
**Game automatically advances to FirstReveal phase**
---
## 🎮 PHASE 3: FIRST REVEAL
> **Goal:** Both players reveal their original cleartext strings to prove their moves
### Step 3.1: Alice Reveals
**Function:** `revealInitialMoves(uint gameId, string memory clear1, string memory clear2)`
**Alice's Input:**
```
gameId: 1
clear1: "1-alicepass1" (the exact string she hashed)
clear2: "2-alicepass2" (the exact string she hashed)
```
**Smart Contract Call:**
```javascript
revealInitialMoves(1, "1-alicepass1", "2-alicepass2");
```
**Returns:** `(Rock, Paper)` (enum values for moves 1 and 3)
✅ Alice's moves are now public:
- Move 1: Rock
- Move 2: Paper
---
### Step 3.2: Bob Reveals
**Function:** `revealInitialMoves(uint gameId, string memory clear1, string memory clear2)`
**Bob's Input:**
```
gameId: 1
clear1: "1-bobsecret1" (the exact string he hashed)
clear2: "3-bobsecret2" (the exact string he hashed)
```
**Smart Contract Call:**
```javascript
revealInitialMoves(1, "1-bobsecret1", "3-bobsecret2");
```
**Returns:** `(Rock, Scissors)` (enum values for moves 1 and 3)
✅ Bob's moves are now public:
- Move 1: Rock
- Move 2: Scissors
**Current Game State:**
- Alice has: Rock (move 1) and Paper (move 2)
- Bob has: Rock (move 1) and Scissors (move 2)
**Game automatically advances to WithdrawalCommit phase**
---
## 🎮 PHASE 4: WITHDRAWAL COMMIT
> **Goal:** Both players secretly commit which move to withdraw (1 or 2)
### Step 4.1: Alice Prepares Withdrawal Hash (Off-chain)
**Alice's Decision:**
- She wants to keep Rock (move 1)
- So she will withdraw move index **2** (Paper)
**Generate Withdrawal Hash:**
1. Go to: https://emn178.github.io/online-tools/keccak_256.html
2. Input: `2-alicewith` (UTF-8) ← Note: "2" is the index to withdraw
3. Output: `0x7c9ff59a4d298766d9480c130f6ed67c06f135a1c2f45326583593bb9c2e6363`
💾 **Alice must save:**
- Cleartext: `2-alicewith`
- Hash: `0x9a3f...`
---
### Step 4.2: Alice Commits Withdrawal
**Function:** `commitWithdraw(uint gameId, bytes32 wHash)`
**Alice's Input:**
```
gameId: 1
wHash: 0x7c9ff59a4d298766d9480c130f6ed67c06f135a1c2f45326583593bb9c2e6363
```
**Smart Contract Call:**
```javascript
commitWithdraw(
1,
"0x7c9ff59a4d298766d9480c130f6ed67c06f135a1c2f45326583593bb9c2e6363"
);
```
**Returns:** `true`
✅ Alice has secretly committed to withdraw move 2
---
### Step 4.3: Bob Prepares Withdrawal Hash (Off-chain)
**Bob's Decision:**
- He wants to keep Scissors (move 2)
- So he will withdraw move index **1** (Rock)
**Generate Withdrawal Hash:**
1. Go to: https://emn178.github.io/online-tools/keccak_256.html
2. Input: `1-bobwith` (UTF-8) ← Note: "1" is the index to withdraw
3. Output: `0x29e944e8859972eb35622d3149120d878d7931aaa9ac2e213d0b321ee564b7dc`
💾 **Bob must save:**
- Cleartext: `1-bobwith`
- Hash: `0x29e9...`
---
### Step 4.4: Bob Commits Withdrawal
**Function:** `commitWithdraw(uint gameId, bytes32 wHash)`
**Bob's Input:**
```
gameId: 1
wHash: 0x29e944e8859972eb35622d3149120d878d7931aaa9ac2e213d0b321ee564b7dc
```
**Smart Contract Call:**
```javascript
commitWithdraw(
1,
"0x29e944e8859972eb35622d3149120d878d7931aaa9ac2e213d0b321ee564b7dc"
);
```
**Returns:** `true`
✅ Bob has secretly committed to withdraw move 1
**Game automatically advances to WithdrawalReveal phase**
---
## 🎮 PHASE 5: WITHDRAWAL REVEAL
> **Goal:** Both players reveal which move they withdrew
### Step 5.1: Alice Reveals Withdrawal
**Function:** `withdrawMove(uint gameId, string memory clear)`
**Alice's Input:**
```
gameId: 1
clear: "2-alicewith" (the exact string she hashed for withdrawal)
```
**Smart Contract Call:**
```javascript
withdrawMove(1, "2-alicewith");
```
**Returns:** `2`
✅ Alice withdrew move 2 (Paper)
✅ Alice keeps: **Rock** (move 1)
---
### Step 5.2: Bob Reveals Withdrawal
**Function:** `withdrawMove(uint gameId, string memory clear)`
**Bob's Input:**
```
gameId: 1
clear: "1-bobwith" (the exact string he hashed for withdrawal)
```
**Smart Contract Call:**
```javascript
withdrawMove(1, "1-bobwith");
```
**Returns:** `1`
✅ Bob withdrew move 1 (Rock)
✅ Bob keeps: **Scissors** (move 2)
**Final Battle:**
- Alice's final move: **Rock**
- Bob's final move: **Scissors**
- **Winner: Alice!** (Rock beats Scissors)
**Game automatically determines outcome and advances to Done phase**
---
## 🎮 PHASE 6: GET OUTCOME
> **Goal:** Either player calls to finalize and receive payout
### Step 6.1: Get Outcome (Either Player Can Call)
**Function:** `getOutcome(uint gameId)`
**Input:**
```
gameId: 1
```
**Smart Contract Call:**
```javascript
getOutcome(1);
```
**Returns:** `Outcomes.A` (Alice wins)
**What Happens:**
1. Contract calculates winner: Rock beats Scissors
2. Alice receives **0.02 ETH** (both players' bets)
3. Game is marked inactive
4. Contract balance reduced by payout
**Game Complete!** Alice won and received 0.02 ETH
---
## 📊 Summary Table
| Step | Who Acts | Function | Key Inputs | Result |
| ---- | -------- | --------------------------------------- | ------------------------------------------- | ----------------------------- |
| 1.1 | Alice | `register(0, "Alice")` | gameId=0, name="Alice", value=0.01 ETH | Player 1 in Game 1 |
| 1.2 | Bob | `register(1, "Bob")` | gameId=1, name="Bob", value=0.01 ETH | Player 2 in Game 1 |
| 2.1 | Alice | `commitInitialMoves(1, hash1, hash2)` | Hashes of "1-alicepass1" and "2-alicepass2" | Committed |
| 2.2 | Bob | `commitInitialMoves(1, hash1, hash2)` | Hashes of "1-bobsecret1" and "3-bobsecret2" | Committed |
| 3.1 | Alice | `revealInitialMoves(1, clear1, clear2)` | "1-alicepass1", "2-alicepass2" | Reveals Rock + Paper |
| 3.2 | Bob | `revealInitialMoves(1, clear1, clear2)` | "1-bobsecret1", "3-bobsecret2" | Reveals Rock + Scissors |
| 4.1 | Alice | `commitWithdraw(1, wHash)` | Hash of "2-alicewith" | Committed withdrawal |
| 4.2 | Bob | `commitWithdraw(1, wHash)` | Hash of "1-bobwith" | Committed withdrawal |
| 5.1 | Alice | `withdrawMove(1, clear)` | "2-alicewith" | Withdrew Paper, keeps Rock |
| 5.2 | Bob | `withdrawMove(1, clear)` | "1-bobwith" | Withdrew Rock, keeps Scissors |
| 6.1 | Either | `getOutcome(1)` | gameId=1 | Alice wins, gets 0.02 ETH |
---
## ⚠️ Important Notes
### Must Save These Strings!
- **Initial moves cleartext:** You need them in Phase 3
- **Withdrawal cleartext:** You need it in Phase 5
- **If you lose these strings, you cannot reveal and will timeout!**
### Order Matters
- Either player can act first within each phase
- Game won't advance until **both players** complete the current phase
- You have **10 minutes** per phase (commit or reveal)
### Common Mistakes
❌ Using different strings in reveal than in commit → "Hash mismatch"
❌ Committing same move twice (e.g., Rock + Rock) → "Moves must differ"
❌ Second player betting different amount → "Bet must match initial"
❌ Withdrawing index 0 or 3 → "Index must be 1 or 2"
❌ Losing your cleartext strings → Cannot reveal, will timeout
### Timeout Protection
If your opponent doesn't act within 10 minutes:
```javascript
resolveTimeout(1); // Call this to win by default
```