-
Notifications
You must be signed in to change notification settings - Fork 0
/
21.ts
130 lines (114 loc) · 4.74 KB
/
21.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// TS playground link: https://tsplay.dev/advent-of-typescript-2023-21
// What is Tic Tac Toe?
// Tic-Tac-Toe is a two-player game where players alternate marking ❌s and ⭕s in a 3x3 grid, aiming to get three in a row.
// fun fact: Did you know that tic tac toe is widely considered to be the first computer video game ever created?! That's right! A S Douglas implemented it all the way back in 1952, the same year as the coronation of Queen Elizabeth II.
// Solving Tic Tac Toe
// Your goal for this challenge is to use TypeScript types to encode the game logic of Tic Tac Toe. Eventually, every game will end with one of the players winning or a draw.
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 Invalid = "invalid";
type Fill<
Content extends unknown,
Size extends number,
Container extends Array<Content> = [],
> = Container["length"] extends Size ? Container : Fill<Content, Size, [...Container, Content]>;
type TicTacToeBoard = Fill<Fill<TicTacToeCell, 3>, 3>;
type TicTacToeGame = {
board: TicTacToeBoard;
state: TicTacToeState;
};
type EmptyBoard = [[" ", " ", " "], [" ", " ", " "], [" ", " ", " "]];
type NewGame = {
board: EmptyBoard;
state: "❌";
};
type PlaceChip<A extends Array<unknown>, I extends keyof A, E extends unknown> = {
[Key in keyof A]: Key extends `${I extends number ? I : never}`
? A[Key] extends TicTacToeEmptyCell
? E
: Invalid
: A[Key];
};
type ChangeArrayElement<A extends Array<unknown>, I extends keyof A, E extends unknown> = {
[Key in keyof A]: Key extends `${I extends number ? I : never}` ? E : A[Key];
};
type TranslateCellYCoordinateToIndex<YCoordinate extends TicTacToeYPositions> =
YCoordinate extends "top" ? 0 : YCoordinate extends "middle" ? 1 : 2;
type TranslateCellXCoordinateToIndex<XCoordinate extends TicTacToeXPositions> =
XCoordinate extends "left" ? 0 : XCoordinate extends "center" ? 1 : 2;
type GenerateNextBoard<
CurrentGame extends TicTacToeGame,
YCoordinate extends TicTacToeYPositions,
XCoordinate extends TicTacToeXPositions,
> = ChangeArrayElement<
CurrentGame["board"],
TranslateCellYCoordinateToIndex<YCoordinate>,
PlaceChip<
CurrentGame["board"][TranslateCellYCoordinateToIndex<YCoordinate>],
TranslateCellXCoordinateToIndex<XCoordinate>,
CurrentGame["state"]
>
> extends infer NextBoard extends [
[TicTacToeCell, TicTacToeCell, TicTacToeCell],
[TicTacToeCell, TicTacToeCell, TicTacToeCell],
[TicTacToeCell, TicTacToeCell, TicTacToeCell],
]
? NextBoard
: never;
// https://stackoverflow.com/a/55128956
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
? I
: never;
type LastOf<T> = UnionToIntersection<T extends any ? () => T : never> extends () => infer R
? R
: never;
// TS4.0+
type Push<T extends any[], V> = [...T, V];
// TS4.1+
type TuplifyUnion<T, L = LastOf<T>, N = [T] extends [never] ? true : false> = true extends N
? []
: Push<TuplifyUnion<Exclude<T, L>>, L>;
// https://stackoverflow.com/a/55128956
type FindWinner<A extends Array<unknown>> = TuplifyUnion<A[number]> extends infer Winner extends
Array<TicTacToeChip>
? Winner["length"] extends 0
? never
: Winner["length"] extends 1
? Winner[0]
: never
: never;
type GetWinner<Board extends TicTacToeBoard> = [
FindWinner<Board[0]>,
FindWinner<Board[1]>,
FindWinner<Board[2]>,
FindWinner<[Board[0][0], Board[1][0], Board[2][0]]>,
FindWinner<[Board[0][1], Board[1][1], Board[2][1]]>,
FindWinner<[Board[0][2], Board[1][2], Board[2][2]]>,
] extends infer Winner extends Array<TicTacToeChip>
? Winner[number]
: "no winner (get winner)";
type IsInvalid<Board extends TicTacToeBoard> = Board[number][number] extends Invalid ? true : false;
type IsFinal<Board extends TicTacToeBoard> = TicTacToeEmptyCell extends Board[number][number] ? false : true
type TicTacToe<
CurrentGame extends TicTacToeGame,
NextMoveCellCoordinates extends TicTacToePositions,
> = NextMoveCellCoordinates extends `${infer YCoordinate extends
TicTacToeYPositions}-${infer XCoordinate extends TicTacToeXPositions}`
? GenerateNextBoard<CurrentGame, YCoordinate, XCoordinate> extends infer NextBoard extends
TicTacToeBoard
? IsInvalid<NextBoard> extends true
? CurrentGame
: {
board: NextBoard;
state: GetWinner<NextBoard> extends never
? IsFinal<NextBoard> extends true ? 'Draw' : Exclude<TicTacToeChip, CurrentGame["state"]>
: `${GetWinner<NextBoard>} Won`;
}
: never
: never;