Skip to content

Commit

Permalink
change format of args to endPhase and endTurn
Browse files Browse the repository at this point in the history
OLD
endPhase('new phase')
endTurn('new player')

NEW
endPhase({ next: 'new phase' })
endTurn({ next: 'new player' })
  • Loading branch information
nicolodavis committed Nov 9, 2018
1 parent 0568857 commit 992416a
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 92 deletions.
26 changes: 26 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,32 @@ phases: {
game reverts to in case it detects an infinite loop of
`endPhase` events caused by a cycle.

4. The format of the argument to `endPhase` or the return
value of `endPhaseIf` is now an object of type `{ next: 'phase name' }`

```
// old
endPhase('new phase')
endPhaseIf: () => 'new phase'
// new
endPhase({ next: 'new phase' })
endPhaseIf: () => ({ next: 'new phase' })
```

5. The format of the argument to `endTurn` or the return
value of `endTurnIf` is now an object of type `{ next: playerID }`

```
// old
endTurn(playerID)
endTurnIf: () => playerID
// new
endTurn({ next: playerID })
endTurnIf: () => ({ next: playerID })
```

## v0.26.3

#### Features
Expand Down
55 changes: 38 additions & 17 deletions docs/phases.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,31 +88,52 @@ has three phases, even though we never use the `default` phase.

A phase ends when one of the following happens:

###### 1. `endPhaseIf` triggers:
###### `endPhaseIf` triggers:

This is a simple boolean function that terminates the phase when
it returns `true` (see the example above).
This is a function that terminates the phase when it returns a truthy value (see the example above).

###### 2. The `endPhase` event is dispatched:
###### The `endPhase` event is dispatched:

This can happen either in the game logic or from the client
directly. See the [Events API](events.md) for more details
on how to dispatch events.

###### What happens when a phase terminates?
### What happens when a phase terminates?

The game moves on to the "next" phase. This phase is determined by the
following in increasing order of precedence (i.e. if [2] and [4] are both
relevant, the result of [4] is used):
The game moves on to the "next" phase. This phase is determined by the following (the latter ones overruling the former):

1. The `default` phase is chosen as the next phase if no other option is present.
#### 1

2. If a phase specifies the `next` option (like our example above does), then that is
chosen as the next phase.
The `default` phase is chosen as the next phase if no other option is present.

3. `endPhaseIf` can return the name of the next phase.
#### 2

4. The `endPhase` event accepts the name of the next phase as an argument.
If a phase specifies the `next` option (like our example above does), then that is chosen as the next phase.

```js
phases: {
A: {
next: 'B';
}
}
```

#### 3

The `endPhase` event accepts an argument that can specify the
next phase:

```js
endPhase({ next: 'B' });
```

#### 4

`endPhaseIf` can return an object specifying the next phase:

```js
endPhaseIf: () => ({ next: 'B' });
```

Watch our game in action now with phases. Notice that you can only draw cards in the first
phase, and you can only play cards in the second phase.
Expand Down Expand Up @@ -149,11 +170,11 @@ Let's take a look at some of these:

```js
flow: {
// Ends the turn if this returns true.
endTurnIf: (G, ctx) => boolean
// Ends the turn if this returns a truthy value.
endTurnIf: (G, ctx) => boolean|object

// Ends the game if this returns anything other than undefined.
endGameIf: (G, ctx) => boolean
endGameIf: (G, ctx) => {}

// Run at the start of a turn.
onTurnBegin: (G, ctx) => G
Expand All @@ -167,7 +188,7 @@ flow: {
phases: {
A: {
// Ends the phase if this returns a truthy value.
endPhaseIf: (G, ctx) => {}
endPhaseIf: (G, ctx) => boolean|object

// Run at the beginning of a phase.
onPhaseBegin: (G, ctx) => G
Expand Down
20 changes: 12 additions & 8 deletions docs/turn-order.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Game({
phases: {
A: { turnOrder: TurnOrder.ANY },
B: { turnOrder: TurnOrder.ONCE },
],
},
}
}
```
Expand Down Expand Up @@ -175,21 +175,25 @@ returning an object of type:
### endTurn / endTurnIf
You can also specify the next player during the `endTurn` event.
The `endTurn` event takes an additional argument specifying
the next player. If `endTurnIf` returns a string that is a playerID,
that player is made the next player (instead of following the turn
order).
The `endTurn` event takes an additional argument that may specify
the next player:
Player '3' is made the new player in both the following examples:
```js
endTurn({ next: playerID });
```
This argument can also be the return value of `endTurnIf` and works the same way.
Player `3` is made the new player in both the following examples:
```js
onClickEndTurn() {
this.props.endTurn('3');
this.props.events.endTurn({ next: '3' });
}
```
```js
flow: {
endTurnIf: (G, ctx) => '3',
endTurnIf: (G, ctx) => ({ next: '3' }),
}
```
8 changes: 4 additions & 4 deletions examples/react/turnorder/example-militia.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ const code = `{
},
onTurnBegin(G, ctx) {
ctx.events.endPhase('play');
ctx.events.endPhase({ next: 'play' });
return G;
},
},
moves: {
play(G, ctx) {
ctx.events.endPhase('discard');
ctx.events.endPhase({ next: 'discard' });
ctx.events.setActionPlayers({ allOthers: true, once: true });
return G;
},
Expand Down Expand Up @@ -63,14 +63,14 @@ export default {
},

onTurnBegin(G, ctx) {
ctx.events.endPhase('play');
ctx.events.endPhase({ next: 'play' });
return G;
},
},

moves: {
play(G, ctx) {
ctx.events.endPhase('discard');
ctx.events.endPhase({ next: 'discard' });
ctx.events.setActionPlayers({ allOthers: true, once: true });
return G;
},
Expand Down
4 changes: 2 additions & 2 deletions examples/react/turnorder/simulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Board extends React.Component {
this.props.ctx.allowedMoves.includes(e[0])
)
.map(e => (
<button key={e[0]} onClick={e[1]}>
<button key={e[0]} onClick={() => e[1]()}>
{e[0]}
</button>
));
Expand All @@ -63,7 +63,7 @@ class Board extends React.Component {
.filter(() => current && active)
.filter(e => e[0] != 'setActionPlayers')
.map(e => (
<button key={e[0]} onClick={e[1]}>
<button key={e[0]} onClick={() => e[1]()}>
{e[0]}
</button>
));
Expand Down
37 changes: 19 additions & 18 deletions src/core/flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ export function Flow({
* @param {...object} endTurnIf - The turn automatically ends if this
* returns a truthy value
* (checked after each move).
* If the return value is a playerID,
* If the return value is { next: playerID },
* that player is the next player
* (instead of following the turn order).
* (G, ctx) => boolean|string
* (G, ctx) => boolean|object
*
* @param {...object} endGameIf - The game automatically ends if this function
* returns anything (checked after each move).
Expand Down Expand Up @@ -199,15 +199,14 @@ export function Flow({
* onPhaseEnd: (G, ctx) => G,
*
* // The phase ends if this function returns a truthy value.
* // If the return value is the name of another phase,
* // that will be chosen as the next phase (as opposed
* // to the next one in round-robin order).
* endPhaseIf: (G, ctx) => boolean|string,
* // If the return value is of the form { next: 'phase name' }
* // then that will be chosen as the next phase.
* endPhaseIf: (G, ctx) => boolean|object,
*
* Phase-specific options that override their global equivalents:
*
* // A phase-specific endTurnIf.
* endTurnIf: (G, ctx) => boolean,
* endTurnIf: (G, ctx) => boolean|object,
*
* // A phase-specific endGameIf.
* endGameIf: (G, ctx) => {},
Expand Down Expand Up @@ -292,7 +291,7 @@ export function FlowWithPhases({
const conf = phaseMap[phase];

if (conf.endPhaseIf === undefined) {
conf.endPhaseIf = () => false;
conf.endPhaseIf = () => undefined;
}
if (conf.onPhaseBegin === undefined) {
conf.onPhaseBegin = G => G;
Expand Down Expand Up @@ -414,7 +413,7 @@ export function FlowWithPhases({
* If this call results in a cycle, the phase is reset to
* the default phase.
*/
function endPhaseEvent(state, nextPhase, visitedPhases) {
function endPhaseEvent(state, arg, visitedPhases) {
let G = state.G;
let ctx = state.ctx;

Expand All @@ -428,14 +427,16 @@ export function FlowWithPhases({
}

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

// Run any setup code for the new phase.
Expand All @@ -456,7 +457,7 @@ export function FlowWithPhases({
state,
automaticGameEvent(
'endPhase',
['default', visitedPhases],
[{ next: 'default' }, visitedPhases],
this.playerID
)
);
Expand Down Expand Up @@ -490,7 +491,7 @@ export function FlowWithPhases({
* Ends the current turn.
* Passes the turn to the next turn in a round-robin fashion.
*/
function endTurnEvent(state, nextPlayer) {
function endTurnEvent(state, arg) {
let { G, ctx } = state;

const conf = phaseMap[ctx.phase];
Expand Down Expand Up @@ -518,7 +519,7 @@ export function FlowWithPhases({
G,
ctx,
conf.turnOrder,
nextPlayer
arg
);
endPhase = a;
ctx = b;
Expand Down
Loading

0 comments on commit 992416a

Please sign in to comment.