@mnbroatch/boardgame.io 0.0.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.
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/ai/package.json +7 -0
- package/client/package.json +7 -0
- package/core/package.json +7 -0
- package/debug/package.json +7 -0
- package/dist/boardgameio.es.js +14238 -0
- package/dist/boardgameio.js +14277 -0
- package/dist/boardgameio.min.js +16 -0
- package/dist/cjs/Debug-9d141c06.js +9586 -0
- package/dist/cjs/ai-e0e8a768.js +377 -0
- package/dist/cjs/ai.js +20 -0
- package/dist/cjs/client-76dec77b.js +258 -0
- package/dist/cjs/client-a22d7500.js +524 -0
- package/dist/cjs/client.js +26 -0
- package/dist/cjs/core.js +52 -0
- package/dist/cjs/debug.js +18 -0
- package/dist/cjs/filter-player-view-bb02e2f6.js +89 -0
- package/dist/cjs/initialize-267fcd69.js +61 -0
- package/dist/cjs/internal.js +25 -0
- package/dist/cjs/master-2904879d.js +320 -0
- package/dist/cjs/master.js +18 -0
- package/dist/cjs/multiplayer.js +23 -0
- package/dist/cjs/plugin-random-7425844d.js +229 -0
- package/dist/cjs/plugins.js +59 -0
- package/dist/cjs/react-native.js +182 -0
- package/dist/cjs/react.js +727 -0
- package/dist/cjs/reducer-16eec232.js +1203 -0
- package/dist/cjs/server.js +4087 -0
- package/dist/cjs/socketio-7a0837eb.js +478 -0
- package/dist/cjs/testing.js +30 -0
- package/dist/cjs/transport-b1874dfa.js +37 -0
- package/dist/cjs/turn-order-b2ff8740.js +1136 -0
- package/dist/cjs/util-fcfd8fb8.js +140 -0
- package/dist/esm/Debug-0141fe2d.js +9577 -0
- package/dist/esm/ai-5c06e761.js +371 -0
- package/dist/esm/ai.js +8 -0
- package/dist/esm/client-2e653027.js +522 -0
- package/dist/esm/client-5f57c3f2.js +255 -0
- package/dist/esm/client.js +16 -0
- package/dist/esm/core.js +40 -0
- package/dist/esm/debug.js +10 -0
- package/dist/esm/filter-player-view-2c6cc96f.js +87 -0
- package/dist/esm/initialize-11d626ca.js +59 -0
- package/dist/esm/internal.js +10 -0
- package/dist/esm/master-fa8f2e43.js +318 -0
- package/dist/esm/master.js +10 -0
- package/dist/esm/multiplayer.js +14 -0
- package/dist/esm/plugin-random-087f861e.js +226 -0
- package/dist/esm/plugins.js +55 -0
- package/dist/esm/react-native.js +173 -0
- package/dist/esm/react.js +716 -0
- package/dist/esm/reducer-c46da7e5.js +1198 -0
- package/dist/esm/socketio-c22ffa65.js +455 -0
- package/dist/esm/testing.js +26 -0
- package/dist/esm/transport-ce07b771.js +35 -0
- package/dist/esm/turn-order-376d315e.js +1091 -0
- package/dist/esm/util-b6147cef.js +135 -0
- package/dist/types/packages/ai.d.ts +5 -0
- package/dist/types/packages/client.d.ts +3 -0
- package/dist/types/packages/core.d.ts +5 -0
- package/dist/types/packages/debug.d.ts +2 -0
- package/dist/types/packages/internal.d.ts +8 -0
- package/dist/types/packages/master.d.ts +2 -0
- package/dist/types/packages/multiplayer.d.ts +3 -0
- package/dist/types/packages/plugins.d.ts +3 -0
- package/dist/types/packages/react-native.d.ts +2 -0
- package/dist/types/packages/react.d.ts +3 -0
- package/dist/types/packages/server.d.ts +6 -0
- package/dist/types/packages/testing.d.ts +1 -0
- package/dist/types/src/ai/ai.d.ts +53 -0
- package/dist/types/src/ai/ai.test.d.ts +1 -0
- package/dist/types/src/ai/bot.d.ts +40 -0
- package/dist/types/src/ai/mcts-bot.d.ts +60 -0
- package/dist/types/src/ai/random-bot.d.ts +27 -0
- package/dist/types/src/client/client.d.ts +104 -0
- package/dist/types/src/client/client.test.d.ts +1 -0
- package/dist/types/src/client/debug/tests/debug.test.d.ts +1 -0
- package/dist/types/src/client/manager.d.ts +61 -0
- package/dist/types/src/client/react.d.ts +75 -0
- package/dist/types/src/client/react.ssr.test.d.ts +4 -0
- package/dist/types/src/client/react.test.d.ts +1 -0
- package/dist/types/src/client/transport/dummy.d.ts +18 -0
- package/dist/types/src/client/transport/local.d.ts +59 -0
- package/dist/types/src/client/transport/local.test.d.ts +1 -0
- package/dist/types/src/client/transport/socketio.d.ts +45 -0
- package/dist/types/src/client/transport/socketio.test.d.ts +1 -0
- package/dist/types/src/client/transport/transport.d.ts +50 -0
- package/dist/types/src/client/transport/transport.test.d.ts +1 -0
- package/dist/types/src/core/action-creators.d.ts +144 -0
- package/dist/types/src/core/action-types.d.ts +10 -0
- package/dist/types/src/core/backwards-compatibility.d.ts +12 -0
- package/dist/types/src/core/constants.d.ts +6 -0
- package/dist/types/src/core/errors.d.ts +15 -0
- package/dist/types/src/core/flow.d.ts +28 -0
- package/dist/types/src/core/flow.test.d.ts +1 -0
- package/dist/types/src/core/game-methods.d.ts +9 -0
- package/dist/types/src/core/game.d.ts +26 -0
- package/dist/types/src/core/game.test.d.ts +1 -0
- package/dist/types/src/core/initialize.d.ts +9 -0
- package/dist/types/src/core/logger.d.ts +2 -0
- package/dist/types/src/core/player-view.d.ts +7 -0
- package/dist/types/src/core/player-view.test.d.ts +1 -0
- package/dist/types/src/core/reducer.d.ts +155 -0
- package/dist/types/src/core/reducer.test.d.ts +1 -0
- package/dist/types/src/core/turn-order.d.ts +179 -0
- package/dist/types/src/core/turn-order.test.d.ts +8 -0
- package/dist/types/src/lobby/client.d.ts +194 -0
- package/dist/types/src/lobby/client.test.d.ts +1 -0
- package/dist/types/src/lobby/connection.d.ts +44 -0
- package/dist/types/src/lobby/connection.test.d.ts +1 -0
- package/dist/types/src/lobby/create-match-form.d.ts +26 -0
- package/dist/types/src/lobby/login-form.d.ts +23 -0
- package/dist/types/src/lobby/match-instance.d.ts +31 -0
- package/dist/types/src/lobby/react.d.ts +113 -0
- package/dist/types/src/lobby/react.ssr.test.d.ts +4 -0
- package/dist/types/src/lobby/react.test.d.ts +1 -0
- package/dist/types/src/master/filter-player-view.d.ts +96 -0
- package/dist/types/src/master/filter-player-view.test.d.ts +1 -0
- package/dist/types/src/master/master.d.ts +94 -0
- package/dist/types/src/master/master.test.d.ts +1 -0
- package/dist/types/src/plugins/events/events.d.ts +54 -0
- package/dist/types/src/plugins/events/events.test.d.ts +1 -0
- package/dist/types/src/plugins/main.d.ts +75 -0
- package/dist/types/src/plugins/main.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-events.d.ts +5 -0
- package/dist/types/src/plugins/plugin-immer.d.ts +7 -0
- package/dist/types/src/plugins/plugin-immer.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-log.d.ts +14 -0
- package/dist/types/src/plugins/plugin-log.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-player.d.ts +29 -0
- package/dist/types/src/plugins/plugin-player.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-random.d.ts +4 -0
- package/dist/types/src/plugins/plugin-serializable.d.ts +7 -0
- package/dist/types/src/plugins/plugin-serializable.test.d.ts +1 -0
- package/dist/types/src/plugins/random/random.alea.d.ts +19 -0
- package/dist/types/src/plugins/random/random.d.ts +54 -0
- package/dist/types/src/plugins/random/random.test.d.ts +1 -0
- package/dist/types/src/server/api.d.ts +13 -0
- package/dist/types/src/server/api.test.d.ts +1 -0
- package/dist/types/src/server/auth.d.ts +38 -0
- package/dist/types/src/server/auth.test.d.ts +1 -0
- package/dist/types/src/server/cors.d.ts +4 -0
- package/dist/types/src/server/cors.test.d.ts +1 -0
- package/dist/types/src/server/db/base.d.ts +192 -0
- package/dist/types/src/server/db/flatfile.d.ts +44 -0
- package/dist/types/src/server/db/flatfile.test.d.ts +1 -0
- package/dist/types/src/server/db/index.d.ts +4 -0
- package/dist/types/src/server/db/index.test.d.ts +1 -0
- package/dist/types/src/server/db/inmemory.d.ts +43 -0
- package/dist/types/src/server/db/inmemory.test.d.ts +1 -0
- package/dist/types/src/server/db/localstorage.d.ts +7 -0
- package/dist/types/src/server/db/localstorage.test.d.ts +1 -0
- package/dist/types/src/server/index.d.ts +68 -0
- package/dist/types/src/server/index.test.d.ts +1 -0
- package/dist/types/src/server/transport/pubsub/generic-pub-sub.d.ts +6 -0
- package/dist/types/src/server/transport/pubsub/in-memory-pub-sub.d.ts +7 -0
- package/dist/types/src/server/transport/pubsub/in-memory-pub-sub.test.d.ts +1 -0
- package/dist/types/src/server/transport/socketio-simultaneous.test.d.ts +1 -0
- package/dist/types/src/server/transport/socketio.d.ts +65 -0
- package/dist/types/src/server/transport/socketio.test.d.ts +1 -0
- package/dist/types/src/server/util.d.ts +35 -0
- package/dist/types/src/testing/mock-random.d.ts +15 -0
- package/dist/types/src/testing/mock-random.test.d.ts +1 -0
- package/dist/types/src/types.d.ts +387 -0
- package/internal/package.json +7 -0
- package/master/package.json +7 -0
- package/multiplayer/package.json +7 -0
- package/package.json +211 -0
- package/plugins/package.json +7 -0
- package/react/package.json +7 -0
- package/react-native/package.json +7 -0
- package/server/package.json +6 -0
- package/src/ai/ai.test.ts +433 -0
- package/src/ai/ai.ts +84 -0
- package/src/ai/bot.ts +122 -0
- package/src/ai/mcts-bot.ts +331 -0
- package/src/ai/random-bot.ts +20 -0
- package/src/client/client.test.ts +993 -0
- package/src/client/client.ts +588 -0
- package/src/client/debug/Debug.svelte +239 -0
- package/src/client/debug/Menu.svelte +65 -0
- package/src/client/debug/ai/AI.svelte +215 -0
- package/src/client/debug/ai/Options.svelte +48 -0
- package/src/client/debug/info/Info.svelte +22 -0
- package/src/client/debug/info/Item.svelte +24 -0
- package/src/client/debug/log/Log.svelte +157 -0
- package/src/client/debug/log/LogEvent.svelte +149 -0
- package/src/client/debug/log/LogMetadata.svelte +7 -0
- package/src/client/debug/log/PhaseMarker.svelte +27 -0
- package/src/client/debug/log/TurnMarker.svelte +23 -0
- package/src/client/debug/main/ClientSwitcher.svelte +59 -0
- package/src/client/debug/main/Controls.svelte +58 -0
- package/src/client/debug/main/Hotkey.svelte +84 -0
- package/src/client/debug/main/InteractiveFunction.svelte +85 -0
- package/src/client/debug/main/Main.svelte +121 -0
- package/src/client/debug/main/Move.svelte +68 -0
- package/src/client/debug/main/PlayerInfo.svelte +70 -0
- package/src/client/debug/mcts/Action.svelte +22 -0
- package/src/client/debug/mcts/MCTS.svelte +78 -0
- package/src/client/debug/mcts/Table.svelte +98 -0
- package/src/client/debug/tests/JSONTree.mock.svelte +3 -0
- package/src/client/debug/tests/debug.test.ts +183 -0
- package/src/client/debug/utils/shortcuts.js +50 -0
- package/src/client/debug/utils/shortcuts.test.js +49 -0
- package/src/client/manager.ts +177 -0
- package/src/client/react-native.js +136 -0
- package/src/client/react-native.test.js +229 -0
- package/src/client/react.ssr.test.tsx +24 -0
- package/src/client/react.test.tsx +213 -0
- package/src/client/react.tsx +192 -0
- package/src/client/transport/dummy.ts +19 -0
- package/src/client/transport/local.test.ts +353 -0
- package/src/client/transport/local.ts +230 -0
- package/src/client/transport/socketio.test.ts +328 -0
- package/src/client/transport/socketio.ts +210 -0
- package/src/client/transport/transport.test.ts +27 -0
- package/src/client/transport/transport.ts +95 -0
- package/src/core/action-creators.ts +159 -0
- package/src/core/action-types.ts +18 -0
- package/src/core/backwards-compatibility.ts +23 -0
- package/src/core/constants.ts +6 -0
- package/src/core/errors.ts +35 -0
- package/src/core/flow.test.ts +2433 -0
- package/src/core/flow.ts +897 -0
- package/src/core/game-methods.ts +9 -0
- package/src/core/game.test.ts +286 -0
- package/src/core/game.ts +114 -0
- package/src/core/initialize.ts +77 -0
- package/src/core/logger.test.js +90 -0
- package/src/core/logger.ts +18 -0
- package/src/core/player-view.test.ts +50 -0
- package/src/core/player-view.ts +39 -0
- package/src/core/reducer.test.ts +991 -0
- package/src/core/reducer.ts +532 -0
- package/src/core/turn-order.test.ts +1123 -0
- package/src/core/turn-order.ts +473 -0
- package/src/lobby/client.test.ts +385 -0
- package/src/lobby/client.ts +358 -0
- package/src/lobby/connection.test.ts +207 -0
- package/src/lobby/connection.ts +162 -0
- package/src/lobby/create-match-form.tsx +122 -0
- package/src/lobby/login-form.tsx +75 -0
- package/src/lobby/match-instance.tsx +135 -0
- package/src/lobby/react.ssr.test.tsx +22 -0
- package/src/lobby/react.test.tsx +594 -0
- package/src/lobby/react.tsx +402 -0
- package/src/master/filter-player-view.test.ts +381 -0
- package/src/master/filter-player-view.ts +102 -0
- package/src/master/master.test.ts +1068 -0
- package/src/master/master.ts +492 -0
- package/src/plugins/events/events.test.ts +108 -0
- package/src/plugins/events/events.ts +209 -0
- package/src/plugins/main.test.ts +411 -0
- package/src/plugins/main.ts +314 -0
- package/src/plugins/plugin-events.ts +40 -0
- package/src/plugins/plugin-immer.test.ts +86 -0
- package/src/plugins/plugin-immer.ts +37 -0
- package/src/plugins/plugin-log.test.ts +37 -0
- package/src/plugins/plugin-log.ts +40 -0
- package/src/plugins/plugin-player.test.ts +172 -0
- package/src/plugins/plugin-player.ts +100 -0
- package/src/plugins/plugin-random.ts +40 -0
- package/src/plugins/plugin-serializable.test.ts +40 -0
- package/src/plugins/plugin-serializable.ts +55 -0
- package/src/plugins/random/random.alea.ts +109 -0
- package/src/plugins/random/random.test.ts +167 -0
- package/src/plugins/random/random.ts +198 -0
- package/src/server/api.test.ts +1699 -0
- package/src/server/api.ts +527 -0
- package/src/server/auth.test.ts +275 -0
- package/src/server/auth.ts +89 -0
- package/src/server/cors.test.ts +121 -0
- package/src/server/cors.ts +7 -0
- package/src/server/db/base.ts +296 -0
- package/src/server/db/flatfile.test.ts +221 -0
- package/src/server/db/flatfile.ts +228 -0
- package/src/server/db/index.test.ts +8 -0
- package/src/server/db/index.ts +12 -0
- package/src/server/db/inmemory.test.ts +143 -0
- package/src/server/db/inmemory.ts +143 -0
- package/src/server/db/localstorage.test.ts +73 -0
- package/src/server/db/localstorage.ts +44 -0
- package/src/server/index.test.ts +265 -0
- package/src/server/index.ts +175 -0
- package/src/server/transport/pubsub/generic-pub-sub.ts +11 -0
- package/src/server/transport/pubsub/in-memory-pub-sub.test.ts +47 -0
- package/src/server/transport/pubsub/in-memory-pub-sub.ts +28 -0
- package/src/server/transport/socketio-simultaneous.test.ts +603 -0
- package/src/server/transport/socketio.test.ts +303 -0
- package/src/server/transport/socketio.ts +279 -0
- package/src/server/util.ts +85 -0
- package/src/testing/mock-random.test.ts +45 -0
- package/src/testing/mock-random.ts +27 -0
- package/src/types.ts +511 -0
- package/testing/package.json +7 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2018 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ProcessGameConfig } from './game';
|
|
10
|
+
import { Client } from '../client/client';
|
|
11
|
+
import { error } from '../core/logger';
|
|
12
|
+
import { InitializeGame } from './initialize';
|
|
13
|
+
import type { Game } from '../types';
|
|
14
|
+
|
|
15
|
+
jest.mock('../core/logger', () => ({
|
|
16
|
+
info: jest.fn(),
|
|
17
|
+
error: jest.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
describe('basic', () => {
|
|
21
|
+
let game;
|
|
22
|
+
beforeAll(() => {
|
|
23
|
+
game = ProcessGameConfig({
|
|
24
|
+
moves: {
|
|
25
|
+
A: ({ G }) => G,
|
|
26
|
+
B: () => null,
|
|
27
|
+
C: {
|
|
28
|
+
move: () => 'C',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
phases: {
|
|
33
|
+
PA: {
|
|
34
|
+
moves: {
|
|
35
|
+
A: () => 'PA.A',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('sanity', () => {
|
|
43
|
+
expect(game.moveNames).toEqual(['A', 'B', 'C']);
|
|
44
|
+
expect(typeof game.processMove).toEqual('function');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('processMove', () => {
|
|
48
|
+
const G = { test: true };
|
|
49
|
+
const ctx = { phase: '' };
|
|
50
|
+
const state = { G, ctx, plugins: {} };
|
|
51
|
+
|
|
52
|
+
expect(game.processMove(state, { type: 'A' })).toEqual(G);
|
|
53
|
+
expect(game.processMove(state, { type: 'D' })).toEqual(G);
|
|
54
|
+
expect(game.processMove(state, { type: 'B' })).toEqual(null);
|
|
55
|
+
|
|
56
|
+
state.ctx.phase = 'PA';
|
|
57
|
+
expect(game.processMove(state, { type: 'A' })).toEqual('PA.A');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('long-form move syntax', () => {
|
|
61
|
+
expect(
|
|
62
|
+
game.processMove({ ctx: { phase: '' }, plugins: {} }, { type: 'C' })
|
|
63
|
+
).toEqual('C');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Following turn order is often used in worker placement games like Agricola and Viticulture.
|
|
68
|
+
test('rounds with starting player token', () => {
|
|
69
|
+
const game: Game = {
|
|
70
|
+
setup: () => ({ startingPlayerToken: 0 }),
|
|
71
|
+
|
|
72
|
+
moves: {
|
|
73
|
+
takeStartingPlayerToken: ({ G, ctx }) => {
|
|
74
|
+
G.startingPlayerToken = ctx.currentPlayer;
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
phases: {
|
|
79
|
+
main: {
|
|
80
|
+
start: true,
|
|
81
|
+
turn: {
|
|
82
|
+
order: {
|
|
83
|
+
first: ({ G }) => G.startingPlayerToken,
|
|
84
|
+
next: ({ ctx }) => (+ctx.playOrderPos + 1) % ctx.playOrder.length,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const client = Client({ game, numPlayers: 4 });
|
|
92
|
+
|
|
93
|
+
expect(client.getState().ctx.currentPlayer).toBe('0');
|
|
94
|
+
|
|
95
|
+
client.events.endTurn();
|
|
96
|
+
expect(client.getState().ctx.currentPlayer).toBe('1');
|
|
97
|
+
|
|
98
|
+
client.events.endTurn();
|
|
99
|
+
expect(client.getState().ctx.currentPlayer).toBe('2');
|
|
100
|
+
|
|
101
|
+
client.moves.takeStartingPlayerToken();
|
|
102
|
+
client.events.endTurn();
|
|
103
|
+
expect(client.getState().ctx.currentPlayer).toBe('3');
|
|
104
|
+
|
|
105
|
+
client.events.endTurn();
|
|
106
|
+
client.events.setPhase('main');
|
|
107
|
+
expect(client.getState().ctx.currentPlayer).toBe('2');
|
|
108
|
+
|
|
109
|
+
client.events.endTurn();
|
|
110
|
+
expect(client.getState().ctx.currentPlayer).toBe('3');
|
|
111
|
+
|
|
112
|
+
client.events.endTurn();
|
|
113
|
+
expect(client.getState().ctx.currentPlayer).toBe('0');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// The following pattern is used in Catan, Twilight Imperium, and (sort of) Powergrid.
|
|
117
|
+
test('serpentine setup phases', () => {
|
|
118
|
+
const game: Game = {
|
|
119
|
+
phases: {
|
|
120
|
+
'first setup round': {
|
|
121
|
+
start: true,
|
|
122
|
+
turn: {
|
|
123
|
+
order: {
|
|
124
|
+
first: () => 0,
|
|
125
|
+
next: ({ ctx }) => (+ctx.playOrderPos + 1) % ctx.playOrder.length,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
next: 'second setup round',
|
|
129
|
+
},
|
|
130
|
+
'second setup round': {
|
|
131
|
+
turn: {
|
|
132
|
+
order: {
|
|
133
|
+
first: ({ ctx }) => ctx.playOrder.length - 1,
|
|
134
|
+
next: ({ ctx }) => (+ctx.playOrderPos - 1) % ctx.playOrder.length,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
next: 'main phase',
|
|
138
|
+
},
|
|
139
|
+
'main phase': {
|
|
140
|
+
turn: {
|
|
141
|
+
order: {
|
|
142
|
+
first: () => 0,
|
|
143
|
+
next: ({ ctx }) => (+ctx.playOrderPos + 1) % ctx.playOrder.length,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const numPlayers = 4;
|
|
151
|
+
const client = Client({ game, numPlayers });
|
|
152
|
+
|
|
153
|
+
expect(client.getState().ctx.currentPlayer).toBe('0');
|
|
154
|
+
expect(client.getState().ctx.phase).toBe('first setup round');
|
|
155
|
+
|
|
156
|
+
client.events.endTurn();
|
|
157
|
+
expect(client.getState().ctx.currentPlayer).toBe('1');
|
|
158
|
+
expect(client.getState().ctx.phase).toBe('first setup round');
|
|
159
|
+
|
|
160
|
+
client.events.endTurn();
|
|
161
|
+
expect(client.getState().ctx.currentPlayer).toBe('2');
|
|
162
|
+
expect(client.getState().ctx.phase).toBe('first setup round');
|
|
163
|
+
|
|
164
|
+
client.events.endTurn();
|
|
165
|
+
expect(client.getState().ctx.currentPlayer).toBe('3');
|
|
166
|
+
expect(client.getState().ctx.phase).toBe('first setup round');
|
|
167
|
+
|
|
168
|
+
client.events.endTurn();
|
|
169
|
+
client.events.endPhase();
|
|
170
|
+
expect(client.getState().ctx.currentPlayer).toBe('3');
|
|
171
|
+
expect(client.getState().ctx.phase).toBe('second setup round');
|
|
172
|
+
|
|
173
|
+
client.events.endTurn();
|
|
174
|
+
expect(client.getState().ctx.currentPlayer).toBe('2');
|
|
175
|
+
expect(client.getState().ctx.phase).toBe('second setup round');
|
|
176
|
+
|
|
177
|
+
client.events.endTurn();
|
|
178
|
+
expect(client.getState().ctx.currentPlayer).toBe('1');
|
|
179
|
+
expect(client.getState().ctx.phase).toBe('second setup round');
|
|
180
|
+
|
|
181
|
+
client.events.endTurn();
|
|
182
|
+
expect(client.getState().ctx.currentPlayer).toBe('0');
|
|
183
|
+
expect(client.getState().ctx.phase).toBe('second setup round');
|
|
184
|
+
|
|
185
|
+
client.events.endTurn();
|
|
186
|
+
client.events.endPhase();
|
|
187
|
+
expect(client.getState().ctx.currentPlayer).toBe('0');
|
|
188
|
+
expect(client.getState().ctx.phase).toBe('main phase');
|
|
189
|
+
|
|
190
|
+
client.events.endTurn();
|
|
191
|
+
expect(client.getState().ctx.currentPlayer).toBe('1');
|
|
192
|
+
expect(client.getState().ctx.phase).toBe('main phase');
|
|
193
|
+
|
|
194
|
+
client.events.endTurn();
|
|
195
|
+
expect(client.getState().ctx.currentPlayer).toBe('2');
|
|
196
|
+
expect(client.getState().ctx.phase).toBe('main phase');
|
|
197
|
+
|
|
198
|
+
client.events.endTurn();
|
|
199
|
+
expect(client.getState().ctx.currentPlayer).toBe('3');
|
|
200
|
+
expect(client.getState().ctx.phase).toBe('main phase');
|
|
201
|
+
|
|
202
|
+
client.events.endTurn();
|
|
203
|
+
expect(client.getState().ctx.currentPlayer).toBe('0');
|
|
204
|
+
expect(client.getState().ctx.phase).toBe('main phase');
|
|
205
|
+
|
|
206
|
+
client.events.endTurn();
|
|
207
|
+
expect(client.getState().ctx.currentPlayer).toBe('1');
|
|
208
|
+
expect(client.getState().ctx.phase).toBe('main phase');
|
|
209
|
+
|
|
210
|
+
client.events.endTurn();
|
|
211
|
+
expect(client.getState().ctx.currentPlayer).toBe('2');
|
|
212
|
+
expect(client.getState().ctx.phase).toBe('main phase');
|
|
213
|
+
|
|
214
|
+
client.events.endTurn();
|
|
215
|
+
expect(client.getState().ctx.currentPlayer).toBe('3');
|
|
216
|
+
expect(client.getState().ctx.phase).toBe('main phase');
|
|
217
|
+
|
|
218
|
+
client.events.endTurn();
|
|
219
|
+
expect(client.getState().ctx.currentPlayer).toBe('0');
|
|
220
|
+
expect(client.getState().ctx.phase).toBe('main phase');
|
|
221
|
+
|
|
222
|
+
client.events.endTurn();
|
|
223
|
+
expect(client.getState().ctx.currentPlayer).toBe('1');
|
|
224
|
+
expect(client.getState().ctx.phase).toBe('main phase');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('config errors', () => {
|
|
228
|
+
test('game name with spaces', () => {
|
|
229
|
+
const game = () => {
|
|
230
|
+
ProcessGameConfig({ name: 'tic tac toe' });
|
|
231
|
+
};
|
|
232
|
+
expect(game).toThrow();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('plugin name with spaces', () => {
|
|
236
|
+
const plugins = [
|
|
237
|
+
{
|
|
238
|
+
name: 'my cool plugin',
|
|
239
|
+
api: () => {},
|
|
240
|
+
},
|
|
241
|
+
];
|
|
242
|
+
const game = () => {
|
|
243
|
+
ProcessGameConfig({ plugins });
|
|
244
|
+
};
|
|
245
|
+
expect(game).toThrow();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('plugin name missing', () => {
|
|
249
|
+
const plugins = [
|
|
250
|
+
{
|
|
251
|
+
api: () => {},
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
const game = () => {
|
|
255
|
+
ProcessGameConfig({ plugins } as unknown as Game);
|
|
256
|
+
};
|
|
257
|
+
expect(game).toThrow();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test('invalid move object', () => {
|
|
261
|
+
const game = ProcessGameConfig({ moves: { A: 1 } } as unknown as Game);
|
|
262
|
+
const state = InitializeGame({ game });
|
|
263
|
+
game.processMove(state, { type: 'A', args: null, playerID: '0' });
|
|
264
|
+
expect(error).toBeCalledWith(
|
|
265
|
+
expect.stringContaining('invalid move object')
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe('disableUndo', () => {
|
|
271
|
+
test('set disableUndo to false by default', () => {
|
|
272
|
+
const game = ProcessGameConfig({
|
|
273
|
+
moves: {},
|
|
274
|
+
});
|
|
275
|
+
expect(game.disableUndo).toBeFalsy();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('set disableUndo to true', () => {
|
|
279
|
+
const game = ProcessGameConfig({
|
|
280
|
+
moves: {},
|
|
281
|
+
disableUndo: true,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
expect(game.disableUndo).toBeTruthy();
|
|
285
|
+
});
|
|
286
|
+
});
|
package/src/core/game.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2017 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as plugins from '../plugins/main';
|
|
10
|
+
import { Flow } from './flow';
|
|
11
|
+
import type { INVALID_MOVE } from './constants';
|
|
12
|
+
import type { ActionPayload, Game, Move, LongFormMove, State } from '../types';
|
|
13
|
+
import * as logging from './logger';
|
|
14
|
+
import { GameMethod } from './game-methods';
|
|
15
|
+
|
|
16
|
+
type ProcessedGame = Game & {
|
|
17
|
+
flow: ReturnType<typeof Flow>;
|
|
18
|
+
moveNames: string[];
|
|
19
|
+
pluginNames: string[];
|
|
20
|
+
processMove: (
|
|
21
|
+
state: State,
|
|
22
|
+
action: ActionPayload.MakeMove
|
|
23
|
+
) => State | typeof INVALID_MOVE;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function IsProcessed(game: Game | ProcessedGame): game is ProcessedGame {
|
|
27
|
+
return game.processMove !== undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Helper to generate the game move reducer. The returned
|
|
32
|
+
* reducer has the following signature:
|
|
33
|
+
*
|
|
34
|
+
* (G, action, ctx) => {}
|
|
35
|
+
*
|
|
36
|
+
* You can roll your own if you like, or use any Redux
|
|
37
|
+
* addon to generate such a reducer.
|
|
38
|
+
*
|
|
39
|
+
* The convention used in this framework is to
|
|
40
|
+
* have action.type contain the name of the move, and
|
|
41
|
+
* action.args contain any additional arguments as an
|
|
42
|
+
* Array.
|
|
43
|
+
*/
|
|
44
|
+
export function ProcessGameConfig(game: Game | ProcessedGame): ProcessedGame {
|
|
45
|
+
// The Game() function has already been called on this
|
|
46
|
+
// config object, so just pass it through.
|
|
47
|
+
if (IsProcessed(game)) {
|
|
48
|
+
return game;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (game.name === undefined) game.name = 'default';
|
|
52
|
+
if (game.deltaState === undefined) game.deltaState = false;
|
|
53
|
+
if (game.disableUndo === undefined) game.disableUndo = false;
|
|
54
|
+
if (game.setup === undefined) game.setup = () => ({});
|
|
55
|
+
if (game.moves === undefined) game.moves = {};
|
|
56
|
+
if (game.playerView === undefined) game.playerView = ({ G }) => G;
|
|
57
|
+
if (game.plugins === undefined) game.plugins = [];
|
|
58
|
+
|
|
59
|
+
game.plugins.forEach((plugin) => {
|
|
60
|
+
if (plugin.name === undefined) {
|
|
61
|
+
throw new Error('Plugin missing name attribute');
|
|
62
|
+
}
|
|
63
|
+
if (plugin.name.includes(' ')) {
|
|
64
|
+
throw new Error(plugin.name + ': Plugin name must not include spaces');
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (game.name.includes(' ')) {
|
|
69
|
+
throw new Error(game.name + ': Game name must not include spaces');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const flow = Flow(game);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
...game,
|
|
76
|
+
|
|
77
|
+
flow,
|
|
78
|
+
|
|
79
|
+
moveNames: flow.moveNames as string[],
|
|
80
|
+
|
|
81
|
+
pluginNames: game.plugins.map((p) => p.name) as string[],
|
|
82
|
+
|
|
83
|
+
processMove: (state: State, action: ActionPayload.MakeMove) => {
|
|
84
|
+
let moveFn = flow.getMove(state.ctx, action.type, action.playerID);
|
|
85
|
+
|
|
86
|
+
if (IsLongFormMove(moveFn)) {
|
|
87
|
+
moveFn = moveFn.move;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (moveFn instanceof Function) {
|
|
91
|
+
const fn = plugins.FnWrap(moveFn, GameMethod.MOVE, game.plugins);
|
|
92
|
+
let args = [];
|
|
93
|
+
if (action.args !== undefined) {
|
|
94
|
+
args = Array.isArray(action.args) ? action.args : [action.args];
|
|
95
|
+
}
|
|
96
|
+
const context = {
|
|
97
|
+
...plugins.GetAPIs(state),
|
|
98
|
+
G: state.G,
|
|
99
|
+
ctx: state.ctx,
|
|
100
|
+
playerID: action.playerID,
|
|
101
|
+
};
|
|
102
|
+
return fn(context, ...args);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
logging.error(`invalid move object: ${action.type}`);
|
|
106
|
+
|
|
107
|
+
return state.G;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function IsLongFormMove(move: Move): move is LongFormMove {
|
|
113
|
+
return move instanceof Object && (move as LongFormMove).move !== undefined;
|
|
114
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ProcessGameConfig } from './game';
|
|
10
|
+
import * as plugins from '../plugins/main';
|
|
11
|
+
import type { Ctx, Game, PartialGameState, State } from '../types';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates the initial game state.
|
|
15
|
+
*/
|
|
16
|
+
export function InitializeGame({
|
|
17
|
+
game,
|
|
18
|
+
numPlayers,
|
|
19
|
+
setupData,
|
|
20
|
+
}: {
|
|
21
|
+
game: Game;
|
|
22
|
+
numPlayers?: number;
|
|
23
|
+
setupData?: any;
|
|
24
|
+
}) {
|
|
25
|
+
game = ProcessGameConfig(game);
|
|
26
|
+
|
|
27
|
+
if (!numPlayers) {
|
|
28
|
+
numPlayers = 2;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ctx: Ctx = game.flow.ctx(numPlayers);
|
|
32
|
+
|
|
33
|
+
let state: PartialGameState = {
|
|
34
|
+
// User managed state.
|
|
35
|
+
G: {},
|
|
36
|
+
// Framework managed state.
|
|
37
|
+
ctx,
|
|
38
|
+
// Plugin related state.
|
|
39
|
+
plugins: {},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Run plugins over initial state.
|
|
43
|
+
state = plugins.Setup(state, { game });
|
|
44
|
+
state = plugins.Enhance(state, { game, playerID: undefined });
|
|
45
|
+
|
|
46
|
+
const pluginAPIs = plugins.GetAPIs(state);
|
|
47
|
+
state.G = game.setup({ ...pluginAPIs, ctx: state.ctx }, setupData);
|
|
48
|
+
|
|
49
|
+
let initial: State = {
|
|
50
|
+
...state,
|
|
51
|
+
|
|
52
|
+
// List of {G, ctx} pairs that can be undone.
|
|
53
|
+
_undo: [],
|
|
54
|
+
// List of {G, ctx} pairs that can be redone.
|
|
55
|
+
_redo: [],
|
|
56
|
+
// A monotonically non-decreasing ID to ensure that
|
|
57
|
+
// state updates are only allowed from clients that
|
|
58
|
+
// are at the same version that the server.
|
|
59
|
+
_stateID: 0,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
initial = game.flow.init(initial);
|
|
63
|
+
[initial] = plugins.FlushAndValidate(initial, { game });
|
|
64
|
+
|
|
65
|
+
// Initialize undo stack.
|
|
66
|
+
if (!game.disableUndo) {
|
|
67
|
+
initial._undo = [
|
|
68
|
+
{
|
|
69
|
+
G: initial.G,
|
|
70
|
+
ctx: initial.ctx,
|
|
71
|
+
plugins: initial.plugins,
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return initial;
|
|
77
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2018 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
describe('logging', () => {
|
|
10
|
+
const oldConsoleLog = console.log;
|
|
11
|
+
const oldConsoleError = console.error;
|
|
12
|
+
const oldNodeEnv = process.env.NODE_ENV;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
console.log.mockReset();
|
|
16
|
+
console.error.mockReset();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
console.log = oldConsoleLog;
|
|
21
|
+
console.error = oldConsoleError;
|
|
22
|
+
process.env.NODE_ENV = oldNodeEnv;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
console.log = jest.fn();
|
|
26
|
+
console.error = jest.fn();
|
|
27
|
+
|
|
28
|
+
describe('dev', () => {
|
|
29
|
+
let logging;
|
|
30
|
+
|
|
31
|
+
beforeAll(() => {
|
|
32
|
+
logging = require('./logger');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('error', () => {
|
|
36
|
+
logging.error('msg1');
|
|
37
|
+
expect(console.error).toHaveBeenCalledWith('ERROR:', 'msg1');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('info', () => {
|
|
41
|
+
logging.info('msg2');
|
|
42
|
+
expect(console.log).toHaveBeenCalledWith('INFO: msg2');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('production', () => {
|
|
47
|
+
let logging;
|
|
48
|
+
|
|
49
|
+
beforeAll(() => {
|
|
50
|
+
process.env.NODE_ENV = 'production';
|
|
51
|
+
jest.resetModules();
|
|
52
|
+
logging = require('./logger');
|
|
53
|
+
});
|
|
54
|
+
afterAll(() => {
|
|
55
|
+
process.env.NODE_ENV = oldNodeEnv;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('info stripped', () => {
|
|
59
|
+
logging.info('msg2');
|
|
60
|
+
expect(console.log).not.toHaveBeenCalled();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('error not stripped', () => {
|
|
64
|
+
logging.error('msg1');
|
|
65
|
+
expect(console.error).toHaveBeenCalled();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('spying after load', () => {
|
|
70
|
+
let logging;
|
|
71
|
+
|
|
72
|
+
beforeAll(() => {
|
|
73
|
+
jest.resetModules();
|
|
74
|
+
console.log = oldConsoleLog;
|
|
75
|
+
console.error = oldConsoleError;
|
|
76
|
+
logging = require('./logger');
|
|
77
|
+
console.log = jest.fn();
|
|
78
|
+
console.error = jest.fn();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('should allow console log to be spied', () => {
|
|
82
|
+
logging.info('test');
|
|
83
|
+
expect(console.log).toHaveBeenCalledWith('INFO: test');
|
|
84
|
+
});
|
|
85
|
+
test('should allow console error to be spied', () => {
|
|
86
|
+
logging.error('test');
|
|
87
|
+
expect(console.error).toHaveBeenCalledWith('ERROR:', 'test');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2018 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const production = process.env.NODE_ENV === 'production';
|
|
10
|
+
const logfn = production ? () => {} : (...msg) => console.log(...msg);
|
|
11
|
+
const errorfn = (...msg) => console.error(...msg);
|
|
12
|
+
|
|
13
|
+
export function info(msg: string) {
|
|
14
|
+
logfn(`INFO: ${msg}`);
|
|
15
|
+
}
|
|
16
|
+
export function error(error: string) {
|
|
17
|
+
errorfn('ERROR:', error);
|
|
18
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2017 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { PlayerView } from './player-view';
|
|
10
|
+
import type { Ctx } from '../types';
|
|
11
|
+
|
|
12
|
+
test('no change', () => {
|
|
13
|
+
const G = { test: true };
|
|
14
|
+
const newG = PlayerView.STRIP_SECRETS({ G, ctx: {} as Ctx, playerID: '0' });
|
|
15
|
+
expect(newG).toEqual(G);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('secret', () => {
|
|
19
|
+
const G = { secret: true };
|
|
20
|
+
const newG = PlayerView.STRIP_SECRETS({ G, ctx: {} as Ctx, playerID: '0' });
|
|
21
|
+
expect(newG).toEqual({});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('players', () => {
|
|
25
|
+
const G = {
|
|
26
|
+
players: {
|
|
27
|
+
'0': {},
|
|
28
|
+
'1': {},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
test('playerID: "0"', () => {
|
|
33
|
+
const newG = PlayerView.STRIP_SECRETS({ G, ctx: {} as Ctx, playerID: '0' });
|
|
34
|
+
expect(newG.players).toEqual({ '0': {} });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('playerID: "1"', () => {
|
|
38
|
+
const newG = PlayerView.STRIP_SECRETS({ G, ctx: {} as Ctx, playerID: '1' });
|
|
39
|
+
expect(newG.players).toEqual({ '1': {} });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('playerID: null', () => {
|
|
43
|
+
const newG = PlayerView.STRIP_SECRETS({
|
|
44
|
+
G,
|
|
45
|
+
ctx: {} as Ctx,
|
|
46
|
+
playerID: null,
|
|
47
|
+
});
|
|
48
|
+
expect(newG.players).toEqual({});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2018 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Game, PlayerID } from '../types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* PlayerView reducers.
|
|
13
|
+
*/
|
|
14
|
+
export const PlayerView: { STRIP_SECRETS: Game['playerView'] } = {
|
|
15
|
+
/**
|
|
16
|
+
* STRIP_SECRETS
|
|
17
|
+
*
|
|
18
|
+
* Reducer which removes a key named `secret` and
|
|
19
|
+
* removes all the keys in `players`, except for the one
|
|
20
|
+
* corresponding to the current playerID.
|
|
21
|
+
*/
|
|
22
|
+
STRIP_SECRETS: ({ G, playerID }: { G: any; playerID: PlayerID | null }) => {
|
|
23
|
+
const r = { ...G };
|
|
24
|
+
|
|
25
|
+
if (r.secret !== undefined) {
|
|
26
|
+
delete r.secret;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (r.players) {
|
|
30
|
+
r.players = playerID
|
|
31
|
+
? {
|
|
32
|
+
[playerID]: r.players[playerID],
|
|
33
|
+
}
|
|
34
|
+
: {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return r;
|
|
38
|
+
},
|
|
39
|
+
};
|