Created
March 17, 2023 04:01
-
-
Save aranajhonny/b3e2ef366c4bcc07edc8d5feedb369ed to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { nodes, root, state } from "membrane"; | |
state.game = state.game ?? {}; | |
export const Root = { | |
setup() { | |
const board = Array.from({ length: 3 }, () => Array.from({ length: 3 }, () => "")); | |
const turn = "X"; | |
let winner: any = null; | |
let resolve: any = null; | |
let reject: any = null; | |
const result = new Promise((res, rej) => { | |
resolve = res; | |
reject = rej; | |
}); | |
state.game = { board, turn, winner, resolve, reject, result }; | |
}, | |
endpoint: ({ args: { path, method } }) => { | |
const [, cell] = path.split("/"); | |
const game = state.game; | |
if (!game) { | |
return JSON.stringify({ status: 404, body: "Game not found" }); | |
} | |
if (game.winner) { | |
return JSON.stringify({ status: 400, body: "Game already finished" }); | |
} | |
if (method === "POST") { | |
const move = Number(cell); | |
if ( | |
isNaN(move) || | |
move < 1 || | |
move > 9 || | |
game.board[Math.floor((move - 1) / 3)][(move - 1) % 3] !== "" | |
) { | |
return JSON.stringify({ status: 400, body: "Invalid move" }); | |
} | |
game.board[Math.floor((move - 1) / 3)][(move - 1) % 3] = game.turn; | |
if (checkWinner(game.board, game.turn)) { | |
game.winner = game.turn; | |
game.resolve(game.turn); | |
} else if (checkTie(game.board)) { | |
game.resolve("TIE"); | |
return JSON.stringify({ status: 200, body: "Game over: tie" }); | |
} | |
// Update the turn | |
game.turn = game.turn === "X" ? "O" : "X"; | |
return html(game); | |
} else if (method === "GET") { | |
return html(game); | |
} else { | |
return { status: 405, body: "Method not allowed" }; | |
} | |
}, | |
}; | |
function checkTie(board) { | |
for (let row = 0; row < board.length; row++) { | |
for (let col = 0; col < board[0].length; col++) { | |
if (board[row][col] === "") { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
function checkWinner(board, player) { | |
// Check rows | |
for (let i = 0; i < board.length; i++) { | |
if (board[i][0] === player && board[i][1] === player && board[i][2] === player) { | |
return true; | |
} | |
} | |
// Check columns | |
for (let i = 0; i < board[0].length; i++) { | |
if (board[0][i] === player && board[1][i] === player && board[2][i] === player) { | |
return true; | |
} | |
} | |
// Check diagonals | |
if (board[0][0] === player && board[1][1] === player && board[2][2] === player) { | |
return true; | |
} | |
if (board[0][2] === player && board[1][1] === player && board[2][0] === player) { | |
return true; | |
} | |
// No winner | |
return false; | |
} | |
function html(game) { | |
const board = game.board | |
.map((row, i) => { | |
return row | |
.map((cell, j) => { | |
const num = i * 3 + j + 1; | |
const disabled = typeof cell !== "number" ? "disabled" : ""; | |
return ` | |
<td> | |
<form action="/${num}" method="POST"> | |
<button ${cell ? "disabled" : ""}><span>${cell || " "}</span></button> | |
</form> | |
</td> | |
`; | |
}) | |
.join(""); | |
}) | |
.map((row) => `<tr>${row}</tr>`) | |
.join(""); | |
const message = game.winner ? `${game.winner} wins!` : `Turn: ${game.turn}`; | |
return ` | |
<!DOCTYPE html> | |
<head> | |
<style> | |
td button { | |
padding: 32px; | |
font-size: 42px; | |
} | |
</style> | |
<meta charset="utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
<title>Tic Tac Toe</title> | |
<link rel="stylesheet" href="https://www.membrane.io/light.css"></script> | |
</head> | |
<body> | |
<div style="position: absolute; inset: 0px; display: flex; flex-direction: row; justify-content: center; align-items: center;"> | |
<div style="display: flex; flex-direction: column; align-items: center; max-width: 800px;"> | |
<section> | |
<h2>Tic Tac Toe</h2> | |
<table> | |
${board} | |
</table> | |
<div style="display:flex;font-size: x-large;padding-top: .5rem;justify-content: center;width: 100%;"> | |
<h1 style="margin: 0px;">${message}</h1> | |
</div> | |
</section> | |
</div> | |
</div> | |
</body> | |
</html> | |
`; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment