Skip to content

Commit fa58e5b

Browse files
committed
retire allowedMoves
1 parent 7a411c9 commit fa58e5b

11 files changed

Lines changed: 74 additions & 132 deletions

File tree

examples/react-web/src/phases/phases.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@ const game = Game({
2525
phases: {
2626
take: {
2727
endPhaseIf: G => G.deck <= 0,
28-
allowedMoves: ['takeCard'],
2928
next: 'play',
3029
},
3130
play: {
32-
allowedMoves: ['playCard'],
3331
endPhaseIf: G => G.hand <= 0,
3432
next: 'take',
3533
},

examples/react-web/src/turnorder/example-any-once.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const code = `{
1414
startingPhase: 'A',
1515
phases: {
1616
A: { turnOrder: TurnOrder.ANY_ONCE, next: 'B' },
17-
B: { allowedMoves: [] },
17+
B: {},
1818
}
1919
},
2020
}
@@ -40,7 +40,7 @@ export default {
4040

4141
phases: {
4242
A: { turnOrder: TurnOrder.ANY_ONCE, next: 'B' },
43-
B: { allowedMoves: [] },
43+
B: {},
4444
},
4545
},
4646
}),

examples/react-web/src/turnorder/example-others-once.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,10 @@ const code = `{
1414
startingPhase: 'play',
1515
1616
phases: {
17-
play: {
18-
allowedMoves: ['play'],
19-
},
17+
play: {},
2018
2119
discard: {
2220
turnOrder: TurnOrder.OTHERS_ONCE,
23-
allowedMoves: ['discard'],
2421
},
2522
},
2623
},
@@ -53,13 +50,10 @@ export default {
5350
startingPhase: 'play',
5451

5552
phases: {
56-
play: {
57-
allowedMoves: ['play'],
58-
},
53+
play: {},
5954

6055
discard: {
6156
turnOrder: TurnOrder.OTHERS_ONCE,
62-
allowedMoves: ['discard'],
6357
},
6458
},
6559
},

examples/react-web/src/turnorder/simulator.js

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,11 @@ class Board extends React.Component {
5353
current = true;
5454
}
5555

56-
const moves = Object.entries(this.props.moves)
57-
.filter(
58-
e =>
59-
this.props.ctx.allowedMoves === null ||
60-
this.props.ctx.allowedMoves.includes(e[0])
61-
)
62-
.map(e => (
63-
<button key={e[0]} onClick={() => e[1]()}>
64-
{e[0]}
65-
</button>
66-
));
56+
const moves = Object.entries(this.props.moves).map(e => (
57+
<button key={e[0]} onClick={() => e[1]()}>
58+
{e[0]}
59+
</button>
60+
));
6761

6862
const events = Object.entries(this.props.events)
6963
.filter(() => current && active)

src/client/client.test.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ describe('namespaced moves', () => {
4141
client = Client({
4242
game: Game({
4343
moves: {
44-
A: () => {},
44+
A: () => 'A',
4545
},
4646

4747
flow: {
4848
phases: {
4949
PA: {
5050
moves: {
51-
B: () => {},
52-
C: () => {},
51+
B: () => 'B',
52+
C: () => 'C',
5353
},
5454
},
5555
},
@@ -67,6 +67,26 @@ describe('namespaced moves', () => {
6767
expect(client.moves.PA.C).toBeInstanceOf(Function);
6868
});
6969

70+
test('moves are allowed only when phase is active', () => {
71+
client.moves.A();
72+
expect(client.getState().G).toEqual('A');
73+
74+
client.moves.PA.B();
75+
expect(error).toHaveBeenCalledWith('disallowed move: PA.B');
76+
client.moves.PA.C();
77+
expect(error).toHaveBeenCalledWith('disallowed move: PA.C');
78+
79+
client.events.endPhase({ next: 'PA' });
80+
expect(client.getState().ctx.phase).toBe('PA');
81+
82+
client.moves.PA.B();
83+
expect(client.getState().G).toEqual('B');
84+
client.moves.PA.C();
85+
expect(client.getState().G).toEqual('C');
86+
client.moves.A();
87+
expect(client.getState().G).toEqual('A');
88+
});
89+
7090
test('name clash', () => {
7191
Client({
7292
game: Game({

src/core/flow.js

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,6 @@ export function Flow({
186186
*
187187
* @param {...object} setActionPlayers - Set to true to enable the `setActionPlayers` event.
188188
*
189-
* @param {...object} allowedMoves - List of moves that are allowed.
190-
* This can be either a list of
191-
* move names or a function with the
192-
* signature (G, ctx) => [].
193-
* (default: null, i.e. all moves are allowed).
194-
*
195189
* @param {...object} undoableMoves - List of moves that are undoable,
196190
* (default: null, i.e. all moves are undoable).
197191
*
@@ -250,10 +244,6 @@ export function Flow({
250244
* // A phase-specific movesPerTurn.
251245
* movesPerTurn: integer,
252246
*
253-
* // List of moves or a function that returns a list of moves
254-
* // that are allowed in this phase.
255-
* allowedMoves: (G, ctx) => ['moveA', ...],
256-
*
257247
* // List of moves that are undoable.
258248
* undoableMoves: ['moveA', ...],
259249
* }
@@ -273,7 +263,6 @@ export function FlowWithPhases({
273263
endGame,
274264
setActionPlayers,
275265
undoableMoves,
276-
allowedMoves,
277266
redactedMoves,
278267
optimisticUpdate,
279268
game,
@@ -305,7 +294,6 @@ export function FlowWithPhases({
305294
if (!onTurnEnd) onTurnEnd = G => G;
306295
if (!onMove) onMove = G => G;
307296
if (!turnOrder) turnOrder = TurnOrder.DEFAULT;
308-
if (allowedMoves === undefined) allowedMoves = null;
309297
if (undoableMoves === undefined) undoableMoves = null;
310298

311299
const phaseMap = phases;
@@ -365,13 +353,6 @@ export function FlowWithPhases({
365353
if (conf.undoableMoves === undefined) {
366354
conf.undoableMoves = undoableMoves;
367355
}
368-
if (conf.allowedMoves === undefined) {
369-
conf.allowedMoves = allowedMoves;
370-
}
371-
if (typeof conf.allowedMoves !== 'function') {
372-
const t = conf.allowedMoves;
373-
conf.allowedMoves = () => t;
374-
}
375356
}
376357

377358
const shouldEndPhase = ({ G, ctx }) => {
@@ -408,8 +389,7 @@ export function FlowWithPhases({
408389
},
409390
};
410391

411-
const allowedMoves = config.allowedMoves(G, ctx);
412-
return { ...state, G, ctx: { ...ctx, allowedMoves } };
392+
return { ...state, G, ctx };
413393
};
414394

415395
const startTurn = function(state, config) {
@@ -419,7 +399,6 @@ export function FlowWithPhases({
419399
const _undo = [{ G, ctx: plainCtx }];
420400

421401
const ctx = { ...state.ctx };
422-
ctx.allowedMoves = config.allowedMoves(G, ctx);
423402

424403
// Reset stats.
425404
ctx.stats = {
@@ -678,10 +657,6 @@ export function FlowWithPhases({
678657
return { ...state, ctx: { ...state.ctx, gameover } };
679658
}
680659

681-
// Update allowedMoves.
682-
const allowedMoves = conf.allowedMoves(state.G, state.ctx);
683-
state = { ...state, ctx: { ...state.ctx, allowedMoves } };
684-
685660
// Update undo / redo state.
686661
if (!endTurn) {
687662
const undo = state._undo || [];
@@ -700,10 +675,14 @@ export function FlowWithPhases({
700675
}
701676

702677
const canMakeMove = (G, ctx, moveName) => {
703-
const conf = phaseMap[ctx.phase];
704-
const moves = conf.allowedMoves(G, ctx);
705-
if (!moves) return true;
706-
return moves.includes(moveName);
678+
// If this is a namespaced move, verify that
679+
// we are in the correct phase.
680+
if (moveName.includes('.')) {
681+
const tokens = moveName.split('.');
682+
return tokens[0] == ctx.phase;
683+
}
684+
// If not, then the move is allowed.
685+
return true;
707686
};
708687

709688
const canUndoMove = (G, ctx, moveName) => {

src/core/flow.test.js

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -487,17 +487,6 @@ test('canMakeMove', () => {
487487
B: () => ({ B: true }),
488488
C: () => ({ C: true }),
489489
},
490-
491-
flow: {
492-
allowedMoves: ['A', 'B'],
493-
startingPhase: 'A',
494-
phases: {
495-
A: { allowedMoves: () => ['A'] },
496-
B: { allowedMoves: ['B'] },
497-
C: {},
498-
D: { allowedMoves: null },
499-
},
500-
},
501490
});
502491

503492
const reducer = CreateGameReducer({ game });
@@ -510,53 +499,7 @@ test('canMakeMove', () => {
510499
flow = Flow({ canMakeMove: () => false });
511500
expect(flow.canMakeMove(state.G, state.ctx)).toBe(false);
512501

513-
// Phase A (A is allowed).
514-
expect(state.ctx.phase).toBe('A');
515-
516-
state = reducer(state, makeMove('A'));
517-
expect(state.G).toMatchObject({ A: true });
518-
state = reducer(state, makeMove('B'));
519-
expect(state.G).not.toMatchObject({ B: true });
520-
state = reducer(state, makeMove('C'));
521-
expect(state.G).not.toMatchObject({ C: true });
522-
523-
// Phase B (B is allowed).
524-
state = reducer(state, gameEvent('endPhase', { next: 'B' }));
525-
state.G = {};
526-
expect(state.ctx.phase).toBe('B');
527-
528-
state = reducer(state, makeMove('A'));
529-
expect(state.G).not.toMatchObject({ A: true });
530-
state = reducer(state, makeMove('B'));
531-
expect(state.G).toMatchObject({ B: true });
532-
state = reducer(state, makeMove('C'));
533-
expect(state.G).not.toMatchObject({ C: true });
534-
535-
// Phase C (A and B allowed).
536-
state = reducer(state, gameEvent('endPhase', { next: 'C' }));
537-
state.G = {};
538-
expect(state.ctx.phase).toBe('C');
539-
540-
state = reducer(state, makeMove('A'));
541-
expect(state.G).toMatchObject({ A: true });
542-
state = reducer(state, makeMove('B'));
543-
expect(state.G).toMatchObject({ B: true });
544-
state = reducer(state, makeMove('C'));
545-
expect(state.G).not.toMatchObject({ C: true });
546-
547-
// Phase D (A, B and C allowed).
548-
state = reducer(state, gameEvent('endPhase', { next: 'D' }));
549-
state.G = {};
550-
expect(state.ctx.phase).toBe('D');
551-
552-
state = reducer(state, makeMove('A'));
553-
expect(state.G).toMatchObject({ A: true });
554-
state = reducer(state, makeMove('B'));
555-
expect(state.G).toMatchObject({ B: true });
556-
state = reducer(state, makeMove('C'));
557-
expect(state.G).toMatchObject({ C: true });
558-
559-
// But not once the game is over.
502+
// No moves are allowed after the game is over.
560503
state.ctx.gameover = true;
561504
state.G = {};
562505
state = reducer(state, makeMove('A'));

src/core/reducer.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import * as Actions from './action-types';
1111
import { Random } from './random';
1212
import { Events } from './events';
1313
import * as plugins from '../plugins/main';
14+
import { error } from './logger';
1415

1516
/**
1617
* Moves can return this when they want to indicate
@@ -235,11 +236,13 @@ export function CreateGameReducer({ game, multiplayer }) {
235236

236237
// Check whether the game knows the move at all.
237238
if (!game.moveNames.includes(action.payload.type)) {
239+
error(`unrecognized move: ${action.payload.type}`);
238240
return state;
239241
}
240242

241243
// Ignore the move if it isn't allowed at this point.
242244
if (!game.flow.canMakeMove(state.G, state.ctx, action.payload.type)) {
245+
error(`disallowed move: ${action.payload.type}`);
243246
return state;
244247
}
245248

src/core/reducer.test.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ import {
1717
undo,
1818
redo,
1919
} from './action-creators';
20+
import { error } from '../core/logger';
21+
22+
jest.mock('../core/logger', () => ({
23+
info: jest.fn(),
24+
error: jest.fn(),
25+
}));
2026

2127
const game = Game({
2228
moves: {
@@ -57,6 +63,7 @@ test('makeMove', () => {
5763
state = reducer(state, makeMove('unknown'));
5864
expect(state._stateID).toBe(0);
5965
expect(state.G).not.toMatchObject({ moved: true });
66+
expect(error).toHaveBeenCalledWith('unrecognized move: unknown');
6067

6168
state = reducer(state, makeMove('A'));
6269
expect(state._stateID).toBe(1);

0 commit comments

Comments
 (0)