{"id":23731104,"url":"https://github.com/0xvoidmain/a8-tic-tac-toe-hackathon","last_synced_at":"2026-02-20T15:30:21.125Z","repository":{"id":269748250,"uuid":"908314075","full_name":"0xvoidmain/a8-tic-tac-toe-hackathon","owner":"0xvoidmain","description":null,"archived":false,"fork":false,"pushed_at":"2024-12-25T20:27:13.000Z","size":3057,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-25T21:18:40.790Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0xvoidmain.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-25T17:49:57.000Z","updated_at":"2024-12-25T20:27:17.000Z","dependencies_parsed_at":"2024-12-25T21:18:43.394Z","dependency_job_id":"86b90b28-c8a7-4de2-858b-76375b5876a5","html_url":"https://github.com/0xvoidmain/a8-tic-tac-toe-hackathon","commit_stats":null,"previous_names":["0xvoidmain/a8-tic-tac-toe-hackathon"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xvoidmain%2Fa8-tic-tac-toe-hackathon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xvoidmain%2Fa8-tic-tac-toe-hackathon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xvoidmain%2Fa8-tic-tac-toe-hackathon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xvoidmain%2Fa8-tic-tac-toe-hackathon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0xvoidmain","download_url":"https://codeload.github.com/0xvoidmain/a8-tic-tac-toe-hackathon/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239846530,"owners_count":19706788,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-31T03:16:46.265Z","updated_at":"2026-02-20T15:30:19.073Z","avatar_url":"https://github.com/0xvoidmain.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Setup project\n\n### Smart contract\n```\nnpm init\nnpm install --save-dev hardhat\nnpx hardhat init\n```\n\n### Create or rename contract file\n\n`contrants \u003e Lock.sol =\u003e TicTacToe.sol`\n\n```js\n// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.28;\n\ncontract TicTacToe {\n}\n```\n\n### Create or rename deploy file\n\n`ingnition \u003e modules \u003e Lock.ts =\u003e Deploy.ts`\n\n```js\nimport { buildModule } from \"@nomicfoundation/hardhat-ignition/modules\";\n\nconst DeployModule = buildModule(\"DeployModule\", (m) =\u003e {\n  \n  const contract = m.contract(\"TicTacToe\");\n\n  return { contract };\n});\n\nexport default DeployModule;\n```\n\n### Config Hardhat\nUpdate `package.json`\n```js\n\"scripts\": {\n    \"compile\": \"hardhat compile\",\n    \"deploy\": \"npx hardhat ignition deploy ignition/modules/Deploy.ts\",\n    \"deploy-verify\": \"npx hardhat ignition deploy ignition/modules/Deploy.ts --verify\"\n}\n```\n\nconfig `hardhat.config.ts`\n```js\nimport { HardhatUserConfig } from \"hardhat/config\";\nimport \"@nomicfoundation/hardhat-toolbox\";\n\nconst config: HardhatUserConfig = {\n  solidity: {\n    version: \"0.8.28\",\n    settings: {\n      optimizer: {\n        enabled: true,\n        runs: 200\n      },\n      viaIR: true\n    }\n  },\n  ignition: {\n    requiredConfirmations: 1\n  },\n  networks: {\n    hardhat: {\n    },\n    a8testnet: {\n      url: 'https://rpcv2-testnet.ancient8.gg',\n      accounts: ['your private key'],\n      gasPrice: 1000000000\n    }\n  },\n  etherscan: {\n    apiKey: {\n      a8testnet: '',\n    },\n    customChains: [\n      {\n        network: \"a8testnet\",\n        chainId: 28122024,\n        urls: {\n          apiURL: \"https://scanv2-testnet.ancient8.gg/api\",\n          browserURL: \"https://scanv2-testnet.ancient8.gg\",\n        }\n      }\n    ]\n  },\n  defaultNetwork: 'a8testnet',\n};\n\nexport default config;\n\n```\n\n\n# Đặt mục tiêu\n\n### Sản phẩm\n1. Game cờ cá rô 256 ô ~ 16x16\n2. 2 người chơi với nhau\n3. Mỗi người chơi sẽ đặt cược một lượng ETH\n4. Ai thắng trò chơi sẽ nhận được toàn bộ token của người chơi còn lại\n6. Quá 60s mà không đi nước cờ của mình thì thua cuộc\n7. Người chơi truy cập vào web-app kết nối metamask\n8. Người chơi có thể tạo một game hoặc join vào game của người chơi khác\n9. Người chơi claim reward khi chiến thắng\n\n### Luật chơi cờ cá rô\n1. Ai đạt được 5 quân liên tiếp theo hàng ngang, dọc, chéo là chiến thắng\n2. Chấp nhận chặn 2 đầu\n4. Hoà khi cả 2 bên cùng xác nhận là hoà hoặc không còn ô nào để đi\n\n\n\n.\n# Code, code, code\n\n### game structure - v1\n1. Người chơi X\n2. Người chơi O\n3. Ma trận bàn cờ\n4. Số tiền cược\n5. Nước đi của người chơi\n6. Kết quả trận đấu\n\n```js\nstruct GameV1 {\n    address playerX;\n    address playerO;\n    uint betAmount;\n    uint8[256] matrix;\n    uint8 winner; //1: playerX, 2: playerO\n    uint8 moveOf; // 1/2: Ai vừa đi\n    uint8 movePosition; // vị trí\n    uint32 movedAt; // timestamp của nước cờ gần nhất\n}\n```\n\n### Matrix\n```js\nmatrix = [0, 0, 0, 1, 1, 2, 1, 0, 2]\n\n//OR\n\nmatrix = [\n    [0, 0, 0, 0, 0, 0],\n    [0, 0, 1, 1, 0, 1],\n    [0, 0, 1, 2, 2, 1],\n    [0, 0, 1, 2, 2, 1],\n    [0, 0, 1, 2, 1, 1]\n]\n```\n\n### Tối ưu matrix\n```js\nuint X = 0b000001010101010101011110101010101\nuint O = 0b101010101010101010100001010000000\n\nuint matrix = X | O //OR BIT = 0b101011111111111111111111111010101\n```\n\n### Game Struct\n```js\nstruct Game {\n    address playerX;\n    address playerO;\n    uint X;\n    uint O;\n    uint betAmount;\n    uint8 winner; //0: no winner/draw, 1: playerX, 2: playerO\n    uint8 moveOf; \n    uint8 movePos;\n    uint64 movedAt;\n}\n\n\nGame[] public games;\n\nuint8 public constant X = 1;\nuint8 public constant O = 2;\nuint8 public constant SIZE = 16;\nuint8 public constant MAX_TIME = 60;\nuint public constant DRAW_MARK = type(uint).max;\n```\n\n### Modifier\n```js\nmodifier onlyPlayer(uint gameId) {\n  require(games[gameId].playerX == msg.sender || games[gameId].playerO == msg.sender, \"You are not in this game\");\n  _;\n}\n```\n\n### CreateGame \u0026 JoinGame\n\n```js\nfunction createGame() public payable {\n    games.push(Game({\n        playerX: msg.sender,\n        playerO: address(0),\n        X: 0,\n        O: 0,\n        betAmount: msg.value,\n        winner: 0,\n        move: 0\n    }));\n}\n\nfunction joinGame(uint gameId) public payable {\n    require(games[gameId].playerX != address(0), \"Game does not exist\");\n    require(games[gameId].playerO == address(0), \"Game is full\");\n    require(games[gameId].playerX != msg.sender, \"You can't join your own game\");\n    require(games[gameId].betAmount == msg.value, \"Bet amount is not correct\");\n    games[gameId].playerO = msg.sender;\n    games[gameId].movedAt = uint64(block.timestamp);\n}\n```\n\n### Đi nước cờ của mình và kiểm tra chiến thắng - version 1\n```js\nfunction checkWin(uint board, uint pos) public pure returns (bool) {\n  // Quét bàn cờ dựa vào vị trí đánh mới nhất của người chơi\n  // Nếu tạo thành 5 quân cờ liên tiếp thì chiến thắng\n  // Cần 4 vòng lặp và các phép tính dịch bit để check\n  return false;\n}\n\nfunction makeMove(uint gameId, uint8 pos) public onlyPlayer(gameId) {\n    Game storage game = games[gameId];\n\n    require(games[gameId].playerX != address(0) \u0026\u0026 games[gameId].playerO != address(0), \"Game is not ready\");\n    require(game.winner == 0, \"Game is over\");\n    require(game.movedAt == 0 || uint256(game.movedAt) + MAX_TIME \u003e block.timestamp, \"Time is up\");\n\n    require(pos \u003c SIZE * SIZE, \"Invalid position 1\");\n    require(game.X \u0026 game.O \u0026 (1 \u003c\u003c pos) == 0, \"Invalied position 2\");\n\n    game.movePos = pos;\n    game.movedAt = uint64(block.timestamp);\n    if (game.moveOf == X) {\n        require(game.playerO == msg.sender, \"Wrong turn\");\n        game.moveOf = O;\n        game.O |= (1 \u003c\u003c pos);\n\n        if (checkWin(game.O, uint(pos))) {\n            game.winner = O;\n        }\n    }\n    else { // X goes first\n        require(game.playerX == msg.sender, \"Wrong turn\");\n        game.moveOf = X;\n        game.X |= (1 \u003c\u003c pos);\n\n        if (checkWin(game.X, uint(pos))) {\n            game.winner = X;\n        }\n    }\n}\n```\n\n### Nâng cấp hàm đi nước cờ của mình kết hợp với client để tối ưu gas\n```js\n// winline: ROW = 1, COLUMN = 2, LEFT-RIGHT: 3, RIGHT-LEFT: 4\nfunction checkWin(uint board, uint winLine, uint start) public pure returns(bool) {\n    if (start \u003e= SIZE * SIZE) return false;\n\n    uint mark = 0;\n\n    if (winLine == 1) {\n        if (start % SIZE \u003e= SIZE - 4) return false;\n        for (uint i = start; i \u003c= start + 4; i++) {\n            mark |= 1 \u003c\u003c i;\n        }\n    }\n    \n    if (winLine == 2) {\n        if (start / SIZE \u003e=  SIZE - 4) return false;\n\n        for (uint i = start; i \u003c= start + SIZE * 4; i += SIZE) {\n            mark |= 1 \u003c\u003c i;\n        }\n    }\n    \n    if (winLine == 3) {\n        if (start % SIZE \u003e= SIZE - 4 || start / SIZE \u003e=  SIZE - 4) return false;\n\n        for (uint i = start; i \u003c= start + SIZE * 4 + 4; i += SIZE + 1) {\n            mark |= 1 \u003c\u003c i;\n        }\n    }\n    \n    if (winLine == 4) {\n        if (start % SIZE \u003c 4 || start / SIZE \u003e=  SIZE - 4) return false;\n\n        for (uint i = start; i \u003c= start + SIZE * 4 - 4; i += SIZE - 1) {\n            mark |= 1 \u003c\u003c i;\n        }\n    }\n\n    return mark \u0026 board == mark;\n}\n```\n\n```js\nfunction makeMove(uint gameId, uint8 pos, uint8 winLine, uint8 winPos) public onlyPlayer(gameId) {\n    Game storage game = games[gameId];\n\n    require(games[gameId].playerX != address(0) \u0026\u0026 games[gameId].playerO != address(0), \"Game is not ready\");\n    require(game.winner == 0, \"Game is over\");\n    require(game.movedAt == 0 || uint256(game.movedAt) + MAX_TIME \u003e block.timestamp, \"Time is up\");\n\n    require(pos \u003c SIZE * SIZE, \"Invalid position 1\");\n    require(game.X \u0026 game.O \u0026 (1 \u003c\u003c pos) == 0, \"Invalied position 2\");\n\n    game.movePos = pos;\n    game.movedAt = uint64(block.timestamp);\n    if (game.moveOf == X) {\n        require(game.playerO == msg.sender, \"Wrong turn\");\n        game.moveOf = O;\n        game.O |= (1 \u003c\u003c pos);\n\n        if (winLine \u003e 0 \u0026\u0026 checkWin(game.O, uint(winLine), uint(winPos))) {\n            game.winner = O;\n        }\n    }\n    else { // X goes first\n        require(game.playerX == msg.sender, \"Wrong turn\");\n        game.moveOf = X;\n        game.X |= (1 \u003c\u003c pos);\n\n        if (winLine \u003e 0 \u0026\u0026 checkWin(game.X, uint(winLine), uint(winPos))) {\n            game.winner = X;\n        }\n    }\n}\n```\n\n\n### ClaimReward\n```js\n// Khai báo thêm mapping đánh dấu đã claim\nmapping(address =\u003e mapping(uint =\u003e bool)) public claimed;\n```\n```js\nfunction claimReward(uint gameId) public onlyPlayer(gameId) {\n    require(claimed[msg.sender][gameId] == false, \"You have already claimed this game\");\n    claimed[msg.sender][gameId] = true;\n    \n    Game memory game = games[gameId];\n    uint lastMoveAt = game.move % 10 ** 10;\n    uint lastMoveIsX_O = game.move / 10 ** 20;\n\n    require(game.X | game.O != DRAW_MARK, \"Game is draw\");\n\n    if (game.winner == 0 \u0026\u0026 lastMoveIsX_O == X \u0026\u0026 lastMoveAt + MAX_TIME \u003c block.timestamp) {\n        game.winner = O;\n    }\n    else if (game.winner == 0 \u0026\u0026 lastMoveIsX_O == O \u0026\u0026 lastMoveAt + MAX_TIME \u003c block.timestamp) {\n        game.winner = X;\n    }\n\n    require(game.winner != 0, \"No winner yet\");\n\n    if (game.winner == X \u0026\u0026 game.playerX == msg.sender) {\n        payable(msg.sender).transfer(game.betAmount * 2);\n    }\n    else if (game.winner == O \u0026\u0026 game.playerO == msg.sender) {\n        payable(msg.sender).transfer(game.betAmount * 2);\n    }\n}\n```\n\n### Add some get functions\n```js\nfunction allGames() public view returns(Game[] memory) {\n    return games;\n}\n\nfunction myGame(address add) public view returns(uint index, Game memory game) {\n    for (uint i = games.length - 1; i \u003e= 0; i--) {\n        if (games[i].playerX == add || games[i].playerO == add) {\n            return (i, games[i]);\n        }\n\n        if (i == 0) {\n            break;\n        }\n    }\n    return (0, Game({\n        playerX: address(0),\n        playerO: address(0),\n        X: 0,\n        O: 0,\n        betAmount: 0,\n        winner: 0,\n        moveOf: 0,\n        movePos: 0,\n        movedAt: 0\n    }));\n}\n```\n# Deploy SmartContract\n```\nnpm run compile\nnpm run deploy-verify\n```\n\n# WEBAPP\n\n```\nnpm create wagmi@latest\n```\n\nopen `wagmi.ts`\nsetup\n```\nexport const config = createConfig({\n  chains: [ancient8Sepolia],\n  connectors: [\n    injected()\n  ],\n  transports: {\n    [ancient8Sepolia.id]: http(),\n  },\n})\n```\n\n### create `contractConfig.ts`\n\n```js\nexport const contractConfigs = {\n    // ignition \u003e deployments \u003e chain-*** \u003e deployed_addresses.json\n    address: '0x3ED9Fb4ae0EFAEEb96B1ec9DaD256b6f48A51909',\n    \"abi\": [\n        //run compile and deploy\n        //open ignition \u003e deployments \u003e chain-*** \u003e artifacts \u003e DeployModule.json\n        //Copy ABI \n        \n        {\n            \"inputs\": [],\n            \"name\": \"DRAW_MARK\",\n            \"outputs\": [\n                {\n                \"internalType\": \"uint256\",\n                \"name\": \"\",\n                \"type\": \"uint256\"\n                }\n            ],\n            \"stateMutability\": \"view\",\n            \"type\": \"function\"\n        },\n        ...\n    ]\n```\n\n### Create Wallet components\n```js\nimport { formatEther } from \"viem\"\nimport { useAccount, useBalance, useConnect, useDisconnect } from \"wagmi\"\n\nexport default () =\u003e {\n    const account = useAccount()\n    const { connectors, connect, status, error } = useConnect()\n    const { disconnect } = useDisconnect()\n\n    var { data } = useBalance({ address: account.address })\n\n    if (account.status === 'connected') {\n        return (\n        \u003cdiv\u003e\n            \u003ch2\u003eAccount\u003c/h2\u003e\n            \u003cdiv\u003e\n                status: {account.status}({account.chainId}) | {account.address}\n                \u0026nbsp;\u0026nbsp;\n                \u003cbutton type=\"button\" onClick={() =\u003e disconnect()}\u003e\n                    Disconnect\n                \u003c/button\u003e\n            \u003c/div\u003e\n            \u003cdiv\u003eBalance: {formatEther(data?.value || BigInt(0))}\u003c/div\u003e\n        \u003c/div\u003e)\n    }\n\n    return (\n        \u003cdiv\u003e\n            \u003ch2\u003eConnect Wallet ({status})\u003c/h2\u003e\n            {connectors.map((connector) =\u003e (\n                \u003cbutton\n                key={connector.uid}\n                onClick={() =\u003e connect({ connector })}\n                type=\"button\"\n                \u003e\n                {connector.name}\n                \u003c/button\u003e\n            ))}\n            \u003cdiv\u003e{error?.message}\u003c/div\u003e\n        \u003c/div\u003e\n    )\n}\n```\n\n### Create ListOfGames component\n```js\nimport { useEffect } from \"react\"\nimport { useReadContract, useWriteContract } from \"wagmi\"\nimport { contractConfigs } from \"../../contractConfig\"\nimport { formatEther, zeroAddress } from \"viem\"\n\nexport default () =\u003e {\n    const { data: games, refetch: refetchGames } = useReadContract({\n        ...contractConfigs,\n        functionName: 'allGames'\n    })\n\n\n    const { \n        writeContract \n    } = useWriteContract()\n\n\n    useEffect(() =\u003e {\n        setInterval(() =\u003e {\n            refetchGames()\n        }, 5000)\n    })\n\n    const joinGame = async (game: any, index: number) =\u003e {\n        writeContract({\n            ...contractConfigs,\n            functionName: 'joinGame',\n            args: [BigInt(index)],\n            value: game.betAmount\n        })\n    }\n    return (\n        \u003cdiv\u003e\n            \u003cdiv\u003e\n                \u003ch3\u003eList Game\u003c/h3\u003e\n                \u003cul\u003e\n                {games?.map((game, index) =\u003e (\n                    \u003cli key={index}\u003e\n                        \u003cb\u003e{index}\u003c/b\u003e: {game.playerX.substring(0, 5)}...{game.playerX.substring(28, 32)}: {formatEther(game.betAmount)} ETH\n                        \u0026nbsp;\n                        {game.playerO == zeroAddress \u0026\u0026 \u003cbutton type=\"button\" onClick={() =\u003e joinGame(game, index)}\u003eJoin\u003c/button\u003e}\n                    \u003c/li\u003e\n                ))}\n                \u003c/ul\u003e\n            \u003c/div\u003e\n        \u003c/div\u003e\n    )\n}\n```\n\n### Create Game component\n```js\nimport { useAccount, useReadContract, useWriteContract } from \"wagmi\"\nimport { contractConfigs } from \"../../contractConfig\"\nimport { zeroAddress } from \"viem\"\nimport { useEffect, useState } from \"react\"\nimport checkWin from \"../../utils/checkWin\"\n\nexport default () =\u003e {\n  const account = useAccount()\n  const [currentTime, setCurrentTime] = useState(0)\n\n  const { data: SIZE } = useReadContract({\n    ...contractConfigs,\n    functionName: 'SIZE',\n  })\n\n  const { data: MAX_TIME } = useReadContract({\n    ...contractConfigs,\n    functionName: 'MAX_TIME',\n  })\n\n  const size = Number(SIZE)\n  const maxTime = Number(MAX_TIME)\n\n  const { data: myGame, refetch } = useReadContract({\n    ...contractConfigs,\n    functionName: 'myGame',\n    args: [account.address || zeroAddress]\n  })\n  \n  const gameId = myGame?.[0]\n  const game = myGame?.[1]\n  const lastMoveAt = Number(myGame?.[1].movedAt || BigInt(0))\n  const lastMoveIsX_O = Number((myGame?.[1].moveOf || BigInt(0)))\n\n  const { \n    writeContract \n  } = useWriteContract()\n\n\n  useEffect(() =\u003e {\n      setInterval(() =\u003e {\n        refetch()\n      }, 5000)\n  }, [])\n\n  useEffect(() =\u003e {\n    const id = setInterval(() =\u003e {\n      setCurrentTime(Math.round(new Date().getTime() / 1000))\n    }, 1000)\n    return () =\u003e clearInterval(id)\n  })\n\n\n  const move = async (index: number) =\u003e {\n    var win = checkWin(\n      game?.X.toString(2).split('').reverse() || [], \n      game?.O.toString(2).split('').reverse() || [], \n      index, \n      game?.playerX == account.address ? 1 : 2, \n      size)\n    console.log(win)\n    \n    writeContract({\n      ...contractConfigs,\n      functionName: 'makeMove',\n      args: [\n        BigInt(gameId || 0), \n        index, \n        win ? win.winLine : 0, \n        win ? win.start : 0\n      ]\n    })\n  }\n\n  const claimReward = async () =\u003e {\n    writeContract({\n      ...contractConfigs,\n      functionName: 'claimReward',\n      args: [BigInt(gameId || 0)]\n    })\n  }\n\n  const renderValue = (index: number) =\u003e {\n    if (game?.X.toString(2).split('').reverse()[index] == '1') {\n      return \u003cspan\u003e❌\u003c/span\u003e\n    }\n    if (game?.O.toString(2).split('').reverse()[index] == '1') {\n      return \u003cspan\u003e🟢\u003c/span\u003e\n    }\n    return \u003cspan style={{opacity: 0.2, fontSize: 12}}\u003e{index}\u003c/span\u003e\n  }\n\n  const countDown = lastMoveAt + maxTime - currentTime\n\n  if (game?.playerO == account.address || game?.playerX == account.address) {\n    return \u003cdiv\u003e\n      \u003cdiv style={{textAlign: 'center'}}\u003e\n        \u003ch3\u003eGame: #{gameId?.toString() || 0}\u003c/h3\u003e\n        \u003cspan\u003e\n          \u003cb\u003ePlayer ❌\u003c/b\u003e: {game?.playerX.substring(0, 5)}...{game?.playerX.substring(28, 32)}\n          \u003cb style={{color: 'red'}}\u003e\u0026nbsp;\u0026nbsp;{(lastMoveIsX_O == 0 || lastMoveIsX_O == 2) \u0026\u0026 Number(game?.winner) == 0 \u0026\u0026 `[${countDown \u003e 0 ? countDown : 0}]`}\u003c/b\u003e\n          \u003cb style={{color: 'red'}}\u003e\u0026nbsp;\u0026nbsp;{Number(game?.winner) == 1 \u0026\u0026 `[WINNER!]`}\u003c/b\u003e\n        \u003c/span\u003e\n        \u003cspan\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003cb\u003e- VS -\u003c/b\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/span\u003e\n        \u003cspan\u003e\n          \u003cb\u003ePlayer 🟢\u003c/b\u003e: {game?.playerO.substring(0, 5)}...{game?.playerO.substring(28, 32)}\n          \u003cb style={{color: 'red'}}\u003e\u0026nbsp;\u0026nbsp;{lastMoveIsX_O == 1 \u0026\u0026 Number(game?.winner) == 0  \u0026\u0026 `[${ countDown \u003e 0 ? countDown : 0}]`}\u003c/b\u003e\n          \u003cb style={{color: 'red'}}\u003e\u0026nbsp;\u0026nbsp;{Number(game?.winner) == 2 \u0026\u0026 `[WINNER!]`}\u003c/b\u003e\n        \u003c/span\u003e\n      \u003c/div\u003e\n      \u003cdiv style={{textAlign: 'center'}}\u003e\n        {((Number(game?.winner) == 1 \u0026\u0026 game?.playerX == account.address) \n        || (Number(game?.winner) == 2 \u0026\u0026 game?.playerO == account.address)) \u0026\u0026\n          \u003cbutton onClick={() =\u003e claimReward()}\u003eclaim reward #{gameId?.toString()}\u003c/button\u003e\n        }\n      \u003c/div\u003e\n      \u003cbr/\u003e\n      {Array.from({ length: size }, (_, index) =\u003e index).map((row) =\u003e (\n        \u003cdiv key={row} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}\u003e\n          {Array.from({ length: size }, (_, index) =\u003e index).map((col) =\u003e (\n            \u003cbutton key={col} \n              style={{ \n                width: 35, height: 35,\n                border: '1px solid #5f5f5f', \n                backgroundColor: 'black',\n                display: 'flex', justifyContent: 'center', alignItems: 'center', \n                cursor: 'pointer' \n              }}\n              onClick={() =\u003e move(row * size + col)}\n            \u003e\n              {renderValue(row * size + col)}\n            \u003c/button\u003e\n          ))}\n        \u003c/div\u003e\n      ))}\n    \u003c/div\u003e\n  }\n\n  return \u003c\u003e\u003c/\u003e\n}\n```\n\n### Update App component\n```js\nimport { useAccount, useWriteContract } from 'wagmi'\nimport Wallet from './components/Wallet'\nimport { contractConfigs } from './contractConfig'\nimport { parseEther } from 'viem'\nimport Game from './components/Game'\nimport ListOfGames from './components/ListOfGames'\n\nfunction App() {\n  const account = useAccount()\n\n  const { \n    writeContract \n  } = useWriteContract()\n\n  const createGame = async () =\u003e {\n    writeContract({\n      ...contractConfigs,\n      functionName: 'createGame',\n      value: parseEther('0.001')\n    })\n  }\n\n  if (account.status !== 'connected') {\n    return \u003cWallet /\u003e\n  }\n  return (\u003cdiv\u003e\n    \u003cWallet /\u003e\n    \u003cdiv\u003e\n      \u003ch2\u003e\n        Game \u0026nbsp; \u003cbutton type=\"button\" onClick={createGame}\u003eCreate A Game\u003c/button\u003e\n      \u003c/h2\u003e\n      \u003cGame /\u003e\n      \u003cListOfGames /\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e)\n}\n\nexport default App\n```\n# RUN, RUN, RUN......\n```\nnpm run dev\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xvoidmain%2Fa8-tic-tac-toe-hackathon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0xvoidmain%2Fa8-tic-tac-toe-hackathon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xvoidmain%2Fa8-tic-tac-toe-hackathon/lists"}