board-game-engine 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/board-game-engine.test.js +2 -41
  2. package/dist/board-game-engine.js +32556 -5074
  3. package/dist/board-game-engine.min.js +2 -1
  4. package/dist/board-game-engine.min.js.LICENSE.txt +14 -0
  5. package/package.json +6 -3
  6. package/patch-bgio.cjs +23 -0
  7. package/src/client/client.js +287 -0
  8. package/src/game-factory/bank/bank-slot.js +69 -0
  9. package/src/game-factory/bank/bank.js +114 -0
  10. package/src/game-factory/board.js +3 -0
  11. package/src/game-factory/condition/condition-factory.js +52 -0
  12. package/src/game-factory/condition/condition.js +39 -0
  13. package/src/game-factory/condition/contains-condition.js +21 -0
  14. package/src/game-factory/condition/contains-same-condition.js +27 -0
  15. package/src/game-factory/condition/evaluate-condition.js +18 -0
  16. package/src/game-factory/condition/every-condition.js +25 -0
  17. package/src/game-factory/condition/has-line-condition.js +14 -0
  18. package/src/game-factory/condition/in-line-condition.js +19 -0
  19. package/src/game-factory/condition/is-condition.js +23 -0
  20. package/src/game-factory/condition/is-full-condition.js +9 -0
  21. package/src/game-factory/condition/no-possible-moves-condition.js +14 -0
  22. package/src/game-factory/condition/not-condition.js +14 -0
  23. package/src/game-factory/condition/or-condition.js +15 -0
  24. package/src/game-factory/condition/position-condition.js +12 -0
  25. package/src/game-factory/condition/some-condition.js +24 -0
  26. package/src/game-factory/condition/would-condition.js +94 -0
  27. package/src/game-factory/entity.js +29 -0
  28. package/src/game-factory/expand-game-rules.js +276 -0
  29. package/src/game-factory/game-factory.js +239 -0
  30. package/src/game-factory/move/end-turn.js +7 -0
  31. package/src/game-factory/move/for-each.js +18 -0
  32. package/src/game-factory/move/index.js +7 -0
  33. package/src/game-factory/move/move-entity.js +16 -0
  34. package/src/game-factory/move/move-factory.js +89 -0
  35. package/src/game-factory/move/move.js +131 -0
  36. package/src/game-factory/move/pass-turn.js +10 -0
  37. package/src/game-factory/move/pass.js +7 -0
  38. package/src/game-factory/move/place-new.js +33 -0
  39. package/src/game-factory/move/remove-entity.js +7 -0
  40. package/src/game-factory/move/set-active-players.js +23 -0
  41. package/src/game-factory/move/set-state.js +13 -0
  42. package/src/game-factory/move/shuffle.js +7 -0
  43. package/src/game-factory/move/take-from.js +7 -0
  44. package/src/game-factory/space/space.js +30 -0
  45. package/src/game-factory/space-group/grid.js +43 -0
  46. package/src/game-factory/space-group/space-group.js +29 -0
  47. package/src/index.js +2 -0
  48. package/src/registry.js +17 -0
  49. package/src/utils/any-valid-moves.js +157 -0
  50. package/src/utils/check-conditions.js +28 -0
  51. package/src/utils/create-payload.js +16 -0
  52. package/src/utils/deserialize-bgio-arguments.js +8 -0
  53. package/src/utils/do-moves.js +18 -0
  54. package/src/utils/entity-matches.js +20 -0
  55. package/src/utils/find-met-condition.js +22 -0
  56. package/src/utils/get-current-moves.js +12 -0
  57. package/src/utils/get-scenario-results.js +23 -0
  58. package/src/utils/get-steps.js +28 -0
  59. package/src/utils/get.js +25 -0
  60. package/src/utils/grid-contains-sequence.js +226 -0
  61. package/src/utils/json-transformer.js +12 -0
  62. package/src/utils/prepare-payload.js +16 -0
  63. package/src/utils/resolve-entity.js +9 -0
  64. package/src/utils/resolve-expression.js +10 -0
  65. package/src/utils/resolve-properties.js +157 -0
  66. package/src/utils/simulate-move.js +25 -0
  67. package/webpack.config.js +4 -1
  68. package/src/action/action-factory.js +0 -13
  69. package/src/action/action.js +0 -34
  70. package/src/action/move-piece-action.js +0 -11
  71. package/src/action/select-piece-action.js +0 -23
  72. package/src/action/swap-action.js +0 -14
  73. package/src/board/board-factory.js +0 -12
  74. package/src/board/board-group.js +0 -9
  75. package/src/board/board.js +0 -11
  76. package/src/board/grid.js +0 -52
  77. package/src/board/stack.js +0 -16
  78. package/src/condition/action-type-matches-condition.js +0 -7
  79. package/src/condition/bingo-condition.js +0 -50
  80. package/src/condition/blackout-condition.js +0 -9
  81. package/src/condition/condition-factory.js +0 -31
  82. package/src/condition/condition.js +0 -9
  83. package/src/condition/contains-condition.js +0 -14
  84. package/src/condition/does-not-contain-condition.js +0 -15
  85. package/src/condition/is-valid-player-condition.js +0 -7
  86. package/src/condition/piece-matches-condition.js +0 -23
  87. package/src/condition/relative-move-condition.js +0 -16
  88. package/src/condition/some-condition.js +0 -7
  89. package/src/game/game.ts +0 -362
  90. package/src/index.ts +0 -1
  91. package/src/piece/piece-factory.js +0 -5
  92. package/src/piece/piece.ts +0 -25
  93. package/src/piece/pile.js +0 -70
  94. package/src/player/player.ts +0 -13
  95. package/src/registry.ts +0 -51
  96. package/src/round/round-factory.js +0 -7
  97. package/src/round/round.js +0 -41
  98. package/src/round/sequential-player-turn.js +0 -18
  99. package/src/space/space.ts +0 -22
  100. package/src/utils/find-value-path.js +0 -37
  101. package/src/utils/resolve-board.ts +0 -38
  102. package/src/utils/resolve-piece.ts +0 -43
