Hacker News new | ask | show | jobs
by phaedrus 621 days ago
(With the caveat I haven't read OP article yet) I was enamored of using relational programming for a games engine for many years. Ultimately I concluded it would be better to code such an engine in C++ and write custom solvers for just the aspects you want to be relational, rather than do the whole game in a general purpose relational language.

The resolution algorithm(s) implemented by a general purpose logic programming or relational language are not the only ones possible, and being more general may not be efficient for the type of problem you want the game engine to solve efficiently. Conversely extending a logic programming language to natively include these features may invalidate simplifying assumptions or require a change in evaluation order that makes the language impossible to implement as simply or efficiently.

A concrete example (of extending logic programming with game specific features):

I wanted to both use logic programming relations and also linear constraints, so that you could say something like "A <= (B-10.0) .OR. B <= (A-10.0)" which you could picture as constraining the position of the centers of two width=10.0 game objects so that their boundaries do not overlap, but you don't care which is in front of the other. (Statements like this would be used to build up a scene or more complex game object qualitatively, without pinning down exact coordinates rather letting the linear constraint solver pick them.)

Since running the linear constraint solver to resolve something like "f(A) <= g(B)" actually (potentially) updates all of the interrelated variables (A or B might also have constraints against C,D,E,F), running it as soon as you pick the LHS or RHS of the logic expression "prop(X) .OR. prop(Y)" could potentially invalidate propositions elsewhere previously committed to. So what you probably want to do is, rather than interpreting the "<=" on linear variables as a test with a boolean result, interpret it as a command to add "f(A) <= g(B)" to the global store of linear constraints, and then at some later time run the solver on the complete matrix of linear variables.

That leads to design questions like how does the language know when it's done adding constraints and time to solve, what if we really do need a logic clause to depend on testing a value not constraining it, etc. But all of that's just a distraction from the real issue, which is that for the case of BOTH the linear constraint solving and the logic programming, in the context of a game engine we really need to have more control over both when lengthy computation is run and how long it runs.

That is, even if we design a good way for the extension features to run from the POV of the logic language, from the POV of the outer system we still have the problem of sometimes the search time to resolve part of a logic program blows up and it's difficult for the programmer to always predict when/where that will happen. In this sense it's not even required that hand-rolled imperative code be faster, it can be slower - as long as it's predictable.

And in reality you wouldn't even necessarily be hand-rolling it; what I'm talking about is whether the rules engine is a solver that is externally driven (and pre-emptible) by imperative game code, or the whole game runs within the "solver" (i.e. relational language). As tempting as it is to imagine what could be done if literally "the whole" game were relational, the fact is using that technology for a whole game implies a magic relation solver that doesn't actually exist.