board-game-engine 2.0.1 → 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 (251) hide show
  1. package/dist/board-game-engine.cjs +5509 -5613
  2. package/dist/board-game-engine.js +5509 -5613
  3. package/dist/board-game-engine.min.js +22 -22
  4. package/dist/board-game-engine.mjs +5508 -5611
  5. package/package.json +4 -10
  6. package/playwright-report/index.html +1 -1
  7. package/scripts/build.mjs +2 -2
  8. package/src/client/client.js +224 -0
  9. package/src/game-factory/bank/bank-slot.js +69 -0
  10. package/src/game-factory/bank/bank.js +114 -0
  11. package/src/game-factory/{board.ts → board.js} +1 -1
  12. package/src/game-factory/condition/condition-factory.js +52 -0
  13. package/src/game-factory/condition/condition.js +39 -0
  14. package/src/game-factory/condition/{contains-condition.ts → contains-condition.js} +4 -5
  15. package/src/game-factory/condition/{contains-same-condition.ts → contains-same-condition.js} +5 -8
  16. package/src/game-factory/condition/{evaluate-condition.ts → evaluate-condition.js} +3 -4
  17. package/src/game-factory/condition/every-condition.js +25 -0
  18. package/src/game-factory/condition/has-line-condition.js +14 -0
  19. package/src/game-factory/condition/in-line-condition.js +19 -0
  20. package/src/game-factory/condition/is-condition.js +23 -0
  21. package/src/game-factory/condition/is-full-condition.js +9 -0
  22. package/src/game-factory/condition/{no-possible-moves-condition.ts → no-possible-moves-condition.js} +2 -3
  23. package/src/game-factory/condition/{not-condition.ts → not-condition.js} +2 -3
  24. package/src/game-factory/condition/{or-condition.ts → or-condition.js} +2 -3
  25. package/src/game-factory/condition/position-condition.js +12 -0
  26. package/src/game-factory/condition/{some-condition.ts → some-condition.js} +3 -5
  27. package/src/game-factory/condition/would-condition.js +94 -0
  28. package/src/game-factory/entity.js +29 -0
  29. package/src/game-factory/expand-game-rules.js +271 -0
  30. package/src/game-factory/game-factory.js +240 -0
  31. package/{dist/game-factory/move/end-turn.d.ts → src/game-factory/move/end-turn.js} +4 -2
  32. package/src/game-factory/move/for-each.js +18 -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/{dist/game-factory/move/pass.d.ts → src/game-factory/move/pass.js} +4 -2
  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 +11 -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/utils/{any-valid-moves.ts → any-valid-moves.js} +49 -54
  49. package/src/utils/check-conditions.js +28 -0
  50. package/src/utils/create-payload.js +16 -0
  51. package/src/utils/deserialize-bgio-arguments.js +8 -0
  52. package/src/utils/do-moves.js +18 -0
  53. package/src/utils/entity-matches.js +20 -0
  54. package/src/utils/find-met-condition.js +22 -0
  55. package/src/utils/get-current-moves.js +12 -0
  56. package/src/utils/get-scenario-results.js +23 -0
  57. package/src/utils/get-steps.js +29 -0
  58. package/src/utils/get.js +25 -0
  59. package/src/utils/{grid-contains-sequence.ts → grid-contains-sequence.js} +33 -71
  60. package/src/utils/json-transformer.js +12 -0
  61. package/src/utils/prepare-payload.js +17 -0
  62. package/src/utils/resolve-entity.js +9 -0
  63. package/src/utils/resolve-expression.js +10 -0
  64. package/src/utils/resolve-properties.js +149 -0
  65. package/src/utils/simulate-move.js +25 -0
  66. package/.github/workflows/ci.yml +0 -33
  67. package/dist/client/client.d.ts +0 -76
  68. package/dist/client/client.d.ts.map +0 -1
  69. package/dist/game-factory/bank/bank-slot.d.ts +0 -30
  70. package/dist/game-factory/bank/bank-slot.d.ts.map +0 -1
  71. package/dist/game-factory/bank/bank.d.ts +0 -34
  72. package/dist/game-factory/bank/bank.d.ts.map +0 -1
  73. package/dist/game-factory/board.d.ts +0 -4
  74. package/dist/game-factory/board.d.ts.map +0 -1
  75. package/dist/game-factory/condition/condition-factory.d.ts +0 -4
  76. package/dist/game-factory/condition/condition-factory.d.ts.map +0 -1
  77. package/dist/game-factory/condition/condition.d.ts +0 -13
  78. package/dist/game-factory/condition/condition.d.ts.map +0 -1
  79. package/dist/game-factory/condition/contains-condition.d.ts +0 -8
  80. package/dist/game-factory/condition/contains-condition.d.ts.map +0 -1
  81. package/dist/game-factory/condition/contains-same-condition.d.ts +0 -7
  82. package/dist/game-factory/condition/contains-same-condition.d.ts.map +0 -1
  83. package/dist/game-factory/condition/evaluate-condition.d.ts +0 -8
  84. package/dist/game-factory/condition/evaluate-condition.d.ts.map +0 -1
  85. package/dist/game-factory/condition/every-condition.d.ts +0 -12
  86. package/dist/game-factory/condition/every-condition.d.ts.map +0 -1
  87. package/dist/game-factory/condition/has-line-condition.d.ts +0 -8
  88. package/dist/game-factory/condition/has-line-condition.d.ts.map +0 -1
  89. package/dist/game-factory/condition/in-line-condition.d.ts +0 -8
  90. package/dist/game-factory/condition/in-line-condition.d.ts.map +0 -1
  91. package/dist/game-factory/condition/is-condition.d.ts +0 -8
  92. package/dist/game-factory/condition/is-condition.d.ts.map +0 -1
  93. package/dist/game-factory/condition/is-full-condition.d.ts +0 -7
  94. package/dist/game-factory/condition/is-full-condition.d.ts.map +0 -1
  95. package/dist/game-factory/condition/no-possible-moves-condition.d.ts +0 -7
  96. package/dist/game-factory/condition/no-possible-moves-condition.d.ts.map +0 -1
  97. package/dist/game-factory/condition/not-condition.d.ts +0 -7
  98. package/dist/game-factory/condition/not-condition.d.ts.map +0 -1
  99. package/dist/game-factory/condition/or-condition.d.ts +0 -7
  100. package/dist/game-factory/condition/or-condition.d.ts.map +0 -1
  101. package/dist/game-factory/condition/position-condition.d.ts +0 -7
  102. package/dist/game-factory/condition/position-condition.d.ts.map +0 -1
  103. package/dist/game-factory/condition/some-condition.d.ts +0 -8
  104. package/dist/game-factory/condition/some-condition.d.ts.map +0 -1
  105. package/dist/game-factory/condition/would-condition.d.ts +0 -8
  106. package/dist/game-factory/condition/would-condition.d.ts.map +0 -1
  107. package/dist/game-factory/entity.d.ts +0 -13
  108. package/dist/game-factory/entity.d.ts.map +0 -1
  109. package/dist/game-factory/expand-game-rules.d.ts +0 -3
  110. package/dist/game-factory/expand-game-rules.d.ts.map +0 -1
  111. package/dist/game-factory/game-factory.d.ts +0 -10
  112. package/dist/game-factory/game-factory.d.ts.map +0 -1
  113. package/dist/game-factory/move/end-turn.d.ts.map +0 -1
  114. package/dist/game-factory/move/for-each.d.ts +0 -5
  115. package/dist/game-factory/move/for-each.d.ts.map +0 -1
  116. package/dist/game-factory/move/index.d.ts +0 -6
  117. package/dist/game-factory/move/index.d.ts.map +0 -1
  118. package/dist/game-factory/move/move-entity.d.ts +0 -7
  119. package/dist/game-factory/move/move-entity.d.ts.map +0 -1
  120. package/dist/game-factory/move/move-factory.d.ts +0 -18
  121. package/dist/game-factory/move/move-factory.d.ts.map +0 -1
  122. package/dist/game-factory/move/move.d.ts +0 -54
  123. package/dist/game-factory/move/move.d.ts.map +0 -1
  124. package/dist/game-factory/move/pass-turn.d.ts +0 -5
  125. package/dist/game-factory/move/pass-turn.d.ts.map +0 -1
  126. package/dist/game-factory/move/pass.d.ts.map +0 -1
  127. package/dist/game-factory/move/place-new.d.ts +0 -5
  128. package/dist/game-factory/move/place-new.d.ts.map +0 -1
  129. package/dist/game-factory/move/remove-entity.d.ts +0 -5
  130. package/dist/game-factory/move/remove-entity.d.ts.map +0 -1
  131. package/dist/game-factory/move/set-active-players.d.ts +0 -5
  132. package/dist/game-factory/move/set-active-players.d.ts.map +0 -1
  133. package/dist/game-factory/move/set-state.d.ts +0 -5
  134. package/dist/game-factory/move/set-state.d.ts.map +0 -1
  135. package/dist/game-factory/move/shuffle.d.ts +0 -5
  136. package/dist/game-factory/move/shuffle.d.ts.map +0 -1
  137. package/dist/game-factory/move/take-from.d.ts +0 -11
  138. package/dist/game-factory/move/take-from.d.ts.map +0 -1
  139. package/dist/game-factory/space/space.d.ts +0 -10
  140. package/dist/game-factory/space/space.d.ts.map +0 -1
  141. package/dist/game-factory/space-group/grid.d.ts +0 -15
  142. package/dist/game-factory/space-group/grid.d.ts.map +0 -1
  143. package/dist/game-factory/space-group/space-group.d.ts +0 -20
  144. package/dist/game-factory/space-group/space-group.d.ts.map +0 -1
  145. package/dist/index.d.ts +0 -6
  146. package/dist/index.d.ts.map +0 -1
  147. package/dist/registry.d.ts +0 -17
  148. package/dist/registry.d.ts.map +0 -1
  149. package/dist/types/bagel-types.d.ts +0 -339
  150. package/dist/types/bagel-types.d.ts.map +0 -1
  151. package/dist/types/index.d.ts +0 -3
  152. package/dist/types/index.d.ts.map +0 -1
  153. package/dist/types/rule-with-conditions.d.ts +0 -9
  154. package/dist/types/rule-with-conditions.d.ts.map +0 -1
  155. package/dist/utils/any-valid-moves.d.ts +0 -2
  156. package/dist/utils/any-valid-moves.d.ts.map +0 -1
  157. package/dist/utils/bgio-resolve-types.d.ts +0 -25
  158. package/dist/utils/bgio-resolve-types.d.ts.map +0 -1
  159. package/dist/utils/check-conditions.d.ts +0 -7
  160. package/dist/utils/check-conditions.d.ts.map +0 -1
  161. package/dist/utils/create-payload.d.ts +0 -5
  162. package/dist/utils/create-payload.d.ts.map +0 -1
  163. package/dist/utils/deserialize-bgio-arguments.d.ts +0 -3
  164. package/dist/utils/deserialize-bgio-arguments.d.ts.map +0 -1
  165. package/dist/utils/do-moves.d.ts +0 -8
  166. package/dist/utils/do-moves.d.ts.map +0 -1
  167. package/dist/utils/entity-matches.d.ts +0 -6
  168. package/dist/utils/entity-matches.d.ts.map +0 -1
  169. package/dist/utils/find-met-condition.d.ts +0 -6
  170. package/dist/utils/find-met-condition.d.ts.map +0 -1
  171. package/dist/utils/get-current-moves.d.ts +0 -24
  172. package/dist/utils/get-current-moves.d.ts.map +0 -1
  173. package/dist/utils/get-scenario-results.d.ts +0 -3
  174. package/dist/utils/get-scenario-results.d.ts.map +0 -1
  175. package/dist/utils/get-steps.d.ts +0 -13
  176. package/dist/utils/get-steps.d.ts.map +0 -1
  177. package/dist/utils/get.d.ts +0 -7
  178. package/dist/utils/get.d.ts.map +0 -1
  179. package/dist/utils/grid-contains-sequence.d.ts +0 -27
  180. package/dist/utils/grid-contains-sequence.d.ts.map +0 -1
  181. package/dist/utils/json-transformer.d.ts +0 -8
  182. package/dist/utils/json-transformer.d.ts.map +0 -1
  183. package/dist/utils/prepare-payload.d.ts +0 -2
  184. package/dist/utils/prepare-payload.d.ts.map +0 -1
  185. package/dist/utils/resolve-entity.d.ts +0 -3
  186. package/dist/utils/resolve-entity.d.ts.map +0 -1
  187. package/dist/utils/resolve-expression.d.ts +0 -6
  188. package/dist/utils/resolve-expression.d.ts.map +0 -1
  189. package/dist/utils/resolve-properties.d.ts +0 -4
  190. package/dist/utils/resolve-properties.d.ts.map +0 -1
  191. package/dist/utils/simulate-move.d.ts +0 -16
  192. package/dist/utils/simulate-move.d.ts.map +0 -1
  193. package/src/client/client.ts +0 -306
  194. package/src/game-factory/bank/bank-slot.ts +0 -81
  195. package/src/game-factory/bank/bank.ts +0 -125
  196. package/src/game-factory/condition/condition-factory.ts +0 -59
  197. package/src/game-factory/condition/condition.ts +0 -50
  198. package/src/game-factory/condition/every-condition.ts +0 -27
  199. package/src/game-factory/condition/has-line-condition.ts +0 -15
  200. package/src/game-factory/condition/in-line-condition.ts +0 -25
  201. package/src/game-factory/condition/is-condition.ts +0 -24
  202. package/src/game-factory/condition/is-full-condition.ts +0 -10
  203. package/src/game-factory/condition/position-condition.ts +0 -13
  204. package/src/game-factory/condition/would-condition.ts +0 -104
  205. package/src/game-factory/entity.ts +0 -37
  206. package/src/game-factory/expand-game-rules.ts +0 -263
  207. package/src/game-factory/game-factory.ts +0 -263
  208. package/src/game-factory/move/end-turn.ts +0 -7
  209. package/src/game-factory/move/for-each.ts +0 -20
  210. package/src/game-factory/move/move-entity.ts +0 -18
  211. package/src/game-factory/move/move-factory.ts +0 -107
  212. package/src/game-factory/move/move.ts +0 -147
  213. package/src/game-factory/move/pass-turn.ts +0 -15
  214. package/src/game-factory/move/pass.ts +0 -7
  215. package/src/game-factory/move/place-new.ts +0 -42
  216. package/src/game-factory/move/remove-entity.ts +0 -11
  217. package/src/game-factory/move/set-active-players.ts +0 -26
  218. package/src/game-factory/move/set-state.ts +0 -14
  219. package/src/game-factory/move/shuffle.ts +0 -9
  220. package/src/game-factory/move/take-from.ts +0 -12
  221. package/src/game-factory/space/space.ts +0 -36
  222. package/src/game-factory/space-group/grid.ts +0 -48
  223. package/src/game-factory/space-group/space-group.ts +0 -44
  224. package/src/index.ts +0 -5
  225. package/src/types/bagel-types.ts +0 -449
  226. package/src/types/boardgame-io-core.d.ts +0 -7
  227. package/src/types/index.ts +0 -70
  228. package/src/types/rule-with-conditions.ts +0 -9
  229. package/src/utils/bgio-resolve-types.ts +0 -27
  230. package/src/utils/check-conditions.ts +0 -28
  231. package/src/utils/create-payload.ts +0 -19
  232. package/src/utils/deserialize-bgio-arguments.ts +0 -10
  233. package/src/utils/do-moves.ts +0 -22
  234. package/src/utils/entity-matches.ts +0 -30
  235. package/src/utils/expr-eval.d.ts +0 -6
  236. package/src/utils/find-met-condition.ts +0 -23
  237. package/src/utils/get-current-moves.ts +0 -39
  238. package/src/utils/get-scenario-results.ts +0 -30
  239. package/src/utils/get-steps.ts +0 -38
  240. package/src/utils/get.ts +0 -28
  241. package/src/utils/json-transformer.ts +0 -17
  242. package/src/utils/prepare-payload.ts +0 -20
  243. package/src/utils/resolve-entity.ts +0 -15
  244. package/src/utils/resolve-expression.ts +0 -16
  245. package/src/utils/resolve-properties.ts +0 -172
  246. package/src/utils/simulate-move.ts +0 -32
  247. package/src/wackson.d.ts +0 -4
  248. package/tsconfig.build.json +0 -14
  249. package/tsconfig.json +0 -21
  250. /package/src/game-factory/move/{index.ts → index.js} +0 -0
  251. /package/src/{registry.ts → registry.js} +0 -0
