eyeling 1.5.36 → 1.5.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,119 @@
1
+ # ==================
2
+ # Brussels Brew Club
3
+ # ==================
4
+ #
5
+ # A tiny loyalty + compliance reasoner with:
6
+ # - JSON profile parsing (rdf:JSON + string:jsonPointer)
7
+ # - backward rules (<=) used during forward chaining
8
+ # - stable IDs with log:skolem
9
+ # - timestamps with time:localTime
10
+
11
+ @prefix : <http://example.org/brewclub#> .
12
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
13
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
14
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
15
+ @prefix math: <http://www.w3.org/2000/10/swap/math#> .
16
+ @prefix string:<http://www.w3.org/2000/10/swap/string#> .
17
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
18
+
19
+ # ---------------------------
20
+ # Deterministic "run context"
21
+ # ---------------------------
22
+
23
+ :run :now "2000-01-01T00:00:00+00:00"^^xsd:dateTime .
24
+
25
+ # -----
26
+ # Facts
27
+ # -----
28
+
29
+ :alice :profileJson """{ "name": "Alice", "age": 23, "city": "Brussels" }"""^^rdf:JSON .
30
+ :bob :profileJson """{ "name": "Bob", "age": 16, "city": "Brussels" }"""^^rdf:JSON .
31
+
32
+ :order1 a :Order ;
33
+ :id "ORD-2025-001" ;
34
+ :by :alice ;
35
+ :total "31.50"^^xsd:decimal ;
36
+ :contains :Espresso, :Croissant .
37
+
38
+ :order2 a :Order ;
39
+ :id "ORD-2025-002" ;
40
+ :by :bob ;
41
+ :total "12.00"^^xsd:decimal ;
42
+ :contains :HotChocolate .
43
+
44
+ :Beer a :AlcoholicDrink .
45
+ :Espresso a :Drink .
46
+ :HotChocolate a :Drink .
47
+ :Croissant a :Food .
48
+
49
+ # -----
50
+ # Rules
51
+ # -----
52
+
53
+ # 1) Extract fields from JSON profile into normal triples
54
+ {
55
+ ?p :profileJson ?j .
56
+ (?j "/age") string:jsonPointer ?age .
57
+ (?j "/name") string:jsonPointer ?name .
58
+ (?j "/city") string:jsonPointer ?city .
59
+ }
60
+ =>
61
+ {
62
+ ?p :age ?age ;
63
+ :name ?name ;
64
+ :city ?city .
65
+ }.
66
+
67
+ # 2) Backward rule: Adult is defined by age > 17
68
+ { ?p a :Adult }
69
+ <=
70
+ { ?p :age ?a . ?a math:greaterThan 17 }.
71
+
72
+ # 3) Backward rule: BigOrder is defined by total > 25.00
73
+ { ?o a :BigOrder }
74
+ <=
75
+ { ?o :total ?t . ?t math:greaterThan "25.00"^^xsd:decimal }.
76
+
77
+ # 4) Deterministic membership card rule:
78
+ # Uses :run :now ?now instead of time:localTime ?now
79
+ {
80
+ ?p a :Adult .
81
+ ?p :name ?name .
82
+ :run :now ?now .
83
+
84
+ (?p) log:skolem ?card .
85
+ ("Brew Club card for " ?name) string:concatenation ?label .
86
+ }
87
+ =>
88
+ {
89
+ ?card a :MembershipCard ;
90
+ :holder ?p ;
91
+ :issuedAt ?now ;
92
+ rdfs:label ?label .
93
+ }.
94
+
95
+ # 5) Discounts: BigOrder + Adult -> discount eligibility
96
+ {
97
+ ?o a :Order .
98
+ ?o a :BigOrder .
99
+ ?o :by ?p .
100
+ ?p a :Adult .
101
+ }
102
+ =>
103
+ {
104
+ ?o :eligibleDiscount "true"^^xsd:boolean .
105
+ }.
106
+
107
+ # 6) Stable receipt IRI from the order id
108
+ {
109
+ ?o a :Order .
110
+ ?o :id ?id .
111
+ (?id) log:skolem ?receipt .
112
+ }
113
+ =>
114
+ {
115
+ ?receipt a :Receipt ;
116
+ :forOrder ?o ;
117
+ :receiptId ?id .
118
+ }.
119
+
@@ -1,85 +1,193 @@
1
- # =====================================================================================
2
- # Drone Corridor Planner
3
- # Fuel-bounded drone planning in N3: chain quoted-state transitions, aggregate weights,
4
- # and enforce permit/battery constraints while remaining safe under cycles.
5
- # =====================================================================================
1
+ # ========================================================================================
2
+ # Drone Corridor Planner (example N3 program)
3
+ #
4
+ # What this file is
5
+ # - A tiny “planner” written in Notation3 (N3): it searches for a sequence of
6
+ # actions that moves a drone from a start state to a goal state.
7
+ # - Intended to run in an N3 reasoner that supports forward rules (`=>`),
8
+ # backward rules (`<=`), and a few built-ins (e.g., `math:*`, `list:*`).
9
+ #
10
+ # How to run (one simple option)
11
+ # - With the JavaScript `eyeling` CLI:
12
+ # npx eyeling examples/drone-corridor-planner.n3
13
+ # The output will be newly derived facts, including one or more `gps:plan`
14
+ # results (see “Output” below).
15
+ #
16
+ # Big idea (in plain terms)
17
+ # - We describe a “world” as a small set of facts: location, battery level,
18
+ # and whether the drone has a permit.
19
+ # - We describe each possible action (fly, train, charge, buy/get permit) as a
20
+ # state transition: FROM-state -> TO-state plus some numeric “weights”.
21
+ # - The rules then *compose* these transitions into multi-step plans while
22
+ # aggregating the weights (e.g., add durations, multiply beliefs).
23
+ #
24
+ # Reading the action descriptions
25
+ # - Each action is encoded as a `gps:description` with this shape:
26
+ # ( FROM true TO :actionName duration cost belief comfort )
27
+ # where:
28
+ # - FROM / TO are little graphs in `{ ... }` describing the state.
29
+ # - `true` is a placeholder “precondition” (kept for symmetry with richer
30
+ # variants where extra conditions may appear).
31
+ # - duration/cost/belief/comfort are example numbers used for scoring/pruning.
32
+ #
33
+ # Bounded search (why it doesn’t loop forever)
34
+ # - The planner is *fuel-bounded*: it carries a list of “fuel tokens” and
35
+ # consumes one token per step. When fuel runs out, expansion stops.
36
+ # - This is important because the map can contain cycles (e.g., going back to a
37
+ # previous city). Fuel-bounding keeps the search safe and finite.
38
+ #
39
+ # How scores are combined across a multi-step plan
40
+ # - Duration and cost are summed.
41
+ # - Belief and comfort are multiplied (so long plans tend to reduce them).
42
+ # - The list of actions is built by appending each step’s action name.
43
+ #
44
+ # Output (what you should expect to see)
45
+ # - The “Query” section asks for plans that take the drone from the initial
46
+ # state to the goal (Oostende), then prunes bad plans using thresholds like:
47
+ # belief > ... and cost < ...
48
+ # - Each surviving plan is reported as a derived fact like:
49
+ # :d1 gps:plan ( ...actions... duration cost belief comfort battery permit fuelLeft ).
50
+ #
51
+ # Customizing this example
52
+ # - Add/edit `gps:description` lines to introduce new routes/actions.
53
+ # - Change the initial state (`:d1 :location ...`, `:battery ...`, `:permit ...`).
54
+ # - Adjust `:fuel7` (more/less steps) and the pruning thresholds in the Query.
55
+ #
56
+ # Notes
57
+ # - This is a *toy* corridor-planning model meant to illustrate rule-based
58
+ # planning patterns. The numeric values and “units” are illustrative.
59
+ # ========================================================================================
6
60
 
7
61
  @prefix math: <http://www.w3.org/2000/10/swap/math#>.
8
62
  @prefix list: <http://www.w3.org/2000/10/swap/list#>.
9
63
  @prefix gps: <https://eyereasoner.github.io/eye/reasoning/gps/gps-schema#>.
10
64
  @prefix : <https://eyereasoner.github.io/eye/reasoning#>.
11
65
 
12
- # Fuel-bounded drone planning in N3: chain quoted-state transitions, aggregate weights, and enforce permit/battery constraints while remaining safe under cycles.
13
-
14
- # ----------------
66
+ # -------------
15
67
  # current state
16
- # ----------------
68
+ # -------------
17
69
  :d1 :location :Gent.
18
70
  :d1 :battery :full.
19
71
  :d1 :permit :none.
20
72
 
21
- # bounded search horizon (6 steps max)
22
- :fuel6 :value (:t :t :t :t :t :t).
73
+ # bounded search horizon (7 steps max)
74
+ :fuel7 :value (:t :t :t :t :t :t :t).
23
75
 
24
- # -------------------------------------------------
76
+ # -----------------------------------------------------------------
25
77
  # "map" / action descriptions (as backward rules)
26
- # -------------------------------------------------
78
+ # State = { ?S :location ... . ?S :battery ... . ?S :permit ... . }
79
+ # -----------------------------------------------------------------
80
+ # two ways to reach Brugge (tradeoff: cost/comfort)
27
81
  {:map-DRONE gps:description (
28
- {?S :location :Gent. ?S :battery :full. ?S :permit ?P.} true
29
- {?S :location :Brugge. ?S :battery :mid. ?S :permit ?P.}
82
+ {?S :location :Gent. ?S :battery :full. ?S :permit ?P.} true
83
+ {?S :location :Brugge. ?S :battery :mid. ?S :permit ?P.}
30
84
  :fly_gent_brugge
31
85
  1500.0 0.006 0.99 0.99
32
86
  )} <= true.
33
87
 
34
88
  {:map-DRONE gps:description (
35
- {?S :location :Gent. ?S :battery :full. ?S :permit ?P.} true
89
+ {?S :location :Gent. ?S :battery ?B. ?S :permit ?P.} true
90
+ {?S :location :Brugge. ?S :battery ?B. ?S :permit ?P.}
91
+ :train_gent_brugge
92
+ 1700.0 0.012 0.999 0.995
93
+ )} <= true.
94
+
95
+ # via Kortrijk (permit + charging opportunities)
96
+ {:map-DRONE gps:description (
97
+ {?S :location :Gent. ?S :battery :full. ?S :permit ?P.} true
36
98
  {?S :location :Kortrijk. ?S :battery :mid. ?S :permit ?P.}
37
99
  :fly_gent_kortrijk
38
100
  1600.0 0.007 0.99 0.99
39
101
  )} <= true.
40
102
 
41
103
  {:map-DRONE gps:description (
42
- {?S :location :Kortrijk. ?S :battery :mid. ?S :permit ?P.} true
104
+ {?S :location :Kortrijk. ?S :battery :mid. ?S :permit ?P.} true
43
105
  {?S :location :Brugge. ?S :battery :low. ?S :permit ?P.}
44
106
  :fly_kortrijk_brugge
45
107
  1600.0 0.007 0.99 0.99
46
108
  )} <= true.
47
109
 
48
- # cycle edge (safe due to fuel bound)
110
+ # cycle edge (safe due to fuel bound; typically pruned by belief threshold)
49
111
  {:map-DRONE gps:description (
50
- {?S :location :Brugge. ?S :battery :mid. ?S :permit ?P.} true
112
+ {?S :location :Brugge. ?S :battery :mid. ?S :permit ?P.} true
51
113
  {?S :location :Kortrijk. ?S :battery :low. ?S :permit ?P.}
52
114
  :fly_brugge_kortrijk
53
115
  1600.0 0.007 0.985 0.98
54
116
  )} <= true.
55
117
 
56
- # permit acquisition (only in Kortrijk)
118
+ # get permit in Kortrijk (best belief)
57
119
  {:map-DRONE gps:description (
58
- {?S :location :Kortrijk. ?S :battery ?B. ?S :permit :none.} true
59
- {?S :location :Kortrijk. ?S :battery ?B. ?S :permit :yes.}
60
- :get_zone_permit
120
+ {?S :location :Kortrijk. ?S :battery ?B. ?S :permit :none.} true
121
+ {?S :location :Kortrijk. ?S :battery ?B. ?S :permit :yes.}
122
+ :get_zone_permit_kortrijk
61
123
  300.0 0.001 0.999 1.0
62
124
  )} <= true.
63
125
 
64
- # charging (low -> full)
126
+ # get permit in Brugge (faster, but lower belief)
65
127
  {:map-DRONE gps:description (
66
- {?S :location :Brugge. ?S :battery :low. ?S :permit ?P.} true
67
- {?S :location :Brugge. ?S :battery :full. ?S :permit ?P.}
128
+ {?S :location :Brugge. ?S :battery ?B. ?S :permit :none.} true
129
+ {?S :location :Brugge. ?S :battery ?B. ?S :permit :yes.}
130
+ :buy_permit_brugge
131
+ 450.0 0.002 0.98 1.0
132
+ )} <= true.
133
+
134
+ # charging options
135
+ {:map-DRONE gps:description (
136
+ {?S :location :Brugge. ?S :battery :low. ?S :permit ?P.} true
137
+ {?S :location :Brugge. ?S :battery :full. ?S :permit ?P.}
68
138
  :quick_charge_brugge
69
139
  600.0 0.004 0.999 0.97
70
140
  )} <= true.
71
141
 
72
- # restricted corridor to Oostende requires permit=yes
73
142
  {:map-DRONE gps:description (
74
- {?S :location :Brugge. ?S :battery :full. ?S :permit :yes.} true
75
- {?S :location :Oostende. ?S :battery :mid. ?S :permit :yes.}
143
+ {?S :location :Brugge. ?S :battery :mid. ?S :permit ?P.} true
144
+ {?S :location :Brugge. ?S :battery :full. ?S :permit ?P.}
145
+ :topup_brugge
146
+ 400.0 0.003 0.999 0.98
147
+ )} <= true.
148
+
149
+ {:map-DRONE gps:description (
150
+ {?S :location :Kortrijk. ?S :battery :mid. ?S :permit ?P.} true
151
+ {?S :location :Kortrijk. ?S :battery :full. ?S :permit ?P.}
152
+ :emergency_charge_kortrijk
153
+ 500.0 0.003 0.999 0.95
154
+ )} <= true.
155
+
156
+ # to Oostende: three alternatives
157
+ # A) restricted corridor: fastest+comfortable, but requires permit=yes and full battery
158
+ {:map-DRONE gps:description (
159
+ {?S :location :Brugge. ?S :battery :full. ?S :permit :yes.} true
160
+ {?S :location :Oostende. ?S :battery :mid. ?S :permit :yes.}
76
161
  :cross_corridor_brugge_oostende
77
162
  900.0 0.004 0.98 1.0
78
163
  )} <= true.
79
164
 
80
- # -------------------------------------------------
165
+ # B) public coastline: no permit required, slower
166
+ {:map-DRONE gps:description (
167
+ {?S :location :Brugge. ?S :battery :mid. ?S :permit ?P.} true
168
+ {?S :location :Oostende. ?S :battery :low. ?S :permit ?P.}
169
+ :public_coastline_brugge_oostende
170
+ 1300.0 0.006 0.97 0.96
171
+ )} <= true.
172
+
173
+ {:map-DRONE gps:description (
174
+ {?S :location :Brugge. ?S :battery :full. ?S :permit ?P.} true
175
+ {?S :location :Oostende. ?S :battery :mid. ?S :permit ?P.}
176
+ :public_coastline_brugge_oostende
177
+ 1200.0 0.006 0.975 0.96
178
+ )} <= true.
179
+
180
+ # C) Kortrijk shortcut: requires permit and full battery; quicker but lower comfort
181
+ {:map-DRONE gps:description (
182
+ {?S :location :Kortrijk. ?S :battery :full. ?S :permit :yes.} true
183
+ {?S :location :Oostende. ?S :battery :mid. ?S :permit :yes.}
184
+ :direct_corridor_kortrijk_oostende
185
+ 1100.0 0.009 0.955 0.92
186
+ )} <= true.
187
+
188
+ # ----------------------------------
81
189
  # planner: compute all bounded paths
82
- # -------------------------------------------------
190
+ # ----------------------------------
83
191
  # Base: one description is a path (consume 1 fuel token)
84
192
  {
85
193
  (?From ?To (?Act) ?Dur ?Cost ?Bel ?Comf ?FuelIn ?FuelOut) :path true.
@@ -109,23 +217,21 @@
109
217
  (?Comf1 ?Comf2) math:product ?Comf.
110
218
  }.
111
219
 
112
- # -------------------------------------------------
113
- # Query: plans for d1 to Oostende with thresholds
114
- # -------------------------------------------------
220
+ # -------------------------------------------------------
221
+ # Query: plans for d1 to Oostende with pruning thresholds
222
+ # -------------------------------------------------------
115
223
  {
116
- :fuel6 :value ?Fuel.
224
+ :fuel7 :value ?Fuel.
117
225
 
118
226
  ({:d1 :location :Gent. :d1 :battery :full. :d1 :permit :none.}
119
227
  {:d1 :location :Oostende. :d1 :battery ?B. :d1 :permit ?P.}
120
228
  ?Acts ?Dur ?Cost ?Bel ?Comf ?Fuel ?FuelLeft) :path true.
121
229
 
122
- ?Bel math:greaterThan 0.95.
123
- ?Cost math:lessThan 0.05.
230
+ ?Bel math:greaterThan 0.94.
231
+ ?Cost math:lessThan 0.03.
124
232
  }
125
233
  =>
126
234
  {
127
- :d1 gps:plan (?Acts ?Dur ?Cost ?Bel ?Comf ?FuelLeft).
128
- :d1 :finalBattery ?B.
129
- :d1 :finalPermit ?P.
235
+ :d1 gps:plan (?Acts ?Dur ?Cost ?Bel ?Comf ?B ?P ?FuelLeft).
130
236
  }.
131
237
 
@@ -0,0 +1,189 @@
1
+ # ======================================================================================
2
+ # EV Roadtrip Planner
3
+ #
4
+ # World state (toy):
5
+ # :car1 :at <city>.
6
+ # :car1 :battery :high|:mid|:low.
7
+ # :car1 :pass :none|:yes.
8
+ #
9
+ # Actions are gps:description:
10
+ # ( FROM true TO :actionName duration cost belief comfort )
11
+ #
12
+ # Planner:
13
+ # (:FromState :ToState :Actions :Dur :Cost :Bel :Comf :FuelIn :FuelOut) :path true.
14
+ #
15
+ # Query emits:
16
+ # :car1 gps:plan ( ...actions... duration cost belief comfort battery pass fuelLeft ).
17
+ # ======================================================================================
18
+
19
+ @prefix math: <http://www.w3.org/2000/10/swap/math#>.
20
+ @prefix list: <http://www.w3.org/2000/10/swap/list#>.
21
+ @prefix gps: <https://eyereasoner.github.io/eye/reasoning/gps/gps-schema#>.
22
+ @prefix : <https://eyereasoner.github.io/eye/reasoning#>.
23
+
24
+ # -------------------------------------------------------------------------
25
+ # current state (for readability; query also cites an explicit start state)
26
+ # -------------------------------------------------------------------------
27
+ :car1 :at :Brussels.
28
+ :car1 :battery :high.
29
+ :car1 :pass :none.
30
+
31
+ # bounded search horizon (8 steps max)
32
+ :fuel8 :value (:t :t :t :t :t :t :t :t).
33
+
34
+ # ------------------------------------------------------------------
35
+ # "map" / action descriptions (as backward rules)
36
+ # State = { :car1 :at ... . :car1 :battery ... . :car1 :pass ... . }
37
+ # ------------------------------------------------------------------
38
+ # Brussels -> Liège (two alternatives: drive vs train)
39
+ {:map-EV gps:description (
40
+ {:car1 :at :Brussels. :car1 :battery :high. :car1 :pass ?P.}
41
+ true
42
+ {:car1 :at :Liege. :car1 :battery :mid. :car1 :pass ?P.}
43
+ :drive_bru_liege
44
+ 85.0 0.018 0.985 0.960
45
+ )} <= true.
46
+
47
+ {:map-EV gps:description (
48
+ {:car1 :at :Brussels. :car1 :battery ?B. :car1 :pass ?P.}
49
+ true
50
+ {:car1 :at :Liege. :car1 :battery ?B. :car1 :pass ?P.}
51
+ :train_bru_liege
52
+ 95.0 0.020 0.999 0.990
53
+ )} <= true.
54
+
55
+ # Liège -> Aachen (consumes battery)
56
+ {:map-EV gps:description (
57
+ {:car1 :at :Liege. :car1 :battery :mid. :car1 :pass ?P.}
58
+ true
59
+ {:car1 :at :Aachen. :car1 :battery :low. :car1 :pass ?P.}
60
+ :drive_liege_aachen
61
+ 55.0 0.012 0.990 0.950
62
+ )} <= true.
63
+
64
+ # A tiny “cycle edge” back (to test fuel-bounding + pruning)
65
+ {:map-EV gps:description (
66
+ {:car1 :at :Aachen. :car1 :battery :mid. :car1 :pass ?P.}
67
+ true
68
+ {:car1 :at :Liege. :car1 :battery :low. :car1 :pass ?P.}
69
+ :drive_aachen_liege
70
+ 60.0 0.013 0.970 0.930
71
+ )} <= true.
72
+
73
+ # Buy charging pass (Brussels or Liège)
74
+ {:map-EV gps:description (
75
+ {:car1 :at :Brussels. :car1 :battery ?B. :car1 :pass :none.}
76
+ true
77
+ {:car1 :at :Brussels. :car1 :battery ?B. :car1 :pass :yes.}
78
+ :buy_pass_brussels
79
+ 10.0 0.004 0.999 0.990
80
+ )} <= true.
81
+
82
+ {:map-EV gps:description (
83
+ {:car1 :at :Liege. :car1 :battery ?B. :car1 :pass :none.}
84
+ true
85
+ {:car1 :at :Liege. :car1 :battery ?B. :car1 :pass :yes.}
86
+ :buy_pass_liege
87
+ 15.0 0.003 0.995 0.980
88
+ )} <= true.
89
+
90
+ # Charging options (better if you have a pass)
91
+ {:map-EV gps:description (
92
+ {:car1 :at :Liege. :car1 :battery :low. :car1 :pass ?P.}
93
+ true
94
+ {:car1 :at :Liege. :car1 :battery :mid. :car1 :pass ?P.}
95
+ :quick_charge_liege
96
+ 35.0 0.010 0.999 0.970
97
+ )} <= true.
98
+
99
+ {:map-EV gps:description (
100
+ {:car1 :at :Aachen. :car1 :battery :low. :car1 :pass :yes.}
101
+ true
102
+ {:car1 :at :Aachen. :car1 :battery :mid. :car1 :pass :yes.}
103
+ :fast_charge_aachen_pass
104
+ 25.0 0.009 0.999 0.980
105
+ )} <= true.
106
+
107
+ {:map-EV gps:description (
108
+ {:car1 :at :Aachen. :car1 :battery :low. :car1 :pass :none.}
109
+ true
110
+ {:car1 :at :Aachen. :car1 :battery :mid. :car1 :pass :none.}
111
+ :fast_charge_aachen_payg
112
+ 30.0 0.016 0.990 0.950
113
+ )} <= true.
114
+
115
+ # Aachen -> Cologne (three alternatives: premium corridor vs public road)
116
+ # A) Premium corridor: requires pass=yes and mid battery (best comfort)
117
+ {:map-EV gps:description (
118
+ {:car1 :at :Aachen. :car1 :battery :mid. :car1 :pass :yes.}
119
+ true
120
+ {:car1 :at :Cologne. :car1 :battery :low. :car1 :pass :yes.}
121
+ :premium_corridor_aachen_cologne
122
+ 45.0 0.020 0.980 0.995
123
+ )} <= true.
124
+
125
+ # B) Public road: no pass required (slower)
126
+ {:map-EV gps:description (
127
+ {:car1 :at :Aachen. :car1 :battery :mid. :car1 :pass ?P.}
128
+ true
129
+ {:car1 :at :Cologne. :car1 :battery :low. :car1 :pass ?P.}
130
+ :public_road_aachen_cologne
131
+ 60.0 0.011 0.975 0.960
132
+ )} <= true.
133
+
134
+ # C) “Last-mile” taxi shuttle: keeps battery, higher cost, high belief
135
+ {:map-EV gps:description (
136
+ {:car1 :at :Aachen. :car1 :battery :low. :car1 :pass ?P.}
137
+ true
138
+ {:car1 :at :Cologne. :car1 :battery :low. :car1 :pass ?P.}
139
+ :shuttle_aachen_cologne
140
+ 70.0 0.024 0.999 0.985
141
+ )} <= true.
142
+
143
+ # ----------------------------------
144
+ # planner: compute all bounded paths
145
+ # ----------------------------------
146
+ # Base: one description is a path (consume 1 fuel token)
147
+ { (?From ?To (?Act) ?Dur ?Cost ?Bel ?Comf ?FuelIn ?FuelOut) :path true. }
148
+ <=
149
+ {
150
+ :map-EV gps:description (?From true ?To ?Act ?Dur ?Cost ?Bel ?Comf).
151
+ ?FuelIn list:rest ?FuelOut.
152
+ }.
153
+
154
+ # Recursive: chain one step + rest path, aggregate weights, consume 1 fuel token
155
+ { (?From ?To ?Actions ?Dur ?Cost ?Bel ?Comf ?FuelIn ?FuelOut) :path true. }
156
+ <=
157
+ {
158
+ :map-EV gps:description (?From true ?Mid ?Act ?Dur1 ?Cost1 ?Bel1 ?Comf1).
159
+ ?FuelIn list:rest ?FuelMid.
160
+ (?Mid ?To ?RestActs ?Dur2 ?Cost2 ?Bel2 ?Comf2 ?FuelMid ?FuelOut) :path true.
161
+
162
+ ((?Act) ?RestActs) list:append ?Actions.
163
+ (?Dur1 ?Dur2) math:sum ?Dur.
164
+ (?Cost1 ?Cost2) math:sum ?Cost.
165
+ (?Bel1 ?Bel2) math:product ?Bel.
166
+ (?Comf1 ?Comf2) math:product ?Comf.
167
+ }.
168
+
169
+ # --------------------------------------------------------
170
+ # Query: plans for car1 to Cologne with pruning thresholds
171
+ # --------------------------------------------------------
172
+ {
173
+ :fuel8 :value ?Fuel.
174
+
175
+ (
176
+ {:car1 :at :Brussels. :car1 :battery :high. :car1 :pass :none.}
177
+ {:car1 :at :Cologne. :car1 :battery ?B. :car1 :pass ?P.}
178
+ ?Acts ?Dur ?Cost ?Bel ?Comf ?Fuel ?FuelLeft
179
+ ) :path true.
180
+
181
+ ?Bel math:greaterThan 0.93.
182
+ ?Cost math:lessThan 0.090.
183
+ ?Dur math:lessThan 260.0.
184
+ }
185
+ =>
186
+ {
187
+ :car1 gps:plan (?Acts ?Dur ?Cost ?Bel ?Comf ?B ?P ?FuelLeft).
188
+ }.
189
+