examples/hex-game/README.md
Hex is a game where player One tries to connect the left and right sides of the board and player
Two tries to connect top to bottom. The board is rhombic and has a configurable side length s.
It consists of s * s hexagonal cells, indexed like this:
(0, 0) (1, 0) (2, 0)
(0, 1) (1, 1) (2, 1)
(0, 2) (1, 2) (2, 2)
The players alternate placing a stone in their color on an empty cell until one of them wins.
This implementation shows how to write a game that is played on a shared temporary chain: Users make turns by submitting operations to the chain, not by sending messages, so a player does not have to wait for any other chain owner to accept any message.
Make sure you have the linera binary in your PATH, and that it is compatible with your
linera-sdk version.
For scripting purposes, we also assume that the BASH function linera_spawn is defined.
From the root of Linera repository, this can be achieved as follows:
export PATH="$PWD/target/debug:$PATH"
source /dev/stdin <<<"$(linera net helper 2>/dev/null)"
Start the local Linera network and run a faucet:
FAUCET_PORT=8079
FAUCET_URL=http://localhost:$FAUCET_PORT
linera_spawn linera net up --with-faucet --faucet-port $FAUCET_PORT
# If you're using a testnet, run this instead:
# LINERA_TMP_DIR=$(mktemp -d)
# FAUCET_URL=https://faucet.testnet-XXX.linera.net # for some value XXX
Enable logs for user applications:
export LINERA_APPLICATION_LOGS=true
Create the user wallets and add chains to them:
export LINERA_WALLET_1="$LINERA_TMP_DIR/wallet_1.json"
export LINERA_KEYSTORE_1="$LINERA_TMP_DIR/keystore_1.json"
export LINERA_STORAGE_1="rocksdb:$LINERA_TMP_DIR/client_1.db"
export LINERA_WALLET_2="$LINERA_TMP_DIR/wallet_2.json"
export LINERA_KEYSTORE_2="$LINERA_TMP_DIR/keystore_2.json"
export LINERA_STORAGE_2="rocksdb:$LINERA_TMP_DIR/client_2.db"
linera --with-wallet 1 wallet init --faucet $FAUCET_URL
linera --with-wallet 2 wallet init --faucet $FAUCET_URL
INFO_1=($(linera --with-wallet 1 wallet request-chain --faucet $FAUCET_URL))
INFO_2=($(linera --with-wallet 2 wallet request-chain --faucet $FAUCET_URL))
CHAIN_1="${INFO_1[0]}"
CHAIN_2="${INFO_2[0]}"
OWNER_1="${INFO_1[1]}"
OWNER_2="${INFO_2[1]}"
Note that linera --with-wallet 1 or linera -w1 is equivalent to linera --wallet "$LINERA_WALLET_1" --keystore "$LINERA_KEYSTORE_1" --storage "$LINERA_STORAGE_1".
We open a new chain owned by both $OWNER_1 and $OWNER_2, create the application on it, and
start the node service.
APP_ID=$(linera -w1 --wait-for-outgoing-messages \
project publish-and-create examples/hex-game hex_game $CHAIN_1 \
--json-argument "{
\"startTime\": 600000000,
\"increment\": 600000000,
\"blockDelay\": 100000000
}")
OWNER_1=$(linera -w1 keygen)
OWNER_2=$(linera -w2 keygen)
linera -w1 service --port 8080 &
sleep 1
Type each of these in the GraphiQL interface and substitute the env variables with their actual values that we've defined above.
The start mutation starts a new game. We specify the two players using their new public keys,
on the URL you get by running echo "http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID":
mutation {
start(
players: [
"$OWNER_1",
"$OWNER_2"
],
boardSize: 11,
feeBudget: "1"
)
}
The app's main chain keeps track of the games in progress, by public key:
query {
gameChains {
keys(count: 3)
}
}
It contains the temporary chain's ID, and the ID of the message that created it:
query {
gameChains {
entry(key: "$OWNER_1") {
value {
chainId
}
}
}
}
Set the QUERY_RESULT variable to have the result returned by the previous query, and HEX_CHAIN will be properly set for you.
Alternatively you can set the variable to the chainId, returned by the previous query yourself.
Using the chain ID, we can assign the new chain to the key in each wallet:
kill %% && sleep 1 # Kill the service so we can use CLI commands for wallet 0.
HEX_CHAIN=$(echo "$QUERY_RESULT" | jq -r '.gameChains.entry.value[0].chainId')
linera -w1 assign --owner $OWNER_1 --chain-id $HEX_CHAIN
linera -w2 assign --owner $OWNER_2 --chain-id $HEX_CHAIN
linera -w1 service --port 8080 &
linera -w2 service --port 8081 &
sleep 1
Now the first player can make a move by navigating to the URL you get by running echo "http://localhost:8080/chains/$HEX_CHAIN/applications/$APP_ID":
mutation { makeMove(x: 4, y: 4) }
And the second player at the URL you get by running echo "http://localhost:8080/chains/$HEX_CHAIN/applications/$APP_ID":
mutation { makeMove(x: 4, y: 5) }