board-game-engine 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/board-game-engine.test.js +2 -41
  2. package/dist/board-game-engine.js +32556 -5074
  3. package/dist/board-game-engine.min.js +2 -1
  4. package/dist/board-game-engine.min.js.LICENSE.txt +14 -0
  5. package/package.json +6 -3
  6. package/patch-bgio.cjs +23 -0
  7. package/src/client/client.js +287 -0
  8. package/src/game-factory/bank/bank-slot.js +69 -0
  9. package/src/game-factory/bank/bank.js +114 -0
  10. package/src/game-factory/board.js +3 -0
  11. package/src/game-factory/condition/condition-factory.js +52 -0
  12. package/src/game-factory/condition/condition.js +39 -0
  13. package/src/game-factory/condition/contains-condition.js +21 -0
  14. package/src/game-factory/condition/contains-same-condition.js +27 -0
  15. package/src/game-factory/condition/evaluate-condition.js +18 -0
  16. package/src/game-factory/condition/every-condition.js +25 -0
  17. package/src/game-factory/condition/has-line-condition.js +14 -0
  18. package/src/game-factory/condition/in-line-condition.js +19 -0
  19. package/src/game-factory/condition/is-condition.js +23 -0
  20. package/src/game-factory/condition/is-full-condition.js +9 -0
  21. package/src/game-factory/condition/no-possible-moves-condition.js +14 -0
  22. package/src/game-factory/condition/not-condition.js +14 -0
  23. package/src/game-factory/condition/or-condition.js +15 -0
  24. package/src/game-factory/condition/position-condition.js +12 -0
  25. package/src/game-factory/condition/some-condition.js +24 -0
  26. package/src/game-factory/condition/would-condition.js +94 -0
  27. package/src/game-factory/entity.js +29 -0
  28. package/src/game-factory/expand-game-rules.js +276 -0
  29. package/src/game-factory/game-factory.js +239 -0
  30. package/src/game-factory/move/end-turn.js +7 -0
  31. package/src/game-factory/move/for-each.js +18 -0
  32. package/src/game-factory/move/index.js +7 -0
  33. package/src/game-factory/move/move-entity.js +16 -0
  34. package/src/game-factory/move/move-factory.js +89 -0
  35. package/src/game-factory/move/move.js +131 -0
  36. package/src/game-factory/move/pass-turn.js +10 -0
  37. package/src/game-factory/move/pass.js +7 -0
  38. package/src/game-factory/move/place-new.js +33 -0
  39. package/src/game-factory/move/remove-entity.js +7 -0
  40. package/src/game-factory/move/set-active-players.js +23 -0
  41. package/src/game-factory/move/set-state.js +13 -0
  42. package/src/game-factory/move/shuffle.js +7 -0
  43. package/src/game-factory/move/take-from.js +7 -0
  44. package/src/game-factory/space/space.js +30 -0
  45. package/src/game-factory/space-group/grid.js +43 -0
  46. package/src/game-factory/space-group/space-group.js +29 -0
  47. package/src/index.js +2 -0
  48. package/src/registry.js +17 -0
  49. package/src/utils/any-valid-moves.js +157 -0
  50. package/src/utils/check-conditions.js +28 -0
  51. package/src/utils/create-payload.js +16 -0
  52. package/src/utils/deserialize-bgio-arguments.js +8 -0
  53. package/src/utils/do-moves.js +18 -0
  54. package/src/utils/entity-matches.js +20 -0
  55. package/src/utils/find-met-condition.js +22 -0
  56. package/src/utils/get-current-moves.js +12 -0
  57. package/src/utils/get-scenario-results.js +23 -0
  58. package/src/utils/get-steps.js +28 -0
  59. package/src/utils/get.js +25 -0
  60. package/src/utils/grid-contains-sequence.js +226 -0
  61. package/src/utils/json-transformer.js +12 -0
  62. package/src/utils/prepare-payload.js +16 -0
  63. package/src/utils/resolve-entity.js +9 -0
  64. package/src/utils/resolve-expression.js +10 -0
  65. package/src/utils/resolve-properties.js +157 -0
  66. package/src/utils/simulate-move.js +25 -0
  67. package/webpack.config.js +4 -1
  68. package/src/action/action-factory.js +0 -13
  69. package/src/action/action.js +0 -34
  70. package/src/action/move-piece-action.js +0 -11
  71. package/src/action/select-piece-action.js +0 -23
  72. package/src/action/swap-action.js +0 -14
  73. package/src/board/board-factory.js +0 -12
  74. package/src/board/board-group.js +0 -9
  75. package/src/board/board.js +0 -11
  76. package/src/board/grid.js +0 -52
  77. package/src/board/stack.js +0 -16
  78. package/src/condition/action-type-matches-condition.js +0 -7
  79. package/src/condition/bingo-condition.js +0 -50
  80. package/src/condition/blackout-condition.js +0 -9
  81. package/src/condition/condition-factory.js +0 -31
  82. package/src/condition/condition.js +0 -9
  83. package/src/condition/contains-condition.js +0 -14
  84. package/src/condition/does-not-contain-condition.js +0 -15
  85. package/src/condition/is-valid-player-condition.js +0 -7
  86. package/src/condition/piece-matches-condition.js +0 -23
  87. package/src/condition/relative-move-condition.js +0 -16
  88. package/src/condition/some-condition.js +0 -7
  89. package/src/game/game.ts +0 -362
  90. package/src/index.ts +0 -1
  91. package/src/piece/piece-factory.js +0 -5
  92. package/src/piece/piece.ts +0 -25
  93. package/src/piece/pile.js +0 -70
  94. package/src/player/player.ts +0 -13
  95. package/src/registry.ts +0 -51
  96. package/src/round/round-factory.js +0 -7
  97. package/src/round/round.js +0 -41
  98. package/src/round/sequential-player-turn.js +0 -18
  99. package/src/space/space.ts +0 -22
  100. package/src/utils/find-value-path.js +0 -37
  101. package/src/utils/resolve-board.ts +0 -38
  102. package/src/utils/resolve-piece.ts +0 -43
