Skip to content

Instantly share code, notes, and snippets.

@aghull
Last active March 1, 2024 23:47
Show Gist options
  • Save aghull/5e25b3624575b0eac82e4997d10ffb38 to your computer and use it in GitHub Desktop.
Save aghull/5e25b3624575b0eac82e4997d10ffb38 to your computer and use it in GitHub Desktop.
/**
* i have an idea.
*
* Background:
*
* The flow was designed at first to encompass all flow control, loops and
* branches, etc. later during Cursed development, I had the idea to simplify
* some types of flow control where a single action was completely "embedded"
* within another. I called these "follow-ups". They used a simple mechanism
* where at any point during the execution of a player action, you could call
* game.followUp and the game would just keep a record of the queue of
* follow-ups and then once the game ceded control back to the flow, if any
* follow-ups were in the queue, the flow would need to process them before it
* would consider the current step "complete".
*
* I was a little surprised how useful the idea was, and how many people
* gravitated towards it instead of the flow fundamentals. I considered it a bit
* of a niche. While it's nice and straightfoward that you can call follow-up at
* any point when you need another action, it's a little awkward because when
* you call follow-up, you are queueing up an action *after* the current block
* completes, and I thought that is a little counter-intuitive. However, in
* practice it usually is called at the end of the block anyways so it doesn't
* seem like a big foot-gun. So, I think it might be a useful concept to double
* down on. So here's my idea.
*
* The current follow-up only accepts a single action. But it could take an
* entire sub-flow. The current `game#followUp` would be deprecated but would
* gracefully create a subflow with a single playerAction step of a single
* action. And then you could migrate over to the full subflows with their full
* expressive potential.
*
* It would look a little like this:
*/
game.defineFlow() // as today, but this is now the "main" flow
// a subflow named "cardActivity", takes some set of predetermined, immutable args when called
game.defineSubflow<{card: Card}>("cardActivity", ({ card }) => [
eachPlayer({
do: playerActions({
...
})
}),
...
])
// then you could call it while processing an action, e.g.
playerCard: player => action({
}).chooseOnBoard(
"card",...
).do(
() => {
...
if (card.isSpecial()) return game.doSubflow("cardActivity", { card });
}
)
/**
* When the subflow completes, it cedes control back to it's calling flow, using
* the stack. I don't think there's an easy way to get return values from it, as
* in a function call, but the subflow can set state on the game to be used by
* its parent flow.
*
* This would then be a useful tool to define a high-level main flow, with
* subflows for each phase of the game defined separately. For power grid as an
* example, I could have the auction kick of an entire bidding subflow since
* it's self-contained once you know the auction plant.
*
* There would need to be special rules around the defineSubflow call. It must
* determinstically return the same flow given a set of arguments, and must not
* mutate the game state while it's defining the flow. I think it would be
* possible to put some guard-rails on this to avoid foot-guns.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment