Dynamic reflection allows a Step program to query its own execution history. The two main predicates for this are PreviousCall and UniqueCall.
PreviousCall
[PreviousCall ?call] is true if ?call matches some call that has been successful in the chosen execution path. That is, it ignores calls from failed execution paths and only considers the one we're running. It also ignores calls that don't match ?call.
We generally use this to ask about calls to a specific task. For example, in our previous dumb planner, we wrote the actions to print to the screen because otherwise we wouldn't know what actions were selected:
Shoot ?who: [Gun ?gun] [Require [Location ?gun me]] [Require [Loaded ?gun]] You shoot ?who with the ?gun. [Effect [Dead ?who]]
Pickup ?what: [Require [Location ?what floor]] You pick up the ?what. [Effect [Location ?what me]]
Load ?gun ?ammo: [Gun ?gun] [Require [Location ?gun me]] [Ammo ?ammo] [Require [Location ?ammo me]] You load the ?gun. [Effect [Loaded ?gun]]
But we might now want the planner to print to the screen; we might just want to get a list of the actions in the plan. Easy! Here's our planner:
[predicate]
Require ?condition: [Succeeds ?condition]
Require ?condition: [Fails ?condition] [Achieves ?action ?condition] [Succeeds ?action]
[predicate]
Achieves ?action ?effect: [Action ?action] [Method ?action ?subgoals] [Member [Effect ?effect] ?subgoals]
Every action is run by the second line of Require, and it's generated by Achieves. So to find out what actions were run, we just ask for all the calls to Achieves:
Plan ?goal ?plan: [Require ?goal] [FindAll ?action [PreviousCall [Achieves ?action ?]] ?plan]
Now we can call Plan with a goal to achieve and it will return the generated plan in ?plan.
UniqueCall
UniqueCall finds a solution to a query that doesn't match a previous solution:
[UniqueCall [Task ?args ...]]
This runs [Task ?args ...], which will hopefully find a solution, binding the various ?args in the process. UniqueCall then matches that against previous calls to Task. If it matches, it backtracks and generates a new solution to [Task ?args ...]. It repeats this until it finds a solution that hasn't been used before.
This is useful in systems that do procedural generation. Often times, you want to generate a monster, but you want to generate a different monster that the ones you've previously generated. You can use UniqueCall to do that.
Another use for UniqueCall is for casting in procedural narrative systems. You have a bunch of characters, and you have a bunch of roles. You want to assign characters to roles, but you don't want to cast the same character in two different roles. This will do that for you:
[predicate]
CastCharacter ?c: [Character ?c]
[fluent]
Cast ?c ?role: [UniqueCall [CastCharacter ?c]] [now [Cast ?c ?role]]
Now, anytime you want to know who is cast as the hero, do [Cast ?c hero]. If someone is already cast in that role, it binds ?c to that character. If not, it chooses a character who's never been cast in a role before, then updates the Cast predicate using now.
|
Previous:
Design-rule checking
|
Next:
Tagged grammars
|