board-game-engine 1.0.5 → 2.0.1

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 (254) hide show
  1. package/.github/workflows/ci.yml +33 -0
  2. package/dist/board-game-engine.cjs +463 -349
  3. package/dist/board-game-engine.js +463 -349
  4. package/dist/board-game-engine.min.js +22 -22
  5. package/dist/board-game-engine.mjs +461 -348
  6. package/dist/client/client.d.ts +76 -0
  7. package/dist/client/client.d.ts.map +1 -0
  8. package/dist/game-factory/bank/bank-slot.d.ts +30 -0
  9. package/dist/game-factory/bank/bank-slot.d.ts.map +1 -0
  10. package/dist/game-factory/bank/bank.d.ts +34 -0
  11. package/dist/game-factory/bank/bank.d.ts.map +1 -0
  12. package/dist/game-factory/board.d.ts +4 -0
  13. package/dist/game-factory/board.d.ts.map +1 -0
  14. package/dist/game-factory/condition/condition-factory.d.ts +4 -0
  15. package/dist/game-factory/condition/condition-factory.d.ts.map +1 -0
  16. package/dist/game-factory/condition/condition.d.ts +13 -0
  17. package/dist/game-factory/condition/condition.d.ts.map +1 -0
  18. package/dist/game-factory/condition/contains-condition.d.ts +8 -0
  19. package/dist/game-factory/condition/contains-condition.d.ts.map +1 -0
  20. package/dist/game-factory/condition/contains-same-condition.d.ts +7 -0
  21. package/dist/game-factory/condition/contains-same-condition.d.ts.map +1 -0
  22. package/dist/game-factory/condition/evaluate-condition.d.ts +8 -0
  23. package/dist/game-factory/condition/evaluate-condition.d.ts.map +1 -0
  24. package/dist/game-factory/condition/every-condition.d.ts +12 -0
  25. package/dist/game-factory/condition/every-condition.d.ts.map +1 -0
  26. package/dist/game-factory/condition/has-line-condition.d.ts +8 -0
  27. package/dist/game-factory/condition/has-line-condition.d.ts.map +1 -0
  28. package/dist/game-factory/condition/in-line-condition.d.ts +8 -0
  29. package/dist/game-factory/condition/in-line-condition.d.ts.map +1 -0
  30. package/dist/game-factory/condition/is-condition.d.ts +8 -0
  31. package/dist/game-factory/condition/is-condition.d.ts.map +1 -0
  32. package/dist/game-factory/condition/is-full-condition.d.ts +7 -0
  33. package/dist/game-factory/condition/is-full-condition.d.ts.map +1 -0
  34. package/dist/game-factory/condition/no-possible-moves-condition.d.ts +7 -0
  35. package/dist/game-factory/condition/no-possible-moves-condition.d.ts.map +1 -0
  36. package/dist/game-factory/condition/not-condition.d.ts +7 -0
  37. package/dist/game-factory/condition/not-condition.d.ts.map +1 -0
  38. package/dist/game-factory/condition/or-condition.d.ts +7 -0
  39. package/dist/game-factory/condition/or-condition.d.ts.map +1 -0
  40. package/dist/game-factory/condition/position-condition.d.ts +7 -0
  41. package/dist/game-factory/condition/position-condition.d.ts.map +1 -0
  42. package/dist/game-factory/condition/some-condition.d.ts +8 -0
  43. package/dist/game-factory/condition/some-condition.d.ts.map +1 -0
  44. package/dist/game-factory/condition/would-condition.d.ts +8 -0
  45. package/dist/game-factory/condition/would-condition.d.ts.map +1 -0
  46. package/dist/game-factory/entity.d.ts +13 -0
  47. package/dist/game-factory/entity.d.ts.map +1 -0
  48. package/dist/game-factory/expand-game-rules.d.ts +3 -0
  49. package/dist/game-factory/expand-game-rules.d.ts.map +1 -0
  50. package/dist/game-factory/game-factory.d.ts +10 -0
  51. package/dist/game-factory/game-factory.d.ts.map +1 -0
  52. package/{src/game-factory/move/end-turn.js → dist/game-factory/move/end-turn.d.ts} +2 -4
  53. package/dist/game-factory/move/end-turn.d.ts.map +1 -0
  54. package/dist/game-factory/move/for-each.d.ts +5 -0
  55. package/dist/game-factory/move/for-each.d.ts.map +1 -0
  56. package/dist/game-factory/move/index.d.ts +6 -0
  57. package/dist/game-factory/move/index.d.ts.map +1 -0
  58. package/dist/game-factory/move/move-entity.d.ts +7 -0
  59. package/dist/game-factory/move/move-entity.d.ts.map +1 -0
  60. package/dist/game-factory/move/move-factory.d.ts +18 -0
  61. package/dist/game-factory/move/move-factory.d.ts.map +1 -0
  62. package/dist/game-factory/move/move.d.ts +54 -0
  63. package/dist/game-factory/move/move.d.ts.map +1 -0
  64. package/dist/game-factory/move/pass-turn.d.ts +5 -0
  65. package/dist/game-factory/move/pass-turn.d.ts.map +1 -0
  66. package/{src/game-factory/move/pass.js → dist/game-factory/move/pass.d.ts} +2 -4
  67. package/dist/game-factory/move/pass.d.ts.map +1 -0
  68. package/dist/game-factory/move/place-new.d.ts +5 -0
  69. package/dist/game-factory/move/place-new.d.ts.map +1 -0
  70. package/dist/game-factory/move/remove-entity.d.ts +5 -0
  71. package/dist/game-factory/move/remove-entity.d.ts.map +1 -0
  72. package/dist/game-factory/move/set-active-players.d.ts +5 -0
  73. package/dist/game-factory/move/set-active-players.d.ts.map +1 -0
  74. package/dist/game-factory/move/set-state.d.ts +5 -0
  75. package/dist/game-factory/move/set-state.d.ts.map +1 -0
  76. package/dist/game-factory/move/shuffle.d.ts +5 -0
  77. package/dist/game-factory/move/shuffle.d.ts.map +1 -0
  78. package/dist/game-factory/move/take-from.d.ts +11 -0
  79. package/dist/game-factory/move/take-from.d.ts.map +1 -0
  80. package/dist/game-factory/space/space.d.ts +10 -0
  81. package/dist/game-factory/space/space.d.ts.map +1 -0
  82. package/dist/game-factory/space-group/grid.d.ts +15 -0
  83. package/dist/game-factory/space-group/grid.d.ts.map +1 -0
  84. package/dist/game-factory/space-group/space-group.d.ts +20 -0
  85. package/dist/game-factory/space-group/space-group.d.ts.map +1 -0
  86. package/dist/index.d.ts +6 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/registry.d.ts +17 -0
  89. package/dist/registry.d.ts.map +1 -0
  90. package/dist/types/bagel-types.d.ts +339 -0
  91. package/dist/types/bagel-types.d.ts.map +1 -0
  92. package/dist/types/index.d.ts +3 -0
  93. package/dist/types/index.d.ts.map +1 -0
  94. package/dist/types/rule-with-conditions.d.ts +9 -0
  95. package/dist/types/rule-with-conditions.d.ts.map +1 -0
  96. package/dist/utils/any-valid-moves.d.ts +2 -0
  97. package/dist/utils/any-valid-moves.d.ts.map +1 -0
  98. package/dist/utils/bgio-resolve-types.d.ts +25 -0
  99. package/dist/utils/bgio-resolve-types.d.ts.map +1 -0
  100. package/dist/utils/check-conditions.d.ts +7 -0
  101. package/dist/utils/check-conditions.d.ts.map +1 -0
  102. package/dist/utils/create-payload.d.ts +5 -0
  103. package/dist/utils/create-payload.d.ts.map +1 -0
  104. package/dist/utils/deserialize-bgio-arguments.d.ts +3 -0
  105. package/dist/utils/deserialize-bgio-arguments.d.ts.map +1 -0
  106. package/dist/utils/do-moves.d.ts +8 -0
  107. package/dist/utils/do-moves.d.ts.map +1 -0
  108. package/dist/utils/entity-matches.d.ts +6 -0
  109. package/dist/utils/entity-matches.d.ts.map +1 -0
  110. package/dist/utils/find-met-condition.d.ts +6 -0
  111. package/dist/utils/find-met-condition.d.ts.map +1 -0
  112. package/dist/utils/get-current-moves.d.ts +24 -0
  113. package/dist/utils/get-current-moves.d.ts.map +1 -0
  114. package/dist/utils/get-scenario-results.d.ts +3 -0
  115. package/dist/utils/get-scenario-results.d.ts.map +1 -0
  116. package/dist/utils/get-steps.d.ts +13 -0
  117. package/dist/utils/get-steps.d.ts.map +1 -0
  118. package/dist/utils/get.d.ts +7 -0
  119. package/dist/utils/get.d.ts.map +1 -0
  120. package/dist/utils/grid-contains-sequence.d.ts +27 -0
  121. package/dist/utils/grid-contains-sequence.d.ts.map +1 -0
  122. package/dist/utils/json-transformer.d.ts +8 -0
  123. package/dist/utils/json-transformer.d.ts.map +1 -0
  124. package/dist/utils/prepare-payload.d.ts +2 -0
  125. package/dist/utils/prepare-payload.d.ts.map +1 -0
  126. package/dist/utils/resolve-entity.d.ts +3 -0
  127. package/dist/utils/resolve-entity.d.ts.map +1 -0
  128. package/dist/utils/resolve-expression.d.ts +6 -0
  129. package/dist/utils/resolve-expression.d.ts.map +1 -0
  130. package/dist/utils/resolve-properties.d.ts +4 -0
  131. package/dist/utils/resolve-properties.d.ts.map +1 -0
  132. package/dist/utils/simulate-move.d.ts +16 -0
  133. package/dist/utils/simulate-move.d.ts.map +1 -0
  134. package/examples/checkers.json +2 -2
  135. package/examples/connect-four.json +1 -1
  136. package/examples/eights.json +15 -15
  137. package/package.json +9 -3
  138. package/playwright-report/index.html +1 -1
  139. package/scripts/build.mjs +2 -2
  140. package/src/client/client.ts +306 -0
  141. package/src/game-factory/bank/bank-slot.ts +81 -0
  142. package/src/game-factory/bank/bank.ts +125 -0
  143. package/src/game-factory/{board.js → board.ts} +1 -1
  144. package/src/game-factory/condition/condition-factory.ts +59 -0
  145. package/src/game-factory/condition/condition.ts +50 -0
  146. package/src/game-factory/condition/{contains-condition.js → contains-condition.ts} +5 -4
  147. package/src/game-factory/condition/{contains-same-condition.js → contains-same-condition.ts} +8 -5
  148. package/src/game-factory/condition/{evaluate-condition.js → evaluate-condition.ts} +4 -3
  149. package/src/game-factory/condition/every-condition.ts +27 -0
  150. package/src/game-factory/condition/has-line-condition.ts +15 -0
  151. package/src/game-factory/condition/in-line-condition.ts +25 -0
  152. package/src/game-factory/condition/is-condition.ts +24 -0
  153. package/src/game-factory/condition/is-full-condition.ts +10 -0
  154. package/src/game-factory/condition/{no-possible-moves-condition.js → no-possible-moves-condition.ts} +3 -2
  155. package/src/game-factory/condition/{not-condition.js → not-condition.ts} +3 -2
  156. package/src/game-factory/condition/{or-condition.js → or-condition.ts} +3 -2
  157. package/src/game-factory/condition/position-condition.ts +13 -0
  158. package/src/game-factory/condition/{some-condition.js → some-condition.ts} +5 -3
  159. package/src/game-factory/condition/would-condition.ts +104 -0
  160. package/src/game-factory/entity.ts +37 -0
  161. package/src/game-factory/expand-game-rules.ts +263 -0
  162. package/src/game-factory/game-factory.ts +263 -0
  163. package/src/game-factory/move/end-turn.ts +7 -0
  164. package/src/game-factory/move/for-each.ts +20 -0
  165. package/src/game-factory/move/move-entity.ts +18 -0
  166. package/src/game-factory/move/move-factory.ts +107 -0
  167. package/src/game-factory/move/move.ts +147 -0
  168. package/src/game-factory/move/pass-turn.ts +15 -0
  169. package/src/game-factory/move/pass.ts +7 -0
  170. package/src/game-factory/move/place-new.ts +42 -0
  171. package/src/game-factory/move/remove-entity.ts +11 -0
  172. package/src/game-factory/move/set-active-players.ts +26 -0
  173. package/src/game-factory/move/set-state.ts +14 -0
  174. package/src/game-factory/move/shuffle.ts +9 -0
  175. package/src/game-factory/move/take-from.ts +12 -0
  176. package/src/game-factory/space/space.ts +36 -0
  177. package/src/game-factory/space-group/grid.ts +48 -0
  178. package/src/game-factory/space-group/space-group.ts +44 -0
  179. package/src/index.ts +5 -0
  180. package/src/types/bagel-types.ts +449 -0
  181. package/src/types/boardgame-io-core.d.ts +7 -0
  182. package/src/types/index.ts +70 -0
  183. package/src/types/rule-with-conditions.ts +9 -0
  184. package/src/utils/{any-valid-moves.js → any-valid-moves.ts} +54 -49
  185. package/src/utils/bgio-resolve-types.ts +27 -0
  186. package/src/utils/check-conditions.ts +28 -0
  187. package/src/utils/create-payload.ts +19 -0
  188. package/src/utils/deserialize-bgio-arguments.ts +10 -0
  189. package/src/utils/do-moves.ts +22 -0
  190. package/src/utils/entity-matches.ts +30 -0
  191. package/src/utils/expr-eval.d.ts +6 -0
  192. package/src/utils/find-met-condition.ts +23 -0
  193. package/src/utils/get-current-moves.ts +39 -0
  194. package/src/utils/get-scenario-results.ts +30 -0
  195. package/src/utils/get-steps.ts +38 -0
  196. package/src/utils/get.ts +28 -0
  197. package/src/utils/{grid-contains-sequence.js → grid-contains-sequence.ts} +71 -33
  198. package/src/utils/json-transformer.ts +17 -0
  199. package/src/utils/prepare-payload.ts +20 -0
  200. package/src/utils/resolve-entity.ts +15 -0
  201. package/src/utils/resolve-expression.ts +16 -0
  202. package/src/utils/resolve-properties.ts +172 -0
  203. package/src/utils/simulate-move.ts +32 -0
  204. package/src/wackson.d.ts +4 -0
  205. package/tsconfig.build.json +14 -0
  206. package/tsconfig.json +21 -0
  207. package/src/client/client.js +0 -224
  208. package/src/game-factory/bank/bank-slot.js +0 -69
  209. package/src/game-factory/bank/bank.js +0 -114
  210. package/src/game-factory/condition/condition-factory.js +0 -52
  211. package/src/game-factory/condition/condition.js +0 -39
  212. package/src/game-factory/condition/every-condition.js +0 -25
  213. package/src/game-factory/condition/has-line-condition.js +0 -14
  214. package/src/game-factory/condition/in-line-condition.js +0 -19
  215. package/src/game-factory/condition/is-condition.js +0 -23
  216. package/src/game-factory/condition/is-full-condition.js +0 -9
  217. package/src/game-factory/condition/position-condition.js +0 -12
  218. package/src/game-factory/condition/would-condition.js +0 -94
  219. package/src/game-factory/entity.js +0 -29
  220. package/src/game-factory/expand-game-rules.js +0 -271
  221. package/src/game-factory/game-factory.js +0 -239
  222. package/src/game-factory/move/for-each.js +0 -18
  223. package/src/game-factory/move/move-entity.js +0 -16
  224. package/src/game-factory/move/move-factory.js +0 -89
  225. package/src/game-factory/move/move.js +0 -131
  226. package/src/game-factory/move/pass-turn.js +0 -10
  227. package/src/game-factory/move/place-new.js +0 -33
  228. package/src/game-factory/move/remove-entity.js +0 -7
  229. package/src/game-factory/move/set-active-players.js +0 -23
  230. package/src/game-factory/move/set-state.js +0 -11
  231. package/src/game-factory/move/shuffle.js +0 -7
  232. package/src/game-factory/move/take-from.js +0 -7
  233. package/src/game-factory/space/space.js +0 -30
  234. package/src/game-factory/space-group/grid.js +0 -43
  235. package/src/game-factory/space-group/space-group.js +0 -29
  236. package/src/index.js +0 -2
  237. package/src/utils/check-conditions.js +0 -28
  238. package/src/utils/create-payload.js +0 -16
  239. package/src/utils/deserialize-bgio-arguments.js +0 -8
  240. package/src/utils/do-moves.js +0 -18
  241. package/src/utils/entity-matches.js +0 -20
  242. package/src/utils/find-met-condition.js +0 -22
  243. package/src/utils/get-current-moves.js +0 -12
  244. package/src/utils/get-scenario-results.js +0 -23
  245. package/src/utils/get-steps.js +0 -29
  246. package/src/utils/get.js +0 -25
  247. package/src/utils/json-transformer.js +0 -12
  248. package/src/utils/prepare-payload.js +0 -16
  249. package/src/utils/resolve-entity.js +0 -9
  250. package/src/utils/resolve-expression.js +0 -10
  251. package/src/utils/resolve-properties.js +0 -149
  252. package/src/utils/simulate-move.js +0 -25
  253. /package/src/game-factory/move/{index.js → index.ts} +0 -0
  254. /package/src/{registry.js → registry.ts} +0 -0
