u/drvog

▲ 19 r/prolog

Writing a game in Prolog - how to avoid redundant choice points to ensure tail-call optimisation?

I have started writing a little roguelike game in Prolog (for fun as a first project). I think there are lots of ways in which Prolog is a nice fit for this, and several ways that it isn't. I'm happy to be pragmatic but wanted to ask more experienced folks about the idiomatic way to write Prolog.

My approach is to have a (tail)-recursive predicate which threads state as an argument (rather than using assert/retract) and updates the game based on user input, something like:

game_loop(State) :-
  render(State),
  handle_input(State, NewState),
  game_loop(NewState).

This works well and is tail-call optimised as long as render/1 and handle_input/2 don't leave choice-points that Prolog might want to backtrack into. For a game that might run for many iterations, I want to avoid stack overflow so TCO is important.

To guarantee this, I find that I am writing a lot of predicates using a single clause with (->)/2 so that I don't leave redundant choice points. Pragmatically this is fine, the approach works, the intention is clear, and I still gain many benefits from using Prolog even if it's a bit "extra-logical". But (and I'm perhaps overthinking this) I wonder if this is a unidiomatic? It means my predicates are often one-way and deterministic, which is nice procedurally but does that take away from some of the advantage of using Prolog?

The other thing I'm often doing is making sure that (first) argument indexing will enable Prolog to rule out redundant choice points, but sometimes that's not enough (if for example I need an else-like clause such as functor(_, ...) which could unify with earlier cases).

I've seen some mention of if_/3 but it looks like it's not built-in in SWI-Prolog (or at least not for WASM which I'm targeting?). Welcome any opinions on this approach!

reddit.com
u/drvog — 4 days ago