[Tutor] Best way to save a D&D-like game's state?
dn
PyTutor at DancesWithMice.info
Sat May 22 02:49:41 EDT 2021
On 22/05/2021 13.03, Cameron Simpson wrote:
> On 21May2021 19:25, boB Stepp <robertvstepp at gmail.com> wrote:
>> I am contemplating the design of a D&D-like game where I will
>> prototype various ideas I have in Python. Currently I am pondering
>> how such games typically have their state saved. I imagine there will
>> be plenty of custom data types, which represent the state of the game
>> world when the players save. I imagine the data involved could reach
>> sizable proportions, depending on how complex and rich the game
>> becomes. The first thing that comes to mind is the pickle module,
>> which my understanding is that it can save and restore any custom data
>> types done in Python.
>
> Pretty much. There are things that are not pickleable (I gather) and
> ways to accomodate that, either by adding support to the approriate
> class or by choosing to not save the object (eg a live database
> connection).
This is a key point. Each save-able entity should have a 'save'
subroutine - likely a method. Thus, to save an entire game, each entity
will be saved, in-turn. (so, somewhere you will need a master-list of
objects which comprise the game's data-set)
Question: What do I mean by "save".
Answer: Depends what you know, and what you are able to program, at the
time.
In other words, when you decide to change backing-store, the code within
the method is all that needs to be changed...
>> Next that comes to mind is JSON, but my
>> understanding is that it is limited in what it can save and restore
>> without additional work by the programmer.
>
> Yes, but I would use this as the first cut myself. You just need to make
> sure that things can be serialised to JSON and deserialised. I tend to
> give classes an as_dict() method to return a dict containing only things
> JSONable for that instance's state, and would be inclined to make a
> factory method from_dict(d) to instantiate an instance from a dict.
This is the next step in method-ology (you started with the puns!).
Once you realise that there is common functionality within the various
objects' save-methods, this can be factored-out. Each object prepares
its data in a generalised form, eg serialised or dict-ed, so that a
single I/O routine can perform such tasks. With this, moving between
storage techniques/technologies becomes an even smaller code-change!
> Then saving becomes f.write(json.dumps(obj.as_dict())) and loading
> becomes ObjClass.from_dict(json.load(f)).
Now, it becomes even easier to change between backing-stores.
Modules maketh man!
> If you've got a "root" object representing the whole game state you
> could have its as_dict() method call the as_dict() methods for the
> subsidiary objects, and so on.
>
> This is ok for small things; it scales less well for large data. But
> might be fin for a D&D game.
+1
As this is (also) a learning experience, by all means start with
pickle-ing. Once you have an appreciation for that, and have realised
its strengths and short-comings for yourself, eg speed and
read/testability, then move to JSON.
As speed is a recognised consideration, may I recommend building-in a
performance statistics-gathering module from the get-go. Assuming you
play the game in one incarnation, but then notice a performance lag (at
save-time), facts will be available. Thereafter, as you refactor/improve
the save mechanism, the improvement will be documented as well as (one
hopes) humanly-noticeable!
>> Still staying in the
>> standard library I suppose an SQLite database could be made to work.
>
> Aye, or step up to something with an ORM like SQLAlchemy. That requires
> you to cast all your db state as ORM objects - then doing things in the
> game causes writes to the db as you go. You need to define a schema and
> so on; it might be easier to start with JSON, get things right, then
> _if_ JSON becomes too cumbersome, then make an ORM from you mature data
> structures. You might want an import/export for JSON still - if nothing
> else it would aid the transition to a db :-)
+1
I'm not a great fan of SQLite, but that is largely because of its 'lite'
nature, eg single-user access. However, it is a handy learning-tool
requiring little more than an import command/call to get-started!
Continuing as a learning-path, once you have learned the necessary SQL
and have become able to adapt the code, you will?may be able to justify
the effort of installing an RDBMS server, eg MySQL, MariaDB, PostgreSQL, ...
However, the increased installation-effort may also be a consideration -
if you (ever) plan to copy/deliver the code to another machine.
The spec talks about progressive-saving as well as end-of-game?session.
Such "save" routines need to be fast. So fast that they do not interfere
with play!
As a gross generalisation, NoSQL DBs are faster to write than SQL, but
slower to return specific data. The latter is less of an issue to you
(if I have understood correctly), because someone resetting a game is
not going to begrudge (reasonable) delay/latency. Accordingly, (per
@Liam) MongoDB may be a better choice. Whilst there is still an
installation step, the code-change from a JSON-file structure is minimal
and logically-identical.
Personally, I prefer RDBMS. However, that's rooted in my learning SQL so
long ago it was before Oracle and SQL/DS were $available-products. The
MongoDB people have a 'university' and (several years back) I found
their basic course readily-understandable and immediately applicable.
In such a project, where the data-set may be as extensible/in as much
flux as the code-base, the NoSQL/no-(fixed)schema is going to be much
more 'forgiving' and will demand far fewer 'boiler-plate' type
routine-changes, when code and data-sets evolve!
--
Regards,
=dn
More information about the Tutor
mailing list