board-game-engine 0.0.11 → 1.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.
- package/README.md +138 -1
- package/dist/board-game-engine.cjs +35 -36
- package/dist/board-game-engine.js +35 -36
- package/dist/board-game-engine.min.js +20 -20
- package/dist/board-game-engine.mjs +35 -36
- package/e2e/coverage.js +33 -0
- package/package.json +2 -1
- package/playwright-report/index.html +1 -1
- package/src/client/client.js +41 -38
|
@@ -27062,7 +27062,7 @@ function createPayload(bgioState, moveRule, targets, context) {
|
|
|
27062
27062
|
var Client2 = class {
|
|
27063
27063
|
constructor(options) {
|
|
27064
27064
|
this.options = options;
|
|
27065
|
-
this.game = options.boardgameIOGame || gameFactory(JSON.parse(options.gameRules)
|
|
27065
|
+
this.game = options.boardgameIOGame || gameFactory(JSON.parse(options.gameRules));
|
|
27066
27066
|
if (!options.boardgameIOGame) {
|
|
27067
27067
|
this.moveBuilder = { targets: [], stepIndex: 0, eliminatedMoves: [] };
|
|
27068
27068
|
this.optimisticWinner = null;
|
|
@@ -27076,18 +27076,18 @@ var Client2 = class {
|
|
|
27076
27076
|
collapseOnLoad: true,
|
|
27077
27077
|
impl: Debug
|
|
27078
27078
|
},
|
|
27079
|
-
|
|
27080
|
-
|
|
27081
|
-
|
|
27082
|
-
|
|
27079
|
+
matchID,
|
|
27080
|
+
playerID,
|
|
27081
|
+
credentials,
|
|
27082
|
+
multiplayer = SocketIO({ server, socketOpts: { transports: ["websocket", "polling"] } })
|
|
27083
27083
|
} = this.options;
|
|
27084
27084
|
try {
|
|
27085
|
-
const clientOptions =
|
|
27085
|
+
const clientOptions = !credentials ? { game: this.game, numPlayers, debug } : {
|
|
27086
27086
|
game: this.game,
|
|
27087
|
-
multiplayer
|
|
27088
|
-
matchID
|
|
27089
|
-
playerID
|
|
27090
|
-
credentials
|
|
27087
|
+
multiplayer,
|
|
27088
|
+
matchID,
|
|
27089
|
+
playerID,
|
|
27090
|
+
credentials,
|
|
27091
27091
|
debug
|
|
27092
27092
|
};
|
|
27093
27093
|
this.client = Client(clientOptions);
|
|
@@ -27103,46 +27103,45 @@ var Client2 = class {
|
|
|
27103
27103
|
this.options.onClientUpdate?.();
|
|
27104
27104
|
}
|
|
27105
27105
|
getState() {
|
|
27106
|
-
const
|
|
27107
|
-
if (!
|
|
27106
|
+
const bgioState = this.client?.getState();
|
|
27107
|
+
if (!bgioState) return {};
|
|
27108
|
+
const state = this.options.boardgameIOGame ? bgioState : {
|
|
27109
|
+
...bgioState,
|
|
27110
|
+
G: deserialize(JSON.stringify(bgioState.G), registry)
|
|
27111
|
+
};
|
|
27112
|
+
const gameover = this.optimisticWinner ?? state?.ctx?.gameover;
|
|
27113
|
+
const currentMoves = gameover ? [] : getCurrentMoves(state, this.client);
|
|
27108
27114
|
if (this.options.boardgameIOGame) {
|
|
27109
27115
|
return {
|
|
27110
|
-
state
|
|
27111
|
-
gameover
|
|
27112
|
-
moves: this.client.moves
|
|
27116
|
+
state,
|
|
27117
|
+
gameover,
|
|
27118
|
+
moves: this.client.moves,
|
|
27119
|
+
currentMoves
|
|
27113
27120
|
};
|
|
27114
27121
|
}
|
|
27115
|
-
const
|
|
27116
|
-
...clientState,
|
|
27117
|
-
G: deserialize(JSON.stringify(clientState.G), registry),
|
|
27118
|
-
originalG: clientState.G
|
|
27119
|
-
};
|
|
27120
|
-
const gameover = state?.ctx?.gameover;
|
|
27121
|
-
const moves = !gameover ? Object.entries(getCurrentMoves(state, this.client)).reduce((acc, [moveName, rawMove]) => {
|
|
27122
|
+
const _wrappedMoves = Object.entries(currentMoves).reduce((acc, [moveName, rawMove]) => {
|
|
27122
27123
|
const move = (payload) => {
|
|
27123
27124
|
this.client.moves[moveName](preparePayload(payload));
|
|
27124
27125
|
};
|
|
27125
27126
|
move.moveInstance = rawMove.moveInstance;
|
|
27126
27127
|
return { ...acc, [moveName]: move };
|
|
27127
|
-
}, {})
|
|
27128
|
-
const
|
|
27129
|
-
|
|
27130
|
-
const possibleMoveMeta = possibleMoves.possibleMoveMeta;
|
|
27131
|
-
return { state, gameover, moves, allClickable, possibleMoveMeta };
|
|
27128
|
+
}, {});
|
|
27129
|
+
const { allClickable, _possibleMoveMeta } = getPossibleMoves(state, _wrappedMoves, this.moveBuilder);
|
|
27130
|
+
return { state, gameover, allClickable, _wrappedMoves, _possibleMoveMeta };
|
|
27132
27131
|
}
|
|
27133
27132
|
doStep(_target) {
|
|
27134
27133
|
if (this.options.boardgameIOGame) return;
|
|
27135
|
-
const { state,
|
|
27134
|
+
const { state, _wrappedMoves, _possibleMoveMeta } = this.getState();
|
|
27136
27135
|
const target = _target.abstract ? _target : state.G.bank.locate(_target.entityId);
|
|
27137
|
-
const newEliminated = Object.entries(
|
|
27138
|
-
if (newEliminated.length === Object.keys(
|
|
27136
|
+
const newEliminated = Object.entries(_possibleMoveMeta).filter(([_2, meta]) => !hasTarget(meta.clickableForMove, target)).map(([name]) => name).concat(this.moveBuilder.eliminatedMoves);
|
|
27137
|
+
if (newEliminated.length === Object.keys(_wrappedMoves).length) {
|
|
27139
27138
|
console.error("invalid move with target:", target?.rule);
|
|
27140
27139
|
return;
|
|
27141
27140
|
}
|
|
27142
|
-
const remainingMoveEntries = Object.entries(
|
|
27143
|
-
if (isMoveCompleted(state,
|
|
27141
|
+
const remainingMoveEntries = Object.entries(_possibleMoveMeta).filter(([name]) => !newEliminated.includes(name));
|
|
27142
|
+
if (isMoveCompleted(state, _wrappedMoves, remainingMoveEntries, this.moveBuilder.stepIndex)) {
|
|
27144
27143
|
const [moveName] = remainingMoveEntries[0];
|
|
27145
|
-
const move =
|
|
27144
|
+
const move = _wrappedMoves[moveName];
|
|
27146
27145
|
const payload = createPayload(
|
|
27147
27146
|
state,
|
|
27148
27147
|
move.moveInstance.rule,
|
|
@@ -27185,7 +27184,7 @@ function hasTarget(clickableSet, target) {
|
|
|
27185
27184
|
}
|
|
27186
27185
|
function getPossibleMoves(bgioState, moves, moveBuilder) {
|
|
27187
27186
|
const { eliminatedMoves, stepIndex } = moveBuilder;
|
|
27188
|
-
const
|
|
27187
|
+
const _possibleMoveMeta = {};
|
|
27189
27188
|
const allClickable = /* @__PURE__ */ new Set();
|
|
27190
27189
|
Object.entries(moves).filter(([moveName]) => !eliminatedMoves.includes(moveName)).forEach(([moveName, move]) => {
|
|
27191
27190
|
const moveRule = resolveProperties(bgioState, { ...move.moveInstance.rule, moveName });
|
|
@@ -27203,10 +27202,10 @@ function getPossibleMoves(bgioState, moves, moveBuilder) {
|
|
|
27203
27202
|
const clickableForMove = new Set(
|
|
27204
27203
|
moveIsAllowed && moveSteps?.[stepIndex]?.getClickable(context) || []
|
|
27205
27204
|
);
|
|
27206
|
-
|
|
27205
|
+
_possibleMoveMeta[moveName] = { clickableForMove };
|
|
27207
27206
|
clickableForMove.forEach((entity) => allClickable.add(entity));
|
|
27208
27207
|
});
|
|
27209
|
-
return {
|
|
27208
|
+
return { _possibleMoveMeta, allClickable };
|
|
27210
27209
|
}
|
|
27211
27210
|
function isMoveCompleted(state, moves, remainingMoveEntries, stepIndex) {
|
|
27212
27211
|
return remainingMoveEntries.length === 1 && getSteps(state, moves[remainingMoveEntries[0][0]].moveInstance.rule).length === stepIndex + 1;
|
package/e2e/coverage.js
CHANGED
|
@@ -7,8 +7,41 @@ import { test as base } from '@playwright/test'
|
|
|
7
7
|
import { mkdirSync, writeFileSync } from 'fs'
|
|
8
8
|
import { join } from 'path'
|
|
9
9
|
|
|
10
|
+
/** JSON.stringify with circular reference guard; returns a string safe to log. */
|
|
11
|
+
function safeStringify (value, seen = new WeakSet()) {
|
|
12
|
+
if (value === null) return 'null'
|
|
13
|
+
if (value === undefined) return 'undefined'
|
|
14
|
+
const t = typeof value
|
|
15
|
+
if (t !== 'object') return String(value)
|
|
16
|
+
if (seen.has(value)) return '[Circular]'
|
|
17
|
+
seen.add(value)
|
|
18
|
+
try {
|
|
19
|
+
if (Array.isArray(value)) {
|
|
20
|
+
return '[' + value.map((v) => safeStringify(v, seen)).join(', ') + ']'
|
|
21
|
+
}
|
|
22
|
+
const entries = Object.entries(value).map(([k, v]) => JSON.stringify(k) + ': ' + safeStringify(v, seen))
|
|
23
|
+
return '{ ' + entries.join(', ') + ' }'
|
|
24
|
+
} finally {
|
|
25
|
+
seen.delete(value)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
10
29
|
export const test = base.extend({
|
|
11
30
|
page: async ({ page }, use, testInfo) => {
|
|
31
|
+
if (process.env.DEBUG_CONSOLE && page) {
|
|
32
|
+
page.on('console', async (msg) => {
|
|
33
|
+
const type = msg.type()
|
|
34
|
+
const args = msg.args()
|
|
35
|
+
const values = await Promise.all(
|
|
36
|
+
args.map((arg) => arg.jsonValue().catch(() => '[Unserializable]'))
|
|
37
|
+
)
|
|
38
|
+
const formatted = values.map((v) => {
|
|
39
|
+
if (v !== null && typeof v === 'object') return safeStringify(v)
|
|
40
|
+
return v
|
|
41
|
+
})
|
|
42
|
+
console.log(`[browser ${type}]`, ...formatted)
|
|
43
|
+
})
|
|
44
|
+
}
|
|
12
45
|
await use(page)
|
|
13
46
|
if (!process.env.COVERAGE || !page) return
|
|
14
47
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "board-game-engine",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "wip board game engine",
|
|
5
5
|
"main": "dist/board-game-engine.cjs",
|
|
6
6
|
"module": "dist/board-game-engine.mjs",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"test:coverage": "npm run build:coverage && cross-env COVERAGE=1 playwright test && nyc merge .nyc_output/run .nyc_output/out.json && nyc report --reporter=html --reporter=text",
|
|
16
16
|
"build:coverage": "cross-env COVERAGE=1 node scripts/build.mjs",
|
|
17
17
|
"test:e2e": "playwright test",
|
|
18
|
+
"test:e2e:debug": "cross-env DEBUG_CONSOLE=1 playwright test",
|
|
18
19
|
"test:e2e:install": "playwright install firefox",
|
|
19
20
|
"serve": "http-server . -p 5174 -c-1",
|
|
20
21
|
"lint": "eslint .",
|
|
@@ -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/src/client/client.js
CHANGED
|
@@ -16,7 +16,7 @@ export class Client {
|
|
|
16
16
|
constructor (options) {
|
|
17
17
|
this.options = options
|
|
18
18
|
this.game = options.boardgameIOGame
|
|
19
|
-
|| gameFactory(JSON.parse(options.gameRules)
|
|
19
|
+
|| gameFactory(JSON.parse(options.gameRules))
|
|
20
20
|
|
|
21
21
|
if (!options.boardgameIOGame) {
|
|
22
22
|
this.moveBuilder = { targets: [], stepIndex: 0, eliminatedMoves: [] }
|
|
@@ -32,21 +32,21 @@ export class Client {
|
|
|
32
32
|
collapseOnLoad: true,
|
|
33
33
|
impl: Debug,
|
|
34
34
|
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
matchID,
|
|
36
|
+
playerID,
|
|
37
|
+
credentials,
|
|
38
|
+
multiplayer = SocketIO({ server, socketOpts: { transports: ['websocket', 'polling'] } }),
|
|
39
39
|
} = this.options
|
|
40
40
|
|
|
41
41
|
try {
|
|
42
|
-
const clientOptions =
|
|
42
|
+
const clientOptions = !credentials
|
|
43
43
|
? { game: this.game, numPlayers, debug }
|
|
44
44
|
: {
|
|
45
45
|
game: this.game,
|
|
46
|
-
multiplayer
|
|
47
|
-
matchID
|
|
48
|
-
playerID
|
|
49
|
-
credentials
|
|
46
|
+
multiplayer,
|
|
47
|
+
matchID,
|
|
48
|
+
playerID,
|
|
49
|
+
credentials,
|
|
50
50
|
debug,
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -65,67 +65,70 @@ export class Client {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
getState () {
|
|
68
|
-
const
|
|
69
|
-
if (!
|
|
68
|
+
const bgioState = this.client?.getState()
|
|
69
|
+
if (!bgioState) return {}
|
|
70
|
+
|
|
71
|
+
const state = this.options.boardgameIOGame
|
|
72
|
+
? bgioState
|
|
73
|
+
: {
|
|
74
|
+
...bgioState,
|
|
75
|
+
G: deserialize(JSON.stringify(bgioState.G), registry),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const gameover = this.optimisticWinner ?? state?.ctx?.gameover
|
|
79
|
+
|
|
80
|
+
const currentMoves = gameover
|
|
81
|
+
? []
|
|
82
|
+
: getCurrentMoves(state, this.client)
|
|
70
83
|
|
|
71
84
|
if (this.options.boardgameIOGame) {
|
|
72
85
|
return {
|
|
73
|
-
state
|
|
74
|
-
gameover
|
|
86
|
+
state,
|
|
87
|
+
gameover,
|
|
75
88
|
moves: this.client.moves,
|
|
89
|
+
currentMoves
|
|
76
90
|
}
|
|
77
91
|
}
|
|
78
92
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
G: deserialize(JSON.stringify(clientState.G), registry),
|
|
82
|
-
originalG: clientState.G,
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const gameover = state?.ctx?.gameover
|
|
86
|
-
|
|
87
|
-
const moves = !gameover
|
|
88
|
-
? Object.entries(getCurrentMoves(state, this.client)).reduce((acc, [moveName, rawMove]) => {
|
|
93
|
+
const _wrappedMoves = Object.entries(currentMoves)
|
|
94
|
+
.reduce((acc, [moveName, rawMove]) => {
|
|
89
95
|
const move = (payload) => {
|
|
90
96
|
this.client.moves[moveName](preparePayload(payload))
|
|
91
97
|
}
|
|
92
98
|
move.moveInstance = rawMove.moveInstance
|
|
93
99
|
return { ...acc, [moveName]: move }
|
|
94
100
|
}, {})
|
|
95
|
-
: []
|
|
96
101
|
|
|
97
|
-
const
|
|
98
|
-
const allClickable = possibleMoves.allClickable
|
|
99
|
-
const possibleMoveMeta = possibleMoves.possibleMoveMeta
|
|
102
|
+
const { allClickable, _possibleMoveMeta } = getPossibleMoves(state, _wrappedMoves, this.moveBuilder)
|
|
100
103
|
|
|
101
|
-
return { state, gameover,
|
|
104
|
+
return { state, gameover, allClickable, _wrappedMoves, _possibleMoveMeta }
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
doStep (_target) {
|
|
105
108
|
if (this.options.boardgameIOGame) return
|
|
106
109
|
|
|
107
|
-
const { state,
|
|
110
|
+
const { state, _wrappedMoves, _possibleMoveMeta } = this.getState()
|
|
108
111
|
|
|
109
112
|
const target = _target.abstract
|
|
110
113
|
? _target
|
|
111
114
|
: state.G.bank.locate(_target.entityId)
|
|
112
115
|
|
|
113
|
-
const newEliminated = Object.entries(
|
|
116
|
+
const newEliminated = Object.entries(_possibleMoveMeta)
|
|
114
117
|
.filter(([_, meta]) => !hasTarget(meta.clickableForMove, target))
|
|
115
118
|
.map(([name]) => name)
|
|
116
119
|
.concat(this.moveBuilder.eliminatedMoves);
|
|
117
120
|
|
|
118
|
-
if (newEliminated.length === Object.keys(
|
|
121
|
+
if (newEliminated.length === Object.keys(_wrappedMoves).length) {
|
|
119
122
|
console.error('invalid move with target:', target?.rule);
|
|
120
123
|
return;
|
|
121
124
|
}
|
|
122
125
|
|
|
123
|
-
const remainingMoveEntries = Object.entries(
|
|
126
|
+
const remainingMoveEntries = Object.entries(_possibleMoveMeta)
|
|
124
127
|
.filter(([name]) => !newEliminated.includes(name))
|
|
125
128
|
|
|
126
|
-
if (isMoveCompleted(state,
|
|
129
|
+
if (isMoveCompleted(state, _wrappedMoves, remainingMoveEntries, this.moveBuilder.stepIndex)) {
|
|
127
130
|
const [moveName] = remainingMoveEntries[0]
|
|
128
|
-
const move =
|
|
131
|
+
const move = _wrappedMoves[moveName]
|
|
129
132
|
const payload = createPayload(
|
|
130
133
|
state,
|
|
131
134
|
move.moveInstance.rule,
|
|
@@ -174,7 +177,7 @@ function hasTarget(clickableSet, target) {
|
|
|
174
177
|
|
|
175
178
|
function getPossibleMoves(bgioState, moves, moveBuilder) {
|
|
176
179
|
const { eliminatedMoves, stepIndex } = moveBuilder;
|
|
177
|
-
const
|
|
180
|
+
const _possibleMoveMeta = {};
|
|
178
181
|
const allClickable = new Set();
|
|
179
182
|
|
|
180
183
|
Object.entries(moves)
|
|
@@ -202,11 +205,11 @@ function getPossibleMoves(bgioState, moves, moveBuilder) {
|
|
|
202
205
|
(moveIsAllowed && moveSteps?.[stepIndex]?.getClickable(context)) || []
|
|
203
206
|
);
|
|
204
207
|
|
|
205
|
-
|
|
208
|
+
_possibleMoveMeta[moveName] = { clickableForMove };
|
|
206
209
|
clickableForMove.forEach(entity => allClickable.add(entity));
|
|
207
210
|
});
|
|
208
211
|
|
|
209
|
-
return {
|
|
212
|
+
return { _possibleMoveMeta, allClickable };
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
function isMoveCompleted(state, moves, remainingMoveEntries, stepIndex) {
|