Skip to content

Instantly share code, notes, and snippets.

@aranajhonny
Created March 17, 2023 04:01
Show Gist options
  • Save aranajhonny/b3e2ef366c4bcc07edc8d5feedb369ed to your computer and use it in GitHub Desktop.
Save aranajhonny/b3e2ef366c4bcc07edc8d5feedb369ed to your computer and use it in GitHub Desktop.
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 || "&nbsp;"}</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