@@ -0,0 +1,239 @@
1
+ import { serialize } from "wackson";
2
+ import moveFactory from "./move/move-factory.js";
3
+ import Bank from "./bank/bank.js";
4
+ import expandGameRules from "./expand-game-rules.js";
5
+ import getScenarioResults from '../utils/get-scenario-results.js'
6
+ import doMoves from '../utils/do-moves.js'
7
+ import deserializeBgioArguments from '../utils/deserialize-bgio-arguments.js'
8
+
9
+ export default function gameFactory (gameRules, gameName) {
10
+ const game = { name: gameName }
11
+ const rules = expandGameRules(gameRules)
12
+
13
+ game.setup = (bgioArguments) => {
14
+ const { ctx } = bgioArguments
15
+ const initialState = {
16
+ _meta: {
17
+ passedPlayers: [],
18
+ previousPayloads: {},
19
+ }
20
+ };
21
+
22
+ const entityDefinitions = expandEntityDefinitions(rules.entities, ctx)
23
+ initialState.bank = new Bank(entityDefinitions)
24
+ initialState.sharedBoard = initialState.bank.getOne(
25
+ bgioArguments,
26
+ {
27
+ conditions: [{
28
+ conditionType: 'Is',
29
+ matcher: { name: "sharedBoard" }
30
+ }]
31
+ }
32
+ )
33
+
34
+ if (rules.personalBoard) {
35
+ initialState.personalBoards = bgioArguments.ctx.playOrder.map((playerID) =>
36
+ initialState.bank.getOne(
37
+ bgioArguments,
38
+ {
39
+ conditions: [{
40
+ conditionType: 'Is',
41
+ matcher: {
42
+ name: "personalBoard",
43
+ player: playerID,
44
+ }
45
+ }]
46
+ }
47
+ )
48
+ )
49
+ }
50
+
51
+ rules.initialMoves?.forEach((moveRule) => {
52
+ moveFactory(moveRule, game).moveInstance.doMove(
53
+ { ...bgioArguments, G: initialState }
54
+ );
55
+ })
56
+ return JSON.parse(serialize(initialState));
57
+ }
58
+
59
+ if (rules.moves) {
60
+ game.moves = createMoves(rules.moves, game)
61
+ }
62
+
63
+ if (rules.turn) {
64
+ game.turn = createTurn(rules.turn, game)
65
+ }
66
+
67
+ if (rules.phases) {
68
+ game.phases = Object.entries(rules.phases).reduce((acc, [name, phaseRule]) => ({
69
+ ...acc,
70
+ [name]: createPhase(phaseRule, game)
71
+ }), {})
72
+ }
73
+
74
+ if (rules.endIf) {
75
+ game.endIf = (bgioArguments) => {
76
+ const newBgioArguments = deserializeBgioArguments(bgioArguments)
77
+ return getScenarioResults(newBgioArguments, rules.endIf)
78
+ }
79
+ }
80
+
81
+ if (!gameRules.DEBUG_DISABLE_SECRET_STATE) {
82
+ game.playerView = (bgioArguments) => {
83
+ const { G, playerID } = deserializeBgioArguments(bgioArguments)
84
+ Object.values(G.bank.tracker).forEach(((entity) => {
85
+ if (
86
+ entity.rule.contentsHiddenFrom === 'All'
87
+ || (
88
+ entity.rule.contentsHiddenFrom === 'Others'
89
+ && (
90
+ playerID !== entity.rule.player
91
+ || playerID == undefined
92
+ )
93
+ )
94
+ ) {
95
+ // may want to hide entities inside spaces instead?
96
+ if (entity.spaces) {
97
+ entity.spaces = entity.rule.hideLength
98
+ ? []
99
+ : entity.spaces.map(() => G.bank.createEntity())
100
+ }
101
+ if (entity.entities) {
102
+ entity.entities = entity.rule.hideLength
103
+ ? []
104
+ : entity.entities.map(() => G.bank.createEntity())
105
+ }
106
+ }
107
+ }))
108
+ return JSON.parse(serialize(G))
109
+ }
110
+ }
111
+
112
+ return game
113
+ }
114
+
115
+ // create a new entity for each variant
116
+ function expandEntityDefinitions (entities, ctx) {
117
+ return entities.reduce((acc, entity) => {
118
+ const entityCopy = { ...entity }
119
+
120
+ // perPlayer flag multiplies number of variants
121
+ if (entityCopy.perPlayer) {
122
+ delete entityCopy.perPlayer
123
+ if (entityCopy.variants) {
124
+ entityCopy.variants =
125
+ (new Array(ctx.numPlayers)).fill().reduce((accu, _, i) => [
126
+ ...accu,
127
+ ...entityCopy.variants.map(variant => ({ ...variant, player: `${i}` }))
128
+ ], [])
129
+ } else {
130
+ entityCopy.variants =
131
+ (new Array(ctx.numPlayers)).fill().map((_, i) => ({ player: `${i}` }))
132
+ }
133
+ }
134
+
135
+ // variants becomes new entitites
136
+ if (entityCopy.variants) {
137
+ const variants = entityCopy.variants
138
+ delete entityCopy.variants
139
+
140
+ return [
141
+ ...acc,
142
+ ...variants.map(variant => ({
143
+ ...entityCopy,
144
+ ...variant,
145
+ }))
146
+ ]
147
+ } else {
148
+ return [
149
+ ...acc,
150
+ entityCopy
151
+ ]
152
+ }
153
+ }, [])
154
+ }
155
+
156
+ function createTurn (turnRule, game) {
157
+ const turn = { ...turnRule }
158
+
159
+ turn.onBegin = (bgioArguments) => {
160
+ const newBgioArguments = deserializeBgioArguments(bgioArguments)
161
+ const stageRule = turnRule.stages?.[
162
+ newBgioArguments.ctx.activePlayers?.[newBgioArguments.ctx.currentPlayer]
163
+ ]
164
+
165
+ // not 100% sure about this logic / timing, but it seems to work so far in terms
166
+ // of letting a player pass and still take a turn later if a move becomes available
167
+ newBgioArguments.G._meta.passedPlayers = newBgioArguments.G._meta.passedPlayers
168
+ .filter(p => p !== newBgioArguments.ctx.currentPlayer)
169
+
170
+ doMoves(newBgioArguments, turnRule.initialMoves, { game })
171
+ doMoves(newBgioArguments, stageRule?.initialMoves, { game })
172
+
173
+ return JSON.parse(serialize(newBgioArguments.G));
174
+ }
175
+
176
+ if (turnRule.stages) {
177
+ Object.entries(turnRule.stages).forEach(([stageName, stageRule]) => {
178
+ if (stageRule.moves) {
179
+ turn.stages[stageName].moves = createMoves(stageRule.moves, game)
180
+ }
181
+ })
182
+ }
183
+
184
+ if (turnRule.order?.playOrder === 'RotateFirst') {
185
+ turnRule.order.first = () => 0
186
+ turnRule.order.next = ({ ctx }) => (ctx.playOrderPos + 1) % ctx.numPlayers
187
+ turn.order.playOrder = ({ ctx, G }) => {
188
+ return G._meta.isAfterFirstPhase
189
+ ? [...ctx.playOrder.slice(1), ctx.playOrder[0]]
190
+ : ctx.playOrder
191
+ }
192
+ }
193
+
194
+ return turn
195
+ }
196
+
197
+ function createPhase (phaseRule, game) {
198
+ const phase = {...phaseRule}
199
+ if (phaseRule.turn) {
200
+ phase.turn = createTurn(phaseRule.turn, game)
201
+ }
202
+ if (phaseRule.moves) {
203
+ phase.moves = createMoves(phaseRule.moves, game)
204
+ }
205
+
206
+ phase.onBegin = (bgioArguments) => {
207
+ const newBgioArguments = deserializeBgioArguments(bgioArguments)
208
+ doMoves(newBgioArguments, phaseRule.initialMoves, { game })
209
+ newBgioArguments.G._meta.currentPhaseHasBeenSetUp = true
210
+ newBgioArguments.G._meta.nextPhase = phaseRule.next
211
+ return JSON.parse(serialize(newBgioArguments.G));
212
+ }
213
+
214
+ if (phaseRule.endIf) {
215
+ phase.endIf = (bgioArguments) => {
216
+ const newBgioArguments = deserializeBgioArguments(bgioArguments)
217
+ if (newBgioArguments.G._meta.currentPhaseHasBeenSetUp) {
218
+ const result = getScenarioResults(newBgioArguments, phaseRule.endIf)
219
+ if (result) {
220
+ return result
221
+ }
222
+ }
223
+ }
224
+ }
225
+
226
+ phase.onEnd = ({ G }) => {
227
+ G._meta.currentPhaseHasBeenSetUp = false
228
+ G._meta.isAfterFirstPhase = true
229
+ }
230
+
231
+ return phase
232
+ }
233
+
234
+ function createMoves (moves, game) {
235
+ return Object.entries(moves).reduce((acc, [name, moveDefinition]) => ({
236
+ ...acc,
237
+ [name]: moveFactory({ ...moveDefinition, name }, game)
238
+ }), {})
239
+ }
@@ -0,0 +1,7 @@
1
+ import Move from "./move.js";
2
+
3
+ export default class EndTurn extends Move {
4
+ do(bgioArguments) {
5
+ bgioArguments.events.endTurn()
6
+ }
7
+ }
@@ -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,7 @@
1
+ import MoveEntity from './move-entity.js'
2
+
3
+ const moveIndex = {
4
+ MoveEntity,
5
+ }
6
+
7
+ export default moveIndex
@@ -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 '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))
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.type) {
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 '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
+ }
@@ -0,0 +1,7 @@
1
+ import Move from "./move.js";
2
+
3
+ export default class Pass extends Move {
4
+ do(bgioArguments) {
5
+ bgioArguments.events.endTurn()
6
+ }
7
+ }
@@ -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,13 @@
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
+ console.log('entity', entity)
7
+ console.log('state', state)
8
+ entity.state = {
9
+ ...entity.state,
10
+ [state.property]: state.value,
11
+ }
12
+ }
13
+ }
@@ -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
+ }