Skip to content

Commit 0b7a0a0

Browse files
Stefan-Hankenicolodavis
authored andcommitted
add allOthers option to setActionPlayers (#269)
* fix militia - dropCards now a move * add tests for canPlayerCallEvent * allow other players to make a one-off move via allOthers * use allOthers for setting action players * revert calling events - only allow current player * only the current player can end the turn
1 parent e4636f0 commit 0b7a0a0

6 files changed

Lines changed: 97 additions & 21 deletions

File tree

examples/react/modules/turnorder/components/board.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ const Board = ({ ctx, G, playerID, events, moves }) => {
5555

5656
const deepEquals = (a, b) => JSON.stringify(a) === JSON.stringify(b);
5757

58-
const canEndTurn = deepEquals(ctx.actionPlayers, [playerID]);
58+
const canEndTurn =
59+
deepEquals(ctx.actionPlayers, [playerID]) && playerID === ctx.currentPlayer;
5960
const canDrop =
6061
ctx.actionPlayers.includes(playerID) && ctx.currentPlayer != playerID;
6162
const canPlay = canEndTurn && playerData.actions > 0;
@@ -69,8 +70,7 @@ const Board = ({ ctx, G, playerID, events, moves }) => {
6970
<button
7071
disabled={!canDrop}
7172
onClick={() => {
72-
let ap = ctx.actionPlayers.filter(nr => nr !== playerID);
73-
events.changeActionPlayers(ap);
73+
moves.dropCards();
7474
}}
7575
>
7676
Drop Cards
@@ -92,6 +92,7 @@ const Board = ({ ctx, G, playerID, events, moves }) => {
9292
<span>
9393
<div>{playerData.name}</div>
9494
<div>Actions: {playerData.actions}</div>
95+
<div>Cards: {playerData.cards}</div>
9596
</span>
9697
{buttons}
9798
{currentPlayer}

examples/react/modules/turnorder/game.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,25 @@ const TurnExample = Game({
1515
players: [
1616
{
1717
name: 'Player 1',
18+
cards: 5,
1819
actions: 0,
1920
},
2021
{
2122
name: 'Player 2',
23+
cards: 5,
2224
actions: 0,
2325
},
2426
{
2527
name: 'Player 3',
28+
cards: 5,
2629
actions: 0,
2730
},
2831
],
2932
}),
3033

3134
moves: {
3235
playMilitia: (G, ctx) => {
33-
// Need to keep the currentPlayer inside actionPlayers - otherwise
34-
// he will not be able to make any move anymore.
35-
ctx.events.setActionPlayers(['0', '1', '2']);
36+
ctx.events.setActionPlayers({ allOthers: true });
3637

3738
const currentPlayer = ctx.currentPlayer;
3839
const playersNext = [...G.players];
@@ -41,6 +42,15 @@ const TurnExample = Game({
4142
const nextG = { players: playersNext };
4243
return nextG;
4344
},
45+
46+
dropCards: (G, ctx) => {
47+
const newPlayer = { ...G.players[+ctx.playerID], cards: 3 };
48+
// TODO functional approach to replace element from array?
49+
const newPlayers = [...G.players];
50+
newPlayers[+ctx.playerID] = newPlayer;
51+
52+
return { ...G, players: newPlayers };
53+
},
4454
},
4555

4656
flow: {

src/core/flow.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,10 +550,17 @@ export function FlowWithPhases({
550550

551551
// Update actionPlayers if _actionPlayersOnce is set.
552552
let actionPlayers = state.ctx.actionPlayers;
553-
if (state.ctx._actionPlayersOnce == true) {
553+
if (state.ctx._actionPlayersOnce) {
554554
const playerID = action.playerID;
555555
actionPlayers = actionPlayers.filter(id => id !== playerID);
556556
}
557+
if (state.ctx._actionPlayersAllOthers) {
558+
const playerID = action.playerID;
559+
actionPlayers = actionPlayers.filter(id => id !== playerID);
560+
if (actionPlayers.length === 0) {
561+
actionPlayers = [state.ctx.currentPlayer];
562+
}
563+
}
557564

558565
state = {
559566
...state,

src/core/flow.test.js

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -479,23 +479,41 @@ test('canMakeMove', () => {
479479
});
480480

481481
test('canPlayerMakeMove', () => {
482-
// default behaviour
483-
const pid = '0';
482+
const playerID = '0';
484483

485484
let flow = Flow({});
486-
expect(flow.canPlayerMakeMove({}, {}, pid)).toBe(false);
485+
expect(flow.canPlayerMakeMove({}, {}, playerID)).toBe(false);
487486
// NOTE: currentPlayer is not allowed to make a move by default.
488487
// Their playerID must be included in the actionPlayers array.
489-
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['1'] }, pid)).toBe(false);
490-
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['0'] }, pid)).toBe(true);
488+
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['1'] }, playerID)).toBe(
489+
false
490+
);
491+
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['0'] }, playerID)).toBe(
492+
true
493+
);
491494

492495
// no one can make a move
493496
flow = Flow({ canPlayerMakeMove: () => false });
494-
expect(flow.canPlayerMakeMove({}, {}, pid)).toBe(false);
495-
expect(flow.canPlayerMakeMove({}, { actionPlayers: [] }, pid)).toBe(false);
497+
expect(flow.canPlayerMakeMove({}, {}, playerID)).toBe(false);
498+
expect(flow.canPlayerMakeMove({}, { actionPlayers: [] }, playerID)).toBe(
499+
false
500+
);
496501
expect(flow.canPlayerMakeMove({}, {}, '5')).toBe(false);
497502
});
498503

