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
@@ -0,0 +1,240 @@
1
+ import { serialize } from "wackson";
2
+ import { registry } from '../registry.js'
3
+ import moveFactory from "./move/move-factory.js";
4
+ import Bank from "./bank/bank.js";
5
+ import expandGameRules from "./expand-game-rules.js";
6
+ import getScenarioResults from '../utils/get-scenario-results.js'
7
+ import doMoves from '../utils/do-moves.js'
8
+ import deserializeBgioArguments from '../utils/deserialize-bgio-arguments.js'
9
+
10
+ export default function gameFactory (gameRules, gameName) {
11
+ const game = { name: gameName }
12
+ const rules = expandGameRules(gameRules)
13
+
14
+ game.setup = (bgioArguments) => {
15
+ const { ctx } = bgioArguments
16
+ const initialState = {
17
+ _meta: {
18
+ passedPlayers: [],
19
+ previousPayloads: {},
20
+ }
21
+ };
22
+
23
+ const entityDefinitions = expandEntityDefinitions(rules.entities, ctx)
24
+ initialState.bank = new Bank(entityDefinitions)
25
+ initialState.sharedBoard = initialState.bank.getOne(
26
+ bgioArguments,
27
+ {
28
+ conditions: [{
29
+ conditionType: 'Is',
30
+ matcher: { name: "sharedBoard" }
31
+ }]
32
+ }
33
+ )
34
+
35
+ if (rules.personalBoard) {
36
+ initialState.personalBoards = bgioArguments.ctx.playOrder.map((playerID) =>
37
+ initialState.bank.getOne(
38
+ bgioArguments,
39
+ {
40
+ conditions: [{
41
+ conditionType: 'Is',
42
+ matcher: {
43
+ name: "personalBoard",
44
+ player: playerID,
45
+ }
46
+ }]
47
+ }
48
+ )
49
+ )
50
+ }
51
+
52
+ rules.initialMoves?.forEach((moveRule) => {
53
+ moveFactory(moveRule, game).moveInstance.doMove(
54
+ { ...bgioArguments, G: initialState }
55
+ );
56
+ })
57
+ return JSON.parse(serialize(initialState, {registry}));
58
+ }
59
+
60
+ if (rules.moves) {
61
+ game.moves = createMoves(rules.moves, game)
62
+ }
63
+
64
+ if (rules.turn) {
65
+ game.turn = createTurn(rules.turn, game)
66
+ }
67
+
68
+ if (rules.phases) {
69
+ game.phases = Object.entries(rules.phases).reduce((acc, [name, phaseRule]) => ({
70
+ ...acc,
71
+ [name]: createPhase(phaseRule, game)
72
+ }), {})
73
+ }
74
+
75
+ if (rules.endIf) {
76
+ game.endIf = (bgioArguments) => {
77
+ const newBgioArguments = deserializeBgioArguments(bgioArguments)
78
+ return getScenarioResults(newBgioArguments, rules.endIf)
79
+ }
80
+ }
81
+
82
+ if (!gameRules.DEBUG_DISABLE_SECRET_STATE) {
83
+ game.playerView = (bgioArguments) => {
84
+ const { G, playerID } = deserializeBgioArguments(bgioArguments)
85
+ Object.values(G.bank.tracker).forEach(((entity) => {
86
+ if (
87
+ entity.rule.contentsHiddenFrom === 'All'
88
+ || (
89
+ entity.rule.contentsHiddenFrom === 'Others'
90
+ && (
91
+ playerID !== entity.rule.player
92
+ || playerID == undefined
93
+ )
94
+ )
95
+ ) {
96
+ // may want to hide entities inside spaces instead?
97
+ if (entity.spaces) {
98
+ entity.spaces = entity.rule.hideLength
99
+ ? []
100
+ : entity.spaces.map(() => G.bank.createEntity())
101
+ }
102
+ if (entity.entities) {
103
+ entity.entities = entity.rule.hideLength
104
+ ? []
105
+ : entity.entities.map(() => G.bank.createEntity())
106
+ }
107
+ }
108
+ }))
109
+ return JSON.parse(serialize(G, {registry}))
110
+ }
111
+ }
112
+
113
+ return game
114
+ }
115
+
116
+ // create a new entity for each variant
117
+ function expandEntityDefinitions (entities, ctx) {
118
+ return entities.reduce((acc, entity) => {
119
+ const entityCopy = { ...entity }
120
+
121
+ // perPlayer flag multiplies number of variants
122
+ if (entityCopy.perPlayer) {
123
+ delete entityCopy.perPlayer
124
+ if (entityCopy.variants) {
125
+ entityCopy.variants =
126
+ (new Array(ctx.numPlayers)).fill().reduce((accu, _, i) => [
127
+ ...accu,
128
+ ...entityCopy.variants.map(variant => ({ ...variant, player: `${i}` }))
129
+ ], [])
130
+ } else {
131
+ entityCopy.variants =
132
+ (new Array(ctx.numPlayers)).fill().map((_, i) => ({ player: `${i}` }))
133
+ }
134
+ }
135
+
136
+ // variants becomes new entitites
137
+ if (entityCopy.variants) {
138
+ const variants = entityCopy.variants
139
+ delete entityCopy.variants
140
+
141
+ return [
142
+ ...acc,
143
+ ...variants.map(variant => ({
144
+ ...entityCopy,
145
+ ...variant,
146
+ }))
147
+ ]
148
+ } else {
149
+ return [
150
+ ...acc,
151
+ entityCopy
152
+ ]
153
+ }
154
+ }, [])
155
+ }
156
+
157
+ function createTurn (turnRule, game) {
158
+ const turn = { ...turnRule }
159
+
160
+ turn.onBegin = (bgioArguments) => {
161
+ const newBgioArguments = deserializeBgioArguments(bgioArguments)
162
+ const stageRule = turnRule.stages?.[
163
+ newBgioArguments.ctx.activePlayers?.[newBgioArguments.ctx.currentPlayer]
164
+ ]
165
+
166
+ // not 100% sure about this logic / timing, but it seems to work so far in terms
167
+ // of letting a player pass and still take a turn later if a move becomes available
168
+ newBgioArguments.G._meta.passedPlayers = newBgioArguments.G._meta.passedPlayers
169
+ .filter(p => p !== newBgioArguments.ctx.currentPlayer)
170
+
171
+ doMoves(newBgioArguments, turnRule.initialMoves, { game })
172
+ doMoves(newBgioArguments, stageRule?.initialMoves, { game })
173
+
174
+ return JSON.parse(serialize(newBgioArguments.G, {registry}));
175
+ }
176
+
177
+ if (turnRule.stages) {
178
+ Object.entries(turnRule.stages).forEach(([stageName, stageRule]) => {
179
+ if (stageRule.moves) {
180
+ turn.stages[stageName].moves = createMoves(stageRule.moves, game)
181
+ }
182
+ })
183
+ }
184
+
185
+ if (turnRule.order?.playOrder === 'RotateFirst') {
186
+ turnRule.order.first = () => 0
187
+ turnRule.order.next = ({ ctx }) => (ctx.playOrderPos + 1) % ctx.numPlayers
188
+ turn.order.playOrder = ({ ctx, G }) => {
189
+ return G._meta.isAfterFirstPhase
190
+ ? [...ctx.playOrder.slice(1), ctx.playOrder[0]]
191
+ : ctx.playOrder
192
+ }
193
+ }
194
+
195
+ return turn
196
+ }
197
+
198
+ function createPhase (phaseRule, game) {
199
+ const phase = {...phaseRule}
200
+ if (phaseRule.turn) {
201
+ phase.turn = createTurn(phaseRule.turn, game)
202
+ }
203
+ if (phaseRule.moves) {
204
+ phase.moves = createMoves(phaseRule.moves, game)
205
+ }
206
+
207
+ phase.onBegin = (bgioArguments) => {
208
+ const newBgioArguments = deserializeBgioArguments(bgioArguments)
209
+ doMoves(newBgioArguments, phaseRule.initialMoves, { game })
210
+ newBgioArguments.G._meta.currentPhaseHasBeenSetUp = true
211
+ newBgioArguments.G._meta.nextPhase = phaseRule.next
212
+ return JSON.parse(serialize(newBgioArguments.G, {registry}));
213
+ }
214
+
215
+ if (phaseRule.endIf) {
216
+ phase.endIf = (bgioArguments) => {
217
+ const newBgioArguments = deserializeBgioArguments(bgioArguments)
218
+ if (newBgioArguments.G._meta.currentPhaseHasBeenSetUp) {
219
+ const result = getScenarioResults(newBgioArguments, phaseRule.endIf)
220
+ if (result) {
221
+ return result
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ phase.onEnd = ({ G }) => {
228
+ G._meta.currentPhaseHasBeenSetUp = false
229
+ G._meta.isAfterFirstPhase = true
230
+ }
231
+
232
+ return phase
233
+ }
234
+
235
+ function createMoves (moves, game) {
236
+ return Object.entries(moves).reduce((acc, [name, moveDefinition]) => ({
237
+ ...acc,
238
+ [name]: moveFactory({ ...moveDefinition, name }, game)
239
+ }), {})
240
+ }
@@ -1,5 +1,7 @@
1
1
  import Move from "./move.js";
2
+
2
3
  export default class EndTurn extends Move {
3
- do(bgioArguments: unknown): void;
4
+ do(bgioArguments) {
5
+ bgioArguments.events.endTurn()
6
+ }
4
7
  }
5
- //# sourceMappingURL=end-turn.d.ts.map
@@ -0,0 +1,18 @@
1
+ import Move from "./move.js";
2
+ import { getMoveInstance } from "./move-factory.js";
3
+
4
+ export default class ForEach extends Move {
5
+ do(bgioArguments, rule, { arguments: { targets } }, context) {
6
+ targets.forEach((target) => {
7
+ const loopContext = {
8
+ ...context,
9
+ loopTarget: target
10
+ }
11
+ getMoveInstance(rule.move).doMove(
12
+ bgioArguments,
13
+ undefined,
14
+ loopContext
15
+ )
16
+ })
17
+ }
18
+ }
@@ -0,0 +1,16 @@
1
+ import Move from "./move.js";
2
+
3
+ export default class MoveEntity extends Move {
4
+ do(bgioArguments, rule, { arguments: { entity, destination } }) {
5
+ // todo: move all such things to always be multiple
6
+ if (Array.isArray(entity)) {
7
+ entity.forEach((e) => {
8
+ bgioArguments.G.bank.findParent(e)?.remove(e)
9
+ destination.placeEntity(e, rule.position)
10
+ })
11
+ } else {
12
+ bgioArguments.G.bank.findParent(entity)?.remove(entity)
13
+ destination.placeEntity(entity, rule.position)
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,89 @@
1
+ import { serialize, deserialize } from 'wackson'
2
+ import { INVALID_MOVE } from '@mnbroatch/boardgame.io/dist/cjs/core.js';
3
+ import { registry } from '../../registry.js'
4
+ import deserializeBgioArguments from '../../utils/deserialize-bgio-arguments.js'
5
+ import MoveEntity from "./move-entity.js";
6
+ import RemoveEntity from "./remove-entity.js";
7
+ import PlaceNew from "./place-new.js";
8
+ import TakeFrom from "./take-from.js";
9
+ import SetState from "./set-state.js";
10
+ import SetActivePlayers from "./set-active-players.js";
11
+ import EndTurn from "./end-turn.js";
12
+ import PassTurn from "./pass-turn.js";
13
+ import ForEach from "./for-each.js";
14
+ import Pass from "./pass.js";
15
+ import Shuffle from "./shuffle.js";
16
+
17
+ export default function moveFactory(moveRule, game) {
18
+ const moveInstance = getMoveInstance(moveRule)
19
+
20
+ // accepts serialized G and payload, returns serialized
21
+ const compatibleMove = function (
22
+ bgioArguments,
23
+ serializablePayload
24
+ ) {
25
+ const newBgioArguments = deserializeBgioArguments(bgioArguments)
26
+ const { G } = newBgioArguments
27
+ const payload = revivePayload(serializablePayload, G)
28
+ const context = { moveInstance, game }
29
+ const moveConditionResults = moveInstance.doMove(newBgioArguments, payload, context)
30
+
31
+ context.moveConditionResults = [moveConditionResults]
32
+
33
+ if (moveConditionResults !== INVALID_MOVE && moveRule.then) {
34
+ for (let automaticMoveRule of moveRule.then) {
35
+ const result = getMoveInstance(automaticMoveRule).doMove(
36
+ newBgioArguments,
37
+ {},
38
+ {...context} // spread here so prevArguments doesn't change for sibling
39
+ )
40
+ context.moveConditionResults.push(result)
41
+ }
42
+ }
43
+
44
+ return JSON.parse(serialize(G, {registry}))
45
+ }
46
+ compatibleMove.moveInstance = moveInstance
47
+ return compatibleMove
48
+ }
49
+
50
+ function revivePayload (serializablePayload, G) {
51
+ if (serializablePayload) {
52
+ const payload = deserialize(JSON.stringify(serializablePayload), registry)
53
+ payload.arguments =
54
+ Object.entries(payload.arguments).reduce((acc, [key, argOrEntityId]) => ({
55
+ ...acc,
56
+ [key]: typeof argOrEntityId === 'number' ? G.bank.locate(argOrEntityId) : argOrEntityId
57
+ }), {})
58
+ return payload
59
+ } else {
60
+ return serializablePayload
61
+ }
62
+ }
63
+
64
+ export function getMoveInstance (moveRule) {
65
+ switch (moveRule.moveType) {
66
+ case 'MoveEntity':
67
+ return new MoveEntity(moveRule);
68
+ case 'PlaceNew':
69
+ return new PlaceNew(moveRule);
70
+ case 'RemoveEntity':
71
+ return new RemoveEntity(moveRule);
72
+ case 'TakeFrom':
73
+ return new TakeFrom(moveRule);
74
+ case 'SetState':
75
+ return new SetState(moveRule);
76
+ case 'ForEach':
77
+ return new ForEach(moveRule);
78
+ case 'Pass':
79
+ return new Pass(moveRule);
80
+ case 'Shuffle':
81
+ return new Shuffle(moveRule);
82
+ case 'SetActivePlayers':
83
+ return new SetActivePlayers(moveRule);
84
+ case 'EndTurn':
85
+ return new EndTurn(moveRule);
86
+ case 'PassTurn':
87
+ return new PassTurn(moveRule);
88
+ }
89
+ }
@@ -0,0 +1,131 @@
1
+ import { INVALID_MOVE } from '@mnbroatch/boardgame.io/dist/cjs/core.js';
2
+ import checkConditions from "../../utils/check-conditions.js";
3
+ import resolveProperties from "../../utils/resolve-properties.js";
4
+
5
+ export default class Move {
6
+ constructor (rule) {
7
+ this.rule = this.transformRule(rule)
8
+ }
9
+
10
+ checkValidity (bgioArguments, payload, context) {
11
+ const argRuleEntries = Object.entries(this.rule.arguments ?? {})
12
+
13
+ if (
14
+ !argRuleEntries.every(([argName]) => {
15
+ const arg = payload.arguments[argName]
16
+ return arg !== undefined && (!Array.isArray(arg) || arg.length)
17
+ })
18
+ ) {
19
+ // not the best return value but we don't want to do expensive checks
20
+ // when we know the operation is doomed. At least for now.
21
+ return false
22
+ }
23
+
24
+ const argumentResults = {}
25
+
26
+ for (let i = 0, len = argRuleEntries.length; i < len; i++) {
27
+ const [argName, argRule] = argRuleEntries[i]
28
+ const payloadArg = payload.arguments[argName]
29
+ const args = Array.isArray(payloadArg)
30
+ ? payloadArg
31
+ : [payloadArg]
32
+
33
+ const argResults = []
34
+ for (let j = 0, len = args.length; j < len; j++) {
35
+ const arg = args[j]
36
+ const result = checkConditions(
37
+ bgioArguments,
38
+ argRule,
39
+ { target: arg },
40
+ { ...context, moveArguments: payload.arguments }
41
+ )
42
+ argResults.push(result)
43
+ if (!result.conditionsAreMet) {
44
+ break
45
+ }
46
+ }
47
+
48
+ const argConditionsAreMet = argResults.at(-1).conditionsAreMet
49
+ argumentResults[argName] = {
50
+ results: argResults,
51
+ conditionsAreMet: argConditionsAreMet
52
+ }
53
+ if (!argConditionsAreMet) {
54
+ break
55
+ }
56
+ }
57
+
58
+ const moveResults = checkConditions(
59
+ bgioArguments,
60
+ { conditions: this.rule.conditions },
61
+ undefined,
62
+ { ...context, moveArguments: payload.arguments }
63
+ )
64
+
65
+ return {
66
+ argumentResults,
67
+ moveResults,
68
+ conditionsAreMet: moveResults.conditionsAreMet
69
+ && Object.values(argumentResults).every(a => a.conditionsAreMet)
70
+ }
71
+ }
72
+
73
+ isValid (bgioArguments, payload, context) {
74
+ const conditionResults = this.checkValidity(
75
+ bgioArguments,
76
+ payload,
77
+ context
78
+ )
79
+ return conditionResults.conditionsAreMet
80
+ }
81
+
82
+ doMove (bgioArguments, payload, context, { skipCheck = false } = {}) {
83
+ const rule = resolveProperties(
84
+ bgioArguments,
85
+ this.rule,
86
+ context
87
+ )
88
+ const resolvedPayload = {
89
+ ...payload,
90
+ arguments: Object.entries(rule.arguments ?? {})
91
+ .reduce((acc, [argName, arg]) => {
92
+ return {
93
+ ...acc,
94
+ [argName]: payload?.arguments?.[argName] ?? arg
95
+ };
96
+ }, {})
97
+ };
98
+
99
+ // does not store automatic moves, do we need that?
100
+ if (rule.name) {
101
+ bgioArguments.G._meta.previousPayloads[rule.name] = resolvedPayload
102
+ }
103
+
104
+ let conditionResults
105
+ if (!skipCheck) {
106
+ conditionResults = this.checkValidity(bgioArguments, resolvedPayload, context)
107
+ }
108
+
109
+ if (!skipCheck && !conditionResults.conditionsAreMet) {
110
+ return INVALID_MOVE
111
+ } else {
112
+ this.do(bgioArguments, rule, resolvedPayload, context)
113
+ if (context) {
114
+ context.previousArguments = resolvedPayload.arguments
115
+ }
116
+ }
117
+
118
+ return { conditionResults }
119
+ }
120
+
121
+ transformRule (rule) {
122
+ const args = rule.arguments
123
+ for (let key in args) {
124
+ const arg = args[key]
125
+ if (!arg.playerChoice) {
126
+ arg.resolveAsEntity = true
127
+ }
128
+ }
129
+ return rule
130
+ }
131
+ }
@@ -0,0 +1,10 @@
1
+ import Move from "./move.js";
2
+
3
+ export default class PassTurn extends Move {
4
+ do(bgioArguments) {
5
+ if (bgioArguments.G._meta.passedPlayers.length < bgioArguments.ctx.numPlayers) {
6
+ bgioArguments.G._meta.passedPlayers.push(bgioArguments.ctx.currentPlayer)
7
+ bgioArguments.events.pass()
8
+ }
9
+ }
10
+ }
@@ -1,5 +1,7 @@
1
1
  import Move from "./move.js";
2
+
2
3
  export default class Pass extends Move {
3
- do(bgioArguments: unknown): void;
4
+ do(bgioArguments) {
5
+ bgioArguments.events.endTurn()
6
+ }
4
7
  }
5
- //# sourceMappingURL=pass.d.ts.map
@@ -0,0 +1,33 @@
1
+ import Move from "./move.js";
2
+
3
+ export default class PlaceNew extends Move {
4
+ do(bgioArguments, rule, { arguments: { destination } }, context) {
5
+ const entities = rule.matchMultiple
6
+ ? bgioArguments.G.bank.getMultiple(
7
+ bgioArguments,
8
+ {
9
+ ...rule.entity,
10
+ conditions: [
11
+ ...(rule.entity?.conditions || []),
12
+ ...(rule.conditions || []),
13
+ ]
14
+ },
15
+ rule.count,
16
+ context
17
+ )
18
+ : [bgioArguments.G.bank.getOne(
19
+ bgioArguments,
20
+ {
21
+ ...rule.entity,
22
+ conditions: [
23
+ ...(rule.entity?.conditions || []),
24
+ ...(rule.conditions || []),
25
+ ]
26
+ },
27
+ context
28
+ )]
29
+ entities.forEach((entity) => {
30
+ destination.placeEntity(entity, rule.position)
31
+ })
32
+ }
33
+ }
@@ -0,0 +1,7 @@
1
+ import Move from "./move.js";
2
+
3
+ export default class RemoveEntity extends Move {
4
+ do(bgioArguments, rule, { arguments: { entity } }) {
5
+ bgioArguments.G.bank.returnToBank(bgioArguments, entity)
6
+ }
7
+ }
@@ -0,0 +1,23 @@
1
+ import Move from "./move.js";
2
+ import doMoves from '../../utils/do-moves.js'
3
+
4
+ export default class SetActivePlayers extends Move {
5
+ do(bgioArguments, rule, _, context) {
6
+ bgioArguments.events.setActivePlayers(rule.options)
7
+
8
+ // this is going to need to be expanded to handle more complex things
9
+ // than "move current player to new stage"
10
+ const phaseName = bgioArguments.ctx.phase
11
+ const stageName = rule.options.currentPlayer?.stage
12
+ const phaseOrRoot = context.game.phases?.[phaseName] ?? context.game
13
+ const stage = phaseOrRoot?.turn?.stages?.[stageName]
14
+ doMoves(
15
+ bgioArguments,
16
+ stage?.initialMoves,
17
+ {
18
+ ...context,
19
+ stageName,
20
+ }
21
+ )
22
+ }
23
+ }
@@ -0,0 +1,11 @@
1
+ import Move from "./move.js";
2
+
3
+ // todo: invariant conditions like "is one of the allowed values"
4
+ export default class SetState extends Move {
5
+ do(_, __, { arguments: { entity, state } }) {
6
+ entity.state = {
7
+ ...entity.state,
8
+ [state.property]: state.value,
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,7 @@
1
+ import Move from "./move.js";
2
+
3
+ export default class Shuffle extends Move {
4
+ do(bgioArguments, _, { arguments: { target } }) {
5
+ target.entities = bgioArguments.random.Shuffle(target.entities)
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ import Move from "./move.js";
2
+
3
+ export default class TakeFrom extends Move {
4
+ do(bgioArguments, rule, { arguments: { source, destination } }) {
5
+ destination.placeEntity(source.takeOne(rule.arguments.source.position))
6
+ }
7
+ }
@@ -0,0 +1,30 @@
1
+ import Entity from "../entity.js";
2
+
3
+ export default class Space extends Entity {
4
+ constructor (...args) {
5
+ super(...args)
6
+ this.entities = []
7
+ }
8
+
9
+ placeEntity (entity, position = 'Last') {
10
+ if (position === 'Last') {
11
+ this.entities.push(entity);
12
+ } else if (position === 'First') {
13
+ this.entities.unshift(entity);
14
+ }
15
+ }
16
+
17
+ remove (entity) {
18
+ this.entities.splice(this.entities.indexOf(entity), 1);
19
+ }
20
+
21
+ takeOne (position = 'First') {
22
+ if (position === 'First') {
23
+ return this.entities.splice(0, 1)[0];
24
+ }
25
+ }
26
+
27
+ isEmpty() {
28
+ return this.entities.length === 0;
29
+ }
30
+ }