diff --git a/src/pages/Tic-tac-toe 5x5/index.html.ts b/src/pages/Tic-tac-toe 5x5/index.html.ts new file mode 100644 index 0000000..2849311 --- /dev/null +++ b/src/pages/Tic-tac-toe 5x5/index.html.ts @@ -0,0 +1,197 @@ +// metadata +export const version = "0.1.0" +export const title = "Tic-Tac-Toe 5x5" +export const description = "Tic-Tac-Toe 5x5 in sCrypt" +export const replitLink = "https://replit.com/@yusufedresmaina/tictactoe5x5?embed=true" + +const html = `
To initiate a game, Alice and Bob each lock up X amount of bitcoins into a contract UTXO. They take turns to play the game by sending signed transactions interacting with the stateful contract. If one of them wins, the winner claims the 2X bitcoins. If there is a draw, they take back their bitcoins.
+class TicTacToe extends SmartContract {
+ @prop()
+ alice: PubKey
+ @prop()
+ bob: PubKey
+
+ @prop(true)
+ is_alice_turn: boolean
+
+ @prop(true)
+ board: FixedArray<bigint, 25>
+
+ @prop()
+ static readonly TURNLEN: bigint = 1n
+ @prop()
+ static readonly BOARDLEN: bigint = 25n
+ @prop()
+ static readonly EMPTY: bigint = 0n
+ @prop()
+ static readonly ALICE: bigint = 1n
+ @prop()
+ static readonly BOB: bigint = 2n
+
+ constructor(
+ alice: PubKey,
+ bob: PubKey,
+ is_alice_turn: boolean,
+ board: FixedArray<bigint, 25>
+ ) {
+ super(alice, bob, is_alice_turn, board)
+ this.alice = alice
+ this.bob = bob
+ this.is_alice_turn = is_alice_turn
+ this.board = board
+ }
+
+ @method()
+ public move(n: bigint, sig: Sig, amount: bigint) {
+ assert(n >= 0n && n < TicTacToe.BOARDLEN, "Field out of bounds")
+ assert(this.board[Number(n)] == TicTacToe.EMPTY, "Field not empty")
+
+ let play = this.is_alice_turn ? TicTacToe.ALICE : TicTacToe.BOB
+ let player: PubKey = this.is_alice_turn ? this.alice : this.bob
+
+ assert(this.checkSig(sig, player), "Bad sig")
+ // make the move
+ this.board[Number(n)] = play
+ this.is_alice_turn = !this.is_alice_turn
+
+ let outputs = toByteString("")
+ if (this.won(play)) {
+ let outputScript = Utils.buildPublicKeyHashScript(hash160(player))
+ let output = Utils.buildOutput(outputScript, amount)
+ outputs = output
+ } else if (this.full()) {
+ let aliceScript = Utils.buildPublicKeyHashScript(hash160(this.alice))
+ let aliceOutput = Utils.buildOutput(aliceScript, amount)
+
+ let bobScript = Utils.buildPublicKeyHashScript(hash160(this.bob))
+ let bobOutput = Utils.buildOutput(bobScript, amount)
+
+ outputs = aliceOutput + bobOutput
+ } else {
+ outputs = this.buildStateOutput(amount)
+ }
+
+ assert(this.ctx.hashOutputs == hash256(outputs), "Output hashes don't match")
+ }
+
+ @method()
+ won(play: bigint): boolean {
+ let lines: FixedArray<FixedArray<BigInt, 5>, 87> = [
+ [0n, 1n, 2n, 3n, 4n],
+ [1n, 2n, 3n, 4n, 5n],
+ [2n, 3n, 4n, 5n, 6n],
+ [5n, 6n, 7n, 8n, 9n],
+ [6n, 7n, 8n, 9n, 10n],
+ [7n, 8n, 9n, 10n, 11n],
+ [10n, 11n, 12n, 13n, 14n],
+ [11n, 12n, 13n, 14n, 15n],
+ [12n, 13n, 14n, 15n, 16n],
+ [15n, 16n, 17n, 18n, 19n],
+ [16n, 17n, 18n, 19n, 20n],
+ [17n, 18n, 19n, 20n, 21n],
+ [20n, 21n, 22n, 23n, 24n],
+ [21n, 22n, 23n, 24n, 25n],
+ [22n, 23n, 24n, 25n, 26n],
+ [25n, 26n, 27n, 28n, 29n],
+ [26n, 27n, 28n, 29n, 30n],
+ [27n, 28n, 29n, 30n, 31n],
+ [30n, 31n, 32n, 33n, 34n],
+ [31n, 32n, 33n, 34n, 35n],
+ [32n, 33n, 34n, 35n, 36n],
+ [35n, 36n, 37n, 38n, 39n],
+ [36n, 37n, 38n, 39n, 40n],
+ [37n, 38n, 39n, 40n, 41n],
+ [40n, 41n, 42n, 43n, 44n],
+ [41n, 42n, 43n, 44n, 45n],
+ [42n, 43n, 44n, 45n, 46n],
+ [45n, 46n, 47n, 48n, 49n],
+ [46n, 47n, 48n, 49n, 50n],
+ [47n, 48n, 49n, 50n, 51n],
+ [50n, 51n, 52n, 53n, 54n],
+ [51n, 52n, 53n, 54n, 55n],
+ [52n, 53n, 54n, 55n, 56n],
+
+ // Columns
+ [0n, 5n, 10n, 15n, 20n],
+ [5n, 10n, 15n, 20n, 25n],
+ [10n, 15n, 20n, 25n, 30n],
+ [15n, 20n, 25n, 30n, 35n],
+ [20n, 25n, 30n, 35n, 40n],
+ [25n, 30n, 35n, 40n, 45n],
+ [30n, 35n, 40n, 45n, 50n],
+ [35n, 40n, 45n, 50n, 55n],
+ [40n, 45n, 50n, 55n, 60n],
+ [1n, 6n, 11n, 16n, 21n],
+ [6n, 11n, 16n, 21n, 26n],
+ [11n, 16n, 21n, 26n, 31n],
+ [16n, 21n, 26n, 31n, 36n],
+ [21n, 26n, 31n, 36n, 41n],
+ [26n, 31n, 36n, 41n, 46n],
+ [31n, 36n, 41n, 46n, 51n],
+ [36n, 41n, 46n, 51n, 56n],
+ [41n, 46n, 51n, 56n, 61n],
+ [2n, 7n, 12n, 17n, 22n],
+ [7n, 12n, 17n, 22n, 27n],
+ [12n, 17n, 22n, 27n, 32n],
+ [17n, 22n, 27n, 32n, 37n],
+ [22n, 27n, 32n, 37n, 42n],
+ [27n, 32n, 37n, 42n, 47n],
+ [32n, 37n, 42n, 47n, 52n],
+ [37n, 42n, 47n, 52n, 57n],
+ [42n, 47n, 52n, 57n, 62n],
+ [3n, 8n, 13n, 18n, 23n],
+ [8n, 13n, 18n, 23n, 28n],
+ [13n, 18n, 23n, 28n, 33n],
+ [18n, 23n, 28n, 33n, 38n],
+ [23n, 28n, 33n, 38n, 43n],
+ [28n, 33n, 38n, 43n, 48n],
+ [33n, 38n, 43n, 48n, 53n],
+ [38n, 43n, 48n, 53n, 58n],
+ [43n, 48n, 53n, 58n, 63n],
+ [4n, 9n, 14n, 19n, 24n],
+ [9n, 14n, 19n, 24n, 29n],
+ [14n, 19n, 24n, 29n, 34n],
+ [19n, 24n, 29n, 34n, 39n],
+ [24n, 29n, 34n, 39n, 44n],
+ [29n, 34n, 39n, 44n, 49n],
+ [34n, 39n, 44n, 49n, 54n],
+ [39n, 44n, 49n, 54n, 59n],
+ [44n, 49n, 54n, 59n, 64n],
+ [5n, 10n, 15n, 20n, 25n],
+ [10n, 15n, 20n, 25n, 30n],
+ [15n, 20n, 25n, 30n, 35n],
+ [20n, 25n, 30n, 35n, 40n],
+ [25n, 30n, 35n, 40n, 45n],
+ [30n, 35n, 40n, 45n, 50n],
+ [35n, 40n, 45n, 50n, 55n],
+ [40n, 45n, 50n, 55n, 60n],
+ [45n, 50n, 55n, 60n, 65n],
+ ];
+
+ let anyLine = false
+
+ for (let i = 0; i < 87; i++) {
+ let line = true
+ for (let j = 0; j < 5; j++) {
+ line = line && this.board[Number(lines[i][j])] == play
+ }
+
+ anyLine = anyLine || line
+ }
+
+ return anyLine
+ }
+
+ @method()
+ full(): boolean {
+ let full = true
+ for (let i = 0; i < TicTacToe.BOARDLEN; i++) {
+ full = full && this.board[i] != TicTacToe.EMPTY
+ }
+ return full
+ }
+}
+
+`
+
+export default html
diff --git a/src/pages/Tic-tac-toe 5x5/index.tsx b/src/pages/Tic-tac-toe 5x5/index.tsx
new file mode 100644
index 0000000..65e6167
--- /dev/null
+++ b/src/pages/Tic-tac-toe 5x5/index.tsx
@@ -0,0 +1,29 @@
+import React from "react"
+import Example from "../../components/Example"
+import html, { version, title, description, replitLink } from "./index.html"
+
+interface Path {
+ path: string
+ title: string
+}
+
+interface Props {
+ prev: Path | null
+ next: Path | null
+}
+
+const ExamplePage: React.FC