Skip to content

Commit cb6111b

Browse files
committed
retire the string constant 'any'
This was an old hack that enabled any player to play during a turn. This has since been subsumed by the actionPlayers mechanism, which can now be used to allow any player to play (by setting it to an array with all players in it).
1 parent 92dbc0c commit cb6111b

7 files changed

Lines changed: 92 additions & 61 deletions

File tree

src/client/client.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class _ClientImpl {
182182
// but we also strip them here so that game developers
183183
// can see their effects while prototyping.
184184
let playerID = this.playerID;
185-
if (!this.multiplayer && !playerID && state.ctx.currentPlayer != 'any') {
185+
if (!this.multiplayer && !playerID) {
186186
playerID = state.ctx.currentPlayer;
187187
}
188188
const G = this.game.playerView(state.G, state.ctx, playerID);

src/client/react-native.test.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,6 @@ test('board props', () => {
6868
board: TestBoard,
6969
multiplayer: true,
7070
});
71-
board = Enzyme.mount(<Board />).find(TestBoard);
72-
expect(board.props().isActive).toBe(true);
7371
board = Enzyme.mount(<Board playerID={'0'} />).find(TestBoard);
7472
expect(board.props().isActive).toBe(true);
7573
board = Enzyme.mount(<Board playerID={'1'} />).find(TestBoard);

src/client/react.test.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ test('board props', () => {
6969
board: TestBoard,
7070
multiplayer: true,
7171
});
72-
board = Enzyme.mount(<Board />).find(TestBoard);
73-
expect(board.props().isActive).toBe(true);
7472
board = Enzyme.mount(<Board playerID={'0'} />).find(TestBoard);
7573
expect(board.props().isActive).toBe(true);
7674
board = Enzyme.mount(<Board playerID={'1'} />).find(TestBoard);

src/core/flow.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export function Flow({
106106

107107
canPlayerMakeMove: (G, ctx, playerID) => {
108108
const actionPlayers = ctx.actionPlayers || [];
109-
return actionPlayers.includes(playerID) || actionPlayers.includes('any');
109+
return actionPlayers.includes(playerID);
110110
},
111111

112112
canMakeMove: (G, ctx, moveName) => {

src/core/flow.test.js

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -478,26 +478,20 @@ test('canMakeMove', () => {
478478

479479
test('canPlayerMakeMove', () => {
480480
// default behaviour
481-
const pid = 0;
481+
const pid = '0';
482482

483483
let flow = Flow({});
484484
expect(flow.canPlayerMakeMove({}, {}, pid)).toBe(false);
485485
// NOTE: currentPlayer is not allowed to make a move by default.
486-
// his playerID must be included in the actionPlayers array.
487-
expect(flow.canPlayerMakeMove({}, { currentPlayer: 0 }, pid)).toBe(false);
488-
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['any'] }, pid)).toBe(
489-
true
490-
);
491-
expect(flow.canPlayerMakeMove({}, { actionPlayers: [0] }, pid)).toBe(true);
492-
expect(flow.canPlayerMakeMove({}, { actionPlayers: [1, 2, 3] }, pid)).toBe(
493-
false
494-
);
486+
// Their playerID must be included in the actionPlayers array.
487+
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['1'] }, pid)).toBe(false);
488+
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['0'] }, pid)).toBe(true);
495489

496490
// no one can make a move
497491
flow = Flow({ canPlayerMakeMove: () => false });
498492
expect(flow.canPlayerMakeMove({}, {}, pid)).toBe(false);
499-
expect(flow.canPlayerMakeMove({}, { currentPlayer: 0 }, pid)).toBe(false);
500-
expect(flow.canPlayerMakeMove({}, {}, 'any')).toBe(false);
493+
expect(flow.canPlayerMakeMove({}, { actionPlayers: [] }, pid)).toBe(false);
494+
expect(flow.canPlayerMakeMove({}, {}, '5')).toBe(false);
501495
});
502496

