board-game-engine 2.1.0 → 2.1.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 (256) hide show
  1. package/README.md +2 -2
  2. package/dist/board-game-engine.cjs +5509 -5613
  3. package/dist/board-game-engine.js +5509 -5613
  4. package/dist/board-game-engine.min.js +22 -22
  5. package/dist/board-game-engine.mjs +5508 -5611
  6. package/e2e/fixtures/bge-checkers.html +2 -1
  7. package/e2e/fixtures/bge-minimal.html +2 -1
  8. package/e2e/fixtures/bge-ttt.html +2 -1
  9. package/examples/index.html +1 -1
  10. package/package.json +4 -10
  11. package/playwright-report/index.html +1 -1
  12. package/scripts/build.mjs +2 -2
  13. package/src/client/client.js +224 -0
  14. package/src/game-factory/bank/bank-slot.js +69 -0
  15. package/src/game-factory/bank/bank.js +114 -0
  16. package/src/game-factory/{board.ts → board.js} +1 -1
  17. package/src/game-factory/condition/condition-factory.js +52 -0
  18. package/src/game-factory/condition/condition.js +39 -0
  19. package/src/game-factory/condition/{contains-condition.ts → contains-condition.js} +4 -5
  20. package/src/game-factory/condition/{contains-same-condition.ts → contains-same-condition.js} +5 -8
  21. package/src/game-factory/condition/{evaluate-condition.ts → evaluate-condition.js} +3 -4
  22. package/src/game-factory/condition/every-condition.js +25 -0
  23. package/src/game-factory/condition/has-line-condition.js +14 -0
  24. package/src/game-factory/condition/in-line-condition.js +19 -0
  25. package/src/game-factory/condition/is-condition.js +23 -0
  26. package/src/game-factory/condition/is-full-condition.js +9 -0
  27. package/src/game-factory/condition/{no-possible-moves-condition.ts → no-possible-moves-condition.js} +2 -3
  28. package/src/game-factory/condition/{not-condition.ts → not-condition.js} +2 -3
  29. package/src/game-factory/condition/{or-condition.ts → or-condition.js} +2 -3
  30. package/src/game-factory/condition/position-condition.js +12 -0
  31. package/src/game-factory/condition/{some-condition.ts → some-condition.js} +3 -5
  32. package/src/game-factory/condition/would-condition.js +94 -0
  33. package/src/game-factory/entity.js +29 -0
  34. package/src/game-factory/expand-game-rules.js +271 -0
  35. package/src/game-factory/game-factory.js +240 -0
  36. package/{dist/game-factory/move/end-turn.d.ts → src/game-factory/move/end-turn.js} +4 -2
  37. package/src/game-factory/move/for-each.js +18 -0
  38. package/src/game-factory/move/move-entity.js +16 -0
  39. package/src/game-factory/move/move-factory.js +89 -0
  40. package/src/game-factory/move/move.js +131 -0
  41. package/src/game-factory/move/pass-turn.js +10 -0
  42. package/{dist/game-factory/move/pass.d.ts → src/game-factory/move/pass.js} +4 -2
  43. package/src/game-factory/move/place-new.js +33 -0
  44. package/src/game-factory/move/remove-entity.js +7 -0
  45. package/src/game-factory/move/set-active-players.js +23 -0
  46. package/src/game-factory/move/set-state.js +11 -0
  47. package/src/game-factory/move/shuffle.js +7 -0
  48. package/src/game-factory/move/take-from.js +7 -0
  49. package/src/game-factory/space/space.js +30 -0
  50. package/src/game-factory/space-group/grid.js +43 -0
  51. package/src/game-factory/space-group/space-group.js +29 -0
  52. package/src/index.js +2 -0
  53. package/src/utils/{any-valid-moves.ts → any-valid-moves.js} +49 -54
  54. package/src/utils/check-conditions.js +28 -0
  55. package/src/utils/create-payload.js +16 -0
  56. package/src/utils/deserialize-bgio-arguments.js +8 -0
  57. package/src/utils/do-moves.js +18 -0
  58. package/src/utils/entity-matches.js +20 -0
  59. package/src/utils/find-met-condition.js +22 -0
  60. package/src/utils/get-current-moves.js +12 -0
  61. package/src/utils/get-scenario-results.js +23 -0
  62. package/src/utils/get-steps.js +29 -0
  63. package/src/utils/get.js +25 -0
  64. package/src/utils/{grid-contains-sequence.ts → grid-contains-sequence.js} +33 -71
  65. package/src/utils/json-transformer.js +12 -0
  66. package/src/utils/prepare-payload.js +17 -0
  67. package/src/utils/resolve-entity.js +9 -0
  68. package/src/utils/resolve-expression.js +10 -0
  69. package/src/utils/resolve-properties.js +149 -0
  70. package/src/utils/simulate-move.js +25 -0
  71. package/.github/workflows/ci.yml +0 -33
  72. package/dist/client/client.d.ts +0 -76
  73. package/dist/client/client.d.ts.map +0 -1
  74. package/dist/game-factory/bank/bank-slot.d.ts +0 -30
  75. package/dist/game-factory/bank/bank-slot.d.ts.map +0 -1
  76. package/dist/game-factory/bank/bank.d.ts +0 -34
  77. package/dist/game-factory/bank/bank.d.ts.map +0 -1
  78. package/dist/game-factory/board.d.ts +0 -4
  79. package/dist/game-factory/board.d.ts.map +0 -1
  80. package/dist/game-factory/condition/condition-factory.d.ts +0 -4
  81. package/dist/game-factory/condition/condition-factory.d.ts.map +0 -1
  82. package/dist/game-factory/condition/condition.d.ts +0 -13
  83. package/dist/game-factory/condition/condition.d.ts.map +0 -1
  84. package/dist/game-factory/condition/contains-condition.d.ts +0 -8
  85. package/dist/game-factory/condition/contains-condition.d.ts.map +0 -1
  86. package/dist/game-factory/condition/contains-same-condition.d.ts +0 -7
  87. package/dist/game-factory/condition/contains-same-condition.d.ts.map +0 -1
  88. package/dist/game-factory/condition/evaluate-condition.d.ts +0 -8
  89. package/dist/game-factory/condition/evaluate-condition.d.ts.map +0 -1
  90. package/dist/game-factory/condition/every-condition.d.ts +0 -12
  91. package/dist/game-factory/condition/every-condition.d.ts.map +0 -1
  92. package/dist/game-factory/condition/has-line-condition.d.ts +0 -8
  93. package/dist/game-factory/condition/has-line-condition.d.ts.map +0 -1
  94. package/dist/game-factory/condition/in-line-condition.d.ts +0 -8
  95. package/dist/game-factory/condition/in-line-condition.d.ts.map +0 -1
  96. package/dist/game-factory/condition/is-condition.d.ts +0 -8
  97. package/dist/game-factory/condition/is-condition.d.ts.map +0 -1
  98. package/dist/game-factory/condition/is-full-condition.d.ts +0 -7
  99. package/dist/game-factory/condition/is-full-condition.d.ts.map +0 -1
  100. package/dist/game-factory/condition/no-possible-moves-condition.d.ts +0 -7
  101. package/dist/game-factory/condition/no-possible-moves-condition.d.ts.map +0 -1
  102. package/dist/game-factory/condition/not-condition.d.ts +0 -7
  103. package/dist/game-factory/condition/not-condition.d.ts.map +0 -1
  104. package/dist/game-factory/condition/or-condition.d.ts +0 -7
  105. package/dist/game-factory/condition/or-condition.d.ts.map +0 -1
  106. package/dist/game-factory/condition/position-condition.d.ts +0 -7
  107. package/dist/game-factory/condition/position-condition.d.ts.map +0 -1
  108. package/dist/game-factory/condition/some-condition.d.ts +0 -8
  109. package/dist/game-factory/condition/some-condition.d.ts.map +0 -1
  110. package/dist/game-factory/condition/would-condition.d.ts +0 -8
  111. package/dist/game-factory/condition/would-condition.d.ts.map +0 -1
  112. package/dist/game-factory/entity.d.ts +0 -13
  113. package/dist/game-factory/entity.d.ts.map +0 -1
  114. package/dist/game-factory/expand-game-rules.d.ts +0 -3
  115. package/dist/game-factory/expand-game-rules.d.ts.map +0 -1
  116. package/dist/game-factory/game-factory.d.ts +0 -10
  117. package/dist/game-factory/game-factory.d.ts.map +0 -1
  118. package/dist/game-factory/move/end-turn.d.ts.map +0 -1
  119. package/dist/game-factory/move/for-each.d.ts +0 -5
  120. package/dist/game-factory/move/for-each.d.ts.map +0 -1
  121. package/dist/game-factory/move/index.d.ts +0 -6
  122. package/dist/game-factory/move/index.d.ts.map +0 -1
  123. package/dist/game-factory/move/move-entity.d.ts +0 -7
  124. package/dist/game-factory/move/move-entity.d.ts.map +0 -1
  125. package/dist/game-factory/move/move-factory.d.ts +0 -18
  126. package/dist/game-factory/move/move-factory.d.ts.map +0 -1
  127. package/dist/game-factory/move/move.d.ts +0 -54
  128. package/dist/game-factory/move/move.d.ts.map +0 -1
  129. package/dist/game-factory/move/pass-turn.d.ts +0 -5
  130. package/dist/game-factory/move/pass-turn.d.ts.map +0 -1
  131. package/dist/game-factory/move/pass.d.ts.map +0 -1
  132. package/dist/game-factory/move/place-new.d.ts +0 -5
  133. package/dist/game-factory/move/place-new.d.ts.map +0 -1
  134. package/dist/game-factory/move/remove-entity.d.ts +0 -5
  135. package/dist/game-factory/move/remove-entity.d.ts.map +0 -1
  136. package/dist/game-factory/move/set-active-players.d.ts +0 -5
  137. package/dist/game-factory/move/set-active-players.d.ts.map +0 -1
  138. package/dist/game-factory/move/set-state.d.ts +0 -5
  139. package/dist/game-factory/move/set-state.d.ts.map +0 -1
  140. package/dist/game-factory/move/shuffle.d.ts +0 -5
  141. package/dist/game-factory/move/shuffle.d.ts.map +0 -1
  142. package/dist/game-factory/move/take-from.d.ts +0 -11
  143. package/dist/game-factory/move/take-from.d.ts.map +0 -1
  144. package/dist/game-factory/space/space.d.ts +0 -10
  145. package/dist/game-factory/space/space.d.ts.map +0 -1
  146. package/dist/game-factory/space-group/grid.d.ts +0 -15
  147. package/dist/game-factory/space-group/grid.d.ts.map +0 -1
  148. package/dist/game-factory/space-group/space-group.d.ts +0 -20
  149. package/dist/game-factory/space-group/space-group.d.ts.map +0 -1
  150. package/dist/index.d.ts +0 -6
  151. package/dist/index.d.ts.map +0 -1
  152. package/dist/registry.d.ts +0 -17
  153. package/dist/registry.d.ts.map +0 -1
  154. package/dist/types/bagel-types.d.ts +0 -339
  155. package/dist/types/bagel-types.d.ts.map +0 -1
  156. package/dist/types/index.d.ts +0 -3
  157. package/dist/types/index.d.ts.map +0 -1
  158. package/dist/types/rule-with-conditions.d.ts +0 -9
  159. package/dist/types/rule-with-conditions.d.ts.map +0 -1
  160. package/dist/utils/any-valid-moves.d.ts +0 -2
  161. package/dist/utils/any-valid-moves.d.ts.map +0 -1
  162. package/dist/utils/bgio-resolve-types.d.ts +0 -25
  163. package/dist/utils/bgio-resolve-types.d.ts.map +0 -1
  164. package/dist/utils/check-conditions.d.ts +0 -7
  165. package/dist/utils/check-conditions.d.ts.map +0 -1
  166. package/dist/utils/create-payload.d.ts +0 -5
  167. package/dist/utils/create-payload.d.ts.map +0 -1
  168. package/dist/utils/deserialize-bgio-arguments.d.ts +0 -3
  169. package/dist/utils/deserialize-bgio-arguments.d.ts.map +0 -1
  170. package/dist/utils/do-moves.d.ts +0 -8
  171. package/dist/utils/do-moves.d.ts.map +0 -1
  172. package/dist/utils/entity-matches.d.ts +0 -6
  173. package/dist/utils/entity-matches.d.ts.map +0 -1
  174. package/dist/utils/find-met-condition.d.ts +0 -6
  175. package/dist/utils/find-met-condition.d.ts.map +0 -1
  176. package/dist/utils/get-current-moves.d.ts +0 -24
  177. package/dist/utils/get-current-moves.d.ts.map +0 -1
  178. package/dist/utils/get-scenario-results.d.ts +0 -3
  179. package/dist/utils/get-scenario-results.d.ts.map +0 -1
  180. package/dist/utils/get-steps.d.ts +0 -13
  181. package/dist/utils/get-steps.d.ts.map +0 -1
  182. package/dist/utils/get.d.ts +0 -7
  183. package/dist/utils/get.d.ts.map +0 -1
  184. package/dist/utils/grid-contains-sequence.d.ts +0 -27
  185. package/dist/utils/grid-contains-sequence.d.ts.map +0 -1
  186. package/dist/utils/json-transformer.d.ts +0 -8
  187. package/dist/utils/json-transformer.d.ts.map +0 -1
  188. package/dist/utils/prepare-payload.d.ts +0 -2
  189. package/dist/utils/prepare-payload.d.ts.map +0 -1
  190. package/dist/utils/resolve-entity.d.ts +0 -3
  191. package/dist/utils/resolve-entity.d.ts.map +0 -1
  192. package/dist/utils/resolve-expression.d.ts +0 -6
  193. package/dist/utils/resolve-expression.d.ts.map +0 -1
  194. package/dist/utils/resolve-properties.d.ts +0 -4
  195. package/dist/utils/resolve-properties.d.ts.map +0 -1
  196. package/dist/utils/simulate-move.d.ts +0 -16
  197. package/dist/utils/simulate-move.d.ts.map +0 -1
  198. package/src/client/client.ts +0 -305
  199. package/src/game-factory/bank/bank-slot.ts +0 -81
  200. package/src/game-factory/bank/bank.ts +0 -125
  201. package/src/game-factory/condition/condition-factory.ts +0 -59
  202. package/src/game-factory/condition/condition.ts +0 -50
  203. package/src/game-factory/condition/every-condition.ts +0 -27
  204. package/src/game-factory/condition/has-line-condition.ts +0 -15
  205. package/src/game-factory/condition/in-line-condition.ts +0 -25
  206. package/src/game-factory/condition/is-condition.ts +0 -24
  207. package/src/game-factory/condition/is-full-condition.ts +0 -10
  208. package/src/game-factory/condition/position-condition.ts +0 -13
  209. package/src/game-factory/condition/would-condition.ts +0 -104
  210. package/src/game-factory/entity.ts +0 -37
  211. package/src/game-factory/expand-game-rules.ts +0 -263
  212. package/src/game-factory/game-factory.ts +0 -263
  213. package/src/game-factory/move/end-turn.ts +0 -7
  214. package/src/game-factory/move/for-each.ts +0 -20
  215. package/src/game-factory/move/move-entity.ts +0 -18
  216. package/src/game-factory/move/move-factory.ts +0 -107
  217. package/src/game-factory/move/move.ts +0 -147
  218. package/src/game-factory/move/pass-turn.ts +0 -15
  219. package/src/game-factory/move/pass.ts +0 -7
  220. package/src/game-factory/move/place-new.ts +0 -42
  221. package/src/game-factory/move/remove-entity.ts +0 -11
  222. package/src/game-factory/move/set-active-players.ts +0 -26
  223. package/src/game-factory/move/set-state.ts +0 -14
  224. package/src/game-factory/move/shuffle.ts +0 -9
  225. package/src/game-factory/move/take-from.ts +0 -12
  226. package/src/game-factory/space/space.ts +0 -36
  227. package/src/game-factory/space-group/grid.ts +0 -48
  228. package/src/game-factory/space-group/space-group.ts +0 -44
  229. package/src/index.ts +0 -5
  230. package/src/types/bagel-types.ts +0 -449
  231. package/src/types/boardgame-io-core.d.ts +0 -7
  232. package/src/types/index.ts +0 -70
  233. package/src/types/rule-with-conditions.ts +0 -9
  234. package/src/utils/bgio-resolve-types.ts +0 -27
  235. package/src/utils/check-conditions.ts +0 -28
  236. package/src/utils/create-payload.ts +0 -19
  237. package/src/utils/deserialize-bgio-arguments.ts +0 -10
  238. package/src/utils/do-moves.ts +0 -22
  239. package/src/utils/entity-matches.ts +0 -30
  240. package/src/utils/expr-eval.d.ts +0 -6
  241. package/src/utils/find-met-condition.ts +0 -23
  242. package/src/utils/get-current-moves.ts +0 -39
  243. package/src/utils/get-scenario-results.ts +0 -30
  244. package/src/utils/get-steps.ts +0 -38
  245. package/src/utils/get.ts +0 -28
  246. package/src/utils/json-transformer.ts +0 -17
  247. package/src/utils/prepare-payload.ts +0 -20
  248. package/src/utils/resolve-entity.ts +0 -15
  249. package/src/utils/resolve-expression.ts +0 -16
  250. package/src/utils/resolve-properties.ts +0 -172
  251. package/src/utils/simulate-move.ts +0 -32
  252. package/src/wackson.d.ts +0 -4
  253. package/tsconfig.build.json +0 -14
  254. package/tsconfig.json +0 -21
  255. /package/src/game-factory/move/{index.ts → index.js} +0 -0
  256. /package/src/{registry.ts → registry.js} +0 -0