package/package.json CHANGED
@@ -1,13 +1,11 @@
1
1
  {
2
2
  "name": "board-game-engine",
3
- "version": "2.0.1",
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
+ }
@@ -1,18 +1,17 @@
1
1
  import _matches from "lodash/matches.js";
2
- import type { Condition as ConditionRule } from "../../types/bagel-types.js";
3
2
  import Condition from "../condition/condition.js";
4
3
  import checkConditions from "../../utils/check-conditions.js";
5
4
 
6
5
  export default class ContainsCondition extends Condition {
7
- checkCondition (bgioArguments: unknown, rule: unknown, payload: Record<string, unknown>, context: Record<string, unknown>) {
8
- const target = payload.target as { entities?: unknown[]; spaces?: unknown[] } | undefined;
6
+ checkCondition(bgioArguments, rule, payload, context) {
7
+ const { target } = payload
9
8
  if (!target) {
10
9
  return { matches: [], conditionIsMet: false }
11
10
  } else {
12
11
  const candidates = target.entities ?? target.spaces
13
- const matches = candidates?.filter((entity: unknown) => checkConditions(
12
+ const matches = candidates?.filter(entity => checkConditions(
14
13
  bgioArguments,
15
- (rule as { conditions?: ConditionRule[] }).conditions,
14
+ rule,
16
15
  { target: entity },
17
16
  context
18
17
  ).conditionsAreMet) ?? []