I’m thinking about how to save/load game state in SWI-Prolog.
Here is the code skeleton of my game:
main.pl
:- dynamic script_iterator/2.
init_iterator(Goal, Iterator) :-
reset(Goal,YE,Cont),
( Cont == 0
-> Iterator = done
; YE = yield(Element)
-> Iterator = next(Element, Cont)
).
next(next(Element,Cont), Element, Iterator) :-
init_iterator(Cont, Iterator).
yield(Term) :-
shift(yield(Term)).
wait_frames(0) :- !.
wait_frames(N) :-
yield(_),
N1 is N - 1,
wait_frames(N1).
%% -- game loop --
game_update(FrameID) :-
format("game_update FrameID=~w~n", [FrameID]),
forall(script_iterator(Goal, Iterator),
( Iterator \= done
-> next(Iterator, _, NewIterator),
retract(script_iterator(Goal, Iterator)),
assertz(script_iterator(Goal, NewIterator))
; true
)
).
game_init :-
load_files('script.pl'),
init_iterator(on_game_init, Iterator),
assertz(script_iterator(on_game_init, Iterator)).
game_fini :-
retractall(script_iterator(_,_)),
unload_file('script.pl').
main :-
game_init,
game_update(1),
game_update(2),
game_update(3),
game_update(4),
game_update(5),
game_update(6),
game_update(7),
game_update(8),
game_fini.
Note that this game is very flexible, it loads another Prolog file script.pl
. Game designers or players can write their own script.pl
to extend game logic.
script.pl
on_game_init :-
writeln("Hello, this game will start in 5 frames"),
wait_frames(5),
writeln("Game start!")
%% ... other logic ...
.
Notice that the script supports the “wait N seconds” feature. Here I use frames instead of seconds for simplicity.
A query for first 8 frames:
?- main.
Hello, this game will start in 5 frames
game_update FrameID=1
game_update FrameID=2
game_update FrameID=3
game_update FrameID=4
game_update FrameID=5
Game start!
game_update FrameID=6
game_update FrameID=7
game_update FrameID=8
It works well.
My question is that if the game needs to support SAVE/LOAD, what should I do? For example, at the 3rd frame, the player might click the SAVE button in the menu and exit the game. How do I serialize the continuation state for script_iterator/2
?
I have tried the library(persistency), but it doesn’t work for continuations.
A feasible workaround is to write a Meta-Interpreter, which seems not difficult in Prolog, but what’s the drawbacks of using Meta-Interpreter. Does Meta-Interpreter scale well to other SWI-Prolog features?
Besides Meta-Interpreter, is there any other way to solve this problem? For example, is it possible to run a Prolog program inside a sandbox and the sandbox supports serializing/deserializing?.
Thanks in advance for any advice.