@@ -18,7 +18,8 @@
18
18
  let gameRules;
19
19
  try {
20
20
  const res = await fetch('/examples/checkers.json');
21
- gameRules = await res.json();
21
+ const rules = await res.json();
22
+ gameRules = JSON.stringify(rules);
22
23
  } catch (e) {
23
24
  out.textContent = 'Fetch failed: ' + (e && e.message);
24
25
  return;
@@ -18,7 +18,8 @@
18
18
  let gameRules;
19
19
  try {
20
20
  const res = await fetch('/e2e/fixtures/minimal-game.json');
21
- gameRules = await res.json();
21
+ const rules = await res.json();
22
+ gameRules = JSON.stringify(rules);
22
23
  } catch (e) {
23
24
  out.textContent = 'Fetch failed: ' + (e && e.message);
24
25
  return;
@@ -18,7 +18,8 @@
18
18
  let gameRules;
19
19
  try {
20
20
  const res = await fetch('/examples/tic-tac-toe.json');
21
- gameRules = await res.json();
21
+ const rules = await res.json();
22
+ gameRules = JSON.stringify(rules);
22
23
  } catch (e) {
23
24
  out.textContent = 'Fetch failed: ' + (e && e.message);
24
25
  return;
@@ -186,7 +186,7 @@
186
186
  2;
187
187
 
188
188
  const client = new window.BoardGameEngine.Client({
189
- gameRules: rules,
189
+ gameRules: JSON.stringify(rules),
190
190
  gameName: key,
191
191
  numPlayers,
192
192
  onClientUpdate: () => {
package/package.json CHANGED
@@ -1,13 +1,11 @@
1
1
  {
2
2
  "name": "board-game-engine",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "wip board game engine",
5
5
  "main": "dist/board-game-engine.cjs",
6
6
  "module": "dist/board-game-engine.mjs",
7
- "types": "dist/index.d.ts",
8
7
  "exports": {
9
8
  ".": {
10
- "types": "./dist/index.d.ts",
11
9
  "import": "./dist/board-game-engine.mjs",
12
10
  "require": "./dist/board-game-engine.cjs"
13
11
  }
@@ -21,16 +19,12 @@
21
19
  "test:e2e:install": "playwright install firefox",
22
20
  "serve": "http-server . -p 5174 -c-1",
23
21
  "lint": "eslint .",
24
- "build": "node scripts/build.mjs && npm run build:types",
25
- "build:types": "tsc -p tsconfig.build.json",
26
- "typecheck": "tsc --noEmit",
22
+ "build": "node scripts/build.mjs",
27
23
  "prepublishOnly": "npm run build"
28
24
  },
29
25
  "author": "Matthew Broatch",
30
26
  "license": "ISC",
31
27
  "devDependencies": {
32
- "@playwright/test": "^1.49.0",
33
- "@types/lodash": "^4.17.24",
34
28
  "cross-env": "^7.0.3",
35
29
  "esbuild": "^0.24.0",
36
30
  "esbuild-plugin-istanbul": "^0.3.0",
@@ -40,12 +34,12 @@
40
34
  "http-server": "^14.1.1",
41
35
  "nyc": "^15.1.0",
42
36
  "standard": "^16.0.4",
43
- "typescript": "^5.9.3"
37
+ "@playwright/test": "^1.49.0"
44
38
  },
45
39
  "dependencies": {
46
40
  "@mnbroatch/boardgame.io": "0.0.1",
47
41
  "expr-eval": "^2.0.2",
48
- "wackson": "^1.1.0"
42
+ "wackson": "^2.0.0"
49
43
  },
50
44
  "repository": {
51
45
  "type": "git",
@@ -82,4 +82,4 @@ Error generating stack: `+a.message+`
82
82
  <div id='root'></div>
83
83
  </body>
84
84
  </html>
85
- <script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,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</script>
85
+ <script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,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</script>
package/scripts/build.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as esbuild from 'esbuild'
2
2
  import { esbuildPluginIstanbul } from 'esbuild-plugin-istanbul'
3
3
 
4
- const entry = 'src/index.ts'
4
+ const entry = 'src/index.js'
5
5
  const outdir = 'dist'
6
6
  const globalName = 'BoardGameEngine'
7
7
  const coverage = process.env.COVERAGE === '1'
@@ -24,7 +24,7 @@ if (coverage) {
24
24
  esbuildPluginIstanbul({
25
25
  name: 'istanbul',
26
26
  filter: /\.(js|ts)$/,
27
- include: ['src/**/*.{js,ts}'],
27
+ include: ['src/**/*.js'],
28
28
  exclude: ['**/*.spec.js', '**/node_modules/**'],
29
29
  }),
30
30
  ],
@@ -0,0 +1,224 @@
1
+ import { Client as BoardgameIOClient } from '@mnbroatch/boardgame.io/client'
2
+ import { Debug } from '@mnbroatch/boardgame.io/debug';
3
+ import { SocketIO } from '@mnbroatch/boardgame.io/multiplayer'
4
+ import { serialize, deserialize } from 'wackson';
5
+ import gameFactory from '../game-factory/game-factory.js'
6
+ import { registry } from '../registry.js';
7
+ import simulateMove from '../utils/simulate-move.js';
8
+ import getCurrentMoves from '../utils/get-current-moves.js';
9
+ import resolveProperties from '../utils/resolve-properties.js';
10
+ import checkConditions from '../utils/check-conditions.js';
11
+ import preparePayload from '../utils/prepare-payload.js';
12
+ import getSteps from '../utils/get-steps.js';
13
+ import createPayload from '../utils/create-payload.js';
14
+
15
+ export class Client {
16
+ constructor (options) {
17
+ this.options = options
18
+ this.game = options.boardgameIOGame
19
+ || gameFactory(JSON.parse(options.gameRules), options.gameName)
20
+
21
+ if (!options.boardgameIOGame) {
22
+ this.moveBuilder = { targets: [], stepIndex: 0, eliminatedMoves: [] }
23
+ this.optimisticWinner = null
24
+ }
25
+ }
26
+
27
+ connect () {
28
+ const {
29
+ server,
30
+ numPlayers,
31
+ debug = {
32
+ collapseOnLoad: true,
33
+ impl: Debug,
34
+ },
35
+ matchID,
36
+ playerID,
37
+ credentials,
38
+ multiplayer = SocketIO({ server, socketOpts: { transports: ['websocket', 'polling'] } }),
39
+ } = this.options
40
+
41
+ try {
42
+ const clientOptions = !credentials
43
+ ? { game: this.game, numPlayers, debug }
44
+ : {
45
+ game: this.game,
46
+ multiplayer,
47
+ matchID,
48
+ playerID,
49
+ credentials,
50
+ numPlayers,
51
+ debug,
52
+ }
53
+
54
+ this.client = BoardgameIOClient(clientOptions)
55
+ this.client.subscribe(() => this.update())
56
+ this.client.start()
57
+ return this
58
+ } catch (error) {
59
+ console.error('Failed to join game:', error?.message ?? error)
60
+ if (error?.stack) console.error(error.stack)
61
+ }
62
+ }
63
+
64
+ update () {
65
+ this.options.onClientUpdate?.()
66
+ }
67
+
68
+ getState () {
69
+ const bgioState = this.client?.getState()
70
+ if (!bgioState) return {}
71
+
72
+ const state = this.options.boardgameIOGame
73
+ ? bgioState
74
+ : {
75
+ ...bgioState,
76
+ G: deserialize(JSON.stringify(bgioState.G), registry),
77
+ }
78
+
79
+ const gameover = this.optimisticWinner ?? state?.ctx?.gameover
80
+
81
+ const currentMoves = gameover
82
+ ? []
83
+ : getCurrentMoves(state, this.client)
84
+
85
+ if (this.options.boardgameIOGame) {
86
+ return {
87
+ state,
88
+ gameover,
89
+ moves: this.client.moves,
90
+ currentMoves
91
+ }
92
+ }
93
+
94
+ const _wrappedMoves = Object.entries(currentMoves)
95
+ .reduce((acc, [moveName, rawMove]) => {
96
+ const move = (payload) => {
97
+ this.client.moves[moveName](preparePayload(payload))
98
+ }
99
+ move.moveInstance = rawMove.moveInstance
100
+ return { ...acc, [moveName]: move }
101
+ }, {})
102
+
103
+ const { allClickable, _possibleMoveMeta } = getPossibleMoves(state, _wrappedMoves, this.moveBuilder)
104
+
105
+ return { state, gameover, allClickable, _wrappedMoves, _possibleMoveMeta }
106
+ }
107
+
108
+ doStep (_target) {
109
+ if (this.options.boardgameIOGame) return
110
+
111
+ const { state, _wrappedMoves, _possibleMoveMeta } = this.getState()
112
+
113
+ const target = _target.abstract
114
+ ? _target
115
+ : state.G.bank.locate(_target.entityId)
116
+
117
+ const newEliminated = Object.entries(_possibleMoveMeta)
118
+ .filter(([_, meta]) => !hasTarget(meta.clickableForMove, target))
119
+ .map(([name]) => name)
120
+ .concat(this.moveBuilder.eliminatedMoves);
121
+
122
+ if (newEliminated.length === Object.keys(_wrappedMoves).length) {
123
+ console.error('invalid move with target:', target?.rule);
124
+ return;
125
+ }
126
+
127
+ const remainingMoveEntries = Object.entries(_possibleMoveMeta)
128
+ .filter(([name]) => !newEliminated.includes(name))
129
+
130
+ if (isMoveCompleted(state, _wrappedMoves, remainingMoveEntries, this.moveBuilder.stepIndex)) {
131
+ const [moveName] = remainingMoveEntries[0]
132
+ const move = _wrappedMoves[moveName]
133
+ const payload = createPayload(
134
+ state,
135
+ move.moveInstance.rule,
136
+ [...this.moveBuilder.targets, target],
137
+ { moveInstance: move.moveInstance }
138
+ );
139
+
140
+ this.optimisticWinner = getWinnerAfterMove(state, this.game, move.moveInstance, payload);
141
+ move(payload);
142
+ this.moveBuilder = { targets: [], stepIndex: 0, eliminatedMoves: [] };
143
+ } else {
144
+ this.moveBuilder = {
145
+ eliminatedMoves: newEliminated,
146
+ stepIndex: this.moveBuilder.stepIndex + 1,
147
+ targets: [...this.moveBuilder.targets, target]
148
+ }
149
+ }
150
+
151
+ this.update()
152
+ }
153
+
154
+ reset () {
155
+ if (this.options.boardgameIOGame) return
156
+ this.moveBuilder = { targets: [], stepIndex: 0, eliminatedMoves: [] };
157
+ this.optimisticWinner = null
158
+ this.update()
159
+ }
160
+
161
+ undoStep () {
162
+ if (this.options.boardgameIOGame) return
163
+ if (this.moveBuilder.targets.length) {
164
+ this.moveBuilder = {
165
+ targets: this.moveBuilder.targets.slice(0, -1),
166
+ stepIndex: Math.max(0, this.moveBuilder.stepIndex - 1),
167
+ eliminatedMoves: []
168
+ }
169
+ }
170
+ this.update()
171
+ }
172
+ }
173
+
174
+ function hasTarget(clickableSet, target) {
175
+ if (!target.abstract) return clickableSet.has(target);
176
+ return [...clickableSet].some(item => item.abstract && item.value === target.value);
177
+ }
178
+
179
+ function getPossibleMoves(bgioState, moves, moveBuilder) {
180
+ const { eliminatedMoves, stepIndex } = moveBuilder;
181
+ const _possibleMoveMeta = {};
182
+ const allClickable = new Set();
183
+
184
+ Object.entries(moves)
185
+ .filter(([moveName]) => !eliminatedMoves.includes(moveName))
186
+ .forEach(([moveName, move]) => {
187
+ const moveRule = resolveProperties(bgioState, { ...move.moveInstance.rule, moveName })
188
+
189
+ const context = {
190
+ moveInstance: move.moveInstance,
191
+ moveArguments: moveRule.arguments
192
+ }
193
+
194
+ const targets = moveBuilder.targets.map(t =>
195
+ t.abstract ? t : bgioState.G.bank.locate(t.entityId)
196
+ )
197
+
198
+ const payload = createPayload(bgioState, moveRule, targets, context);
199
+
200
+ context.moveArguments = { ...context.moveArguments, ...payload.arguments }
201
+
202
+ const moveIsAllowed = checkConditions(bgioState, moveRule, {}, context).conditionsAreMet;
203
+ const moveSteps = getSteps(bgioState, moveRule);
204
+
205
+ const clickableForMove = new Set(
206
+ (moveIsAllowed && moveSteps?.[stepIndex]?.getClickable(context)) || []
207
+ );
208
+
209
+ _possibleMoveMeta[moveName] = { clickableForMove };
210
+ clickableForMove.forEach(entity => allClickable.add(entity));
211
+ });
212
+
213
+ return { _possibleMoveMeta, allClickable };
214
+ }
215
+
216
+ function isMoveCompleted(state, moves, remainingMoveEntries, stepIndex) {
217
+ return remainingMoveEntries.length === 1 &&
218
+ getSteps(state, moves[remainingMoveEntries[0][0]].moveInstance.rule).length === stepIndex + 1;
219
+ }
220
+
221
+ function getWinnerAfterMove (state, game, moveInstance, movePayload) {
222
+ const simulatedG = simulateMove(state, preparePayload(movePayload), { moveInstance })
223
+ return game.endIf?.({ ...state, G: JSON.parse(serialize(simulatedG, { registry })) })
224
+ }
@@ -0,0 +1,69 @@
1
+ import resolveProperties from '../../utils/resolve-properties.js'
2
+
3
+ // lazily create entities as needed, and also function as an index of entities created
4
+ class BankSlot {
5
+ constructor (rule, bank) {
6
+ this.bank = bank
7
+ this.rule = rule
8
+ this.pool = []
9
+ this.remaining = +rule.count || 1
10
+ }
11
+
12
+ getOne (bgioArguments, options, context) {
13
+ return this.getMultiple(bgioArguments, 1, options, context)[0]
14
+ }
15
+
16
+ getMultiple (bgioArguments, count = Infinity, options = {}, context) {
17
+ const toReturn = []
18
+
19
+ if (this.remaining === Infinity && count === Infinity) {
20
+ throw new Error(`Cannot get infinite pieces from slot with infinite remaining: ${this.rule.name}`)
21
+ }
22
+
23
+ if (count !== Infinity && count > this.remaining) {
24
+ throw new Error(`Requested ${count} pieces but only ${this.remaining} available in slot: ${this.rule.name}`)
25
+ }
26
+
27
+ // Determine actual count to fetch
28
+ const actualCount = count === Infinity ? this.remaining : count
29
+
30
+ if (this.remaining !== Infinity) {
31
+ this.remaining -= actualCount
32
+ }
33
+
34
+ const fromPool = Math.min(actualCount, this.pool.length)
35
+ toReturn.push(...this.pool.splice(0, fromPool))
36
+
37
+ const remainder = actualCount - fromPool
38
+ if (remainder > 0) {
39
+ toReturn.push(
40
+ ...Array.from(new Array(remainder)).map(() =>
41
+ this.bank.createEntity(this.rule)
42
+ )
43
+ )
44
+ }
45
+
46
+ if (options.state) {
47
+ const newState = resolveProperties(bgioArguments, options.state, context)
48
+ toReturn.forEach(entity => {
49
+ entity.state = { ...entity.state, ...newState }
50
+ })
51
+ }
52
+
53
+ return toReturn
54
+ }
55
+
56
+ returnToBank (entity) {
57
+ if (entity.rule.state) {
58
+ entity.state = entity.rule.state
59
+ } else {
60
+ delete entity.state
61
+ }
62
+ if (this.remaining !== undefined) {
63
+ this.remaining += 1
64
+ }
65
+ this.pool.push(entity)
66
+ }
67
+ }
68
+
69
+ export default BankSlot
@@ -0,0 +1,114 @@
1
+ import find from 'lodash/find.js'
2
+ import filter from 'lodash/filter.js'
3
+ import checkConditions from '../../utils/check-conditions.js'
4
+ import { registry } from '../../registry.js'
5
+ import BankSlot from './bank-slot.js'
6
+
7
+ class Bank {
8
+ constructor (entityRules) {
9
+ this.currentEntityId = 0
10
+ this.tracker = {}
11
+ this.slots = entityRules.map(rule => new BankSlot(rule, this))
12
+ }
13
+
14
+ createEntity (definition = {}, options) {
15
+ const entity = new (registry[definition.entityType || 'Entity'])(
16
+ {
17
+ bank: this,
18
+ fromBank: true,
19
+ ...options
20
+ },
21
+ definition,
22
+ this.currentEntityId++
23
+ )
24
+ this.track(entity)
25
+ return entity
26
+ }
27
+
28
+ track (entity) {
29
+ this.tracker[entity.entityId] = entity
30
+ }
31
+
32
+ locate (entityId) {
33
+ return this.tracker[entityId]
34
+ }
35
+
36
+ findAll (bgioArguments, rule, context) {
37
+ if (!rule.conditions) {
38
+ throw new Error (`Cannot find entity with no conditions. Rule: ${JSON.stringify(rule)}`)
39
+ }
40
+ return filter(
41
+ Object.values(this.tracker),
42
+ entity => checkConditions(
43
+ bgioArguments,
44
+ rule,
45
+ { target: entity },
46
+ context
47
+ ).conditionsAreMet
48
+ )
49
+ }
50
+
51
+ findOne (bgioArguments, rule, context) {
52
+ return this.findAll(bgioArguments, rule, context)[0]
53
+ }
54
+
55
+ find (bgioArguments, rule, context) {
56
+ return rule.matchMultiple
57
+ ? this.findAll(bgioArguments, rule, context)
58
+ : this.findOne(bgioArguments, rule, context)
59
+ }
60
+
61
+ findParent (entity) {
62
+ return find(this.tracker, ent =>
63
+ ent.entities?.includes(entity)
64
+ || ent.spaces?.includes(entity)
65
+ )
66
+ }
67
+
68
+ getOne (bgioArguments, rule, context) {
69
+ const slot = this.getSlot(bgioArguments, rule, context)
70
+ if (!slot) {
71
+ console.error(`No matching slot for ${JSON.stringify(rule)}`)
72
+ }
73
+ return slot.getOne(bgioArguments, { state: rule.state }, context)
74
+ }
75
+
76
+ getMultiple (bgioArguments, rule, count, context) {
77
+ const slots = this.getSlots(bgioArguments, rule, context)
78
+ if (!slots.length) {
79
+ console.error(`No matching slots for ${JSON.stringify(rule)}`)
80
+ }
81
+ return slots.reduce((acc, slot) => [
82
+ ...acc,
83
+ ...slot.getMultiple(bgioArguments, count, { state: rule.state })
84
+ ], [])
85
+ }
86
+
87
+ getSlot (bgioArguments, rule, context) {
88
+ return this.slots.find(slot => checkConditions(
89
+ bgioArguments,
90
+ rule,
91
+ { target: slot },
92
+ context
93
+ ).conditionsAreMet
94
+ )
95
+ }
96
+
97
+ getSlots (bgioArguments, rule, context) {
98
+ return this.slots.filter(slot => checkConditions(
99
+ bgioArguments,
100
+ rule,
101
+ { target: slot },
102
+ context
103
+ ).conditionsAreMet
104
+ )
105
+ }
106
+
107
+ returnToBank (bgioArguments, entity) {
108
+ this.findParent(entity).remove(entity)
109
+ this.getSlot(bgioArguments, entity.rule).returnToBank(entity)
110
+ delete this.tracker[entity.entityId]
111
+ }
112
+ }
113
+
114
+ export default Bank
@@ -1,3 +1,3 @@
1
- import Space from "./space/space.js";
1
+ import Space from './space/space.js'
2
2
 
3
3
  export default class Board extends Space {}
@@ -0,0 +1,52 @@
1
+ import Is from "./is-condition.js";
2
+ import Not from "./not-condition.js";
3
+ import Or from "./or-condition.js";
4
+ import Some from "./some-condition.js";
5
+ import Every from "./every-condition.js";
6
+ import ContainsCondition from "./contains-condition.js";
7
+ import ContainsSameCondition from "./contains-same-condition.js";
8
+ import InLine from "./in-line-condition.js";
9
+ import HasLine from "./has-line-condition.js";
10
+ import IsFull from "./is-full-condition.js";
11
+ import Would from "./would-condition.js";
12
+ import NoPossibleMoves from "./no-possible-moves-condition.js";
13
+ import Evaluate from "./evaluate-condition.js";
14
+ import Position from "./position-condition.js";
15
+ // import BingoCondition from "./bingo-condition.js";
16
+ // import RelativeMoveCondition from "./relative-move-condition.js";
17
+
18
+ export default function conditionFactory(rule) {
19
+ if (rule.conditionType === "Is") {
20
+ return new Is(rule);
21
+ } else if (rule.conditionType === "Not") {
22
+ return new Not(rule);
23
+ } else if (rule.conditionType === "Or") {
24
+ return new Or(rule);
25
+ } else if (rule.conditionType === "Some") {
26
+ return new Some(rule);
27
+ } else if (rule.conditionType === "Contains") {
28
+ return new ContainsCondition(rule);
29
+ } else if (rule.conditionType === "ContainsSame") {
30
+ return new ContainsSameCondition(rule);
31
+ } else if (rule.conditionType === "Every") {
32
+ return new Every(rule);
33
+ } else if (rule.conditionType === "InLine") {
34
+ return new InLine(rule);
35
+ } else if (rule.conditionType === "HasLine") {
36
+ return new HasLine(rule);
37
+ } else if (rule.conditionType === "IsFull") {
38
+ return new IsFull(rule);
39
+ } else if (rule.conditionType === "Would") {
40
+ return new Would(rule);
41
+ } else if (rule.conditionType === "NoPossibleMoves") {
42
+ return new NoPossibleMoves(rule);
43
+ } else if (rule.conditionType === "Evaluate") {
44
+ return new Evaluate(rule);
45
+ } else if (rule.conditionType === "Position") {
46
+ return new Position(rule);
47
+ // } else if (rule.conditionType === "bingo") {
48
+ // return new BingoCondition(rule);
49
+ // } else if (rule.conditionType === "relativeMove") {
50
+ // return new RelativeMoveCondition(rule);
51
+ }
52
+ }
@@ -0,0 +1,39 @@
1
+ import resolveProperties from "../../utils/resolve-properties.js";
2
+
3
+ export default class Condition {
4
+ constructor (rule) {
5
+ this.rule = rule
6
+ }
7
+
8
+ check (bgioArguments, payload, context) {
9
+ const conditionPayload = { ...payload }
10
+ const newContext = { ...context }
11
+
12
+ if (conditionPayload.target) {
13
+ newContext.originalTarget = conditionPayload.target
14
+ }
15
+
16
+ const rule = resolveProperties(
17
+ bgioArguments,
18
+ this.rule,
19
+ newContext
20
+ )
21
+
22
+ // We don't simply defer to payload target because of parent and relativePath
23
+ // target types, for instance, which retarget to another entity
24
+ if (rule.target !== undefined) {
25
+ conditionPayload.target = rule.target
26
+ }
27
+
28
+ // Nonexistent entities never fulfill conditions (including "Not" conditions!)
29
+ if (this.rule.target !== undefined && !conditionPayload.target) {
30
+ return { conditionIsMet: false }
31
+ }
32
+
33
+ return this.checkCondition(bgioArguments, rule, conditionPayload, newContext)
34
+ }
35
+
36
+ isMet(...args) {
37
+ return this.check(...args).conditionIsMet
38
+ }
39
+ }