board-game-engine 2.0.0 → 2.1.0
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.
- package/.github/workflows/ci.yml +33 -0
- package/README.md +2 -2
- package/dist/board-game-engine.cjs +463 -349
- package/dist/board-game-engine.js +463 -349
- package/dist/board-game-engine.min.js +22 -22
- package/dist/board-game-engine.mjs +461 -348
- package/dist/client/client.d.ts +76 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/game-factory/bank/bank-slot.d.ts +30 -0
- package/dist/game-factory/bank/bank-slot.d.ts.map +1 -0
- package/dist/game-factory/bank/bank.d.ts +34 -0
- package/dist/game-factory/bank/bank.d.ts.map +1 -0
- package/dist/game-factory/board.d.ts +4 -0
- package/dist/game-factory/board.d.ts.map +1 -0
- package/dist/game-factory/condition/condition-factory.d.ts +4 -0
- package/dist/game-factory/condition/condition-factory.d.ts.map +1 -0
- package/dist/game-factory/condition/condition.d.ts +13 -0
- package/dist/game-factory/condition/condition.d.ts.map +1 -0
- package/dist/game-factory/condition/contains-condition.d.ts +8 -0
- package/dist/game-factory/condition/contains-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/contains-same-condition.d.ts +7 -0
- package/dist/game-factory/condition/contains-same-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/evaluate-condition.d.ts +8 -0
- package/dist/game-factory/condition/evaluate-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/every-condition.d.ts +12 -0
- package/dist/game-factory/condition/every-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/has-line-condition.d.ts +8 -0
- package/dist/game-factory/condition/has-line-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/in-line-condition.d.ts +8 -0
- package/dist/game-factory/condition/in-line-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/is-condition.d.ts +8 -0
- package/dist/game-factory/condition/is-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/is-full-condition.d.ts +7 -0
- package/dist/game-factory/condition/is-full-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/no-possible-moves-condition.d.ts +7 -0
- package/dist/game-factory/condition/no-possible-moves-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/not-condition.d.ts +7 -0
- package/dist/game-factory/condition/not-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/or-condition.d.ts +7 -0
- package/dist/game-factory/condition/or-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/position-condition.d.ts +7 -0
- package/dist/game-factory/condition/position-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/some-condition.d.ts +8 -0
- package/dist/game-factory/condition/some-condition.d.ts.map +1 -0
- package/dist/game-factory/condition/would-condition.d.ts +8 -0
- package/dist/game-factory/condition/would-condition.d.ts.map +1 -0
- package/dist/game-factory/entity.d.ts +13 -0
- package/dist/game-factory/entity.d.ts.map +1 -0
- package/dist/game-factory/expand-game-rules.d.ts +3 -0
- package/dist/game-factory/expand-game-rules.d.ts.map +1 -0
- package/dist/game-factory/game-factory.d.ts +10 -0
- package/dist/game-factory/game-factory.d.ts.map +1 -0
- package/{src/game-factory/move/end-turn.js → dist/game-factory/move/end-turn.d.ts} +2 -4
- package/dist/game-factory/move/end-turn.d.ts.map +1 -0
- package/dist/game-factory/move/for-each.d.ts +5 -0
- package/dist/game-factory/move/for-each.d.ts.map +1 -0
- package/dist/game-factory/move/index.d.ts +6 -0
- package/dist/game-factory/move/index.d.ts.map +1 -0
- package/dist/game-factory/move/move-entity.d.ts +7 -0
- package/dist/game-factory/move/move-entity.d.ts.map +1 -0
- package/dist/game-factory/move/move-factory.d.ts +18 -0
- package/dist/game-factory/move/move-factory.d.ts.map +1 -0
- package/dist/game-factory/move/move.d.ts +54 -0
- package/dist/game-factory/move/move.d.ts.map +1 -0
- package/dist/game-factory/move/pass-turn.d.ts +5 -0
- package/dist/game-factory/move/pass-turn.d.ts.map +1 -0
- package/{src/game-factory/move/pass.js → dist/game-factory/move/pass.d.ts} +2 -4
- package/dist/game-factory/move/pass.d.ts.map +1 -0
- package/dist/game-factory/move/place-new.d.ts +5 -0
- package/dist/game-factory/move/place-new.d.ts.map +1 -0
- package/dist/game-factory/move/remove-entity.d.ts +5 -0
- package/dist/game-factory/move/remove-entity.d.ts.map +1 -0
- package/dist/game-factory/move/set-active-players.d.ts +5 -0
- package/dist/game-factory/move/set-active-players.d.ts.map +1 -0
- package/dist/game-factory/move/set-state.d.ts +5 -0
- package/dist/game-factory/move/set-state.d.ts.map +1 -0
- package/dist/game-factory/move/shuffle.d.ts +5 -0
- package/dist/game-factory/move/shuffle.d.ts.map +1 -0
- package/dist/game-factory/move/take-from.d.ts +11 -0
- package/dist/game-factory/move/take-from.d.ts.map +1 -0
- package/dist/game-factory/space/space.d.ts +10 -0
- package/dist/game-factory/space/space.d.ts.map +1 -0
- package/dist/game-factory/space-group/grid.d.ts +15 -0
- package/dist/game-factory/space-group/grid.d.ts.map +1 -0
- package/dist/game-factory/space-group/space-group.d.ts +20 -0
- package/dist/game-factory/space-group/space-group.d.ts.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/registry.d.ts +17 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/types/bagel-types.d.ts +339 -0
- package/dist/types/bagel-types.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/rule-with-conditions.d.ts +9 -0
- package/dist/types/rule-with-conditions.d.ts.map +1 -0
- package/dist/utils/any-valid-moves.d.ts +2 -0
- package/dist/utils/any-valid-moves.d.ts.map +1 -0
- package/dist/utils/bgio-resolve-types.d.ts +25 -0
- package/dist/utils/bgio-resolve-types.d.ts.map +1 -0
- package/dist/utils/check-conditions.d.ts +7 -0
- package/dist/utils/check-conditions.d.ts.map +1 -0
- package/dist/utils/create-payload.d.ts +5 -0
- package/dist/utils/create-payload.d.ts.map +1 -0
- package/dist/utils/deserialize-bgio-arguments.d.ts +3 -0
- package/dist/utils/deserialize-bgio-arguments.d.ts.map +1 -0
- package/dist/utils/do-moves.d.ts +8 -0
- package/dist/utils/do-moves.d.ts.map +1 -0
- package/dist/utils/entity-matches.d.ts +6 -0
- package/dist/utils/entity-matches.d.ts.map +1 -0
- package/dist/utils/find-met-condition.d.ts +6 -0
- package/dist/utils/find-met-condition.d.ts.map +1 -0
- package/dist/utils/get-current-moves.d.ts +24 -0
- package/dist/utils/get-current-moves.d.ts.map +1 -0
- package/dist/utils/get-scenario-results.d.ts +3 -0
- package/dist/utils/get-scenario-results.d.ts.map +1 -0
- package/dist/utils/get-steps.d.ts +13 -0
- package/dist/utils/get-steps.d.ts.map +1 -0
- package/dist/utils/get.d.ts +7 -0
- package/dist/utils/get.d.ts.map +1 -0
- package/dist/utils/grid-contains-sequence.d.ts +27 -0
- package/dist/utils/grid-contains-sequence.d.ts.map +1 -0
- package/dist/utils/json-transformer.d.ts +8 -0
- package/dist/utils/json-transformer.d.ts.map +1 -0
- package/dist/utils/prepare-payload.d.ts +2 -0
- package/dist/utils/prepare-payload.d.ts.map +1 -0
- package/dist/utils/resolve-entity.d.ts +3 -0
- package/dist/utils/resolve-entity.d.ts.map +1 -0
- package/dist/utils/resolve-expression.d.ts +6 -0
- package/dist/utils/resolve-expression.d.ts.map +1 -0
- package/dist/utils/resolve-properties.d.ts +4 -0
- package/dist/utils/resolve-properties.d.ts.map +1 -0
- package/dist/utils/simulate-move.d.ts +16 -0
- package/dist/utils/simulate-move.d.ts.map +1 -0
- package/e2e/fixtures/bge-checkers.html +1 -2
- package/e2e/fixtures/bge-minimal.html +1 -2
- package/e2e/fixtures/bge-ttt.html +1 -2
- package/examples/index.html +1 -1
- package/package.json +9 -3
- package/playwright-report/index.html +1 -1
- package/scripts/build.mjs +2 -2
- package/src/client/client.ts +305 -0
- package/src/game-factory/bank/bank-slot.ts +81 -0
- package/src/game-factory/bank/bank.ts +125 -0
- package/src/game-factory/{board.js → board.ts} +1 -1
- package/src/game-factory/condition/condition-factory.ts +59 -0
- package/src/game-factory/condition/condition.ts +50 -0
- package/src/game-factory/condition/{contains-condition.js → contains-condition.ts} +5 -4
- package/src/game-factory/condition/{contains-same-condition.js → contains-same-condition.ts} +8 -5
- package/src/game-factory/condition/{evaluate-condition.js → evaluate-condition.ts} +4 -3
- package/src/game-factory/condition/every-condition.ts +27 -0
- package/src/game-factory/condition/has-line-condition.ts +15 -0
- package/src/game-factory/condition/in-line-condition.ts +25 -0
- package/src/game-factory/condition/is-condition.ts +24 -0
- package/src/game-factory/condition/is-full-condition.ts +10 -0
- package/src/game-factory/condition/{no-possible-moves-condition.js → no-possible-moves-condition.ts} +3 -2
- package/src/game-factory/condition/{not-condition.js → not-condition.ts} +3 -2
- package/src/game-factory/condition/{or-condition.js → or-condition.ts} +3 -2
- package/src/game-factory/condition/position-condition.ts +13 -0
- package/src/game-factory/condition/{some-condition.js → some-condition.ts} +5 -3
- package/src/game-factory/condition/would-condition.ts +104 -0
- package/src/game-factory/entity.ts +37 -0
- package/src/game-factory/expand-game-rules.ts +263 -0
- package/src/game-factory/game-factory.ts +263 -0
- package/src/game-factory/move/end-turn.ts +7 -0
- package/src/game-factory/move/for-each.ts +20 -0
- package/src/game-factory/move/move-entity.ts +18 -0
- package/src/game-factory/move/move-factory.ts +107 -0
- package/src/game-factory/move/move.ts +147 -0
- package/src/game-factory/move/pass-turn.ts +15 -0
- package/src/game-factory/move/pass.ts +7 -0
- package/src/game-factory/move/place-new.ts +42 -0
- package/src/game-factory/move/remove-entity.ts +11 -0
- package/src/game-factory/move/set-active-players.ts +26 -0
- package/src/game-factory/move/set-state.ts +14 -0
- package/src/game-factory/move/shuffle.ts +9 -0
- package/src/game-factory/move/take-from.ts +12 -0
- package/src/game-factory/space/space.ts +36 -0
- package/src/game-factory/space-group/grid.ts +48 -0
- package/src/game-factory/space-group/space-group.ts +44 -0
- package/src/index.ts +5 -0
- package/src/types/bagel-types.ts +449 -0
- package/src/types/boardgame-io-core.d.ts +7 -0
- package/src/types/index.ts +70 -0
- package/src/types/rule-with-conditions.ts +9 -0
- package/src/utils/{any-valid-moves.js → any-valid-moves.ts} +54 -49
- package/src/utils/bgio-resolve-types.ts +27 -0
- package/src/utils/check-conditions.ts +28 -0
- package/src/utils/create-payload.ts +19 -0
- package/src/utils/deserialize-bgio-arguments.ts +10 -0
- package/src/utils/do-moves.ts +22 -0
- package/src/utils/entity-matches.ts +30 -0
- package/src/utils/expr-eval.d.ts +6 -0
- package/src/utils/find-met-condition.ts +23 -0
- package/src/utils/get-current-moves.ts +39 -0
- package/src/utils/get-scenario-results.ts +30 -0
- package/src/utils/get-steps.ts +38 -0
- package/src/utils/get.ts +28 -0
- package/src/utils/{grid-contains-sequence.js → grid-contains-sequence.ts} +71 -33
- package/src/utils/json-transformer.ts +17 -0
- package/src/utils/prepare-payload.ts +20 -0
- package/src/utils/resolve-entity.ts +15 -0
- package/src/utils/resolve-expression.ts +16 -0
- package/src/utils/resolve-properties.ts +172 -0
- package/src/utils/simulate-move.ts +32 -0
- package/src/wackson.d.ts +4 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +21 -0
- package/src/client/client.js +0 -224
- package/src/game-factory/bank/bank-slot.js +0 -69
- package/src/game-factory/bank/bank.js +0 -114
- package/src/game-factory/condition/condition-factory.js +0 -52
- package/src/game-factory/condition/condition.js +0 -39
- package/src/game-factory/condition/every-condition.js +0 -25
- package/src/game-factory/condition/has-line-condition.js +0 -14
- package/src/game-factory/condition/in-line-condition.js +0 -19
- package/src/game-factory/condition/is-condition.js +0 -23
- package/src/game-factory/condition/is-full-condition.js +0 -9
- package/src/game-factory/condition/position-condition.js +0 -12
- package/src/game-factory/condition/would-condition.js +0 -94
- package/src/game-factory/entity.js +0 -29
- package/src/game-factory/expand-game-rules.js +0 -271
- package/src/game-factory/game-factory.js +0 -239
- package/src/game-factory/move/for-each.js +0 -18
- package/src/game-factory/move/move-entity.js +0 -16
- package/src/game-factory/move/move-factory.js +0 -89
- package/src/game-factory/move/move.js +0 -131
- package/src/game-factory/move/pass-turn.js +0 -10
- package/src/game-factory/move/place-new.js +0 -33
- package/src/game-factory/move/remove-entity.js +0 -7
- package/src/game-factory/move/set-active-players.js +0 -23
- package/src/game-factory/move/set-state.js +0 -11
- package/src/game-factory/move/shuffle.js +0 -7
- package/src/game-factory/move/take-from.js +0 -7
- package/src/game-factory/space/space.js +0 -30
- package/src/game-factory/space-group/grid.js +0 -43
- package/src/game-factory/space-group/space-group.js +0 -29
- package/src/index.js +0 -2
- package/src/utils/check-conditions.js +0 -28
- package/src/utils/create-payload.js +0 -16
- package/src/utils/deserialize-bgio-arguments.js +0 -8
- package/src/utils/do-moves.js +0 -18
- package/src/utils/entity-matches.js +0 -20
- package/src/utils/find-met-condition.js +0 -22
- package/src/utils/get-current-moves.js +0 -12
- package/src/utils/get-scenario-results.js +0 -23
- package/src/utils/get-steps.js +0 -29
- package/src/utils/get.js +0 -25
- package/src/utils/json-transformer.js +0 -12
- package/src/utils/prepare-payload.js +0 -16
- package/src/utils/resolve-entity.js +0 -9
- package/src/utils/resolve-expression.js +0 -10
- package/src/utils/resolve-properties.js +0 -149
- package/src/utils/simulate-move.js +0 -25
- /package/src/game-factory/move/{index.js → index.ts} +0 -0
- /package/src/{registry.js → registry.ts} +0 -0
|
@@ -1,83 +1,83 @@
|
|
|
1
1
|
import isPlainObject from "lodash/isPlainObject.js";
|
|
2
|
+
import type { BgioResolveState } from "./bgio-resolve-types.js";
|
|
2
3
|
import resolveProperties from "./resolve-properties.js";
|
|
3
4
|
import resolveEntity from "./resolve-entity.js";
|
|
4
5
|
|
|
5
6
|
// Recursively find all contextPath references to moveArguments
|
|
6
|
-
function findMoveArgumentReferences(obj, refs = new Set()) {
|
|
7
|
-
if (!obj || typeof obj !==
|
|
7
|
+
function findMoveArgumentReferences (obj: unknown, refs = new Set<string>()): Set<string> {
|
|
8
|
+
if (!obj || typeof obj !== "object") {
|
|
8
9
|
return refs;
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (
|
|
13
|
-
if (
|
|
14
|
-
refs.add(
|
|
11
|
+
|
|
12
|
+
const o = obj as { type?: string; path?: unknown[] };
|
|
13
|
+
if (o.type === "contextPath" && Array.isArray(o.path)) {
|
|
14
|
+
if (o.path[0] === "moveArguments" && o.path[1]) {
|
|
15
|
+
refs.add(String(o.path[1]));
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
for (const value of Object.values(obj)) {
|
|
18
|
+
|
|
19
|
+
for (const value of Object.values(obj as object)) {
|
|
20
20
|
findMoveArgumentReferences(value, refs);
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
return refs;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Build a dependency graph and return topologically sorted argument names
|
|
27
|
-
function getArgumentOrder(ruleArguments) {
|
|
27
|
+
function getArgumentOrder (ruleArguments: Record<string, unknown>) {
|
|
28
28
|
const argNames = Object.keys(ruleArguments);
|
|
29
|
-
const graph = {};
|
|
30
|
-
const inDegree = {};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
argNames.forEach(name => {
|
|
29
|
+
const graph: Record<string, string[]> = {};
|
|
30
|
+
const inDegree: Record<string, number> = {};
|
|
31
|
+
|
|
32
|
+
argNames.forEach((name) => {
|
|
34
33
|
graph[name] = [];
|
|
35
34
|
inDegree[name] = 0;
|
|
36
35
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
argNames.forEach(argName => {
|
|
36
|
+
|
|
37
|
+
argNames.forEach((argName) => {
|
|
40
38
|
const arg = ruleArguments[argName];
|
|
41
39
|
const referencedArgs = findMoveArgumentReferences(arg);
|
|
42
|
-
|
|
43
|
-
referencedArgs.forEach(refArg => {
|
|
40
|
+
|
|
41
|
+
referencedArgs.forEach((refArg) => {
|
|
44
42
|
if (argNames.includes(refArg) && refArg !== argName) {
|
|
45
43
|
graph[refArg].push(argName);
|
|
46
44
|
inDegree[argName]++;
|
|
47
45
|
}
|
|
48
46
|
});
|
|
49
47
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
|
|
49
|
+
const queue = argNames.filter((name) => inDegree[name] === 0);
|
|
50
|
+
const sorted: string[] = [];
|
|
51
|
+
|
|
55
52
|
while (queue.length > 0) {
|
|
56
|
-
const current = queue.shift()
|
|
53
|
+
const current = queue.shift()!;
|
|
57
54
|
sorted.push(current);
|
|
58
|
-
|
|
59
|
-
graph[current].forEach(neighbor => {
|
|
55
|
+
|
|
56
|
+
graph[current].forEach((neighbor) => {
|
|
60
57
|
inDegree[neighbor]--;
|
|
61
58
|
if (inDegree[neighbor] === 0) {
|
|
62
59
|
queue.push(neighbor);
|
|
63
60
|
}
|
|
64
61
|
});
|
|
65
62
|
}
|
|
66
|
-
|
|
67
|
-
// If not all nodes processed, there's a cycle - fall back to original order
|
|
63
|
+
|
|
68
64
|
return sorted.length === argNames.length ? sorted : argNames;
|
|
69
65
|
}
|
|
70
66
|
|
|
67
|
+
type MoveEntry = {
|
|
68
|
+
moveInstance: { isValid: (a: unknown, p: unknown, c: unknown) => boolean; rule: unknown };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
71
|
// Recursively try to build a valid argument combination
|
|
72
|
-
function findValidCombination(
|
|
73
|
-
bgioArguments,
|
|
74
|
-
moveInstance,
|
|
75
|
-
ruleArguments,
|
|
76
|
-
orderedArgNames,
|
|
77
|
-
context,
|
|
72
|
+
function findValidCombination (
|
|
73
|
+
bgioArguments: BgioResolveState,
|
|
74
|
+
moveInstance: MoveEntry["moveInstance"],
|
|
75
|
+
ruleArguments: Record<string, unknown>,
|
|
76
|
+
orderedArgNames: string[],
|
|
77
|
+
context: Record<string, unknown>,
|
|
78
78
|
index = 0,
|
|
79
|
-
currentArgs = {}
|
|
80
|
-
) {
|
|
79
|
+
currentArgs: Record<string, unknown> = {}
|
|
80
|
+
): boolean {
|
|
81
81
|
// Base case: all arguments resolved
|
|
82
82
|
if (index === orderedArgNames.length) {
|
|
83
83
|
const resolvedPayload = { arguments: currentArgs };
|
|
@@ -98,7 +98,7 @@ function findValidCombination(
|
|
|
98
98
|
const matches = isPlainObject(arg)
|
|
99
99
|
? resolveEntity(
|
|
100
100
|
bgioArguments,
|
|
101
|
-
{ ...arg, matchMultiple: true },
|
|
101
|
+
{ ...(arg as Record<string, unknown>), matchMultiple: true },
|
|
102
102
|
updatedContext,
|
|
103
103
|
argName
|
|
104
104
|
)
|
|
@@ -112,7 +112,7 @@ function findValidCombination(
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
// Try each possible value (short-circuits on first success)
|
|
115
|
-
return matchArray.some(value => {
|
|
115
|
+
return matchArray.some((value: unknown) => {
|
|
116
116
|
return findValidCombination(
|
|
117
117
|
bgioArguments,
|
|
118
118
|
moveInstance,
|
|
@@ -125,21 +125,26 @@ function findValidCombination(
|
|
|
125
125
|
});
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
export default function areThereValidMoves(
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
export default function areThereValidMoves (
|
|
129
|
+
bgioArguments: unknown,
|
|
130
|
+
moves: Record<string, unknown>
|
|
131
|
+
) {
|
|
132
|
+
const bgio = bgioArguments as BgioResolveState;
|
|
133
|
+
return Object.values(moves).some((move) => {
|
|
134
|
+
const moveInstance = (move as MoveEntry | undefined)?.moveInstance;
|
|
135
|
+
if (!moveInstance) return false;
|
|
131
136
|
const context = { moveInstance };
|
|
132
137
|
const rule = resolveProperties(
|
|
133
|
-
|
|
138
|
+
bgio,
|
|
134
139
|
moveInstance.rule,
|
|
135
140
|
context
|
|
136
|
-
);
|
|
141
|
+
) as { arguments?: Record<string, unknown> };
|
|
137
142
|
|
|
138
143
|
const ruleArguments = rule.arguments ?? {};
|
|
139
144
|
|
|
140
145
|
// If no arguments required, just check if move is valid
|
|
141
146
|
if (Object.keys(ruleArguments).length === 0) {
|
|
142
|
-
return moveInstance.isValid(
|
|
147
|
+
return moveInstance.isValid(bgio, { arguments: {} }, context);
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
// Get dependency-ordered argument names
|
|
@@ -147,7 +152,7 @@ export default function areThereValidMoves(bgioArguments, moves) {
|
|
|
147
152
|
|
|
148
153
|
// Recursively search for any valid combination (short-circuits on first valid)
|
|
149
154
|
return findValidCombination(
|
|
150
|
-
|
|
155
|
+
bgio,
|
|
151
156
|
moveInstance,
|
|
152
157
|
ruleArguments,
|
|
153
158
|
orderedArgNames,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RuleWithConditions } from "../types/rule-with-conditions.js";
|
|
2
|
+
|
|
3
|
+
/** boardgame.io match args + engine `G` after setup (deserialized, many fields). */
|
|
4
|
+
export type BgioResolveState = {
|
|
5
|
+
ctx: Record<string, unknown>;
|
|
6
|
+
G: Record<string, unknown>;
|
|
7
|
+
playerID?: string;
|
|
8
|
+
} & Record<string, unknown>;
|
|
9
|
+
|
|
10
|
+
/** Bank API on `G` used by resolution and moves (subset of {@link Bank} methods). */
|
|
11
|
+
export type BankApi = {
|
|
12
|
+
find: (a: unknown, t: unknown, c: unknown) => unknown;
|
|
13
|
+
findAll: (a: unknown, t: unknown, c: unknown) => unknown[];
|
|
14
|
+
findParent: (t: unknown) => unknown;
|
|
15
|
+
getOne: (a: BgioResolveState, rule: RuleWithConditions & { state?: unknown }, c: Record<string, unknown>) => unknown;
|
|
16
|
+
getMultiple: (
|
|
17
|
+
a: BgioResolveState,
|
|
18
|
+
rule: RuleWithConditions & { state?: unknown },
|
|
19
|
+
count: number,
|
|
20
|
+
c: Record<string, unknown>
|
|
21
|
+
) => unknown[];
|
|
22
|
+
returnToBank: (a: BgioResolveState, entity: { entityId: number; rule: Record<string, unknown> }) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function bankOf (bg: BgioResolveState): BankApi {
|
|
26
|
+
return (bg.G as { bank: BankApi }).bank;
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import conditionFactory from "../game-factory/condition/condition-factory.js";
|
|
2
|
+
import type { Condition as ConditionRule } from "../types/bagel-types.js";
|
|
3
|
+
|
|
4
|
+
export default function checkConditions (
|
|
5
|
+
bgioArguments: unknown,
|
|
6
|
+
conditions: ConditionRule[] = [],
|
|
7
|
+
payload: Record<string, unknown> = {},
|
|
8
|
+
context: Record<string, unknown> = {}
|
|
9
|
+
) {
|
|
10
|
+
const results: unknown[] = [];
|
|
11
|
+
let failedAt: unknown;
|
|
12
|
+
for (const conditionRule of conditions) {
|
|
13
|
+
const result = conditionFactory(conditionRule as ConditionRule)!
|
|
14
|
+
.check(bgioArguments, payload, context);
|
|
15
|
+
if (!(result as { conditionIsMet: boolean }).conditionIsMet) {
|
|
16
|
+
failedAt = conditionRule;
|
|
17
|
+
break;
|
|
18
|
+
} else {
|
|
19
|
+
results.push(result);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
results,
|
|
25
|
+
failedAt,
|
|
26
|
+
conditionsAreMet: results.length === conditions.length,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import getSteps from "./get-steps.js";
|
|
2
|
+
|
|
3
|
+
export default function createPayload (
|
|
4
|
+
bgioState: Parameters<typeof getSteps>[0],
|
|
5
|
+
moveRule: Parameters<typeof getSteps>[1],
|
|
6
|
+
targets: unknown[],
|
|
7
|
+
_context: Record<string, unknown>
|
|
8
|
+
) {
|
|
9
|
+
const argNames = getSteps(
|
|
10
|
+
bgioState,
|
|
11
|
+
moveRule
|
|
12
|
+
).map((s) => s.argName);
|
|
13
|
+
return {
|
|
14
|
+
arguments: targets.reduce<Record<string, unknown>>((acc, target, i) => ({
|
|
15
|
+
...acc,
|
|
16
|
+
[argNames[i]]: target,
|
|
17
|
+
}), {}),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { deserialize } from "wackson";
|
|
2
|
+
import { registry } from "../registry.js";
|
|
3
|
+
import type { BgioResolveState } from "./bgio-resolve-types.js";
|
|
4
|
+
|
|
5
|
+
export default function deserializeBgioArguments (bgioArguments: BgioResolveState): BgioResolveState {
|
|
6
|
+
return {
|
|
7
|
+
...bgioArguments,
|
|
8
|
+
G: deserialize(JSON.stringify(bgioArguments.G), registry) as Record<string, unknown>,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { MoveDefinition } from "../types/bagel-types.js";
|
|
2
|
+
import moveFactory from "../game-factory/move/move-factory.js";
|
|
3
|
+
|
|
4
|
+
export default function doMoves (
|
|
5
|
+
bgioArguments: { G: unknown; [k: string]: unknown },
|
|
6
|
+
moves: MoveDefinition[] = [],
|
|
7
|
+
context: { game: Record<string, unknown> } & Record<string, unknown>
|
|
8
|
+
) {
|
|
9
|
+
if (!moves?.length) {
|
|
10
|
+
return bgioArguments.G;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
moves.forEach((moveRule) => {
|
|
14
|
+
moveFactory(moveRule, context.game).moveInstance!.doMove(
|
|
15
|
+
bgioArguments,
|
|
16
|
+
undefined,
|
|
17
|
+
context
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return bgioArguments.G;
|
|
22
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import matches from "lodash/matches.js";
|
|
2
|
+
import resolveProperties from "./resolve-properties.js";
|
|
3
|
+
import type { BgioResolveState } from "./bgio-resolve-types.js";
|
|
4
|
+
|
|
5
|
+
function resolveMatcher (
|
|
6
|
+
bgioArguments: BgioResolveState,
|
|
7
|
+
matcher: Record<string, unknown>,
|
|
8
|
+
context: Record<string, unknown>
|
|
9
|
+
) {
|
|
10
|
+
const resolvedMatcher = { ...matcher };
|
|
11
|
+
delete resolvedMatcher.state;
|
|
12
|
+
delete resolvedMatcher.stateGroups;
|
|
13
|
+
return resolveProperties(bgioArguments, resolvedMatcher, context);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getEntityMatcher (entity: { rule: Record<string, unknown>; state?: Record<string, unknown> }) {
|
|
17
|
+
return {
|
|
18
|
+
...entity.rule,
|
|
19
|
+
...entity.state,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default function entityMatches (
|
|
24
|
+
bgioArguments: BgioResolveState,
|
|
25
|
+
matcher: Record<string, unknown>,
|
|
26
|
+
entity: { rule: Record<string, unknown>; state?: Record<string, unknown> },
|
|
27
|
+
context: Record<string, unknown>
|
|
28
|
+
): boolean {
|
|
29
|
+
return matches(resolveMatcher(bgioArguments, matcher, context))(getEntityMatcher(entity));
|
|
30
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import conditionFactory from "../game-factory/condition/condition-factory.js";
|
|
2
|
+
import type { Condition as ConditionRule } from "../types/bagel-types.js";
|
|
3
|
+
|
|
4
|
+
export default function findMetCondition (
|
|
5
|
+
bgioArguments: unknown,
|
|
6
|
+
conditions: ConditionRule[] = [],
|
|
7
|
+
payload: Record<string, unknown>,
|
|
8
|
+
context: Record<string, unknown>,
|
|
9
|
+
) {
|
|
10
|
+
let success: { conditionRule: unknown; [k: string]: unknown } | undefined;
|
|
11
|
+
for (const conditionRule of conditions) {
|
|
12
|
+
const result = conditionFactory(conditionRule as ConditionRule)!
|
|
13
|
+
.check(bgioArguments, payload, context);
|
|
14
|
+
if ((result as { conditionIsMet: boolean }).conditionIsMet) {
|
|
15
|
+
success = {
|
|
16
|
+
...result as object,
|
|
17
|
+
conditionRule,
|
|
18
|
+
};
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return success;
|
|
23
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal shape of the boardgame.io client (or stand-in) used to resolve the active move set.
|
|
3
|
+
*/
|
|
4
|
+
export interface GetCurrentMovesClient {
|
|
5
|
+
game: {
|
|
6
|
+
phases?: Record<string, unknown>;
|
|
7
|
+
moves?: Record<string, unknown>;
|
|
8
|
+
turn?: { stages?: Record<string, unknown> };
|
|
9
|
+
};
|
|
10
|
+
playerID?: string;
|
|
11
|
+
stageName?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** `state` shape expected by {@link getCurrentMoves} (boardgame.io match state). */
|
|
15
|
+
export interface GetCurrentMovesState {
|
|
16
|
+
ctx: {
|
|
17
|
+
phase?: string;
|
|
18
|
+
activePlayers?: Record<string, string>;
|
|
19
|
+
currentPlayer?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// get the most specific set of moves for current stage/phase
|
|
24
|
+
// this will probably all break for complex stages with multiple active players
|
|
25
|
+
export default function getCurrentMoves (
|
|
26
|
+
state: GetCurrentMovesState,
|
|
27
|
+
client: GetCurrentMovesClient
|
|
28
|
+
): Record<string, unknown> {
|
|
29
|
+
const { game, playerID, stageName } = client;
|
|
30
|
+
const phaseName = state.ctx.phase;
|
|
31
|
+
|
|
32
|
+
const stageNameToUse = stageName ?? state.ctx.activePlayers?.[playerID ?? state.ctx.currentPlayer ?? ""];
|
|
33
|
+
const phaseOrRoot =
|
|
34
|
+
(game as { phases?: Record<string, unknown> }).phases?.[phaseName as string] ?? game;
|
|
35
|
+
const stageOrPhaseOrRoot =
|
|
36
|
+
(phaseOrRoot as { turn?: { stages?: Record<string, unknown> } }).turn?.stages?.[stageNameToUse as string] ?? phaseOrRoot;
|
|
37
|
+
|
|
38
|
+
return (stageOrPhaseOrRoot as { moves?: Record<string, unknown> }).moves ?? {};
|
|
39
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import checkConditions from "./check-conditions.js";
|
|
2
|
+
import resolveProperties from "./resolve-properties.js";
|
|
3
|
+
import type { BgioResolveState } from "./bgio-resolve-types.js";
|
|
4
|
+
import type { Condition } from "../types/bagel-types.js";
|
|
5
|
+
export default function getScenarioResults (
|
|
6
|
+
bgioArguments: BgioResolveState,
|
|
7
|
+
scenarios: unknown[]
|
|
8
|
+
) {
|
|
9
|
+
let match: { scenario: { result?: unknown; [k: string]: unknown }; conditionResults: { results: unknown[] } } | undefined;
|
|
10
|
+
for (const scenario of scenarios) {
|
|
11
|
+
const conditionResults = checkConditions(
|
|
12
|
+
bgioArguments,
|
|
13
|
+
(scenario as { conditions?: Condition[] }).conditions
|
|
14
|
+
);
|
|
15
|
+
if (conditionResults.conditionsAreMet) {
|
|
16
|
+
match = { scenario: scenario as { result?: unknown }, conditionResults };
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (match?.scenario?.result) {
|
|
22
|
+
return resolveProperties(
|
|
23
|
+
bgioArguments,
|
|
24
|
+
match.scenario.result,
|
|
25
|
+
{ results: match.conditionResults.results }
|
|
26
|
+
);
|
|
27
|
+
} else {
|
|
28
|
+
return match;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { bankOf, type BgioResolveState } from "./bgio-resolve-types.js";
|
|
2
|
+
|
|
3
|
+
// controls order of what players need to click first
|
|
4
|
+
const argNamesMap: Record<string, string[]> = {
|
|
5
|
+
PlaceNew: ["destination"],
|
|
6
|
+
RemoveEntity: ["entity"],
|
|
7
|
+
MoveEntity: ["entity", "destination"],
|
|
8
|
+
TakeFrom: ["source", "destination"],
|
|
9
|
+
SetState: ["entity", "state"],
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// this might not be where special handling for setstate wants to live
|
|
13
|
+
export default function getSteps (
|
|
14
|
+
bgioState: BgioResolveState,
|
|
15
|
+
moveRule: {
|
|
16
|
+
moveType: string;
|
|
17
|
+
arguments: Record<string, { playerChoice?: boolean; possibleValues?: unknown[]; [k: string]: unknown }>;
|
|
18
|
+
}
|
|
19
|
+
) {
|
|
20
|
+
const names = argNamesMap[moveRule.moveType];
|
|
21
|
+
if (!names) return [];
|
|
22
|
+
return names
|
|
23
|
+
.filter((argName) => moveRule.arguments[argName]?.playerChoice)
|
|
24
|
+
.map((argName) => ({
|
|
25
|
+
argName,
|
|
26
|
+
getClickable: argName === "state"
|
|
27
|
+
? () => (moveRule.arguments[argName].possibleValues as unknown[]).map((value) => ({
|
|
28
|
+
abstract: true,
|
|
29
|
+
...moveRule.arguments[argName],
|
|
30
|
+
value,
|
|
31
|
+
}))
|
|
32
|
+
: (context: Record<string, unknown>) => bankOf(bgioState).findAll(
|
|
33
|
+
bgioState,
|
|
34
|
+
moveRule.arguments[argName],
|
|
35
|
+
context
|
|
36
|
+
),
|
|
37
|
+
}));
|
|
38
|
+
}
|
package/src/utils/get.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
type PathStep = string | number | { flatten: boolean; map?: (string | number)[] };
|
|
2
|
+
|
|
3
|
+
export default function get (obj: unknown, pathArray: PathStep[]): unknown {
|
|
4
|
+
let current: unknown = obj;
|
|
5
|
+
|
|
6
|
+
for (const step of pathArray) {
|
|
7
|
+
if (current === undefined) {
|
|
8
|
+
return current;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (step && typeof step === "object" && "flatten" in step && step.flatten) {
|
|
12
|
+
if (!Array.isArray(current)) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let flat = current.flat() as unknown[];
|
|
17
|
+
|
|
18
|
+
if (step.map) {
|
|
19
|
+
flat = flat.map((item) => get(item, step.map!));
|
|
20
|
+
}
|
|
21
|
+
current = flat;
|
|
22
|
+
} else {
|
|
23
|
+
current = (current as Record<string | number, unknown>)[step as string | number];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return current;
|
|
28
|
+
}
|