Skip to content

Instantly share code, notes, and snippets.

@msukmanowsky
Created October 18, 2022 02:09
Show Gist options
  • Save msukmanowsky/986fc87ccaa3879a878fef90718fa7bb to your computer and use it in GitHub Desktop.
Save msukmanowsky/986fc87ccaa3879a878fef90718fa7bb to your computer and use it in GitHub Desktop.
TicTacToe using boardgame.io
import type { Game, Move } from "boardgame.io";
import { INVALID_MOVE } from "boardgame.io/core";
export interface TicTacToeState {
cells: (null | string)[];
}
function isVictory(cells: (null | string)[]): boolean {
const positions = [
// Horizontal
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
// Vertical
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
// Diagonal
[0, 4, 8],
[2, 4, 6],
];
const isRowComplete = (row: number[]) => {
const symbols = row.map((i) => cells[i]);
return symbols.every((i) => i !== null && i === symbols[0]);
};
return positions.map(isRowComplete).some((i) => i === true);
}
function isDraw(cells: (null | string)[]): boolean {
return cells.filter((c) => c === null).length === 0;
}
export const TicTacToe: Game<TicTacToeState> = {
name: "TicTacToe",
setup: () => ({ cells: Array(9).fill(null) }),
turn: {
minMoves: 1,
maxMoves: 1,
},
moves: {
clickCell: ({ G, playerID }, id) => {
if (G.cells[id] !== null) {
return INVALID_MOVE;
}
G.cells[id] = playerID;
},
},
endIf: ({ G, ctx }) => {
if (isVictory(G.cells)) {
return { winner: ctx.currentPlayer };
}
if (isDraw(G.cells)) {
return { draw: true };
}
},
};
import type { NextPage } from "next";
import TicTacToeClient from "../components/TicTacToeClient";
const Home: NextPage = () => {
return <TicTacToeClient />;
};
export default Home;
import { Ctx } from "boardgame.io";
import type { BoardProps } from "boardgame.io/react";
import { TicTacToeState } from "../src/Game";
export interface TicTacToeBoardProps extends BoardProps<TicTacToeState> {}
function getWinner(ctx: Ctx): string | null {
if (!ctx.gameover) return null;
if (ctx.gameover.draw) return "Draw";
return `Player ${ctx.gameover.winner} wins!`;
}
export default function TicTacToeBoard({ ctx, G, moves }: TicTacToeBoardProps) {
const winner = getWinner(ctx);
const tbody = [];
for (let row = 0; row < 3; row++) {
let cells = [];
for (let col = 0; col < 3; col++) {
const id = 3 * row + col;
cells.push(
<td key={id}>
{G.cells[id] ? (
<div>{G.cells[id]}</div>
) : (
<button
style={{ width: "2rem", height: "2rem" }}
onClick={() => moves.clickCell(id)}
/>
)}
</td>
);
}
tbody.push(<tr key={row}>{cells}</tr>);
}
return (
<div>
<h1>TicTacToe</h1>
<table id="board">
<tbody>{tbody}</tbody>
</table>
{winner && <p>{winner}</p>}
</div>
);
}
import { Client } from "boardgame.io/react";
import { TicTacToe } from "../src/Game";
import TicTacToeBoard from "./TicTacToeBoard";
export default Client({ game: TicTacToe, board: TicTacToeBoard });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment