Skip to content

Commit

Permalink
add allOthers option to setActionPlayers (#269)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Stefan-Hanke authored and nicolodavis committed Aug 30, 2018
1 parent e4636f0 commit 0b7a0a0
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 21 deletions.
7 changes: 4 additions & 3 deletions examples/react/modules/turnorder/components/board.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ const Board = ({ ctx, G, playerID, events, moves }) => {

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

const canEndTurn = deepEquals(ctx.actionPlayers, [playerID]);
const canEndTurn =
deepEquals(ctx.actionPlayers, [playerID]) && playerID === ctx.currentPlayer;
const canDrop =
ctx.actionPlayers.includes(playerID) && ctx.currentPlayer != playerID;
const canPlay = canEndTurn && playerData.actions > 0;
Expand All @@ -69,8 +70,7 @@ const Board = ({ ctx, G, playerID, events, moves }) => {
<button
disabled={!canDrop}
onClick={() => {
let ap = ctx.actionPlayers.filter(nr => nr !== playerID);
events.changeActionPlayers(ap);
moves.dropCards();
}}
>
Drop Cards
Expand All @@ -92,6 +92,7 @@ const Board = ({ ctx, G, playerID, events, moves }) => {
<span>
<div>{playerData.name}</div>
<div>Actions: {playerData.actions}</div>
<div>Cards: {playerData.cards}</div>
</span>
{buttons}
{currentPlayer}
Expand Down
16 changes: 13 additions & 3 deletions examples/react/modules/turnorder/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,25 @@ const TurnExample = Game({
players: [
{
name: 'Player 1',
cards: 5,
actions: 0,
},
{
name: 'Player 2',
cards: 5,
actions: 0,
},
{
name: 'Player 3',
cards: 5,
actions: 0,
},
],
}),

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

const currentPlayer = ctx.currentPlayer;
const playersNext = [...G.players];
Expand All @@ -41,6 +42,15 @@ const TurnExample = Game({
const nextG = { players: playersNext };
return nextG;
},

dropCards: (G, ctx) => {
const newPlayer = { ...G.players[+ctx.playerID], cards: 3 };
// TODO functional approach to replace element from array?
const newPlayers = [...G.players];
newPlayers[+ctx.playerID] = newPlayer;

return { ...G, players: newPlayers };
},
},

flow: {
Expand Down
9 changes: 8 additions & 1 deletion src/core/flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -550,10 +550,17 @@ export function FlowWithPhases({

// Update actionPlayers if _actionPlayersOnce is set.
let actionPlayers = state.ctx.actionPlayers;
if (state.ctx._actionPlayersOnce == true) {
if (state.ctx._actionPlayersOnce) {
const playerID = action.playerID;
actionPlayers = actionPlayers.filter(id => id !== playerID);
}
if (state.ctx._actionPlayersAllOthers) {
const playerID = action.playerID;
actionPlayers = actionPlayers.filter(id => id !== playerID);
if (actionPlayers.length === 0) {
actionPlayers = [state.ctx.currentPlayer];
}
}

state = {
...state,
Expand Down
32 changes: 25 additions & 7 deletions src/core/flow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,23 +479,41 @@ test('canMakeMove', () => {
});

test('canPlayerMakeMove', () => {
// default behaviour
const pid = '0';
const playerID = '0';

let flow = Flow({});
expect(flow.canPlayerMakeMove({}, {}, pid)).toBe(false);
expect(flow.canPlayerMakeMove({}, {}, playerID)).toBe(false);
// NOTE: currentPlayer is not allowed to make a move by default.
// Their playerID must be included in the actionPlayers array.
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['1'] }, pid)).toBe(false);
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['0'] }, pid)).toBe(true);
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['1'] }, playerID)).toBe(
false
);
expect(flow.canPlayerMakeMove({}, { actionPlayers: ['0'] }, playerID)).toBe(
true
);

// no one can make a move
flow = Flow({ canPlayerMakeMove: () => false });
expect(flow.canPlayerMakeMove({}, {}, pid)).toBe(false);
expect(flow.canPlayerMakeMove({}, { actionPlayers: [] }, pid)).toBe(false);
expect(flow.canPlayerMakeMove({}, {}, playerID)).toBe(false);
expect(flow.canPlayerMakeMove({}, { actionPlayers: [] }, playerID)).toBe(
false
);
expect(flow.canPlayerMakeMove({}, {}, '5')).toBe(false);
});

test('canPlayerCallEvent', () => {
const playerID = '0';

let flow = Flow({});
expect(flow.canPlayerCallEvent({}, {}, playerID)).toBe(false);
expect(flow.canPlayerCallEvent({}, { actionPlayers: ['1'] }, playerID)).toBe(
false
);
expect(flow.canPlayerCallEvent({}, { actionPlayers: ['0'] }, playerID)).toBe(
false
);
});

test('endGame', () => {
const flow = FlowWithPhases({ endGame: true });
const state = { ctx: {} };
Expand Down
19 changes: 12 additions & 7 deletions src/core/turn-order.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,33 @@ export const Pass = (G, ctx) => {
* }
*/
export function SetActionPlayers(state, arg) {
let _actionPlayersOnce = false;
let actionPlayers = [];

if (arg.once) {
_actionPlayersOnce = true;
}

if (arg.value) {
actionPlayers = arg.value;
}

if (arg.all) {
actionPlayers = [...state.ctx.playOrder];
}

if (arg.allOthers) {
actionPlayers = [...state.ctx.playOrder].filter(
nr => nr !== state.ctx.currentPlayer
);
}

if (Array.isArray(arg)) {
actionPlayers = arg;
}

return {
...state,
ctx: { ...state.ctx, actionPlayers, _actionPlayersOnce },
ctx: {
...state.ctx,
actionPlayers,
_actionPlayersOnce: arg.once,
_actionPlayersAllOthers: arg.allOthers,
},
};
}

Expand Down
35 changes: 35 additions & 0 deletions src/core/turn-order.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,41 @@ describe('SetActionPlayers', () => {
expect(state.ctx.actionPlayers).toEqual([]);
});

test('allOthers', () => {
const game = Game({
flow: {
setActionPlayers: true,
},

moves: {
B: (G, ctx) => {
ctx.events.setActionPlayers({
value: ['0', '1', '2'],
allOthers: true,
});
return G;
},
A: G => G,
},
});

const reducer = CreateGameReducer({ game, numPlayers: 3 });

let state = reducer(undefined, { type: 'init' });

// on move B, control switches from player 0 to players 1 and 2
state = reducer(state, makeMove('B', null, '0'));
expect(state.ctx.actionPlayers).toEqual(['1', '2']);

// player 1 makes move
state = reducer(state, makeMove('A', null, '1'));
expect(state.ctx.actionPlayers).toEqual(['2']);

// player 1 makes move - after that, control should return to player 0
state = reducer(state, makeMove('A', null, '2'));
expect(state.ctx.actionPlayers).toEqual(['0']);
});

test('militia', () => {
const game = Game({
flow: { setActionPlayers: true },
Expand Down

0 comments on commit 0b7a0a0

Please sign in to comment.