Skip to content

Instantly share code, notes, and snippets.

@octalmage
Created July 13, 2023 05:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save octalmage/0dbc9bd7a99d17b9bad57e555820f750 to your computer and use it in GitHub Desktop.
Save octalmage/0dbc9bd7a99d17b9bad57e555820f750 to your computer and use it in GitHub Desktop.
War for JAX
class WarContract {
init({ rng }) {
storage.put("rng", rng);
storage.put("id", 1);
}
can_update() {
return blockchain.requireAuth(blockchain.contractAdmin());
}
startGame() {
blockchain.deposit(1000000);
const game = {
player1: tx.publisher,
player1Deck: [],
player2: null,
player2Deck: [],
inProgress: true,
commit: "",
};
const id = storage.get("id");
storage.mapPut("games", id, JSON.stringify(game));
storage.put("id", BigNumber(id).plus(1));
return id;
}
joinGame({ id }) {
const from = tx.publisher;
const gameStr = storage.mapGet("games", id);
if (!gameStr) {
throw new Error("No such game exists");
}
const game = JSON.parse(gameStr);
if (game.player2 !== null) {
throw new Error("Game already has two players");
}
const commit = blockchain.call(storage.get("rng"), "commit");
game.commit = commit;
blockchain.deposit(1000000); // Accept 1000000 tokens
game.player2 = from;
storage.mapPut("games", id, JSON.stringify(game));
}
deal({ id }) {
const gameStr = storage.mapGet("games", id);
if (!gameStr) {
throw new Error("No such game exists");
}
const game = JSON.parse(gameStr);
if (game.player2 === null) {
throw new Error("A second player is needed to play");
}
if (game.player1Deck.length !== 0) {
throw new Error("Cards have already been dealt");
}
const randomNumber = blockchain.call(storage.get("rng"), "reveal", {
id: game.commit,
range: 0,
});
const deck = this._shuffle(randomNumber);
game.player2Deck = deck.slice(deck.length / 2);
game.player1Deck = deck.slice(0, deck.length / 2);
storage.mapPut("games", id, JSON.stringify(game));
}
play({ id }) {
const gameStr = storage.mapGet("games", id);
if (!gameStr) {
throw new Error("You must join a game before playing a turn");
}
const game = JSON.parse(gameStr);
if (!game.inProgress) {
throw new Error("Game complete");
}
let result;
while (game.inProgress) {
let playerDeck, opponentDeck;
if (game.player1 === tx.publisher) {
playerDeck = game.player1Deck;
opponentDeck = game.player2Deck;
} else if (game.player2 === tx.publisher) {
playerDeck = game.player2Deck;
opponentDeck = game.player1Deck;
} else {
throw new Error("You're not part of this game");
}
const playerCard = playerDeck.shift();
const opponentCard = opponentDeck.shift();
if (playerCard > opponentCard) {
playerDeck.push(playerCard, opponentCard);
} else if (opponentCard > playerCard) {
opponentDeck.push(opponentCard, playerCard);
}
if (playerDeck.length === 0 || opponentDeck.length === 0) {
game.inProgress = false;
const winner = playerDeck.length === 0 ? game.player2 : game.player1;
// Payout 2000000 tokens to the game winner
blockchain.withdraw(winner, 2000000);
result = playerDeck.length === 0 ? "You lose the game!" : "You win the game!";
}
}
storage.mapPut("games", id, JSON.stringify(game));
return result;
}
_shuffle(entropy) {
let deck = [];
// Generate deck array using a loop
for (let i = 0; i < 52; i++) {
deck[i] = i + 1;
}
// Expand randomSeed into 52 random numbers out of 100.
const randomSeed = BigNumber(entropy, 16);
const expandedValues = [];
for (let i = 0; i < deck.length; i++) {
const hash = crypto.sha256(randomSeed.plus(i).toString(16));
expandedValues[i] = BigNumber(hash, 16).mod(100).toNumber();
}
// Sort the cards based on the 52 random numbers.
deck.sort((a, b) => {
const aIndex = expandedValues[a - 1];
const bIndex = expandedValues[b - 1];
if (aIndex < bIndex) {
return -1;
}
if (aIndex > bIndex) {
return 1;
}
return 0;
});
return deck;
}
}
module.exports = WarContract;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment