-
Notifications
You must be signed in to change notification settings - Fork 716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: deprecate moveLimit
in favour of minMoves
/maxMoves
#985
feat: deprecate moveLimit
in favour of minMoves
/maxMoves
#985
Conversation
@senritsu Thanks for this! I’ll try to get a review to you soon. |
minMoves
from moveLimit
Thinking about this some more, I am not sure if the whole From how I understand it, endIf: (G, ctx) => ctx.somewhere.numMoves >= MOVE_LIMIT On the other hand, canEnd: (G, ctx) => ctx.somewhere.numMoves >= MIN_MOVES This would have the additional benefit of being way more flexible and useful, since it can be used to check for arbitrarily complex constraints and not just the number of moves. Additionally it would also be very useful for the UI, since a manual "End turn" button could be disabled properly when the turn (or phase/stage) can't be ended. This seems like a better tradeoff between complexity budget and functionality, and it also raises the question if |
To expand on the previous point, supposing the mentioned predicates both exist, it would also be fairly trivial to replace the convenience of a simple endIf: afterMoves(4) // equivalent to old `moveLimit`
endIf: madeMoves(4) // expands to (G, ctx) => ctx.something.numMoves >= 4
endIf: moveLimit({currentPlayer: 2, others: 3}) // different limits per player
endIf: moveCount({allCombined: 5}) // shared limit for all players
// functions should work identical for both predicates, naming should be carefully chosen to fit both
canEnd: afterMoves(4)
canEnd: madeMoves(4)
canEnd: moveLimit({currentPlayer: 2, others: 3})
canEnd: moveCount({allCombined: 5}) As an additional bonus, the relation between the somewhat unclear |
Thanks for the thoughts — lot of good ideas there. This proposal would also impact stages — currently stages have a setActivePlayers({ others: 'A', moveLimit: 2 }); Because this is dynamic, the move limit has to be serializable. (In general we’ve also steered clear of hooks for stages to avoid developers needing to think about who is in which stage too much — i.e. unlike Given that, for consistency across turns and stages, I think we can’t throw out support for the declarative approach. That said, I don’t see anything against a In theory, we could refactor support for the // Deprecate `moveLimit` without a hard break for now.
if (phaseConfig.turn.moveLimit !== undefined) {
logging.info(
'`turn.moveLimit` is deprecated. Please use `turn.maxMoves` & `turn.minMoves` instead.'
);
phaseConfig.turn.minMoves = phaseConfig.turn.moveLimit;
phaseConfig.turn.maxMoves = phaseConfig.turn.moveLimit;
}
// Wrap `endIf` to check the move limit.
if (phaseConfig.turn.maxMoves !== undefined) {
const { endIf, maxMoves } = phaseConfig.turn;
phaseConfig.turn.endIf = (G, ctx) =>
endIf(G, ctx) || ctx.numMoves >= maxMoves;
} Then code in the rest of the flow reducer only needs to worry about calling What do you think? (Just tried this and it works, but I think wrapping |
Stages/DynamicI did actually wonder about how it would integrate with Stages, and I do agree that less cognitive overhead there is definitely a worthwhile goal. Supporting the dynamic case also makes a lot of sense, generally I am a big fan of data-driven/declarative approaches. Professionally I currently use mapbox, which does a lot of cool things in that direction, but having full serializable expression support may be a bit overkill at the moment. Worth a look for inspiration though, if the declarative approach is something that should be a focus. Intuitively it would kind of feel bad if the dynamic case were severely limited compared to the hook-based equivalent, but maybe in actual use it wouldn't matter too much. Having incomplete parallels between Turns and Stages feels similar. Wrapping functionsRegarding implementing the serializable options with a hook wrapper, the main argument in favor would probably be extensibility and simplicity in terms of how much stuff needs to be carted around or inherited during the whole flow/context bookkeeping. There are roughly 35 usages of the internal lookup where the per-player move limits are stored, double now with If internally everything would be compiled down to hook wrappers (which would probably require at least internal support for Stage hooks, in which case you might as well make them public for advanced use cases), it might cut down quite a bit of code, while simultaneously opening the path to add any number of similar declarative options (and of course supporting completely arbitrary behavior via the hooks themselves). The full ramifications are hard to theory-craft though, I would probably have to try some things hands-on to get a clearer picture. ConclusionsI do think that the whole topic of refactoring the declarative options to use hooks, as well as providing a new The normalization function is great, I was hoping for something like that but haven't had the time to look for it yet 😁 That was the main reason I left PS: on mobile, sorry for potential autocorrect screwups 🙃 |
Completely unrelated, docs update for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so mostly as discussed, here’s what I think this PR still needs.
-
Maintain the current behaviour but deprecate
moveLimit
, by adding something like the following around L150 inflow.ts
:// Deprecate `moveLimit` without a hard break for now. if (phaseConfig.turn.moveLimit !== undefined) { logging.info( '`turn.moveLimit` is deprecated. Please use `turn.maxMoves` & `turn.minMoves` instead.' ); phaseConfig.turn.minMoves = phaseConfig.turn.moveLimit; phaseConfig.turn.maxMoves = phaseConfig.turn.moveLimit; }
-
We then should make sure we replace any places that check
moveLimit
to checkmaxMoves
instead. -
Update docs to show
maxMoves
/minMoves
instead ofmoveLimit
. -
Add tests if required to make sure we’re testing the mapping of
moveLimit
tominMoves
/maxMoves
. -
Update the
ActivePlayers
default configs (end ofturn-order.ts
) to useminMoves
/maxMoves
.
minMoves
from moveLimit
moveLimit
in favour of minMoves
/maxMoves
After a long-ish break (thanks to surgery) I finally got around to update this PR. Most open tasks should be addressed, but I am having some trouble writing a test for backwards compatibility of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @senritsu! Hope you’re recovering well — sorry to hear that.
This looks good. Here are a few details and a suggested fix for your commented test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to go — just spotted one tiny testing improvement.
@senritsu Thanks for this! |
EndTurn
now checksminMoves
instead ofmoveLimit
like before to determine if ending the turn is possibleEndStage
now checksminMoves
similar to howEndTurn
does, previously it seemed to have no such check; in contrast toEndTurn
it does not use the phase config, but instead checks the resolved values directly to support usage ofminMoves
in the args ofsetActivePlayers
moveLimits
is kept as-is, to prevent breaking changes; a rename tomaxMoves
or introducingmaxMoves
as an alias while deprecatingmoveLimits
may be done at a later stageIn most places, I tried to keep the usage as much in-line with
moveLimits
as possible. Notable exceptions:moveLimits
is used for checking if a turn should be ended automatically (whereminMoves
is not)minMoves
is used to determine if manually ending a turn or stage is possible (wheremoveLimits
now has no relevance anymore).As this is my first time doing a deep-dive into the codebase, please give a thorough sanity check to everything. I am especially unsure about the integration in
EndStage
, and possible repercussions of checking or not checking forminMoves
there. Additionally I am not sure if there are more places where a check would be needed.I also noticed that
Process
inflow.ts
does not check if it should auto-EndStage
based onmoveLimits
, in contrast to the similar check forEndTurn
. Is this on purpose?Checklist
main
).Closes #934