Terraforming-mars: Feature Request: Game log download

Created on 3 Aug 2020  路  17Comments  路  Source: bafolts/terraforming-mars

Request is to be able to download a text file containing all game logs at game end

enhancement

Most helpful comment

I think this issue may have veered off topic and likely due to me. The original idea was just to be able to get the entire log for the game. Currently, when the game ends, you can look back in the log a few generations but it cuts off there. Is the entire log on the server? Could there be a link to get the whole thing? That was the original question.

It has morphed into this other thing which is really more like a record of all decisions sent by the client to the server and anything the server updates because of those messages. I think this (or whatever it has become) should be a separate issue.

All 17 comments

If the log contains all the choices made then the log can be used to recreate the game which can be very useful for recreating bugs. It also allows for easy Undo (delete the last choice made then re-run the log). In the versions of the Web App boards that I've done, I kept two logs: One human readable and one more machine readable.

They look like this:

Human readable

Anna gains 40 MC;
Anna increases Steel production by 1;
Anna gains 5 Steel;
Bob gains 36 MC;
Bob gains 3 Plants;
Bob increases Plants production by 1;
Bob increases Plants production by 1;
Anna places Ocean tile (r:5 c:4 loc:3);
--Anna increases TR production by 1;
--Anna gains 2 Plants;
Bob spends 14 MC;
Bob increases Temp to -28;
--Bob increases TR production by 1;

Note: lines precede with '--' mean things that were done automatically because of whatever happened above (like placing the Ocean tile means gaining a TR and plants).

Same thing in machine version:

numplayers:2:Anna:Bob:
asset:1:40:MC:
production:1:1:Steel:
asset:1:5:Steel:
asset:2:36:MC:
asset:2:3:Plants:
production:2:1:Plants:
production:2:1:Plants:
tile:1:3:Ocean:
asset:2:-14:MC:
increase:2:1:temp:

If someone knows the format they want this log to be I can write the node code to provide the file download link via a game id.

The ideal scenario would be that a game can be recreated (on a physical board or otherwise) by relying purely on the game logs.

One gap at the moment is that the exact locations of placed tiles are not captured in the logs yet, e.g. Red placed a tile on (x, y) and gained 2 plants. I will send a PR to add a basic log to capture coordinates of placed tiles.

Yeah, this would be great. If we could instead dump it into human readable/machine version and parse it out, it could unlock some potentially cool analysis.

I almost filed this myself today! I strongly agree that a complete and machine-readable game log would be an awesome thing to have.

How do we feel about JSON format? This would be easily parseable by anyone for any purpose, whatever language they wanted to program in.

I would be happy to start drafting a schema if that's okay?

Of course, it needs to record:

  • all game configuration options, including the specific values chosen for any randomized options
  • every single decision made by players
  • all the non-optional consequences of those decisions as well, so that parsing code doesn't have to reimplement the entire game logic, and to enable stepping backwards through the game. Who gained or lost what resources or production, what tiles were placed where, etc.
  • details of endgame scoring
  • I think it should also record all random events (including hidden ones): every time a card was drawn, which card was it. This may not be technically necessary for replay (which could use "Schroedinger cards") but it is necessary for analyzing strategies.

Any thoughts out there?

Yeah, that would be awesome. I would love to help out by showing what I would do with the parsing, though I'm unsure how much I can offer with schema design.

@bafolts what's your level of interest in discussing this offline?

Well. I can get in rudimentary functionality that provides the link to download. At that point the format could be updated without me.

What I'm interested in first is to find out who's interested in a discussion of that format itself.

For example, two of my ideas above will bloat the file size a lot (including all atomic effects, and using JSON format), but I'm hoping that if the file exported is a ZIP of that, then that should make it fine. Just checking for objections to this.

And it would be useful to review the schema with someone before just coding it up...

My opinion, for what it's worth.
JSON - good idea. As you say, it can be ZIPed or otherwise encoded to reduce size, if needed.
Having a flag for decisions by players vs. automatic by app would be useful (see my example above with the '--' marks).
As for 'hidden' cards, are they really hidden? If the game be replayed using the game ID meaning the shuffle will be the same and the 'hidden' cards can all be determined.

Okay how about this: I'll focus on the full format, but also distinguish between player choices vs. the effects that could be inferred from those choices, so that a concise mode would be possible.

Let me restate the part about hidden information. I think the log needs to record the identity of each card drawn at the time it is drawn, and each card discarded at the time it is discarded. (Of course, players must not be able to get access to an unredacted log before the game ends.) There are some purposes for which you don't need that information, only to know the cards actually played, but there are many for which you do, so okay.

Will need to track more information if determining which cards were drawn at the time they were drawn. Only displaying certain information to the user would be handled by filtering within the vue component.

