Skip to content

Instantly share code, notes, and snippets.

@martinkadlec0
Last active December 21, 2023 12:13
Show Gist options
  • Select an option

  • Save martinkadlec0/bd87678c79ef1843ad28805191bab591 to your computer and use it in GitHub Desktop.

Select an option

Save martinkadlec0/bd87678c79ef1843ad28805191bab591 to your computer and use it in GitHub Desktop.
Advent of Typescript - Day 21 - Tic Tac Toe
type TicTacToeChip = '❌' | '⭕';
type TicTacToeEndState = '❌ Won' | '⭕ Won' | 'Draw';
type TicTacToeState = TicTacToeChip | TicTacToeEndState;
type TicTacToeEmptyCell = ' '
type TicTacToeCell = TicTacToeChip | TicTacToeEmptyCell;
type TicTacToeYPositions = 'top' | 'middle' | 'bottom';
type TicTacToeXPositions = 'left' | 'center' | 'right';
type TicTacToePositions = `${TicTacToeYPositions}-${TicTacToeXPositions}`;
type TicTactToeBoard = TicTacToeCell[][];
type TicTacToeGame = {
board: TicTactToeBoard;
state: TicTacToeState;
};
type EmptyBoard = [
[' ', ' ', ' '],
[' ', ' ', ' '],
[' ', ' ', ' ']
];
type NewGame = {
board: EmptyBoard;
state: '❌';
};
type WinLanes = [
// horizontal
[[0,0], [0,1], [0,2]],
[[1,0], [1,1], [1,2]],
[[2,0], [2,1], [2,2]],
// vertical
[[0,0], [1,0], [2,0]],
[[0,1], [1,1], [2,1]],
[[0,2], [1,2], [2,2]],
// diagonal
[[0,0], [1,1], [2,2]],
[[0,2], [1,1], [2,0]],
]
type YMap = {'top': 0, 'middle': 1, 'bottom': 2}
type XMap = {'left': 0, 'center': 1, 'right': 2}
// Check if given Chip has a winning lane
type CheckChip<TLanes extends number[][][], Board extends TicTactToeBoard, Chip extends TicTacToeChip> =
TLanes extends [infer Lane extends number[][], ...infer Rest extends number[][][]]
? `${Board[Lane[0][0]][Lane[0][1]]}${Board[Lane[1][0]][Lane[1][1]]}${Board[Lane[2][0]][Lane[2][1]]}` extends `${Chip}${Chip}${Chip}`
? true
: CheckChip<Rest, Board, Chip>
: false
type Flatten<T extends any[][], Acc extends any[] = []> =
T extends [infer Item extends any[], ...infer Rest extends any[][]]
? Flatten<Rest, [...Acc, ...Item]>
: Acc
type IsDraw<Board extends TicTactToeBoard> =
TicTacToeEmptyCell extends Flatten<Board>[number] ? false : true
// Create new state value based on new board & current chip
type FindState<Board extends TicTactToeBoard, Chip extends TicTacToeChip> =
CheckChip<WinLanes, Board, Chip> extends true
? `${Chip} Won`
: IsDraw<Board> extends true
? 'Draw'
: Chip extends '⭕' ? '❌' : '⭕'
// Goes over cells in given Row and puts Chip into given coordinate
type ProcessCells<TCells extends TicTacToeCell[], XPos extends number, Chip extends TicTacToeChip> = {
[K in keyof TCells]: K extends `${XPos}` ? Chip : TCells[K]
}
// Goes over rows in current board and puts Chip into given coordinates
type ProcessRows<TBoard extends TicTactToeBoard, YPos extends number, XPos extends number, Chip extends TicTacToeChip> = {
[K in keyof TBoard]: K extends `${YPos}`
? ProcessCells<TBoard[K], XPos, Chip>
: TBoard[K]
}
type TicTacToe<TGame extends TicTacToeGame, TPos extends TicTacToePositions> =
// Separate coordinates
TPos extends `${infer YPos extends TicTacToeYPositions}-${infer XPos extends TicTacToeXPositions}`
// Don't allow more moves once game is over
? TGame['state'] extends infer Chip extends TicTacToeChip
// Don't allow overwriting of cells
? TGame['board'][YMap[YPos]][XMap[XPos]] extends TicTacToeEmptyCell
// Create new board
? ProcessRows<TGame['board'], YMap[YPos], XMap[XPos], Chip> extends infer NewBoard extends TicTactToeBoard
? {
board: NewBoard,
state: FindState<NewBoard, Chip>
}
: TGame
: TGame
: TGame
: TGame
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment