←back to thread

333 points freetonik | 3 comments | | HN request time: 1.574s | source
Show context
fleabitdev ◴[] No.42471288[source]
This engine uses a Redux-like architecture. You have a State type (containing data like "the position of the black kingside rook") and a stream of in-game actions (like "knight to F3"). Each action is handled by a pure function which converts the current State to a new State. You can either transmit State deltas from the server to the client, or just transmit the actions themselves (https://longwelwind.net/blog/networking-turn-based-game/).

This design makes it easy to implement optimistic updates, rollback, replays, automated testing, and recovery after a disconnection. It's a surprisingly good fit for UI, too; you can render simple games as a React component which takes the current State as one of its props.

However, a stream of context-free actions can be a really inconvenient representation for some games. The rules of a board game are often like the control flow of a computer program: you'll see branching, iteration, local variables, function calls, structured concurrency, and sometimes even race conditions and reentrancy. When you try to represent all of this logic as a State object, you're basically maintaining a snapshot of a "call stack" as plain data, and manually resuming that "program" whenever you handle an action. It doesn't seem ideal.

I've been sketching a board game engine which would represent the game logic as normal code instead. It seems promising, but it really needs a couple of language features which don't exist in the mainstream yet, like serialisation of suspended async functions.

replies(5): >>42471791 #>>42472084 #>>42472570 #>>42475998 #>>42477605 #
LudwigNagasena ◴[] No.42472084[source]
My main pain point with any sort of Flux-like state management is transitions [1]. The state of UI is not fully described by the state of the game [2]. If I play a card, the game state can be instantly updated to the next decison-making point, but in reality I want to show steps of the game through animations, some of which are concurrent and some of which are consecutive. That usually ends up in a mess; and I've never seen someone implement it nicely.

[1] And generally dynamic stuff like drag-n-drop, which is infinitely times simpler in any other architecture than in React.

[2] That is also true for business apps, but their animations are usually so simple you can simply use CSS.

replies(7): >>42472171 #>>42472685 #>>42475075 #>>42475378 #>>42476566 #>>42476646 #>>42480234 #
1. sjrd ◴[] No.42475075[source]
The way I did this was to design a more-or-less monadic container `Result<A>` for all my game logic functions. It batches a sequence of animation steps with a result. It can also model error conditions (like not having enough resources for example). I can then instantiate it any concrete result type, such as a full game state or just the result of individual computations. It was very nice to concisely write complicated game logic with animations while retaining the happy path.

https://github.com/sjrd/barrage/blob/main/src/main/scala/be/...

replies(1): >>42475752 #
2. LudwigNagasena ◴[] No.42475752[source]
Interesting. Unfortunately, your repo seems to be private.
replies(1): >>42475821 #
3. sjrd ◴[] No.42475821[source]
Oh shoot, yes. I have imitation copies of the original game graphics in there, and so I can't make it public without violating copyright of their assets. :-(

Here is a public gist with the `Result` data structure, as well a good portion of the file handling all the game mechanics, which should show it gets used. https://gist.github.com/sjrd/34fe234d1b6232cf42ffda5d23292d3...