503497
test('endGame', () => {
@@ -540,13 +534,6 @@ test('endTurn / endPhase args', () => {
540534
expect(t.ctx.phase).toBe('C');
541535
}
542536

543-
{
544-
let t = state;
545-
t = flow.processGameEvent(t, gameEvent('endTurn', 'any'));
546-
expect(t.ctx.playOrderPos).toBe(undefined);
547-
expect(t.ctx.currentPlayer).toBe('any');
548-
}
549-
550537
{
551538
let t = state;
552539
t = flow.processGameEvent(t, gameEvent('endTurn', '0'));

src/core/turn-order.js

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ export const Pass = (G, ctx) => {
1818
if (G.passOrder !== undefined) {
1919
passOrder = G.passOrder;
2020
}
21-
const playerID =
22-
ctx.currentPlayer === 'any' ? ctx.playerID : ctx.currentPlayer;
21+
const playerID = ctx.playerID;
2322
passOrder.push(playerID);
2423
G = { ...G, passOrder };
2524
if (passOrder.length >= ctx.numPlayers) {
@@ -53,9 +52,6 @@ export function ChangeActionPlayers(state, actionPlayers) {
5352
* @param {number} playOrderPos - An index into the above.
5453
*/
5554
function getCurrentPlayer(playOrder, playOrderPos) {
56-
if (playOrderPos === undefined) {
57-
return 'any';
58-
}
5955
return playOrder[playOrderPos] + '';
6056
}
6157

@@ -66,9 +62,25 @@ function getCurrentPlayer(playOrder, playOrderPos) {
6662
* @param {object} turnOrder - A turn order object for this phase.
6763
*/
6864
export function InitTurnOrderState(G, ctx, turnOrder) {
69-
const playOrderPos = turnOrder.first(G, ctx);
65+
let playOrderPos;
66+
let actionPlayers;
67+
68+
const t = turnOrder.first(G, ctx);
69+
70+
if (t.playOrderPos !== undefined) {
71+
playOrderPos = t.playOrderPos;
72+
} else {
73+
playOrderPos = t;
74+
}
75+
7076
const currentPlayer = getCurrentPlayer(ctx.playOrder, playOrderPos);
71-
const actionPlayers = [currentPlayer];
77+
78+
if (t.actionPlayers !== undefined) {
79+
actionPlayers = t.actionPlayers;
80+
} else {
81+
actionPlayers = [currentPlayer];
82+
}
83+
7284
return { ...ctx, currentPlayer, playOrderPos, actionPlayers };
7385
}
7486

@@ -83,19 +95,29 @@ export function InitTurnOrderState(G, ctx, turnOrder) {
8395
export function UpdateTurnOrderState(G, ctx, turnOrder, nextPlayer) {
8496
let playOrderPos = ctx.playOrderPos;
8597
let currentPlayer = ctx.currentPlayer;
98+
let actionPlayers;
8699

87-
if (nextPlayer === 'any') {
88-
playOrderPos = undefined;
89-
currentPlayer = nextPlayer;
90-
} else if (ctx.playOrder.includes(nextPlayer)) {
100+
if (ctx.playOrder.includes(nextPlayer)) {
91101
playOrderPos = ctx.playOrder.indexOf(nextPlayer);
92102
currentPlayer = nextPlayer;
103+
actionPlayers = [currentPlayer];
93104
} else {
94-
playOrderPos = turnOrder.next(G, ctx);
105+
const t = turnOrder.next(G, ctx);
106+
107+
if (t && t.playOrderPos !== undefined) {
108+
playOrderPos = t.playOrderPos;
109+
} else {
110+
playOrderPos = t;
111+
}
112+
95113
currentPlayer = getCurrentPlayer(ctx.playOrder, playOrderPos);
96-
}
97114

98-
const actionPlayers = [currentPlayer];
115+
if (t && t.actionPlayers !== undefined) {
116+
actionPlayers = t.actionPlayers;
117+
} else {
118+
actionPlayers = [currentPlayer];
119+
}
120+
}
99121

100122
return {
101123
...ctx,
@@ -121,6 +143,10 @@ export const TurnOrder = {
121143
* Each object defines the first player when the phase / game
122144
* begins, and also a function `next` to determine who the
123145
* next player is when the turn ends.
146+
*
147+
* first / next can also return an object of type
148+
* { playOrderPos, actionPlayers }
149+
* in which case they can also set actionPlayers simultaneously.
124150
*/
125151

126152
/**
@@ -136,11 +162,19 @@ export const TurnOrder = {
136162
/**
137163
* ANY
138164
*
139-
* Any player can play and there isn't a currentPlayer really.
165+
* currentPlayer switches around in round-robin fashion, but any player can play on each turn.
140166
*/
141167
ANY: {
142-
first: () => undefined,
143-
next: () => undefined,
168+
first: (G, ctx) => {
169+
return {
170+
actionPlayers: [...ctx.playOrder],
171+
playOrderPos: ctx.playOrderPos,
172+
};
173+
},
174+
next: (G, ctx) => {
175+
const playOrderPos = (ctx.playOrderPos + 1) % ctx.playOrder.length;
176+
return { actionPlayers: [...ctx.playOrder], playOrderPos };
177+
},
144178
},
145179

146180
/**

src/core/turn-order.test.js

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import { FlowWithPhases } from './flow';
10-
import { TurnOrder, Pass } from './turn-order';
10+
import { UpdateTurnOrderState, TurnOrder, Pass } from './turn-order';
1111
import Game from './game';
1212
import { makeMove, gameEvent } from './action-creators';
1313
import { CreateGameReducer } from './reducer';
@@ -27,20 +27,6 @@ describe('turnOrder', () => {
2727
expect(state.ctx.actionPlayers).toEqual(['1']);
2828
});
2929

30-
test('any', () => {
31-
const flow = FlowWithPhases({
32-
phases: [{ name: 'A', turnOrder: TurnOrder.ANY }],
33-
});
34-
35-
let state = { ctx: flow.ctx(10) };
36-
state = flow.init(state);
37-
expect(state.ctx.currentPlayer).toBe('any');
38-
expect(state.ctx.actionPlayers).toEqual(['any']);
39-
state = flow.processGameEvent(state, gameEvent('endTurn'));
40-
expect(state.ctx.currentPlayer).toBe('any');
41-
expect(state.ctx.actionPlayers).toEqual(['any']);
42-
});
43-
4430
test('custom', () => {
4531
const flow = FlowWithPhases({
4632
phases: [{ name: 'A', turnOrder: { first: () => 9, next: () => 3 } }],
@@ -68,7 +54,7 @@ test('passing', () => {
6854
let state = reducer(undefined, { type: 'init' });
6955

7056
expect(state.ctx.currentPlayer).toBe('0');
71-
state = reducer(state, makeMove('pass'));
57+
state = reducer(state, makeMove('pass', null, '0'));
7258
state = reducer(state, gameEvent('endTurn'));
7359
expect(state.G.allPassed).toBe(undefined);
7460
expect(state.G.passOrder).toEqual(['0']);
@@ -83,7 +69,7 @@ test('passing', () => {
8369
expect(state.G.allPassed).toBe(undefined);
8470

8571
expect(state.ctx.currentPlayer).toBe('1');
86-
state = reducer(state, makeMove('pass'));
72+
state = reducer(state, makeMove('pass', null, '1'));
8773
state = reducer(state, gameEvent('endTurn'));
8874
expect(state.G.allPassed).toBe(undefined);
8975
expect(state.G.passOrder).toEqual(['0', '1']);
@@ -93,7 +79,7 @@ test('passing', () => {
9379
expect(state.G.allPassed).toBe(undefined);
9480

9581
expect(state.ctx.currentPlayer).toBe('2');
96-
state = reducer(state, makeMove('pass'));
82+
state = reducer(state, makeMove('pass', null, '2'));
9783
expect(state.G.allPassed).toBe(true);
9884
expect(state.ctx.currentPlayer).toBe('2');
9985
state = reducer(state, gameEvent('endTurn'));
@@ -114,7 +100,7 @@ test('end game after everyone passes', () => {
114100
const reducer = CreateGameReducer({ game, numPlayers: 3 });
115101

116102
let state = reducer(undefined, { type: 'init' });
117-
expect(state.ctx.currentPlayer).toBe('any');
103+
expect(state.ctx.actionPlayers).toEqual(['0', '1', '2']);
118104

119105
// Passes can be make in any order with TurnOrder.ANY.
120106

@@ -250,3 +236,31 @@ describe('change action players', () => {
250236
expect(state.G).toMatchObject({});
251237
});
252238
});
239+
240+
describe('UpdateTurnOrderState', () => {
241+
const G = {};
242+
const ctx = {
243+
currentPlayer: '0',
244+
playOrder: ['0', '1', '2'],
245+
playOrderPos: 0,
246+
actionPlayers: ['0'],
247+
};
248+
249+
test('without nextPlayer', () => {
250+
const t = UpdateTurnOrderState(G, ctx, TurnOrder.DEFAULT);
251+
expect(t).toMatchObject({ currentPlayer: '1' });
252+
});
253+
254+
test('with nextPlayer', () => {
255+
const t = UpdateTurnOrderState(G, ctx, TurnOrder.DEFAULT, '2');
256+
expect(t).toMatchObject({ currentPlayer: '2' });
257+
});
258+
259+
test('with actionPlayers', () => {
260+
const t = UpdateTurnOrderState(G, ctx, TurnOrder.ANY);
261+
expect(t).toMatchObject({
262+
currentPlayer: '1',
263+
actionPlayers: ['0', '1', '2'],
264+
});
265+
});
266+
});

0 commit comments

Comments
 (0)