@@ -1,20 +1,37 @@
1
1
  // claude ai did most of this
2
2
  import _matches from "lodash/matches.js";
3
+ import type { Condition } from "../types/bagel-types.js";
3
4
  import checkConditions from "./check-conditions.js";
4
5
 
6
+ export type SequenceChunk = {
7
+ count?: number;
8
+ minCount?: number;
9
+ maxCount?: number;
10
+ conditions?: Condition[];
11
+ };
12
+
13
+ type GridEntity = { entities?: unknown[]; [k: string]: unknown };
14
+ type GridSpace = { entities?: GridEntity[]; [k: string]: unknown };
15
+ export type GridLike = {
16
+ attributes: { width: number; height: number };
17
+ entities?: GridSpace[];
18
+ areCoordinatesValid: (c: number[]) => boolean;
19
+ getSpace: (c: number[]) => unknown;
20
+ };
21
+
5
22
  // We'll check reverse directions along each line
6
- const directions = [
23
+ const directions: [number, number][] = [
7
24
  [1, 0], // horizontal
8
25
  [0, 1], // vertical
9
26
  [1, 1], // diagonal down-right
10
27
  [-1, 1], // diagonal down-left
11
28
  ];
12
29
 
13
- const sequenceCache = new WeakMap();
30
+ const sequenceCache = new WeakMap<object, Map<string, { stateKey: string; result: unknown }>>();
14
31
 
15
- function getSequenceKey(sequencePattern, context) {
32
+ function getSequenceKey (sequencePattern: unknown, context: Record<string, unknown>) {
16
33
  const contextKey = {
17
- moveInstance: context.moveInstance?.id,
34
+ moveInstance: (context.moveInstance as { id?: unknown } | undefined)?.id,
18
35
  moveArguments: context.moveArguments,
19
36
  // Add other context properties that conditions might use
20
37
  };
@@ -22,17 +39,17 @@ function getSequenceKey(sequencePattern, context) {
22
39
  }
23
40
 
24
41
  // todo: use stable hash library that we're using for game rules hash
25
- function getGridStateKey(grid) {
42
+ function getGridStateKey (grid: GridLike) {
26
43
  const spaces = grid.entities || [];
27
-
28
- return spaces.map(space => {
44
+
45
+ return spaces.map((space) => {
29
46
  const entities = space.entities || [];
30
- if (entities.length === 0) return 'empty';
31
-
32
- return entities.map(entity => {
47
+ if (entities.length === 0) return "empty";
48
+
49
+ return entities.map((entity) => {
33
50
  const sortedKeys = Object.keys(entity).sort();
34
- const stateObj = {};
35
- sortedKeys.forEach(key => {
51
+ const stateObj: Record<string, unknown> = {};
52
+ sortedKeys.forEach((key) => {
36
53
  stateObj[key] = entity[key];
37
54
  });
38
55
  return JSON.stringify(stateObj);
@@ -40,8 +57,15 @@ function getGridStateKey(grid) {
40
57
  }).join(',');
41
58
  }
42
59
 
43
- function findSequencesInLine(bgioArguments, lineSpaces, sequencePattern, minSequenceLength, context, reverse = false) {
44
- const matches = [];
60
+ function findSequencesInLine (
61
+ bgioArguments: unknown,
62
+ lineSpaces: unknown[],
63
+ sequencePattern: SequenceChunk[],
64
+ minSequenceLength: number,
65
+ context: Record<string, unknown>,
66
+ reverse = false
67
+ ) {
68
+ const matches: unknown[][] = [];
45
69
 
46
70
  // Use original array or iterate in reverse without creating new array
47
71
  const length = lineSpaces.length;
@@ -68,9 +92,9 @@ function findSequencesInLine(bgioArguments, lineSpaces, sequencePattern, minSequ
68
92
  return matches;
69
93
  }
70
94
 
71
- function getLineStartingPoints(grid, dx, dy) {
95
+ function getLineStartingPoints (grid: GridLike, dx: number, dy: number) {
72
96
  const { width, height } = grid.attributes;
73
- const starts = [];
97
+ const starts: [number, number][] = [];
74
98
 
75
99
  if (dx === 1 && dy === 0) {
76
100
  // Horizontal: start at leftmost column
@@ -91,8 +115,8 @@ function getLineStartingPoints(grid, dx, dy) {
91
115
  return starts;
92
116
  }
93
117
 
94
- function getLineSpaces(grid, startX, startY, dx, dy) {
95
- const spaces = [];
118
+ function getLineSpaces (grid: GridLike, startX: number, startY: number, dx: number, dy: number) {
119
+ const spaces: unknown[] = [];
96
120
  let [x, y] = [startX, startY];
97
121
 
98
122
  while (grid.areCoordinatesValid([x, y])) {
@@ -103,9 +127,16 @@ function getLineSpaces(grid, startX, startY, dx, dy) {
103
127
  return spaces;
104
128
  }
105
129
 
106
- function tryMatchSequence(bgioArguments, lineSpaces, startIndex, sequencePattern, context, reverse = false) {
130
+ function tryMatchSequence (
131
+ bgioArguments: unknown,
132
+ lineSpaces: unknown[],
133
+ startIndex: number,
134
+ sequencePattern: SequenceChunk[],
135
+ context: Record<string, unknown>,
136
+ reverse = false
137
+ ) {
107
138
  let spaceIndex = startIndex;
108
- const matchedSpaces = [];
139
+ const matchedSpaces: unknown[] = [];
109
140
  const length = lineSpaces.length;
110
141
 
111
142
  for (const chunk of sequencePattern) {
@@ -122,7 +153,7 @@ function tryMatchSequence(bgioArguments, lineSpaces, startIndex, sequencePattern
122
153
  }
123
154
 
124
155
  let matchedCount = 0;
125
- const chunkMatches = [];
156
+ const chunkMatches: unknown[] = [];
126
157
 
127
158
  // Greedy: try to match as many as possible up to max
128
159
  while (matchedCount < max && spaceIndex < length) {
@@ -152,15 +183,16 @@ function tryMatchSequence(bgioArguments, lineSpaces, startIndex, sequencePattern
152
183
  return matchedSpaces.length > 0 ? matchedSpaces : null;
153
184
  }
154
185
 
155
- function checkSpaceConditions(bgioArguments, space, conditions, chunkMatches = [], context) {
156
- // Early exit if no conditions
157
- if (!conditions || conditions.length === 0) {
158
- return true;
159
- }
160
-
186
+ function checkSpaceConditions (
187
+ bgioArguments: unknown,
188
+ space: unknown,
189
+ conditions: Condition[] | undefined,
190
+ chunkMatches: unknown[] = [],
191
+ context?: Record<string, unknown>
192
+ ) {
161
193
  return checkConditions(
162
194
  bgioArguments,
163
- { conditions },
195
+ conditions,
164
196
  {
165
197
  target: space,
166
198
  targets: [space, ...chunkMatches] // for ContainsSame, other group conditions
@@ -169,7 +201,12 @@ function checkSpaceConditions(bgioArguments, space, conditions, chunkMatches = [
169
201
  ).conditionsAreMet
170
202
  }
171
203
 
172
- export default function gridContainsSequence(bgioArguments, grid, sequencePattern, context) {
204
+ export default function gridContainsSequence (
205
+ bgioArguments: unknown,
206
+ grid: GridLike,
207
+ sequencePattern: SequenceChunk[],
208
+ context: Record<string, unknown>
209
+ ) {
173
210
  const cacheKey = getSequenceKey(sequencePattern, context);
174
211
  let gridCache = sequenceCache.get(grid);
175
212
 
@@ -185,10 +222,11 @@ export default function gridContainsSequence(bgioArguments, grid, sequencePatter
185
222
  return cacheEntry.result;
186
223
  }
187
224
 
188
- const matches = [];
189
-
190
- const minSequenceLength = sequencePattern.reduce((sum, chunk) =>
191
- sum + (chunk.minCount || chunk.count || 1), 0
225
+ const matches: unknown[][] = [];
226
+
227
+ const minSequenceLength = sequencePattern.reduce(
228
+ (sum: number, chunk: SequenceChunk) => sum + (chunk.minCount || chunk.count || 1),
229
+ 0
192
230
  );
193
231
 
194
232
  // For each direction, scan each row/column/diagonal once
@@ -0,0 +1,17 @@
1
+ type TransformRule = {
2
+ test: (val: unknown) => boolean;
3
+ replace: (val: unknown) => unknown;
4
+ };
5
+
6
+ /** Recurse and replace. Circular references not allowed. */
7
+ export default function transformJSON (data: unknown, rules: TransformRule[]): unknown {
8
+ return JSON.parse(JSON.stringify(data), (_key, value: unknown) => {
9
+ let result = value;
10
+ for (const rule of rules) {
11
+ if (rule.test(result)) {
12
+ result = rule.replace(result);
13
+ }
14
+ }
15
+ return result;
16
+ });
17
+ }
@@ -0,0 +1,20 @@
1
+ import { serialize } from "wackson";
2
+
3
+ // todo: remember why reducing to id was preferred?
4
+ export default function preparePayload (payload: unknown): unknown {
5
+ const p = payload as {
6
+ arguments?: Record<string, { abstract?: boolean; entityId?: unknown }>;
7
+ } | null | undefined;
8
+ if (p?.arguments) {
9
+ const payloadCopy = {
10
+ ...p,
11
+ arguments: Object.entries(p.arguments).reduce<Record<string, unknown>>((acc, [key, argument]) => ({
12
+ ...acc,
13
+ [key]: argument.abstract ? argument : argument.entityId,
14
+ }), {}),
15
+ };
16
+ return JSON.parse(serialize(payloadCopy, { deduplicateInstances: false }));
17
+ } else {
18
+ return payload;
19
+ }
20
+ }
@@ -0,0 +1,15 @@
1
+ import isPlainObject from "lodash/isPlainObject.js";
2
+ import { bankOf, type BgioResolveState } from "./bgio-resolve-types.js";
3
+
4
+ const abstractTargetNames = ["state"];
5
+
6
+ export default function resolveEntity (
7
+ bgioArguments: BgioResolveState,
8
+ target: unknown,
9
+ context: unknown,
10
+ targetName: string | undefined
11
+ ): unknown {
12
+ return !abstractTargetNames.includes(targetName ?? "") && isPlainObject(target)
13
+ ? bankOf(bgioArguments).find(bgioArguments, target, context)
14
+ : target;
15
+ }
@@ -0,0 +1,16 @@
1
+ import { Parser } from "expr-eval";
2
+ import resolveProperties from "./resolve-properties.js";
3
+ import type { BgioResolveState } from "./bgio-resolve-types.js";
4
+
5
+ const parser = new Parser();
6
+ (parser.functions as Record<string, (...args: unknown[]) => unknown>).sum = (...args: unknown[]) =>
7
+ (args[0] as number[]).reduce((acc, val) => acc + val, 0);
8
+
9
+ export default function resolveExpression (
10
+ bgioArguments: BgioResolveState,
11
+ rule: { expression: string; arguments?: unknown },
12
+ context: Record<string, unknown>
13
+ ): unknown {
14
+ const args = resolveProperties(bgioArguments, rule.arguments, context) as Record<string, unknown>;
15
+ return parser.evaluate(rule.expression, args);
16
+ }
@@ -0,0 +1,172 @@
1
+ import isPlainObject from "lodash/isPlainObject.js";
2
+ import pick from "lodash/pick.js";
3
+ import get from "./get.js";
4
+ import resolveExpression from "./resolve-expression.js";
5
+ import resolveEntity from "./resolve-entity.js";
6
+ import { bankOf, type BgioResolveState } from "./bgio-resolve-types.js";
7
+
8
+ export type { BgioResolveState };
9
+
10
+ const resolutionTerminators = [
11
+ "conditions",
12
+ "move",
13
+ "then",
14
+ "mapping",
15
+ ];
16
+
17
+ export default function resolveProperties (
18
+ bgioArguments: BgioResolveState,
19
+ obj: unknown,
20
+ context: Record<string, unknown> = {},
21
+ key?: string
22
+ ): unknown {
23
+ if (!isPlainObject(obj) && !Array.isArray(obj)) {
24
+ return obj;
25
+ }
26
+
27
+ let resolvedProperties: Record<string, unknown> | unknown[] = Array.isArray(obj)
28
+ ? [...obj]
29
+ : { ...(obj as Record<string, unknown>) };
30
+
31
+ Object.entries(obj as Record<string, unknown>).forEach(([k, value]) => {
32
+ if (!resolutionTerminators.includes(k)) {
33
+ (resolvedProperties as Record<string, unknown>)[k] = resolveProperties(bgioArguments, value, context, k);
34
+ }
35
+ });
36
+
37
+ const resolved = resolveProperty(bgioArguments, resolvedProperties, context);
38
+
39
+ const resolveAsEntity = (resolved as { resolveAsEntity?: boolean } | null)?.resolveAsEntity
40
+ || key === "target"
41
+ || key === "targets";
42
+
43
+ return resolveAsEntity
44
+ ? resolveEntity(
45
+ bgioArguments,
46
+ resolved,
47
+ context,
48
+ key
49
+ )
50
+ : resolved;
51
+ }
52
+
53
+ function resolveProperty (
54
+ bgioArguments: BgioResolveState,
55
+ value: unknown,
56
+ context: Record<string, unknown>
57
+ ): unknown {
58
+ const v = value as Record<string, unknown> | null;
59
+ if (v?.type === "expression") {
60
+ const expr = v as { expression: string; arguments?: unknown };
61
+ return resolveExpression(
62
+ bgioArguments,
63
+ {
64
+ ...expr,
65
+ arguments: resolveProperties(bgioArguments, expr.arguments, context, "arguments"),
66
+ },
67
+ context
68
+ );
69
+ } else if (v?.type === "count") {
70
+ return bankOf(bgioArguments).findAll(
71
+ bgioArguments,
72
+ value,
73
+ context
74
+ ).length;
75
+ } else if (v?.type === "contextPath") {
76
+ return get(context, v.path as Parameters<typeof get>[1]);
77
+ } else if (v?.type === "ctxPath") {
78
+ return get(bgioArguments.ctx, v.path as Parameters<typeof get>[1]);
79
+ } else if (v?.type === "gamePath") {
80
+ return get(bgioArguments.G, v.path as Parameters<typeof get>[1]);
81
+ } else if (v?.type === "relativePath" || v?.type === "RelativePath") {
82
+ const target = resolveProperties(bgioArguments, v.target, context, "target");
83
+ return get((target as { attributes?: unknown })?.attributes, v.path as Parameters<typeof get>[1]) ?? null;
84
+ } else if (v?.type === "parent" || v?.type === "Parent") {
85
+ const originalTarget = v.target
86
+ ? resolveProperties(bgioArguments, v.target, context, "target")
87
+ : context.originalTarget;
88
+ return bankOf(bgioArguments).findParent(originalTarget) ?? null;
89
+ } else if (v?.type === "map") {
90
+ return getMappedTargets(
91
+ bgioArguments,
92
+ v.targets,
93
+ v.mapping,
94
+ context
95
+ ).map((mappedTarget) => mappedTarget.value);
96
+ } else if (v?.type === "mapMax") {
97
+ const mappedTargets = getMappedTargets(
98
+ bgioArguments,
99
+ v.targets,
100
+ v.mapping,
101
+ context
102
+ );
103
+ let maxValue: number | undefined;
104
+ const maxTargets: unknown[] = [];
105
+ for (let i = 0, len = mappedTargets.length; i < len; i++) {
106
+ const { target, value: val } = mappedTargets[i];
107
+ if (maxValue === undefined || (val as number) > maxValue) {
108
+ maxValue = val as number;
109
+ maxTargets.length = 0;
110
+ maxTargets.push(target);
111
+ } else if (val === maxValue) {
112
+ maxTargets.push(target);
113
+ }
114
+ }
115
+ return maxTargets;
116
+ } else if (v?.type === "pick" || v?.type === "Pick") {
117
+ const target = resolveProperties(bgioArguments, v.target, context, "target");
118
+ return pick(
119
+ resolveProperties(
120
+ bgioArguments,
121
+ (target as { attributes?: unknown })?.attributes,
122
+ context,
123
+ "attributes"
124
+ ) as object,
125
+ v.properties as string[]
126
+ );
127
+ } else if (v?.type === "coordinates" || v?.type === "Coordinates") {
128
+ const originalTarget = v.target
129
+ ? resolveProperties(bgioArguments, v.target, context, "target")
130
+ : context.originalTarget;
131
+ const parent = bankOf(bgioArguments).findParent(originalTarget) as { getCoordinates: (i: unknown) => unknown };
132
+ return parent.getCoordinates((originalTarget as { rule: { index: unknown } }).rule.index);
133
+ } else if (v?.type === "relativeCoordinates") {
134
+ const originalTarget = v.target
135
+ ? resolveProperties(bgioArguments, v.target, context, "target")
136
+ : context.originalTarget;
137
+ const parent = bankOf(bgioArguments).findParent(originalTarget) as {
138
+ getCoordinates: (i: unknown) => unknown;
139
+ getRelativeCoordinates: (a: unknown, b: unknown) => unknown;
140
+ spaces: unknown[];
141
+ getIndex: (c: unknown) => number;
142
+ };
143
+ const oldCoordinates =
144
+ parent.getCoordinates((originalTarget as { rule: { index: unknown } }).rule.index);
145
+ const newCoordinates =
146
+ parent.getRelativeCoordinates(
147
+ oldCoordinates,
148
+ resolveProperties(bgioArguments, v.location, context, "location")
149
+ );
150
+ return (newCoordinates && parent.spaces[parent.getIndex(newCoordinates)]) ?? null;
151
+ } else {
152
+ return value;
153
+ }
154
+ }
155
+
156
+ function getMappedTargets (
157
+ bgioArguments: BgioResolveState,
158
+ targetsRule: unknown,
159
+ mapping: unknown,
160
+ context: Record<string, unknown>
161
+ ): Array<{ target: unknown; value: unknown }> {
162
+ (targetsRule as { resolveAsEntity?: boolean }).resolveAsEntity = true;
163
+ const resolved = resolveProperties(bgioArguments, targetsRule, context) as unknown[] | undefined;
164
+ return resolved?.map((target) => ({
165
+ target,
166
+ value: resolveProperties(
167
+ bgioArguments,
168
+ mapping,
169
+ { ...context, loopTarget: target },
170
+ ),
171
+ })) ?? [];
172
+ }
@@ -0,0 +1,32 @@
1
+ import { serialize, deserialize } from "wackson";
2
+ import { registry } from "../registry.js";
3
+ import type { BgioResolveState } from "./bgio-resolve-types.js";
4
+
5
+ export default function simulateMove (
6
+ bgioArguments: BgioResolveState,
7
+ payload: { arguments: Record<string, { abstract?: boolean; entityId?: unknown } | number> },
8
+ context: { moveInstance: { doMove: (...args: unknown[]) => unknown } }
9
+ ) {
10
+ const simulatedG = deserialize(serialize(bgioArguments.G) as string, registry) as {
11
+ bank: { locate: (id: unknown) => unknown };
12
+ };
13
+ const newBgioArguments = {
14
+ ...bgioArguments,
15
+ G: simulatedG,
16
+ };
17
+ const simulatedPayload = { ...payload, arguments: {} as Record<string, unknown> };
18
+ Object.entries(payload.arguments).forEach(([argName, arg]) => {
19
+ simulatedPayload.arguments[argName] = (arg as { abstract?: boolean }).abstract
20
+ ? arg
21
+ : simulatedG.bank.locate(typeof arg === "number" ? arg : (arg as { entityId: unknown }).entityId);
22
+ });
23
+
24
+ context.moveInstance.doMove(
25
+ newBgioArguments,
26
+ simulatedPayload,
27
+ context,
28
+ { skipCheck: true }
29
+ );
30
+
31
+ return simulatedG;
32
+ }
@@ -0,0 +1,4 @@
1
+ declare module "wackson" {
2
+ export function serialize (value: unknown, options?: { deduplicateInstances?: boolean }): string;
3
+ export function deserialize (json: string, registry?: unknown): unknown;
4
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "emitDeclarationOnly": true,
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "outDir": "dist",
9
+ "rootDir": "src",
10
+ "stripInternal": true
11
+ },
12
+ "include": ["src/**/*.ts"],
13
+ "exclude": ["node_modules", "dist", "**/*.spec.ts", "e2e"]
14
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": ["ES2020", "DOM"],
7
+ "strict": true,
8
+ "noImplicitAny": true,
9
+ "skipLibCheck": true,
10
+ "noEmit": true,
11
+ "allowJs": false,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "rootDir": "."
18
+ },
19
+ "include": ["src/**/*.ts", "src/**/*.d.ts"],
20
+ "exclude": ["node_modules", "dist"]
21
+ }