package/src/game/game.ts DELETED
@@ -1,362 +0,0 @@
1
- import cloneDeep from "lodash/cloneDeep.js";
2
- import matches from "lodash/matches.js";
3
- import merge from "lodash/merge.js";
4
- import get from "lodash/get.js";
5
- import boardFactory from "../board/board-factory.js";
6
- import Player from "../player/player";
7
- import roundFactory from "../round/round-factory.js";
8
- import conditionFactory from "../condition/condition-factory.js";
9
- import actionFactory from "../action/action-factory.js";
10
- import Pile from "../piece/pile.js";
11
- import findValuePath from "../utils/find-value-path.js";
12
- import { serialize, deserialize } from "wackson";
13
- import { registry } from "../registry.ts";
14
-
15
- export interface GameRules {
16
- round: any;
17
- player: any;
18
- pieces: any[];
19
- sharedBoard: Record<string, any>;
20
- personalBoard?: Record<string, any>;
21
- initialPlacements?: any[];
22
- winCondition: any;
23
- drawCondition?: any;
24
- }
25
-
26
- export interface GameOptions {
27
- playerCount: number;
28
- [key: string]: any;
29
- }
30
-
31
- export interface Move {
32
- playerId: number;
33
- type?: string;
34
- piece?: {
35
- id?: string;
36
- name?: string;
37
- player?: {
38
- id: number;
39
- };
40
- };
41
- board?: string[];
42
- [key: string]: any;
43
- }
44
-
45
- export interface GameState {
46
- currentPhaseIndex: number;
47
- currentRoundIndex: number;
48
- context: Record<string, any>;
49
- status: 'waiting' | 'active' | 'done';
50
- winner: Player | null;
51
- sharedBoard: Record<string, any>;
52
- players: Player[];
53
- personalBoards: Record<string, any>;
54
- pieces: Pile[];
55
- }
56
-
57
- function initializeState(state: GameState, rules: GameRules): GameState {
58
- expandRules(rules)
59
- state.sharedBoard = Object.entries(rules.sharedBoard).reduce(
60
- (acc, [id, board]) => ({
61
- ...acc,
62
- [id]: boardFactory({ ...board, path: ["sharedBoard", id] }),
63
- }),
64
- {},
65
- );
66
-
67
- state.personalBoards = state.players.reduce(
68
- (acc, player) => ({
69
- ...acc,
70
- [player.id]: Object.entries(rules.personalBoard || []).reduce(
71
- (acc, [id, board]) => ({
72
- ...acc,
73
- [id]: boardFactory(
74
- { ...board, path: ["sharedBoard", id] },
75
- { player },
76
- ),
77
- }),
78
- {},
79
- ),
80
- }),
81
- {},
82
- );
83
-
84
- state.pieces = rules.pieces.reduce((acc, pieceRule) => {
85
- if (pieceRule.perPlayer) {
86
- return [
87
- ...acc,
88
- ...state.players.map((player) => new Pile(pieceRule, { player })),
89
- ];
90
- } else {
91
- return [...acc, new Pile(pieceRule)];
92
- }
93
- }, []);
94
-
95
- // Apply initial placements
96
- rules.initialPlacements?.forEach((placement) => {
97
- if (placement.perPlayer) {
98
- state.players.forEach((player) => {
99
- doInitialPlacement(placement, player, {
100
- sharedBoard: state.sharedBoard,
101
- personalBoards: state.personalBoards,
102
- pieces: state.pieces,
103
- });
104
- });
105
- } else {
106
- doInitialPlacement(placement, null, {
107
- sharedBoard: state.sharedBoard,
108
- personalBoards: state.personalBoards,
109
- pieces: state.pieces,
110
- });
111
- }
112
- });
113
-
114
- const currentRoundRule = rules.round.phases
115
- ? rules.round.phases[0]
116
- : rules.round;
117
- state.currentRound = roundFactory(currentRoundRule, state);
118
-
119
- state.status = 'active'
120
-
121
- return state;
122
- }
123
-
124
- function checkWinner(state: GameState, rules: GameRules): Player | null {
125
- return (
126
- state.players.find((player) => {
127
- const winCondition = {
128
- ...rules.winCondition,
129
- piece: {
130
- ...rules.winCondition.piece,
131
- player,
132
- },
133
- };
134
- const condition = conditionFactory(winCondition, state);
135
- return condition.isMet();
136
- }) || null
137
- );
138
- }
139
-
140
- function checkDraw(state: GameState, rules: GameRules): boolean {
141
- return !!(
142
- rules.drawCondition && conditionFactory(rules.drawCondition, state).isMet()
143
- );
144
- }
145
-
146
- function expandActionPayload(move: Move, state: GameState, rules: GameRules) {
147
- const player = state.players.find((p) => p.id === move.playerId);
148
- if (!player && move.type !== 'join' && move.type !== 'start') {
149
- throw new Error("Invalid player ID");
150
- }
151
-
152
- const pieceRule = rules.pieces.find((piece) => piece.name === move.piece?.name);
153
-
154
- const expandedMove = cloneDeep(move)
155
- if (pieceRule?.perPlayer && !expandedMove.player) {
156
- expandedMove.piece.player = { id: player.id };
157
- }
158
-
159
- if (!expandedMove.board) {
160
- expandedMove.board = getBoardPathContaining(expandedMove.piece, state);
161
- }
162
-
163
- expandedMove.board = normalizePath(expandedMove.board, { player });
164
-
165
- return expandedMove;
166
- }
167
-
168
- function handlePlayerJoin(state, rules, move) {
169
- if (state.players.length < rules.playerCountRange[1]) {
170
- state.players.push(new Player(rules.player, state.players.length, move.playerId))
171
- } else {
172
- throw new Error('game is full!')
173
- }
174
- }
175
-
176
- function handleStartGame(state,rules) {
177
- if (state.players.length >= rules.playerCountRange[0]) {
178
- initializeState(state, rules)
179
- } else {
180
- throw new Error('not enough players')
181
- }
182
- }
183
-
184
- export function makeMove(
185
- rules: GameRules,
186
- _state?: GameState,
187
- move?: Move,
188
- ): GameState {
189
- if (!_state) {
190
- return serialize({
191
- context: {},
192
- winner: null,
193
- status: 'waiting',
194
- players: [],
195
- })
196
- }
197
-
198
- const state = deserializeState(_state)
199
-
200
- if (state.status === 'done') {
201
- throw new Error("Game is over!");
202
- }
203
-
204
- if (move === undefined) {
205
- return serialize(state);
206
- } else if (move?.type === 'join') {
207
- handlePlayerJoin(state, rules, move)
208
- return serialize(state)
209
- } else if (move.type === 'start') {
210
- handleStartGame(state, rules)
211
- return serialize(state)
212
- }
213
-
214
-
215
- // Expand the move payload with defaults and normalizations
216
- const expandedMove = expandActionPayload(move, state, rules);
217
-
218
- const round = state.currentRound
219
-
220
- round.doAction(expandedMove);
221
- // Check if round is over and advance if needed
222
- if (round.isOver(state)) {
223
- if (rules.round.phases) {
224
- // Get current phase rule
225
- const currentPhaseRule = rules.round.phases[state.currentRound.currentPhaseIndex];
226
-
227
- // Create round for current phase
228
- const phaseRound = roundFactory(currentPhaseRule, state);
229
-
230
- // If this phase is over, move to next phase
231
- if (phaseRound.isOver(state)) {
232
- state.currentRound.currentPhaseIndex++;
233
-
234
- // If we've completed all phases, start new round
235
- if (state.currentRound.currentPhaseIndex >= rules.round.phases.length) {
236
- state.currentRound.currentPhaseIndex = 0;
237
- state.currentRound.currentRoundIndex++;
238
- }
239
- }
240
- } else {
241
- // No phases, just increment round
242
- state.currentRound.currentRoundIndex++;
243
- }
244
- }
245
-
246
- // Check win/draw conditions
247
- const winner = checkWinner(state, rules);
248
- const isDraw = checkDraw(state, rules);
249
-
250
- if (winner || isDraw) {
251
- state.status = 'done'
252
- state.winner = winner
253
- }
254
-
255
- return serialize(state)
256
- }
257
-
258
- function doInitialPlacement(
259
- placement: {
260
- action: any;
261
- payload?: any;
262
- count?: number;
263
- rules: GameRules;
264
- },
265
- player: Player | null,
266
- state: GameState,
267
- ) {
268
- const actionRule = placement.action;
269
- const actionPayload = placement.payload || {};
270
-
271
- if (player) {
272
- actionPayload.player = player;
273
- }
274
-
275
- Array.from(new Array(placement.count || 1)).forEach(() => {
276
- const action = actionFactory(actionRule, state);
277
- action.do(expandActionPayload(actionPayload, state, rules));
278
- });
279
- }
280
-
281
- function getBoardPathContaining(
282
- piece: any,
283
- state: GameState,
284
- options?: any,
285
- ): string[] {
286
- return getPiecePaths(piece, state, options)[0];
287
- }
288
-
289
- function normalizePath(
290
- path: string[],
291
- options: { player?: Player } = {},
292
- ): string[] {
293
- return path[0] === "personalBoard" && options.player
294
- ? ["personalBoards", options.player.id.toString(), ...path.slice(1)]
295
- : path;
296
- }
297
-
298
- function getPiecePaths(
299
- matcher: any,
300
- state: GameState,
301
- options?: any,
302
- ): string[][] {
303
- const placesPiecesCanBe = {
304
- personalBoards: state.personalBoards,
305
- sharedBoard: state.sharedBoard,
306
- pieces: state.pieces,
307
- };
308
-
309
- return Array.from(findValuePath(placesPiecesCanBe, matches(matcher)))
310
- .filter((a) => a[a.length - 1] !== "rule")
311
- .sort((a) => (a[0] === "pieces" ? 1 : -1))
312
- .map((path) => normalizePath(path, options));
313
- }
314
-
315
- function expandOptions(options) {
316
- const defaultOptions = {
317
- playerCount: 2,
318
- };
319
- return merge({}, defaultOptions, options);
320
- }
321
-
322
- // mutates rules
323
- function expandRules (rules) {
324
- addPathToRules(rules)
325
- }
326
-
327
- // mutates rules
328
- export function addPathToRules (rules): void {
329
- const CHILD_KEYS = ['phases', 'rounds'];
330
-
331
- function annotate(node, pathSegs) {
332
- if (!node || typeof node !== 'object') return;
333
-
334
- // lodash.get path string, e.g. "round.phases.0.rounds.2"
335
- node.path = pathSegs.join('.');
336
-
337
- // recurse into either "phases" or "rounds" arrays if present
338
- for (const key of CHILD_KEYS) {
339
- const kids = node[key];
340
- if (Array.isArray(kids)) {
341
- kids.forEach((child, idx) => {
342
- annotate(child, pathSegs.concat(key, idx));
343
- });
344
- }
345
- }
346
- }
347
-
348
- // Top-level: support either "round" (object) or "rounds" (array), or both.
349
- if (rules.round && typeof rules.round === 'object') {
350
- annotate(rules.round, ['round']);
351
- }
352
- if (Array.isArray(rules.rounds)) {
353
- rules.rounds.forEach((r, i) => annotate(r, ['rounds', i]));
354
- }
355
- }
356
-
357
- function deserializeState (state) {
358
- return deserialize(
359
- state,
360
- registry
361
- )
362
- }
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export { makeMove } from "./game/game";
@@ -1,5 +0,0 @@
1
- import Piece from "./piece.ts";
2
-
3
- export default function pieceFactory(pieceRule, options) {
4
- return new Piece(pieceRule, options);
5
- }
@@ -1,25 +0,0 @@
1
- import type { PieceRule, PieceRuleMatcher } from "../../types";
2
- import type Player from "../player/player";
3
-
4
- interface Options {
5
- player?: Player;
6
- }
7
-
8
- export default class Piece {
9
- rule: PieceRule;
10
- player: Player;
11
- constructor(pieceRule: PieceRule, options: Options) {
12
- this.rule = pieceRule;
13
- this.id = `${Math.random()}`
14
- if (options.player !== undefined) {
15
- ({ player: this.player } = options);
16
- }
17
- }
18
-
19
- doesRuleMatch(matcher: PieceRuleMatcher): boolean {
20
- if (matcher.player !== undefined) {
21
- return matcher.player === this.player;
22
- }
23
- return true;
24
- }
25
- }
package/src/piece/pile.js DELETED
@@ -1,70 +0,0 @@
1
- import pieceFactory from './piece-factory.js'
2
-
3
- // all this extra complication is to support arbitrary (infinite) piles of pieces
4
- class Pile {
5
- constructor (pieceRule, options = {}) {
6
- this.pieceRule = pieceRule
7
- this.name = pieceRule.name
8
- this.id = `${Math.random()}`
9
- if (options.player) {
10
- this.player = options.player
11
- }
12
- this.options = options
13
- this.pool = (
14
- pieceRule.variants ? Object.entries(pieceRule.variants) : []
15
- ).reduce((acc, [variantId, variant]) => {
16
- const count = variant.count || 1
17
- return [
18
- ...acc,
19
- ...Array.from(Array(count)).map((_) =>
20
- pieceFactory(
21
- { ...{ ...pieceRule, variantId }, ...variant },
22
- this.options
23
- )
24
- )
25
- ]
26
- }, [])
27
-
28
- if (pieceRule.shuffled) {
29
- this.pool = this.pool
30
- .map((value) => ({ value, sort: Math.random() }))
31
- .sort((a, b) => a.sort - b.sort)
32
- .map(({ value }) => value)
33
- }
34
-
35
- this.count = this.pool.length || pieceRule.count
36
- }
37
-
38
- getOne () {
39
- return this.getMultiple(1)[0]
40
- }
41
-
42
- getMultiple (count) {
43
- const toReturn = []
44
- if (this.count === undefined || this.count >= count) {
45
- if (this.count) {
46
- this.count -= count
47
- }
48
- const remainder = count - this.pool.length
49
- toReturn.push(...this.pool.splice(0, count))
50
-
51
- if (remainder > 0) {
52
- toReturn.push(
53
- ...Array.from(new Array(remainder)).map(() =>
54
- pieceFactory(this.pieceRule, this.options)
55
- )
56
- )
57
- }
58
- }
59
- return toReturn
60
- }
61
-
62
- put (piece) {
63
- if (this.count !== undefined) {
64
- this.count += 1
65
- }
66
- this.pool.push(piece)
67
- }
68
- }
69
-
70
- export default Pile
@@ -1,13 +0,0 @@
1
- import type { PlayerRule } from "../../types";
2
-
3
- class Player {
4
- rule: PlayerRule;
5
- index: number;
6
- constructor(rule: PlayerRule, index: number, id: string) {
7
- this.rule = rule;
8
- this.index = index;
9
- this.id = id || `${Math.random()}`
10
- }
11
- }
12
-
13
- export default Player;
package/src/registry.ts DELETED
@@ -1,51 +0,0 @@
1
- import Cls0 from "./action/action.js";
2
- import Cls1 from "./action/move-piece-action.js";
3
- import Cls2 from "./action/select-piece-action.js";
4
- import Cls3 from "./action/swap-action.js";
5
- import Cls4 from "./board/board-group.js";
6
- import Cls5 from "./board/board.js";
7
- import Cls6 from "./board/grid.js";
8
- import Cls7 from "./board/stack.js";
9
- import Cls8 from "./condition/action-type-matches-condition.js";
10
- import Cls9 from "./condition/bingo-condition.js";
11
- import Cls10 from "./condition/blackout-condition.js";
12
- import Cls11 from "./condition/condition.js";
13
- import Cls12 from "./condition/contains-condition.js";
14
- import Cls13 from "./condition/does-not-contain-condition.js";
15
- import Cls14 from "./condition/is-valid-player-condition.js";
16
- import Cls15 from "./condition/piece-matches-condition.js";
17
- import Cls16 from "./condition/relative-move-condition.js";
18
- import Cls17 from "./condition/some-condition.js";
19
- import Cls18 from "./piece/piece.ts";
20
- import Cls19 from "./piece/pile.js";
21
- import Cls20 from "./player/player.ts";
22
- import Cls21 from "./round/round.js";
23
- import Cls22 from "./round/sequential-player-turn.js";
24
- import Cls24 from "./space/space.ts";
25
-
26
- export const registry = {
27
- "Action": Cls0,
28
- "MovePieceAction": Cls1,
29
- "SelectPieceAction": Cls2,
30
- "SwapAction": Cls3,
31
- "BoardGroup": Cls4,
32
- "Board": Cls5,
33
- "Grid": Cls6,
34
- "Stack": Cls7,
35
- "ActionTypeMatchesCondition": Cls8,
36
- "BingoCondition": Cls9,
37
- "BlackoutCondition": Cls10,
38
- "Condition": Cls11,
39
- "ContainsCondition": Cls12,
40
- "DoesNotContainCondition": Cls13,
41
- "IsValidPlayerCondition": Cls14,
42
- "PieceMatchesCondition": Cls15,
43
- "RelativeMoveCondition": Cls16,
44
- "SomeCondition": Cls17,
45
- "Piece": Cls18,
46
- "Pile": Cls19,
47
- "Player": Cls20,
48
- "Round": Cls21,
49
- "SequentialPlayerTurn": Cls22,
50
- "Space": Cls24
51
- };
@@ -1,7 +0,0 @@
1
- import SequentialPlayerTurn from "./sequential-player-turn.js";
2
-
3
- export default function roundFactory(roundRule, game) {
4
- if (roundRule.type === "sequentialPlayerTurn") {
5
- return new SequentialPlayerTurn(roundRule, game);
6
- }
7
- }
@@ -1,41 +0,0 @@
1
- import actionFactory from '../action/action-factory.js'
2
-
3
- const DEBUG = true
4
-
5
- export default class Round {
6
- constructor (rules, game) {
7
- this.rules = rules
8
- this.game = game
9
- this.history = []
10
- this.id = `${Math.random()}`
11
- this.actions = rules.actions?.map((actionRule) =>
12
- actionFactory(actionRule, this.game)
13
- )
14
- }
15
-
16
- getCorrectAction (actionPayload) {
17
- for (const action of this.actions) {
18
- try {
19
- action.assertIsValid(actionPayload)
20
- return action
21
- } catch (e) {
22
- if (DEBUG) {
23
- throw e
24
- }
25
- }
26
- }
27
- throw new Error(`Invalid Action: ${JSON.stringify(actionPayload)}`)
28
- }
29
-
30
- doAction (actionPayload) {
31
- this.getCorrectAction(actionPayload).do(actionPayload, this.game)
32
- this.afterDoAction()
33
- this.history.push(actionPayload)
34
- }
35
-
36
- isOver () {
37
- return true
38
- }
39
-
40
- afterDoAction () {}
41
- }
@@ -1,18 +0,0 @@
1
- import Round from "./round.js";
2
-
3
- export default class SequentialPlayerTurn extends Round {
4
- constructor(rules, game) {
5
- super(rules, game);
6
- this.currentPlayerIndex = 0;
7
- }
8
-
9
- afterDoAction() {
10
- this.currentPlayerIndex = (this.currentPlayerIndex + 1) % this.game.players.length;
11
- }
12
-
13
- isOver() {
14
- return this.game.players.every((player) =>
15
- this.history.some((p) => p === player),
16
- );
17
- }
18
- }
@@ -1,22 +0,0 @@
1
- import type Piece from "../piece/piece";
2
-
3
- type Coordinates = [number, number];
4
-
5
- export default class Space {
6
- coordinates: Coordinates;
7
- pieces: Piece[];
8
-
9
- constructor(coordinates: Coordinates, startingPieces: Piece[] = []) {
10
- this.coordinates = coordinates;
11
- this.pieces = startingPieces;
12
- this.id = `${Math.random()}`
13
- }
14
-
15
- placePiece(piece: Piece): void {
16
- this.pieces.push(piece);
17
- }
18
-
19
- isEmpty(): boolean {
20
- return this.pieces.length === 0;
21
- }
22
- }
@@ -1,37 +0,0 @@
1
- // todo: return found object along w/ path
2
- export default function findValuePath(
3
- obj,
4
- compare,
5
- currentPath = [],
6
- visited = new Set(),
7
- results = new Set(),
8
- ) {
9
- // Check for circular reference
10
- if (visited.has(obj)) {
11
- return results; // Circular reference detected, short-circuit
12
- }
13
-
14
- visited.add(obj); // Mark the current object as visited
15
-
16
- if (compare(obj)) {
17
- results.add(currentPath);
18
- }
19
-
20
- if (typeof obj === "object" && obj !== null) {
21
- for (const key in obj) {
22
- const newPath = [...currentPath, key];
23
- const result = findValuePath(
24
- obj[key],
25
- compare,
26
- newPath,
27
- visited,
28
- results,
29
- );
30
- if (result.length) {
31
- results.add(currentPath);
32
- }
33
- }
34
- }
35
-
36
- return results;
37
- }
@@ -1,38 +0,0 @@
1
- import get from 'lodash/get.js'
2
-
3
- export default function resolveBoard (board, gameState) {
4
- // Case 1: if board is an array treat it as a path
5
- if (Array.isArray(board)) {
6
- return get(gameState, board)
7
- }
8
-
9
- const visited = new WeakSet()
10
-
11
- function search (node) {
12
- if (!node || typeof node !== 'object') return null
13
-
14
- // Prevent infinite loops by tracking visited objects
15
- if (visited.has(node)) return null
16
- visited.add(node)
17
-
18
- if (node.id === board.id) return node
19
-
20
- if (node.grid && Array.isArray(node.grid)) {
21
- for (const row of node.grid) {
22
- for (const cell of row) {
23
- const found = search(cell)
24
- if (found) return found
25
- }
26
- }
27
- }
28
-
29
- for (const value of Object.values(node)) {
30
- const found = search(value)
31
- if (found) return found
32
- }
33
-
34
- return null
35
- }
36
-
37
- return search(gameState)
38
- }