jkomoros / boardgame

An in-progress framework in golang to easily build boardgame Progressive Web Apps
Apache License 2.0
31 stars 4 forks source link

Implement a notion of diffs #188

Open jkomoros opened 7 years ago

jkomoros commented 7 years ago

Originally captured in #176

Comparing two states to each other you can get a Diff: a record of all of the changes necessary to transform the first state into the second.

For bools it's just a set. for Strings it's also a set (maybe in the future it could be more). But ints its an addition.

For Stacks, it's a record of which stack to which stack, and which component and slot index.

These diffs can be sent to the client as a convenience. They are stored in State, and state also makes sure to sanitize them according to the sanitization policy, such that no information can be recovered that would be impossible to recover by looking at the sanitized-before and sanitized-after.

That means that if, for example, a component moved between two stacks that are PolicyHidden, it would be sanitized out of the reported diff.

Diffs are annoying to calculate if you don't know where all of the components have gone. That's one of the reasons it's nice that we keep the invariants carefully with Stack.MoveComponent, Stack.SwapComponent, and Stack.Shuffle, because basically those are opportunities to just add that into the pending diff right away. (That would make it hard to take two arbitrary states and diff them, but as long as we store the diff in the DB at the time of creation it's fine)

Diffs will be really important for allowing not shipping the entire state to the client at every step, or having move logic that has to be sent to the client--all that needs to happen is that we apply a diff and we're done.

jkomoros commented 7 years ago

Note that computedProperties will also have to be in diffs, or otherwise be possible to compute client-side.

jkomoros commented 7 years ago

If we don't allow diffing between arbitrary versions, it will be much easier.

Does version 0 have a diff from the null state? Most properties are easy, but not Stacks, since otherwise all we have to do is encode stack movement between two stacks, and in the first version components are hanging out in the ether.

jkomoros commented 7 years ago
{
  Game: //...
  Diff: {
    Game : {
      //Only variables that are modified are represented
      BoolVar: true, //Bools are always just the new value
      StringVar: "newvalue", //strings are always just the new value
      IntVar: "-3", //Ints are always a value that is added to the old value,
      //Component changes go in components section, not the section they're logically in 
    },
    ComponentMoves: [
      //An array of move descriptors, in the order they were performed. Every descriptor has the from stack, from index, to stack, to index.
      //Are swaps and shuffles represented differently? I think we need something for swaps, but then we probably also need something for shuffles (having shuffles just being a long list of swaps would be expensive computationally)
      {
        FromStack: {
          //These are PropertyRef's, serialized
          Group: "Player",
          Index: 1,
          PropName: "HandStack",
        },
        //Indexes are always the fully resolved indexes that ComponentAt(index) would return the component before (from) or after (to) the move.
        FromIndex: 3,
        ToStack: {
          //...
        },
        ToIndex: 4,
      },
    ],
  },
  //...
}
jkomoros commented 7 years ago

Hmm, what kinds of Go objects are we going to use? Maybe a property bag?

jkomoros commented 7 years ago

Maybe instead of storing the actual diff for now, we can just calculate which PropertyRef's changed. (map[PropertyRef]bool), and not persist to DB, just use them right before saving to calculate which computed Properties need to be re-calculated