Skip to content

Instantly share code, notes, and snippets.

@qsona
Last active August 17, 2020 13:32
Show Gist options
  • Save qsona/e570cc23179640f00601a7995bc3336e to your computer and use it in GitHub Desktop.
Save qsona/e570cc23179640f00601a7995bc3336e to your computer and use it in GitHub Desktop.
TypeScript example of boardgame.io tutorial
import React from 'react';
import './App.css';
import { Client } from 'boardgame.io/react';
import { Game, IPlayer, IGameCtx } from 'boardgame.io/core';
import { AI, IAIMoveObj } from 'boardgame.io/ai';
type GameState = {
cells: Array<IPlayer | null>
};
const winLines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
const isVictory = (cells: Array<IPlayer | null>, player: IPlayer) => {
return winLines.some(winLine => winLine.every(i => cells[i] === player));
}
const isDraw = (cells: Array<IPlayer | null>) => {
return cells.every(cell => cell === null);
}
const TicTacToe = Game<GameState>({
setup: () => ({ cells: Array(9).fill(null) }),
moves: {
clickCell: (G, ctx, id) => {
if (G.cells[id] === null) {
G.cells[id] = ctx.currentPlayer;
}
}
},
flow: {
movesPerTurn: 1,
endGameIf: (G, ctx) => {
if (isVictory(G.cells, ctx.currentPlayer)) {
return { winner: ctx.currentPlayer };
}
if (isDraw(G.cells)) {
return { draw: true }
}
}
}
})
interface IProps {
moves: any;
events: any;
isActive: boolean;
G: GameState;
ctx: IGameCtx;
}
class TicTacToeBoard extends React.Component<IProps> {
onClick(id: number) {
if (this.isActive(id)) {
this.props.moves.clickCell(id);
this.props.events.endTurn();
}
}
isActive(id: number) {
return this.props.isActive && this.props.G.cells[id] === null;
}
render() {
const cellStyle: React.CSSProperties = {
border: '1px solid #555',
width: '50px',
height: '50px',
lineHeight: '50px',
textAlign: 'center',
};
let tbody: JSX.Element[] = [];
for (let i = 0; i < 3; i++) {
let cells: JSX.Element[] = [];
for (let j = 0; j < 3; j++) {
const id = 3 * i + j;
cells.push(
<td style={cellStyle} key={id} onClick={() => this.onClick(id)}>
{this.props.G.cells[id]}
</td>
);
}
tbody.push(<tr key={i}>{cells}</tr>);
}
let winner: JSX.Element | undefined = undefined;
if (this.props.ctx.gameover) {
winner =
this.props.ctx.gameover.winner !== undefined ? (
<div id="winner">Winner: {this.props.ctx.gameover.winner}</div>
) : (
<div id="winner">Draw!</div>
);
}
return (
<div>
<table id="board">
<tbody>{tbody}</tbody>
</table>
{winner}
</div>
);
}
}
const App: any = Client({
game: TicTacToe,
board: TicTacToeBoard,
ai: AI<GameState>({
enumerate: (G, ctx) => {
let moves: IAIMoveObj[] = [];
for (let i = 0; i < 9; i++) {
if (G.cells[i] === null) {
moves.push({ move: 'clickCell', args: [i] });
}
}
return moves;
},
}),
});
export default App;
// based on https://github.com/freeboardgame/FreeBoardGame.org/blob/2ee85dcdea61f7c36776128a573beb6b75f5caf2/%40types/boardgame.io/index.d.ts
declare module 'boardgame.io/ui' {
import * as React from 'react';
interface ITokenCoord {
x: number;
y: number;
originalX?: number;
originalY?: number;
}
interface ITokenProps {
x?: number;
y?: number;
z?: number;
style?: React.CSSProperties;
animate?: boolean;
draggable?: boolean;
shouldDrag?: (coord: ITokenCoord) => boolean;
onDrag?: (coord: ITokenCoord) => void;
onDrop?: (coord: ITokenCoord) => void;
onClick?: (coord: ITokenCoord) => void;
children?: any;
animationDuration?: number;
square?: string;
}
export class Token extends React.Component<ITokenProps, any> {
}
interface IGridColorMap {
[key: string]: string;
}
interface IGridProps {
rows: number;
cols: number;
outline?: boolean;
style?: React.CSSProperties;
colorMap?: IGridColorMap;
cellSize?: number;
onClick: (coords: any) => void;
children?: any;
}
export class Grid extends React.Component<IGridProps, any> {
}
}
declare module 'boardgame.io/core' {
export type IPlayer = string;
export class FlowObj {
ctx: (players: number) => any;
processGameEvent: (state: any, gameEvent: any) => any;
}
export class GameObj<TGameState> {
processMove: (G: TGameState, action: any, ctx: any) => any;
flow: FlowObj;
}
interface IGameCtx {
numPlayer: number;
turn: number;
currentPlayer: IPlayer;
currentPlayerMoves: number;
gameover?: any;
}
interface IGameMoves<TGameState> {
[key: string]: (G: TGameState, ctx: IGameCtx, ...args: any[]) => void;
}
interface IGameFlowPhase<TGameState> {
name: string;
allowedMoves: string[];
endPhaseIf: (G: TGameState, ctx: IGameCtx) => boolean;
}
interface IGameFlowTrigger<TGameState> {
conditon: (G: TGameState, ctx: IGameCtx) => boolean;
action: (G: TGameState, ctx: IGameCtx) => any;
}
interface IGameFlow<TGameState> {
movesPerTurn?: number;
endGameIf: (G: TGameState, ctx: IGameCtx) => any;
endTurnIf?: (G: TGameState, ctx: IGameCtx) => boolean;
onTurnEnd?: (G: TGameState, ctx: IGameCtx) => void;
triggers?: IGameFlowTrigger<TGameState>[];
phases?: IGameFlowPhase<TGameState>[];
}
interface IGameArgs<TGameState> {
name?: string;
setup: (numPlayers: number) => TGameState;
moves: IGameMoves<TGameState>;
playerView?: (G: TGameState, ctx: IGameCtx, playerID: IPlayer) => any;
flow?: IGameFlow<TGameState>;
}
export function Game<TGameState>(gameArgs: IGameArgs<TGameState>): GameObj<TGameState>;
}
declare module 'boardgame.io/react' {
import { GameObj, IGameCtx } from 'boardgame.io/core';
export class WrapperBoard {
moves: any;
events: any;
store: any;
}
interface IClientArgs {
game: any;
numPlayer?: number;
board?: React.ReactNode;
multiplayer?: boolean;
debug?: boolean;
ai?: any;
}
export function Client(clientArgs: IClientArgs): WrapperBoard;
}
declare module 'boardgame.io/ai' {
import { IGameCtx } from 'boardgame.io/core';
interface IAIMoveObj {
move: string;
args: any[];
}
interface IAIArgs<TGameState> {
enumerate: (G: TGameState, ctx: IGameCtx) => IAIMoveObj[];
}
export function AI<TGameState>(aiArgs: IAIArgs<TGameState>): any;
}
declare module 'boardgame.io/client' {
import { GameObj, IGameMoves } from 'boardgame.io/core';
export class WrapperBoard {
moves: any;
events: any;
store: any;
}
interface IClientArgs {
game: any;
numPlayer?: number;
board?: React.ReactNode;
multiplayer?: boolean;
debug?: boolean;
ai?: any;
}
export function Client(clientArgs: IClientArgs): WrapperBoard;
}
declare module 'boardgame.io/server' {
import { GameObj } from 'boardgame.io/core';
import * as Koa from 'koa';
interface IServerArgs<TGameState> {
games: GameObj<TGameState>[]
}
function Server<TGameState>(serverArgs: IServerArgs<TGameState>): Koa;
export = Server;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment