Parcel Delivery: Finding a Route and Recovering from Failure
A courier must deliver a parcel to a library. It looks up a route when it does not already know one, discovers that the bridge route is blocked, and recovers by travelling through the park. This simulation demonstrates test goals, output variables, plan ordering, sub-goals and local failure recovery.
Run the example
Use an ASTRA 2.6.1 project from the Quick Start. Put the following files in src/main/astra. Each file name must match its agent class.
Courier.astra
agent Courier {
module Console C;
module System S;
types delivery {
formula at(string);
formula directory(string, string);
formula route(string, string);
formula road(string, string);
formula blocked(string);
}
initial at("depot"), directory("library", "bridge"),
road("bridge", "library"), road("park", "library"),
blocked("bridge");
plan +!main(list args) {
!deliver("library");
C.println("delivery complete");
S.exit();
}
plan +!deliver(string Destination) {
?route(Destination, string Road);
try {
!travel(Road);
} recover {
C.println("taking the park route instead");
!travel("park");
}
query(at(Destination));
C.println("parcel delivered to " + Destination);
}
plan +?route(string Destination, string Road)
: directory(Destination, string KnownRoad) {
C.println("looking up a route to " + Destination);
Road = KnownRoad;
+route(Destination, Road);
}
// Put the specific blocked-road plan before the general travel plan.
plan +!travel(string Road) : blocked(Road) {
C.println("route blocked: " + Road);
fail;
}
plan +!travel(string Road) : road(Road, string Destination)
& at(string From) {
C.println("travelling from " + From + " via " + Road);
-at(From);
+at(Destination);
}
}
From the project folder, run:
mvn -Dastra.main=Courier
Expected output
[main] looking up a route to library
[main] route blocked: bridge
[main] taking the park route instead
[main] travelling from depot via park
[main] parcel delivered to library
[main] delivery complete
How it works
The agent initially knows a directory entry, but has no route(...) belief. The test goal ?route(...) therefore raises an event. Its +?route(...) plan binds Road and caches the discovered route as a belief. If the route is requested again, the belief can answer the test goal directly.
The blocked-road plan is written before the general travel plan, so it is selected for bridge. Its fail; statement causes the travel sub-goal to fail. The caller’s recover block handles that failure and tries the park route. The final query(at(Destination)) checks that the delivery location was actually reached before reporting success.
The fallback is deliberately specific to this small map. A larger program would represent alternative roads as beliefs and choose another route rather than hard-code park.
Try a change
Remove blocked("bridge") and compare the output. Then block the park road as well: recovery now fails and the delivery cannot complete. Add a ^!deliver(...) failure plan to report or record an undelivered parcel. Finally, replace the directory lookup with a request to a separate map agent.
See Test Goals, Failure Handling and Agent Communication.