Without considering the format of the output we will need to re-visit how we are storing the log data. When we add a LogMessage the caller is driving the template string.

          game.log(
            LogMessageType.DEFAULT,
            "${0} received ${1} MC from ${2} owner (${3})",
            new LogMessageData(LogMessageDataType.PLAYER, this.id),
            new LogMessageData(LogMessageDataType.STRING, retribution.toString()),
            new LogMessageData(LogMessageDataType.CARD, "Mons Insurance"),
            new LogMessageData(LogMessageDataType.PLAYER, game.monsInsuranceOwner)
          );

This makes trying to use a different output between the download and the vue component cumbersome as the caller is providing the template for their use case. The template provided here ${0} received ${1} MC from ${2} owner (${3}) should be generated by the vue component as that is the format it needs to display to human. The downloaded file most likely would want a different template. These template strings are storing information I assume would be wanted in the downloaded file. For example in this string we know that a player is gaining megacredits from another player through a card. It would be hard to determine this from what we track today which is only setup for displaying information to the user regarding the game progress.

Fairly certain we would need to get rid of LogMessageType.DEFAULT and have a type for every place we log. I would guess we would have LogMessageType.CARD_DISCARDED, LogMessageType.TILE_PLACED, LogMessageType.CARD_DRAWN, LogMessageType.CARD_PURCHASED, etc. Every place being logged today we will need to replace with this equivalent as a starting point. Then in the vue component we can drive what we want to display based off the LogMessageType. In the downloaded version we could then display different information based off this type. The vue component would ignore displaying LogMessageType.CARD_DISCARDED whereas the downloaded version could display it. The example provided would then change to.

game.log(new RetributionLogMessage(this.id, retribution, CardName.MONS_INSURANCE, game.monsInsuranceOwner));

A first step similar to this will be needed before considering outputting a format for computers versus humans.

I can focus on migrating to LogMessage classes which will allow us to change the template when we want to display the data and only store the data.

Unfamiliar as I am with the codebase, this makes sense so far.

Also, since the eventual goal is that every state change is being logged consistently, we'd want to make sure that those variables are never tweaked directly but always go through a function call (i.e. make all those state variables private), so the logging can be added in one place. Awkwardly perhaps, such a function would want to have a string passed in identifying the reason for the change (is it Towing a Comet itself that's doing this, or is it the effect of Arctic Algae on the ocean that TaC placed?). That may seem odd, but if call sites are routinely responsible for separately logging and updating then we will always keep finding that we missed some.

And because this would be way too much for the UI (imagine production happens and 27 messages fly by!), there will have to be some way to distinguish which messages users actually want to see. Perhaps this means making a distinction between "obvious, known direct consequences of player decisions" vs. the state changes that happen for less obvious reasons.

I can see this will be a large project...

I think this issue may have veered off topic and likely due to me. The original idea was just to be able to get the entire log for the game. Currently, when the game ends, you can look back in the log a few generations but it cuts off there. Is the entire log on the server? Could there be a link to get the whole thing? That was the original question.

It has morphed into this other thing which is really more like a record of all decisions sent by the client to the server and anything the server updates because of those messages. I think this (or whatever it has become) should be a separate issue.

The game log is trimmed to have a maximum size of 50 items.

https://github.com/bafolts/terraforming-mars/blob/master/src/Game.ts#L1740

We store serialized versions of Game in the database whenever the game state is saved. These entries would end up in the database. Removing this restriction where we log is going to increase the memory used for the game while it is running. For users hosting the service locally this should be fine. On the heroku instance the server seems to roughly be holding around 20% memory usage on the 512mb cheapest machine being used. I don't think this would put us over the limit on heroku. Removing the restriction where we store and imposing the restriction when we send the game logs to the client until the end of the game should achieve the original intentions.

I can put up change for the original intentions. For the advanced intentions we will need some place to store this data. With the current design we store the serialized game object in the database many times as that approach was simplest. Quickly going to run out of memory with enough games being played on one server as written.

Yep, sorry for my part in the feature creep too!

I think separate issues make sense for "just allow a log to be downloaded, whatever the contents" and "establish a sufficient game export format for arbitrary game analyses". Based on the discussion that's happened in this issue so far, it might be more convenient to repurpose this one for the latter purpose and file the separate one for what this one was originally about, but of course it can be done either way.

I'm still very interested in the full export format, and am in fact hatching plans of the analyses I want to write based on it!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

duckmammal picture duckmammal  路  3Comments

dabewi picture dabewi  路  5Comments

rodrigomp84 picture rodrigomp84  路  5Comments

dabewi picture dabewi  路  5Comments

dabewi picture dabewi  路  5Comments