Protocol
Initial Handshake
- Decide which player is Player 1 and which player is Player 2. In a networked card game, a simple method is to name the player listening for connections Player 1 and the player who who initiated the connection Player 2.
- Generate
SEED_LENGTH
bytes of random data and share it with the other player. shared_seed
isSEED_LENGTH * 2
bytes, consisting of Player 1’s seed followed by Player 2’s seed.- Generate
RAND_LENGTH
bytes of random data and store it privately aslocal_rand_seed
. - Players create their decks of
NUM_CARDS_IN_DECK
cards, storing them as an array of integers (implementation-defined size) inlocal_deck_initial
. - Compute
HASH_FUNC(shared_seed + local_rand_seed + local_deck_initial)
(where + denotes concatenation) and send it to the other player, storing the received data asremote_handshake_hash
. - If card backs differ, generate an array of
NUM_CARDS_IN_DECK
integers (implementation-defined size) specifying the card backs and send it to the opposing player.
Per-Turn Handshake
- Generate
RAND_LENGTH
bytes of random data and store it privately as this turn’sshared_rand_seed[idx]
, where idx is the player number (1 or 2). - Compute
HASH_FUNC(shared_seed + shared_rand_seed[idx])
and send it to the other player. - After both players have received the other player’s hash, send
shared_rand_seed[idx]
, storing the received value in the other slot ofshared_rand_seed
. - Verify that the hash matches the received data.
- Compute
HASH_FUNC(shared_seed + shared_rand_seed[1] + shared_rand_seed[2])
and store it privately asshared_rand_state
. - Compute
HASH_FUNC(shared_rand_state + local_rand_seed)
and store it privately aslocal_rand_state
. - Set both
shared_rand_idx
andlocal_rand_idx
to 0.
Ending Handshake
- Send
local_rand_seed + local_deck_initial
to the other player. - Verify that
remote_handshake_hash
is correct. - For each turn, verify that the actions taken were valid.
Appendix A: Random Number Generation
To generate a random number in [0, n)
:
- Find the lowest number
b
such thatn <= 256^b
(where^
denotes exponentiation). - Find the highest number
m
such thatm
is a multiple ofn
andm <= 256^b
- Call
advanceRNGMulti(b)
until the result is less than or equal tom
. - Return the result of
advanceRNGMulti
modulon
.
advanceRNGMulti(b)
is defined as:
- If b = 0: return
0
. Otherwise: - Store
advanceRNGMulti(b - 1)
asv
. - Return
v + advanceRNG() * 256^(b - 1)
. (Bitwise OR is equivalent to addition here.)
advanceRNG()
is defined as:
- If
*_rand_idx
is less than the length of*_rand_state
: - Store the
*_rand_idx
’th byte of*_rand_state
inx
. - Add 1 to
*_rand_idx
. - Return
x
. - Otherwise:
- Set
*_rand_idx
to 0. - Update
*_rand_state
tonextRNGState(*_rand_state)
. - Return the 0th byte of
*_rand_state
.
nextRNGState(s)
is defined as:
- Return
HASH_FUNC(shared_seed + s)
(where + denotes concatenation).
Appendix B: Shuffling
Basic shuffle is defined as the pseudocode:
for i from 1 to (num_items - 1):
j := random number in [0, i)
swap items[i] and items[j]
Deck shuffle consists of:
- Sort cards in the deck into groups based on their backs, keeping their original order.
- Perform a basic shuffle on the card backs using the shared random number state.
- For each card back type, in a deterministic order, perform a basic shuffle on the group using the local random number state.
- For each card back in the sequence generated by step 2, put the first unused card in that card back’s group into the card back’s sequence position.