504+
test('canPlayerCallEvent', () => {
505+
const playerID = '0';
506+
507+
let flow = Flow({});
508+
expect(flow.canPlayerCallEvent({}, {}, playerID)).toBe(false);
509+
expect(flow.canPlayerCallEvent({}, { actionPlayers: ['1'] }, playerID)).toBe(
510+
false
511+
);
512+
expect(flow.canPlayerCallEvent({}, { actionPlayers: ['0'] }, playerID)).toBe(
513+
false
514+
);
515+
});
516+
499517
test('endGame', () => {
500518
const flow = FlowWithPhases({ endGame: true });
501519
const state = { ctx: {} };

src/core/turn-order.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,33 @@ export const Pass = (G, ctx) => {
3838
* }
3939
*/
4040
export function SetActionPlayers(state, arg) {
41-
let _actionPlayersOnce = false;
4241
let actionPlayers = [];
4342

44-
if (arg.once) {
45-
_actionPlayersOnce = true;
46-
}
47-
4843
if (arg.value) {
4944
actionPlayers = arg.value;
5045
}
51-
5246
if (arg.all) {
5347
actionPlayers = [...state.ctx.playOrder];
5448
}
5549

50+
if (arg.allOthers) {
51+
actionPlayers = [...state.ctx.playOrder].filter(
52+
nr => nr !== state.ctx.currentPlayer
53+
);
54+
}
55+
5656
if (Array.isArray(arg)) {
5757
actionPlayers = arg;
5858
}
5959

6060
return {
6161
...state,
62-
ctx: { ...state.ctx, actionPlayers, _actionPlayersOnce },
62+
ctx: {
63+
...state.ctx,
64+
actionPlayers,
65+
_actionPlayersOnce: arg.once,
66+
_actionPlayersAllOthers: arg.allOthers,
67+
},
6368
};
6469
}
6570

src/core/turn-order.test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,41 @@ describe('SetActionPlayers', () => {
235235
expect(state.ctx.actionPlayers).toEqual([]);
236236
});
237237

238+
test('allOthers', () => {
239+
const game = Game({
240+
flow: {
241+
setActionPlayers: true,
242+
},
243+
244+
moves: {
245+
B: (G, ctx) => {
246+
ctx.events.setActionPlayers({
247+
value: ['0', '1', '2'],
248+
allOthers: true,
249+
});
250+
return G;
251+
},
252+
A: G => G,
253+
},
254+
});
255+
256+
const reducer = CreateGameReducer({ game, numPlayers: 3 });
257+
258+
let state = reducer(undefined, { type: 'init' });
259+
260+
// on move B, control switches from player 0 to players 1 and 2
261+
state = reducer(state, makeMove('B', null, '0'));
262+
expect(state.ctx.actionPlayers).toEqual(['1', '2']);
263+
264+
// player 1 makes move
265+
state = reducer(state, makeMove('A', null, '1'));
266+
expect(state.ctx.actionPlayers).toEqual(['2']);
267+
268+
// player 1 makes move - after that, control should return to player 0
269+
state = reducer(state, makeMove('A', null, '2'));
270+
expect(state.ctx.actionPlayers).toEqual(['0']);
271+
});
272+
238273
test('militia', () => {
239274
const game = Game({
240275
flow: { setActionPlayers: true },

0 commit comments

Comments
 (0)