[HN Gopher] Boardgame.io: an engine for creating turn-based game...
___________________________________________________________________
Boardgame.io: an engine for creating turn-based games using
JavaScript
Author : freetonik
Score : 223 points
Date : 2024-12-18 10:50 UTC (2 days ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| downThePipe wrote:
| Then what, print it out to play on the kitchen table?
| tardibear wrote:
| It supports multiplayer over the network.
| ramon156 wrote:
| > multiplayer networking Its in the description
| vasco wrote:
| Put the screen on the kitchen table and sit around it, call it
| CounterTop Surface, have a little imagination man.
| thaumasiotes wrote:
| You say this like a joke, but that would bring a lot of
| usability and convenience improvements compared to a physical
| board game. Just moving stuff out of and back into the box
| can take a lot of work.
|
| It's easy to overlook rules that a computerized game
| automatically handles for you, though. For example, I'm part
| of a group that plays Heat (
| https://boardgamearena.com/gamepanel?game=heat ) every week.
| An important part of that game is moving the game-controlled
| cars, and since the platform handles that, only one person in
| the group. This is potentially quite bad for strategy,
| depending on the rules you don't know. Another issue that
| comes up in the same game is people losing track of the size
| of their deck. The game is very generous about the allowable
| timing of deck-manipulation effects, but if you're not
| actively paying attention you can draw through your deck and
| trigger a reshuffle, wiping out your discard pile, when you
| wanted to do something special before the reshuffle. This
| isn't a mistake it's possible to make while using a physical
| game.
| jz67 wrote:
| Hey sorry if this is not the place for it but I play heat a
| lot too and I was looking for a group like this on bgm
| since the random matches have a lot of people rage quiting
| etc. can I ask to join this group?
| azemetre wrote:
| Do you play live or turn based games? I never had an
| issue of people rage quitting, I also tend to set the
| games to one turn per day which is reasonable for most
| people.
| thaumasiotes wrote:
| > and since the platform handles that, only one person in
| the group.
|
| This should read "only one person in the group knows how it
| works".
| sethammons wrote:
| a tad further: a central phone or tablet as a main board that
| you interact with via touch and where you can then transfer
| over to your handheld device over the network to manage your
| hand. I've not seen this yet, and it would be a pretty great
| experience I think. No clean up
| dalmo3 wrote:
| This is basically how Jackbox games work.
| brudgers wrote:
| Looks like it might originally have been from Google based on
| this discussion
|
| https://news.ycombinator.com/item?id=15946425
| ErrantX wrote:
| The original author looks to have been at Google till 2020.
|
| https://nicolodavis.com/
| nicolodavis wrote:
| boardgame.io was created as a personal open-source project
| while I was employed at Google. It is not an official Google
| product.
| ferd wrote:
| Looks very interesting.
|
| Somewhat related: I've recently wrote a code-walkthrough (in
| Clojure) of modeling chess-like games.
|
| https://neuroning.com/boardgames-exercise/
|
| It's very basic and intended for teaching/learning functional
| programming, not a real library or engine like the OP.
| nicolodavis wrote:
| Original creator of boardgame.io here. A pleasant surprise to see
| this here after many years.
|
| More recently, I've been working on https://boardgamelab.app/,
| which uses a visual programming language to model game rules
| while also taking care of the UI layer.
| jfengel wrote:
| Oh, thank you. I've used your library a few times for personal
| projects, and it does exactly the thing that I needed. I really
| appreciate you having created this.
| thih9 wrote:
| The boardgamelab.app looks like a stealth, proprietary project;
| at least at the moment its T&C[1] says: "under this license you
| may not (...) use the materials for any commercial purpose, or
| for any public display (commercial or non-commercial)".
|
| Do you have any plans for the business model?
|
| [1]: https://boardgamelab.app/terms-and-conditions
| fourside wrote:
| What do you mean by stealth? I looked at the website and
| nothing about it makes it look like it would necessarily be
| free or open source.
| jzig wrote:
| Still needs clearer language IMO.
| thih9 wrote:
| > What do you mean by stealth?
|
| I meant stealth as in: "or for any public display
| (commercial or non-commercial)", see grandparent comment. I
| edited the grandparent comment now and added a comma
| between "stealth" and "proprietary", hope this is clearer.
| wordglyph wrote:
| Omg! I've been noodling about making my word game
| https://WordGlyph.xyz multi player and been dreading that
| journey but now here it is!
| doctorpangloss wrote:
| > More recently, I've been working on
| https://boardgamelab.app/, which uses a visual programming
| language to model game rules while also taking care of the UI
| layer.
|
| Suppose there were a technology that could turn the canvas you
| authored into finished, consistent art; and a way to turn
| natural language rules into correct code. Would you use it? Why
| or why not?
| anonymoushn wrote:
| It would be great to save time on the implementation of board
| game rules engines. Unfortunately the fine folks at FFG are
| really bad at figuring out what the rules actually are and
| telling people :(
| lukan wrote:
| "turn the canvas you authored into finished, consistent art"
|
| Like a jpeg? Otherwise I don't understand your question.
|
| " a way to turn natural language rules into correct code"
|
| And this is straight impossible, as natural language is by
| definition ambigious in meaning and code is not. Try your
| luck with LLM's, they come closest.
|
| (a subset of natural language might work, but this is kind of
| a complex research topic)
| mankyd wrote:
| I feel like saying that is supports AI players, but not having
| a simple, already hosted example is a disservice. Even tic tac
| toe, or go fish would be a nice hook to help people understand
| what it actually delivers.
| beretguy wrote:
| There was this iOS game which died which I wanted to recreate:
|
| https://web.archive.org/web/20161020010853/http://www.82apps...
|
| But I have 0 knowledge of game development. Maybe this could
| make my job easier? Or maybe somebody else who know how to
| write game can do it? Please?
| LosEstupidos wrote:
| The HN niggaz voted you down instead to point you onto
| something, very thoughtful indeed.
| fleabitdev wrote:
| 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.
| NathanaelRea wrote:
| Can you explain more what type of game would need a call stack
| snapshot? I've never developed a game, but it seems like as
| long as you store like the initial state and prng you could
| always get the current state by replaying the full history. All
| the other logic would be stored outside the state, and only
| added when "committed". As long as prng is stable and you start
| from the clean state every time, you'd get the same outcome.
| yojo wrote:
| I think I have a good example from Magic: the Gathering.
|
| There's a card called "Fact or Fiction". You reveal the top
| five cards of your deck. Then your opponent splits the cards
| into two piles. Then you pick one of the two piles to take
| into your hand.
|
| You'll need to store structures representing the choices that
| are intermediary steps (split cards, pick stack) in your
| state, which is basically function calls and their params (a
| call stack). This example could get hairier - Magic also
| features cards with branching logic "choose 1: do a or b". I
| can imagine designing cards with large and convoluted
| possible execution paths.
|
| You could have the card define a schema for state
| transitions/params and represent all these choices as JSON
| encoded POJOs, but as a developer it sounds a lot nicer to
| just be able to suspend an async function every time a choice
| is made.
| Longwelwind wrote:
| I had the same issue in AGoT:BG and I solved it by
| representing the state of the game as a tree. At any point
| of the game, the current game state is a leaf of the tree.
|
| You'd represent this kind of choice as a child node. When
| the user has made their choice, the code can return to the
| parent node with the choice being made so it can continue
| with the next "step" of the game.
| scrollaway wrote:
| This is the correct response. Hearthstone is structured
| like this internally.
|
| If you are curious about it, I wrote a cc0 spec which
| stores hearthstone game state in xml. It's based on how
| hearthstone stores game state on the server and client,
| and it was the first time a replay format was created for
| hearthstone: https://hearthsim.info/hsreplay/
|
| Incidentally the UI we wrote for hearthstone replays is a
| react app. It's funny because looking back it was the
| first time I used react and typescript, and both were not
| at all adopted by the js community yet at the time.
|
| https://github.com/hearthsim/joust
| fleabitdev wrote:
| That's the exact approach I'm considering for the new engine
| I mentioned!
|
| Although that strategy enables you to store and recover the
| state of a game, it doesn't give you the ability to inspect a
| snapshot of that state. How can you print the card which has
| just been played, if that data only exists as an argument in
| the call stack of a suspended async function? In the same way
| that you can't inspect the local variables captured by a
| closure, mainstream languages also provide no way to inspect
| a suspended stack frame.
|
| This problem interferes with debugging, consistency checks
| (e.g. hashing the game state to check that two clients are in
| sync), and unit testing.
| LudwigNagasena wrote:
| 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.
| semitones wrote:
| In my experience, the way to solve [1] and [2] is to design a
| game state that can also _fully_ describe the state of the
| UI, including animation cues.
| Longwelwind wrote:
| The way I wanted to implement this in my turn-based game
| engine:
|
| If you implement the deterministic update pattern to handle
| state synchronisation you can add "event" inside the logic
| that handles updates that pause the processing allowing your
| animations to be played. In JS, for example:
| async function handleUpdate(update) { if
| (update.type == "sell-items") {
| this.player.inventory[update.itemId] -= 1;
| await emitEvent("itemSold");
| this.player.money += 10; await
| emitEvent("moneyGain"); } }
|
| Server-side, "emitEvents" would be a no-op. Everything would
| resolve synchronously.
|
| Client-side, the UI can listen to those events to pause the
| updating of the game state to see the intermediary state of
| the game and play animations. When the animation is done, it
| can resolve the promise, resuming the game updating logic.
|
| If an update arrives while an update is being handled, it can
| be queued so it can be played after the current update
| finishes.
| sjrd wrote:
| 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/.
| ..
| LudwigNagasena wrote:
| Interesting. Unfortunately, your repo seems to be private.
| sjrd wrote:
| 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.g
| ithub.com/sjrd/34fe234d1b6232cf42ffda5d23292d3...
| nkrisc wrote:
| I toyed with an approach once that separated animations from
| game state updates.
|
| Every player action could cause a cascade of updates, which
| would all be resolved "instantly" to the point no more
| cascaded updates were left to be processed.
|
| While this is happening, any update that includes an
| animation pushes that to an "animation stack", then the
| animations are played back one by one to show the player what
| happened. In this animation state most input in disabled and
| the game is effectively on hold until the animations complete
| (or are skipped by the player).
|
| The "animations" were basically commands that the Model used
| to update the View, just with the option to apply them one by
| one over time.
| bbminner wrote:
| Yep, that's the exact issue I wanted to address with my own
| twist on the idea of an online boardgame engine - I was trying
| to actually persist the callstack of an async wasm vm function
| (game loop) execution into a database in rust. It is working in
| a sense that you can implement battle ships or tick tack toe,
| but I did not quite finish it. Happy to still make repo public
| if helpful.
|
| To be more specific, async wasm function was implemented as a
| poll loop sync function exported to the caller, there was (at
| the time) no way to move wasm mv memory, so it was persisted
| while the game was live and replayed from a message log stored
| in a db after/if preemption.
| billnreed wrote:
| I've seen this engine before and thought it was really cool!
|
| I've been playing around with writing my own turn-based game
| engine with TypeScript: https://github.com/snowbillr/turn-based-
| game-engine
|
| It's a WIP still, but what's really been the struggle for me is
| coming up with a data structure and traversal algorithm for the
| turn order. I wanted that to be extremely configurable by whoever
| uses the library.
|
| ```
|
| engine.defineFlow((f) => { f.node({
| actions: f.actions(WelcomeMessage), }) f.node(
| { actions: f.actions(RoundStart), cleanups:
| f.cleanups(RoundEnd, TestLog), },
| players.map(player => f.node({ playerId: player.id,
| actions: f.actions(TurnStart), cleanups:
| f.cleanups(TurnEnd), })), )
|
| });
|
| ```
|
| This example shows a TicTacToe game defined like this:
|
| ```
|
| - welcome
|
| - round - player 1 turn - player 2
| turn
|
| ```
|
| What I ended up with was essentially a tree that's traversed
| depth-first to enter each node, and then goes back up the same
| traversal path until a sibling node is found to dive into.
|
| That lets the user define rounds/turns/phases as children of each
| other in whatever shape they want.
|
| It's been a real fun project!
| DominikPeters wrote:
| Is there a simple demo that one can test? I see a tutorial about
| tic tac toe, but the result doesn't seem to be hosted anywhere.
| freetonik wrote:
| The tutorial page[1] has a link to open an interactive sandbox
| with a TicTacToe example. The link is titled "Edit in
| CodeSandbox".
|
| 1. https://boardgame.io/documentation/#/tutorial
| tasuki wrote:
| It's really cool that it has a MCTS bot built-in!
| throw94838211 wrote:
| I tried to use this before, but many parts didn't really work
| right, lots of conflict between dependencies, especially the
| server parts. I had to abandon what I wanted to do with it. Shame
| because many ideas were interesting, but felt it was a bit
| untested.
| mvolfik wrote:
| 10 months ago, there was also Boardzilla
| (https://www.boardzilla.io/) posted here:
| https://news.ycombinator.com/item?id=39180953 . Curious to see
| how these two compare
| Lucasoato wrote:
| If anybody knows of any reference, article, book to study and
| implement turn-based engines like this, please add them under
| this thread.
___________________________________________________________________
(page generated 2024-12-20 23:00 UTC)