Last active
August 17, 2020 13:32
-
-
Save qsona/e570cc23179640f00601a7995bc3336e to your computer and use it in GitHub Desktop.
TypeScript example of boardgame.io tutorial
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 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; |
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
// 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