Hi Jeff.
Here’s the full source of Planner for Dialog (very very alpha and at least one known bug: I’m reworking the second version already to be a little more in line with the Dialog stdlib, and also trying to get to an actual game NPC performing the actions, since I7 comes with its standard library wired up for ‘actors’ and Dialog doesn’t. I actually like that Dialog doesn’t, because NPC systems are often very to taste, so I’m happy to brew my own). But it at least works in terms of generating actions.
http://natecull.org/wordpress/wp-content/uploads/2019/06/planner.r1.dg_.txt
This has more comments so it might be easier to follow and compare. Here’s the full source of the Inform 7 version (two files worth). Not quite all of Basic Plans are in Planner.r1.dg , but I think you can see the difference.
https://web.archive.org/web/20140911222205/http://inform7.com:80/extensions/Nate%20Cull/Planner/source.txt
https://web.archive.org/web/20140911222214/http://inform7.com/extensions/Nate%20Cull/Basic%20Plans/source.txt
There are a few differences between the Inform 7 version and the Dialog version. Inform 7 has no lists so I had to use a table plus a bunch of global variables, so the algorithm is a little different. The I7 version loops, while the Dialog version recurses. To implement the occurs check, I used a kind of trick in the Inform 6 / 7 version of checking backwards through a table with integer pointer fields; in Dialog, I use structure sharing of cons cells. This means that ‘Goals’ now are often replaced with ‘Purposes’ (lists of goals) which are more flexible - and this was something I just couldn’t even do in Inform 7, because of its strict typing. Here it was literally just a couple seconds change.
A second change I made - again, a spur-of-the-moment thing because I now had true untyped lists - is changing the goal for ‘to be in a room’ from [$Actor #in $Room] to just [#in $Room]. Removing the $Actor from the goal like this immediately makes the plan code simpler because I don’t need to specify the Actor twice, and in some cases I don’t need to mention it at all. Simplifications like this, again, are much harder in the Inform 6 or 7 versions which have no lists and have to rely on hard-coded global variables. More to the point - I find that the functional/logical style just lends itself to exploratory programming, where I can try different approaches and change them quickly. The Inform 7 style is much slower and harder to change anything because it’s so much work. (Not so much the rules themselves but all the infrastructure around Rulebooks, Named Rules, Kinds, Types, Values That Vary). The preferred style is to write very long and wordy sentences, which might be normal for a Cambridge maths or literature department but really isn’t for many other people.
I agree that recursive loops, in both Lisp/Scheme and Prolog, can be very hard to wrap our heads around if we’re used to imperative loops. I find that myself, as I don’t write that much functional code, which is why being able to write long wordy predicates helps me get straight what it is that I’m doing. The looping construct I’m using is the oldschool Prolog ‘accumulator’ trick (which is why I use the word ‘accumulates’)… it might be able to be made clearer using higher-order programming and closures, perhaps. It’s essentially a functional ‘fold’ operation.
Graham Nelson certainly did a great service by first making it possible to write for the Z-machine at all, and then re-introducing the world to the benefits of the rules-based approach to programming. But Inform 7 very quickly hits a complexity plateau, especially if you’re trying to do anything even a little ‘meta’, in the spirit of the original 1970s AI work from which Zork appears. Under its rules-based hood, I7 is a very old-fashioned 1950s COBOL-style imperative language with global variables and an attempt at using English sentences - much more like coding business data processing on punch cards than AI. And AI was always the ‘fun stuff’ I wanted to get to.