Skip to content

Commit

Permalink
convert startingPhase into boolean option
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolodavis committed Sep 10, 2019
1 parent b9ce7f1 commit cff284b
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 117 deletions.
6 changes: 3 additions & 3 deletions src/client/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ describe('multiplayer', () => {

test('onAction called', () => {
jest.spyOn(client.transport, 'onAction');
client.store.dispatch(sync({ G: {}, ctx: { phase: 'default' } }, []));
client.store.dispatch(sync({ G: {}, ctx: { phase: '' } }, []));
client.moves.A();
expect(client.transport.onAction).toHaveBeenCalled();
});
Expand Down Expand Up @@ -439,13 +439,13 @@ describe('log handling', () => {
{
action: makeMove('A', [], '0'),
_stateID: 0,
phase: 'default',
phase: '',
turn: 0,
},
{
action: makeMove('A', [], '0'),
_stateID: 1,
phase: 'default',
phase: '',
turn: 0,
},
]);
Expand Down
3 changes: 1 addition & 2 deletions src/client/log/log.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ Enzyme.configure({ adapter: new Adapter() });

describe('layout', () => {
const game = {
startingPhase: 'A',
phases: {
A: { next: 'B' },
A: { next: 'B', start: true },
B: { next: 'A' },
},
};
Expand Down
62 changes: 30 additions & 32 deletions src/core/flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,13 @@ export function Flow({
*
* // A phase-specific turn structure that overrides the global.
* turn: { ... },
*
* // Set to true to begin the game in this phase. Only one phase
* // can have this set to true.
* start: false,
* }
*/
export function FlowWithPhases({
phases,
startingPhase,
endIf,
turn,
events,
plugins,
}) {
export function FlowWithPhases({ phases, endIf, turn, events, plugins }) {
// Attach defaults.
if (events === undefined) {
events = {};
Expand All @@ -203,23 +200,28 @@ export function FlowWithPhases({
if (plugins === undefined) {
plugins = [];
}
if (!startingPhase) startingPhase = 'default';

if (!endIf) endIf = () => undefined;
if (!turn) turn = {};

const phaseMap = phases || {};

if ('default' in phaseMap) {
logging.error('cannot specify phase with name "default"');
if ('' in phaseMap) {
logging.error('cannot specify phase with empty name');
}

phaseMap['default'] = {};
phaseMap[''] = {};

let moveMap = {};
let startingPhase = '';

for (let phase in phaseMap) {
const conf = phaseMap[phase];

if (conf.start === true) {
startingPhase = phase;
}

if (conf.moves !== undefined) {
for (let move of Object.keys(conf.moves)) {
moveMap[phase + '.' + move] = conf.moves[move];
Expand Down Expand Up @@ -258,13 +260,17 @@ export function FlowWithPhases({
conf.turn.onEnd = plugin.FnWrap(conf.turn.onEnd, plugins);
}

function GetPhase(ctx) {
return phaseMap[ctx.phase];
}

const shouldEndPhase = ({ G, ctx }) => {
const conf = phaseMap[ctx.phase];
const conf = GetPhase(ctx);
return conf.endIf(G, ctx);
};

const shouldEndTurn = ({ G, ctx }) => {
const conf = phaseMap[ctx.phase];
const conf = GetPhase(ctx);

const currentPlayerMoves = ctx.stats.turn.numMoves[ctx.currentPlayer] || 0;
if (conf.turn.moveLimit && currentPlayerMoves >= conf.turn.moveLimit) {
Expand Down Expand Up @@ -317,12 +323,7 @@ export function FlowWithPhases({
};

const startGame = function(state) {
if (!(state.ctx.phase in phaseMap)) {
logging.error('invalid startingPhase: ' + state.ctx.phase);
return state;
}

const conf = phaseMap[state.ctx.phase];
const conf = GetPhase(state.ctx);
state = startPhase(state, conf);
state = startTurn(state, conf);
return state;
Expand All @@ -346,31 +347,29 @@ export function FlowWithPhases({
let ctx = state.ctx;

// Run any cleanup code for the phase that is about to end.
const conf = phaseMap[ctx.phase];
const conf = GetPhase(ctx);
G = conf.onEnd(G, ctx);

const gameover = endIf(G, ctx);
if (gameover !== undefined) {
return { ...state, G, ctx: { ...ctx, gameover } };
}

const prevPhase = ctx.phase;

// Update the phase.
if (arg && arg !== true) {
if (arg.next in phaseMap) {
ctx = { ...ctx, phase: arg.next, prevPhase };
ctx = { ...ctx, phase: arg.next };
} else {
logging.error('invalid argument to endPhase: ' + arg);
}
} else if (conf.next !== undefined) {
ctx = { ...ctx, phase: conf.next, prevPhase };
ctx = { ...ctx, phase: conf.next };
} else {
ctx = { ...ctx, phase: ctx.prevPhase, prevPhase };
ctx = { ...ctx, phase: '' };
}

// Run any setup code for the new phase.
state = startPhase({ ...state, G, ctx }, phaseMap[ctx.phase]);
state = startPhase({ ...state, G, ctx }, GetPhase(ctx));

const origTurn = state.ctx.turn;

Expand All @@ -387,7 +386,7 @@ export function FlowWithPhases({
state,
automaticGameEvent(
'endPhase',
[{ next: 'default' }, visitedPhases],
[{ next: '' }, visitedPhases],
this.playerID
)
);
Expand Down Expand Up @@ -424,7 +423,7 @@ export function FlowWithPhases({
function endTurnEvent(state, arg) {
let { G, ctx } = state;

const conf = phaseMap[ctx.phase];
const conf = GetPhase(ctx);

// Prevent ending the turn if moveLimit haven't been made.
const currentPlayerMoves = ctx.stats.turn.numMoves[ctx.currentPlayer] || 0;
Expand Down Expand Up @@ -503,7 +502,7 @@ export function FlowWithPhases({
}

function processMove(state, action, dispatch) {
let conf = phaseMap[state.ctx.phase];
let conf = GetPhase(state.ctx);

state = updateStats(state, 'turn', action.playerID);
state = updateStats(state, 'phase', action.playerID);
Expand Down Expand Up @@ -542,7 +541,7 @@ export function FlowWithPhases({
automaticGameEvent('endPhase', [endPhase], action.playerID)
);
// Update to the new phase configuration
conf = phaseMap[state.ctx.phase];
conf = GetPhase(state.ctx);
}

// End the turn automatically if turn.endIf is true or if endIf returns.
Expand Down Expand Up @@ -610,7 +609,6 @@ export function FlowWithPhases({
stats: { turn: { numMoves: {} }, phase: { numMoves: {} } },
allPlayed: false,
phase: startingPhase,
prevPhase: 'default',
stage: {},
}),
init: state => {
Expand Down
68 changes: 16 additions & 52 deletions src/core/flow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,19 @@ test('Flow', () => {
});

describe('phases', () => {
test('invalid startingPhase', () => {
const flow = FlowWithPhases({
startingPhase: 'A',
phases: { B: {} },
});
flow.init({ ctx: flow.ctx(2) });
expect(error).toHaveBeenCalledWith(`invalid startingPhase: A`);
});

test('invalid phase name', () => {
const flow = FlowWithPhases({
startingPhase: 'A',
phases: { default: {} },
phases: { '': {} },
});
flow.init({ ctx: flow.ctx(2) });
expect(error).toHaveBeenCalledWith(
`cannot specify phase with name "default"`
);
expect(error).toHaveBeenCalledWith('cannot specify phase with empty name');
});

test('onBegin / onEnd', () => {
const flow = FlowWithPhases({
startingPhase: 'A',

phases: {
A: {
start: true,
onBegin: s => ({ ...s, setupA: true }),
onEnd: s => ({ ...s, cleanupA: true }),
next: 'B',
Expand Down Expand Up @@ -101,8 +88,7 @@ describe('phases', () => {

test('endIf', () => {
const flow = FlowWithPhases({
startingPhase: 'A',
phases: { A: { endIf: () => true, next: 'B' }, B: {} },
phases: { A: { start: true, endIf: () => true, next: 'B' }, B: {} },
});

const state = { ctx: flow.ctx(2) };
Expand All @@ -126,9 +112,8 @@ describe('phases', () => {
test('infinite loop', () => {
const endIf = () => true;
const flow = FlowWithPhases({
startingPhase: 'A',
phases: {
A: { endIf, next: 'B' },
A: { endIf, next: 'B', start: true },
B: { endIf, next: 'A' },
},
});
Expand All @@ -138,7 +123,7 @@ describe('phases', () => {

expect(state.ctx.phase).toBe('A');
state = flow.processGameEvent(state, gameEvent('endPhase'));
expect(state.ctx.phase).toBe('default');
expect(state.ctx.phase).toBe('');
});

test('end phase on move', () => {
Expand All @@ -147,9 +132,9 @@ describe('phases', () => {
const onMove = () => ({ A: true });

const flow = FlowWithPhases({
startingPhase: 'A',
phases: {
A: {
start: true,
turn: { endIf: () => true, onMove },
endIf: () => true,
onEnd: () => ++endPhaseACount,
Expand All @@ -175,8 +160,7 @@ describe('phases', () => {
test('end turn when final phase is reached', () => {
const flow = FlowWithPhases({
turn: { endIf: (G, ctx) => ctx.phase === 'C' },
startingPhase: 'A',
phases: { A: { next: 'B' }, B: { next: 'C' }, C: {} },
phases: { A: { start: true, next: 'B' }, B: { next: 'C' }, C: {} },
});

let state = { G: {}, ctx: flow.ctx(2) };
Expand Down Expand Up @@ -292,8 +276,7 @@ test('onMove', () => {

test('init', () => {
let flow = FlowWithPhases({
startingPhase: 'A',
phases: { A: { onEnd: () => ({ done: true }) } },
phases: { A: { start: true, onEnd: () => ({ done: true }) } },
});

const orig = flow.ctx(2);
Expand All @@ -302,8 +285,7 @@ test('init', () => {
expect(state).toEqual({ G: {}, ctx: orig });

flow = FlowWithPhases({
startingPhase: 'A',
phases: { A: { onBegin: () => ({ done: true }) } },
phases: { A: { start: true, onBegin: () => ({ done: true }) } },
});

state = { ctx: orig };
Expand Down Expand Up @@ -381,9 +363,8 @@ describe('turn.endIf', () => {
A: () => ({ endTurn: true }),
B: G => G,
},
startingPhase: 'A',
phases: {
A: { turn: { endIf: G => G.endTurn } },
A: { start: true, turn: { endIf: G => G.endTurn } },
},
};
const client = Client({ game });
Expand Down Expand Up @@ -471,8 +452,7 @@ test('endGame', () => {

describe('endTurn / endPhase args', () => {
const flow = FlowWithPhases({
startingPhase: 'A',
phases: { A: { next: 'B' }, B: {}, C: {} },
phases: { A: { start: true, next: 'B' }, B: {}, C: {} },
});

const state = { ctx: flow.ctx(3) };
Expand Down Expand Up @@ -530,9 +510,8 @@ test('undoable moves', () => {
C: () => ({ C: true }),
},

startingPhase: 'A',
phases: {
A: {},
A: { start: true },
B: {},
},
};
Expand Down Expand Up @@ -574,8 +553,7 @@ test('undoable moves', () => {
test('endTurn is not called twice in one move', () => {
const flow = FlowWithPhases({
turn: { endIf: () => true },
startingPhase: 'A',
phases: { A: { endIf: G => G.endPhase, next: 'B' }, B: {} },
phases: { A: { start: true, endIf: G => G.endPhase, next: 'B' }, B: {} },
});

let state = flow.init({ G: {}, ctx: flow.ctx(2) });
Expand Down Expand Up @@ -628,8 +606,7 @@ test('allPlayed', () => {
describe('endPhase returns to previous phase', () => {
let state;
const flow = FlowWithPhases({
startingPhase: 'A',
phases: { A: {}, B: {}, C: {} },
phases: { A: { start: true }, B: {}, C: {} },
});

beforeEach(() => {
Expand All @@ -640,19 +617,6 @@ describe('endPhase returns to previous phase', () => {
test('returns to default', () => {
expect(state.ctx.phase).toBe('A');
state = flow.processGameEvent(state, gameEvent('endPhase'));
expect(state.ctx.phase).toBe('default');
});

test('returns to previous', () => {
expect(state.ctx.phase).toBe('A');
state = flow.processGameEvent(
state,
gameEvent('endPhase', [{ next: 'B' }])
);
expect(state.ctx.phase).toBe('B');
state = flow.processGameEvent(state, gameEvent('endPhase'));
expect(state.ctx.phase).toBe('A');
state = flow.processGameEvent(state, gameEvent('endPhase'));
expect(state.ctx.phase).toBe('B');
expect(state.ctx.phase).toBe('');
});
});

0 comments on commit cff284b

Please sign in to comment.