#Save current game state and load from it in Monopoly

36 messages ยท Page 1 of 1 (latest)

tired swiftBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

echo finch
#

just organise your game logic such that any state (that satisfies some invariant, if any) can be a starting state

round quail
#

save the money and positons of properties to a file like p1 500 pos1 pos2... then store each on a new line then you can use a shell command to get how many lines are in the file to know how many people are participating then you can simply read the data from eachline use atoi to convert it etc

slow summit
#

A very naive but rather simple solution would be to just log everything that happens. If a player rolls a dice you log that, if a player buys a property you log that, if a player does any action you log that (logging could mean just appending to a file).
Then when you want to load a game you read the respective log file and replay all the actions that happened.
Voilร , you're at the old gamestate.
Why is it naive? Because it's rather slow and memory inefficient.
It has however the advantage of being able to always restore your game should it crash and that you can save some inputs for future testing this way.

What would be faster and more memory efficient? Think about what information do you even need to construct the game state.
You need to know how many players there are, what the dice roll order is, who's turn it is, where everyone is, how much money they have, which properties they own and which are flipped around, how much money is on free park, jail counters, etc., etc.
You can either save the data every turn or upon explicitly saving (e.g. via the click of a "Save" button).
Then when you want to load the game you can just read the file, construct the board and continue.

#

If you don't want to use a third party library then using xml, json or csv won't be easier than just using some custom .txt files btw

echo finch
#

the crucial point in all those approaches (except the event-log based one described by Monke in the first paragraph) being: allow the game to restart from any state. the rest is indeed just about (de)serialising

the event-log is a very interesting approach, and one often considers it slower than what it could be. for instance, I've made a small app whose backend is entirely event-log based. it's the fourth year it's running into production, and it just works flawlessly. I suspect for a monopoly game, you wouldn't get a lot of issue. it's less efficient, but that doesn't mean it's inefficient ๐Ÿ˜‰ it usually has a good ratio [maintainance/iteration]/[perf]

tall salmon
#

doesn't really matter how long efficient or slower the implementation is

#

what would be the easiest to implement?

echo finch
#

it depends a bit on you I guess; I personally find event-log based systems easier to reason about and iterate on. basically the file would look like:

player order is Katya Marc John
dices gave 6
dices gave 11
dices gave 3
dealer of lucky cards distributes "go to jail"
dices gave 9
dices gave 2
dices gave 12

and so on. very easy to follow

#

(obviously you don't have to make it that verbose)

#

the event-log should ideally closely follow the code design. (likely you should organise the game to have a kind of "God" object that emits events of the kind "dices gave", "dealer of lucky cards distributes", ... and so on, to encode randomness (when you play live, it's backed by a random generator thing; when you're recovering state from a file, you just follow the instructions in the file)

and for each event, the game state updates itself (the correct player is moved on the board and so on)

the God can keep its state in-memory until SAVE is pressed, or log in the file as the game goes on (or a mix of both, with an auto-save once every N moves). a format like json/xml could be annoying to maintain, because they would always require you to deal with the closing markup. but that's up to you I guess

tall salmon
#

I've implemented a basic dicelog function but after the first round the player names somehow get corrupted

echo finch
#

aside from this issue: you don't really have to log which players rolls what

#

the log includes the ordering (first line); basically your events only store what happens and cann't be recovered. asymptotically, you could even just store a seed and the number of times dices have been rolled: that would be sufficient to entirely recover a game state

tall salmon
#

do you have any guide for that?

echo finch
#

absolutely not

#

but basically in this approach:

player order is Katya Marc John
dices gave 6
dices gave 11
dices gave 3
dealer of lucky cards distributes "go to jail or back from 5 steps"
user chooses "go to jail"
dices gave 9
dices gave 2
dices gave 12

you should see that you can capture the entire game history, since you know what happened every time something was hazardous and/or the user made a choice

#

but it is much harder to implement it if your game hasn't been thought off like this from the start. imo you should really have a kind of:

struct Choice decides_next_choice(struct GameState *state);

that returns the next "random" or "user" choice based on the game state

then a

void update(struct GameState *state, struct Choice *choice);

that updates a state after a choice is made


next you could have a

struct GameState init(void);

to get the initial blank game state, and two helpers

struct Choice read_next_choice(struct FileCursor *cursor);
void write_choice(struct FileCursor *cursor, struct Choice *choice);

something like that, in order to read and write choices from a file


once you have those utilities, I think arranging them would do the job

echo finch
# tall salmon do you have any guide for that?

philosophically, you should change your mindset:

when a user throws a dice, it does not control a dice object to generate a number

rather, it turns into "waiting state" and waits for a "dice has rolled" event. I think that's the key. you should think your game as a machine that reacts to external events (dices have finished a roll, dealer has distributed a card to a player, player has made a choice, ...); rather than a scene where components control each others

once that is done, might the events be decided on the fly by a random generator (or a user answering through CLI), or might they come from a file, it doesn't matter much for the game

slow summit
#

What Judekeyser means is that you need to separate your game logic from your input action logic.

This means that you never want to have a function for e.g. moving a figure that internally does anything else but moving a figure, i.e. inside that move function you can't ask for user input. Getting and parsing user input should be split from the game and only fed to the game whenever something happened.

If you have your code structured like that it also makes it much easier to implement such a log-based save.
And yeah, if you just play all the inputs from the file, even if the game has a few thousand actions, it should still happen basically instantly. Computers are fast.

inland spade
#

I'd personally do it by having a struct that holds all of the game's state:

struct state {
  int player_count;
  // Add whatever stuff your program needs to keep track of here
};

int main() {
  // Making it static initializes all values to 0,
  // and means you can't get a stack overflow, no matter how huge your struct is
  static struct state state;

  init(&state);

  while (true) {
    update(&state);
  }

  return EXIT_SUCCESS;
}

and then writing a save() function that take a pointer to the state variable and the path to save the file to, and writes the struct to the file in JSON format.

The reason being that 1. JSON is one of the easiest formats to find libraries for no matter the language, or to write your own tokenizer and parser for, 2. it makes it easy to manually edit the state of the game by editing the file, 3. it doesn't get larger over time, unlike the logging approach, 4. it makes it easy to implement multiplayer, as the save format won't be machine dependent by being a binary blob

Then you just write a load() function that iterates over the loaded JSON object, and turns it back into your game's state struct. ๐Ÿ™‚

tall salmon
#

would I not need to change bunch of other functions to allow them to be set using load?

inland spade
#

Wdym? Can you give an example?

tall salmon
#

I don't know to be honest

#

it's just an area where I have zero experience with

inland spade
#

That's fair

#

Have you seen JSON before?

#
{ "foo": 42, "bar": { "baz": null } }
tall salmon
#

yea and considered using one but there wasn't any default library for it

inland spade
#

I'd put writing your own tokenizer and parser for something as one of the most educative things one can do as a programmer, after learning how to use a debugger

tall salmon
#

have you actually taken a look at the zip file that I sent?

inland spade
#

I have now, it seems like you know your way around the language pretty well, if you wrote all of that yourself ๐Ÿ˜„

#

What you may have wanted to know was how the save() and load() functions would be used in practice:

void update(struct state *state) { 
  printf("update()\n");

  if (user_pressed_save_key()) {
    save(state, "save.json");
  } else if (user_pressed_load_key()) {
    state = load("save.json");
  }
}
tired swiftBOT
#

<@undefined>

Please Do Not Delete Posts!

Please don't delete forum posts. They can be helpful to refer to later and other members can learn from them. In the future you can use !solved to close a post and mark a post as solved.

inland spade
#

@tall salmon Did you figure out how you want to approach it?