eyeling 1.16.3 → 1.16.4
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.
- package/README.md +0 -1
- package/package.json +2 -3
- package/arctifacts/README.md +0 -59
- package/arctifacts/ackermann.html +0 -678
- package/arctifacts/auroracare.html +0 -1297
- package/arctifacts/bike-trip.html +0 -752
- package/arctifacts/binomial-theorem.html +0 -631
- package/arctifacts/bmi.html +0 -511
- package/arctifacts/building-performance.html +0 -750
- package/arctifacts/clinical-care.html +0 -726
- package/arctifacts/collatz.html +0 -403
- package/arctifacts/complex.html +0 -321
- package/arctifacts/control-system.html +0 -482
- package/arctifacts/delfour.html +0 -849
- package/arctifacts/earthquake-epicenter.html +0 -982
- package/arctifacts/eco-route.html +0 -662
- package/arctifacts/euclid-infinitude.html +0 -564
- package/arctifacts/euler-identity.html +0 -667
- package/arctifacts/exoplanet-transit.html +0 -1000
- package/arctifacts/faltings-theorem.html +0 -1046
- package/arctifacts/fibonacci.html +0 -299
- package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
- package/arctifacts/godel-numbering.html +0 -743
- package/arctifacts/gps-bike.html +0 -759
- package/arctifacts/gps-clinical-bench.html +0 -792
- package/arctifacts/graph-french.html +0 -449
- package/arctifacts/grass-molecular.html +0 -592
- package/arctifacts/group-theory.html +0 -740
- package/arctifacts/health-info.html +0 -833
- package/arctifacts/kaprekar-constant.html +0 -576
- package/arctifacts/lee.html +0 -805
- package/arctifacts/linked-lists.html +0 -502
- package/arctifacts/lldm.html +0 -612
- package/arctifacts/matrix-multiplication.html +0 -502
- package/arctifacts/matrix.html +0 -651
- package/arctifacts/newton-raphson.html +0 -944
- package/arctifacts/peano-factorial.html +0 -456
- package/arctifacts/pi.html +0 -363
- package/arctifacts/polynomial.html +0 -646
- package/arctifacts/prime.html +0 -366
- package/arctifacts/pythagorean-theorem.html +0 -468
- package/arctifacts/rest-path.html +0 -469
- package/arctifacts/roots-of-unity.html +0 -363
- package/arctifacts/turing.html +0 -409
- package/arctifacts/wind-turbines.html +0 -726
|
@@ -1,752 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<title>Bike Trip Planning</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root {
|
|
9
|
-
--bg: #f7f9fc;
|
|
10
|
-
--card: #ffffff;
|
|
11
|
-
--muted: #5b6b87;
|
|
12
|
-
--text: #0f172a;
|
|
13
|
-
--accent: #0ea5e9;
|
|
14
|
-
--ok: #16a34a;
|
|
15
|
-
--bad: #dc2626;
|
|
16
|
-
--border: #e5e7eb;
|
|
17
|
-
}
|
|
18
|
-
html,
|
|
19
|
-
body {
|
|
20
|
-
height: 100%;
|
|
21
|
-
background: var(--bg);
|
|
22
|
-
color: var(--text);
|
|
23
|
-
font:
|
|
24
|
-
15px/1.5 system-ui,
|
|
25
|
-
-apple-system,
|
|
26
|
-
Segoe UI,
|
|
27
|
-
Roboto,
|
|
28
|
-
Inter,
|
|
29
|
-
Helvetica,
|
|
30
|
-
Arial,
|
|
31
|
-
sans-serif;
|
|
32
|
-
}
|
|
33
|
-
h1 {
|
|
34
|
-
font-weight: 700;
|
|
35
|
-
letter-spacing: 0.2px;
|
|
36
|
-
margin: 18px 0 6px;
|
|
37
|
-
}
|
|
38
|
-
h2 {
|
|
39
|
-
font-size: 13px;
|
|
40
|
-
color: var(--muted);
|
|
41
|
-
margin: 0 0 8px;
|
|
42
|
-
letter-spacing: 0.2px;
|
|
43
|
-
text-transform: uppercase;
|
|
44
|
-
}
|
|
45
|
-
.wrap {
|
|
46
|
-
max-width: 980px;
|
|
47
|
-
margin: 0 auto;
|
|
48
|
-
padding: 24px;
|
|
49
|
-
}
|
|
50
|
-
.stack {
|
|
51
|
-
display: grid;
|
|
52
|
-
grid-template-columns: 1fr;
|
|
53
|
-
gap: 14px;
|
|
54
|
-
}
|
|
55
|
-
.card {
|
|
56
|
-
background: var(--card);
|
|
57
|
-
border: 1px solid var(--border);
|
|
58
|
-
border-radius: 14px;
|
|
59
|
-
padding: 14px;
|
|
60
|
-
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.04);
|
|
61
|
-
}
|
|
62
|
-
.row {
|
|
63
|
-
display: flex;
|
|
64
|
-
gap: 10px;
|
|
65
|
-
align-items: center;
|
|
66
|
-
flex-wrap: wrap;
|
|
67
|
-
}
|
|
68
|
-
button {
|
|
69
|
-
border: 0;
|
|
70
|
-
background: linear-gradient(180deg, #7dd3fc, #38bdf8);
|
|
71
|
-
color: #052436;
|
|
72
|
-
padding: 10px 14px;
|
|
73
|
-
border-radius: 12px;
|
|
74
|
-
font-weight: 700;
|
|
75
|
-
cursor: pointer;
|
|
76
|
-
box-shadow: 0 3px 12px rgba(56, 189, 248, 0.35);
|
|
77
|
-
}
|
|
78
|
-
button.secondary {
|
|
79
|
-
background: #f3f4f6;
|
|
80
|
-
color: #111827;
|
|
81
|
-
border: 1px solid var(--border);
|
|
82
|
-
box-shadow: none;
|
|
83
|
-
}
|
|
84
|
-
/* outputs */
|
|
85
|
-
.output {
|
|
86
|
-
min-height: 0;
|
|
87
|
-
white-space: pre-wrap;
|
|
88
|
-
background: #fbfdff;
|
|
89
|
-
border: 1px solid var(--border);
|
|
90
|
-
border-radius: 10px;
|
|
91
|
-
padding: 10px;
|
|
92
|
-
overflow: auto;
|
|
93
|
-
}
|
|
94
|
-
.output.tall {
|
|
95
|
-
min-height: 160px;
|
|
96
|
-
}
|
|
97
|
-
.muted {
|
|
98
|
-
color: var(--muted);
|
|
99
|
-
}
|
|
100
|
-
.badge {
|
|
101
|
-
display: inline-block;
|
|
102
|
-
padding: 2px 8px;
|
|
103
|
-
border-radius: 999px;
|
|
104
|
-
font-size: 12px;
|
|
105
|
-
font-weight: 700;
|
|
106
|
-
letter-spacing: 0.3px;
|
|
107
|
-
border: 1px solid var(--border);
|
|
108
|
-
}
|
|
109
|
-
.ok {
|
|
110
|
-
background: rgba(22, 163, 74, 0.08);
|
|
111
|
-
color: var(--ok);
|
|
112
|
-
border-color: rgba(22, 163, 74, 0.35);
|
|
113
|
-
}
|
|
114
|
-
.bad {
|
|
115
|
-
background: rgba(220, 38, 38, 0.08);
|
|
116
|
-
color: var(--bad);
|
|
117
|
-
border-color: rgba(220, 38, 38, 0.35);
|
|
118
|
-
}
|
|
119
|
-
.tiny {
|
|
120
|
-
font-size: 12px;
|
|
121
|
-
}
|
|
122
|
-
.diag {
|
|
123
|
-
font-size: 12px;
|
|
124
|
-
color: #6b7280;
|
|
125
|
-
}
|
|
126
|
-
/* auto-growing textareas */
|
|
127
|
-
textarea,
|
|
128
|
-
pre,
|
|
129
|
-
code,
|
|
130
|
-
input,
|
|
131
|
-
button {
|
|
132
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, 'Courier New', monospace;
|
|
133
|
-
}
|
|
134
|
-
textarea {
|
|
135
|
-
width: 100%;
|
|
136
|
-
min-height: 0;
|
|
137
|
-
height: auto;
|
|
138
|
-
overflow: hidden;
|
|
139
|
-
resize: none;
|
|
140
|
-
border-radius: 10px;
|
|
141
|
-
padding: 10px;
|
|
142
|
-
border: 1px solid var(--border);
|
|
143
|
-
background: #fbfdff;
|
|
144
|
-
color: var(--text);
|
|
145
|
-
box-sizing: border-box;
|
|
146
|
-
white-space: pre-wrap;
|
|
147
|
-
}
|
|
148
|
-
</style>
|
|
149
|
-
</head>
|
|
150
|
-
<body>
|
|
151
|
-
<div class="wrap">
|
|
152
|
-
<h1>Bike Trip Planning</h1>
|
|
153
|
-
|
|
154
|
-
<div class="stack">
|
|
155
|
-
<!-- What this is? -->
|
|
156
|
-
<div class="card" id="what">
|
|
157
|
-
<h2>What this is?</h2>
|
|
158
|
-
<p>
|
|
159
|
-
One-file, pure JS + JSON route-planning demo for bike trips using ARC: <strong>Answer</strong> •
|
|
160
|
-
<strong>Reason</strong> • <strong>Check</strong>.
|
|
161
|
-
</p>
|
|
162
|
-
<ul>
|
|
163
|
-
<li>
|
|
164
|
-
<strong>Data</strong> — trips, segments, tags (hilly, no-bike-lane…), observations (rain, wind,
|
|
165
|
-
construction), preferences.
|
|
166
|
-
</li>
|
|
167
|
-
<li>
|
|
168
|
-
<strong>Policies</strong> — declarative JSON rules (<code>if</code> atoms, optional
|
|
169
|
-
<code>"not": true</code> negations ⇒ <code>then</code> atoms).
|
|
170
|
-
</li>
|
|
171
|
-
<li><strong>Answer</strong> — only the <em>newly derived</em> facts (keeps strongest trip priority).</li>
|
|
172
|
-
<li><strong>Reason Why</strong> — mathematical-English templates with bound variables.</li>
|
|
173
|
-
<li><strong>Check</strong> — ≥10 JSON assertions against the full fact set.</li>
|
|
174
|
-
<li><strong>Trip Plan Summary</strong> — per-trip priority & tasks (chooses strongest priority).</li>
|
|
175
|
-
</ul>
|
|
176
|
-
<p class="tiny muted">
|
|
177
|
-
Engine: small forward-chainer with unification, subclassing (<code>subClass</code>), transitive
|
|
178
|
-
<code>partOf</code>, segment→trip observation mapping, and negation-as-failure for <code>"not": true</code>.
|
|
179
|
-
</p>
|
|
180
|
-
</div>
|
|
181
|
-
|
|
182
|
-
<div class="card">
|
|
183
|
-
<h2>Data (JSON)</h2>
|
|
184
|
-
<textarea id="dataTA" spellcheck="false">
|
|
185
|
-
{
|
|
186
|
-
"classes": ["Trip","Segment","Observation","Hazard","RedFlagHazard","MajorHazard","MinorHazard",
|
|
187
|
-
"RoadClosed","SevereStorm","HeavyRain","StrongHeadwind","Construction","LowLight","Traffic",
|
|
188
|
-
"Tag","Hilly","Sheltered","NoBikeLane","Unlit",
|
|
189
|
-
"Task","Priority","Blocked","Replan","Caution","Go"],
|
|
190
|
-
|
|
191
|
-
"subclassOf": [
|
|
192
|
-
["RoadClosed","RedFlagHazard"],
|
|
193
|
-
["SevereStorm","RedFlagHazard"],
|
|
194
|
-
["HeavyRain","MajorHazard"],
|
|
195
|
-
["StrongHeadwind","MajorHazard"],
|
|
196
|
-
["Construction","MajorHazard"],
|
|
197
|
-
["LowLight","MinorHazard"],
|
|
198
|
-
["Traffic","MinorHazard"],
|
|
199
|
-
["RedFlagHazard","Hazard"],
|
|
200
|
-
["MajorHazard","Hazard"],
|
|
201
|
-
["MinorHazard","Hazard"]
|
|
202
|
-
],
|
|
203
|
-
|
|
204
|
-
"assets": {
|
|
205
|
-
"T1": {"type":"Trip"},
|
|
206
|
-
"SG1": {"type":"Segment","partOf":"T1"},
|
|
207
|
-
"SG2": {"type":"Segment","partOf":"T1"},
|
|
208
|
-
|
|
209
|
-
"T2": {"type":"Trip"},
|
|
210
|
-
"SG3": {"type":"Segment","partOf":"T2"}
|
|
211
|
-
},
|
|
212
|
-
|
|
213
|
-
"segmentTags": [
|
|
214
|
-
["SG1","Sheltered"],
|
|
215
|
-
["SG2","Hilly"],
|
|
216
|
-
["SG2","Unlit"],
|
|
217
|
-
["SG3","NoBikeLane"]
|
|
218
|
-
],
|
|
219
|
-
|
|
220
|
-
"preferences": [
|
|
221
|
-
["T1","AvoidHills"],
|
|
222
|
-
["T2","PreferProtectedLanes"]
|
|
223
|
-
],
|
|
224
|
-
|
|
225
|
-
"observations": [
|
|
226
|
-
{"id":"ObsRain1","type":"HeavyRain","about":"SG1"},
|
|
227
|
-
{"id":"ObsWind2","type":"StrongHeadwind","about":"SG2"},
|
|
228
|
-
{"id":"ObsLowLight2","type":"LowLight","about":"SG2"},
|
|
229
|
-
{"id":"ObsConstr3","type":"Construction","about":"SG3"},
|
|
230
|
-
{"id":"ObsClosed3","type":"RoadClosed","about":"SG3"}
|
|
231
|
-
]
|
|
232
|
-
}</textarea
|
|
233
|
-
>
|
|
234
|
-
</div>
|
|
235
|
-
|
|
236
|
-
<div class="card">
|
|
237
|
-
<h2>Policies (declarative JSON rules)</h2>
|
|
238
|
-
<textarea id="policyTA" spellcheck="false">
|
|
239
|
-
[
|
|
240
|
-
{
|
|
241
|
-
"id":"R1-Subclass",
|
|
242
|
-
"if":[{"pred":"subClass","s":"?A","o":"?B"},{"pred":"isA","s":"?S","o":"?A"}],
|
|
243
|
-
"then":[{"pred":"isA","s":"?S","o":"?B"}],
|
|
244
|
-
"explain":"Since ?A ⊑ ?B and ?S ∈ ?A, infer ?S ∈ ?B."
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
"id":"R2-PartOfTransitive",
|
|
248
|
-
"if":[{"pred":"partOf","s":"?x","o":"?y"},{"pred":"partOf","s":"?y","o":"?z"}],
|
|
249
|
-
"then":[{"pred":"partOf","s":"?x","o":"?z"}],
|
|
250
|
-
"explain":"Because ?x is part of ?y and ?y is part of ?z, by transitivity ?x is part of ?z."
|
|
251
|
-
},
|
|
252
|
-
{
|
|
253
|
-
"id":"R3-ObsToTrip",
|
|
254
|
-
"if":[{"pred":"about","s":"?obs","o":"?seg"},{"pred":"partOf","s":"?seg","o":"?T"}],
|
|
255
|
-
"then":[{"pred":"aboutTrip","s":"?obs","o":"?T"}],
|
|
256
|
-
"explain":"Observation ?obs concerns segment ?seg of trip ?T; therefore ?obs is about trip ?T."
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
"id":"R4-TagFacts",
|
|
260
|
-
"if":[{"pred":"tag","s":"?S","o":"?Tag"}],
|
|
261
|
-
"then":[{"pred":"hasTag","s":"?S","o":"?Tag"}],
|
|
262
|
-
"explain":"Expose segment tag ?Tag on ?S as a fact."
|
|
263
|
-
},
|
|
264
|
-
|
|
265
|
-
{ "id":"H-RoadClosed",
|
|
266
|
-
"if":[{"pred":"isA","s":"?obs","o":"RoadClosed"},{"pred":"aboutTrip","s":"?obs","o":"?T"}],
|
|
267
|
-
"then":[{"pred":"requiresTask","s":"?T","o":"Reroute"},{"pred":"requiresTask","s":"?T","o":"AvoidRoad"},{"pred":"priority","s":"?T","o":"Blocked"}],
|
|
268
|
-
"explain":"Road closure on ?T requires rerouting and sets priority Blocked."
|
|
269
|
-
},
|
|
270
|
-
{ "id":"H-SevereStorm",
|
|
271
|
-
"if":[{"pred":"isA","s":"?obs","o":"SevereStorm"},{"pred":"aboutTrip","s":"?obs","o":"?T"}],
|
|
272
|
-
"then":[{"pred":"requiresTask","s":"?T","o":"AdjustStartTime"},{"pred":"requiresTask","s":"?T","o":"PackRainGear"},{"pred":"priority","s":"?T","o":"Replan"}],
|
|
273
|
-
"explain":"Severe storm on ?T requires re-planning."
|
|
274
|
-
},
|
|
275
|
-
{ "id":"H-HeavyRain",
|
|
276
|
-
"if":[{"pred":"isA","s":"?obs","o":"HeavyRain"},{"pred":"aboutTrip","s":"?obs","o":"?T"}],
|
|
277
|
-
"then":[{"pred":"requiresTask","s":"?T","o":"PackRainGear"},{"pred":"requiresTask","s":"?T","o":"ChooseShelteredPath"}],
|
|
278
|
-
"explain":"Heavy rain on ?T implies rain gear and a sheltered path."
|
|
279
|
-
},
|
|
280
|
-
{ "id":"H-StrongHeadwind",
|
|
281
|
-
"if":[{"pred":"isA","s":"?obs","o":"StrongHeadwind"},{"pred":"aboutTrip","s":"?obs","o":"?T"}],
|
|
282
|
-
"then":[{"pred":"requiresTask","s":"?T","o":"AdjustStartTime"},{"pred":"requiresTask","s":"?T","o":"ChooseShelteredPath"}],
|
|
283
|
-
"explain":"Strong headwind on ?T implies timing adjustment and a sheltered path."
|
|
284
|
-
},
|
|
285
|
-
{ "id":"H-Construction",
|
|
286
|
-
"if":[{"pred":"isA","s":"?obs","o":"Construction"},{"pred":"aboutTrip","s":"?obs","o":"?T"}],
|
|
287
|
-
"then":[{"pred":"requiresTask","s":"?T","o":"Reroute"}],
|
|
288
|
-
"explain":"Construction on ?T may require a minor reroute."
|
|
289
|
-
},
|
|
290
|
-
{ "id":"H-LowLight",
|
|
291
|
-
"if":[{"pred":"isA","s":"?obs","o":"LowLight"},{"pred":"aboutTrip","s":"?obs","o":"?T"}],
|
|
292
|
-
"then":[{"pred":"requiresTask","s":"?T","o":"AddLights"}],
|
|
293
|
-
"explain":"Low light on ?T implies using lights."
|
|
294
|
-
},
|
|
295
|
-
|
|
296
|
-
{ "id":"P-RedFlag→Replan",
|
|
297
|
-
"if":[{"pred":"isA","s":"?obs","o":"RedFlagHazard"},{"pred":"aboutTrip","s":"?obs","o":"?T"},{"not":true,"pred":"isA","s":"?obs","o":"RoadClosed"}],
|
|
298
|
-
"then":[{"pred":"priority","s":"?T","o":"Replan"}],
|
|
299
|
-
"explain":"Any red-flag hazard (except RoadClosed, which is Blocked) sets priority Replan."
|
|
300
|
-
},
|
|
301
|
-
{ "id":"P-Major-NotRed",
|
|
302
|
-
"if":[{"pred":"isA","s":"?obs","o":"MajorHazard"},{"pred":"aboutTrip","s":"?obs","o":"?T"},{"not":true,"pred":"isA","s":"?obs","o":"RedFlagHazard"}],
|
|
303
|
-
"then":[{"pred":"priority","s":"?T","o":"Caution"}],
|
|
304
|
-
"explain":"A major (but not red-flag) hazard on ?T implies priority Caution."
|
|
305
|
-
},
|
|
306
|
-
{ "id":"P-Minor-Only",
|
|
307
|
-
"if":[{"pred":"isA","s":"?obs","o":"MinorHazard"},{"pred":"aboutTrip","s":"?obs","o":"?T"},
|
|
308
|
-
{"not":true,"pred":"isA","s":"?any","o":"MajorHazard"},
|
|
309
|
-
{"not":true,"pred":"isA","s":"?any2","o":"RedFlagHazard"}],
|
|
310
|
-
"then":[{"pred":"priority","s":"?T","o":"Caution"}],
|
|
311
|
-
"explain":"Only minor hazards on ?T imply priority Caution."
|
|
312
|
-
},
|
|
313
|
-
{ "id":"P-Default-Go",
|
|
314
|
-
"if":[{"pred":"requiresTask","s":"?T","o":"?X"},{"not":true,"pred":"priority","s":"?T","o":"?Any"}],
|
|
315
|
-
"then":[{"pred":"priority","s":"?T","o":"Go"}],
|
|
316
|
-
"explain":"If ?T has tasks but no priority, default to Go."
|
|
317
|
-
},
|
|
318
|
-
|
|
319
|
-
{ "id":"Pref-AvoidHills",
|
|
320
|
-
"if":[{"pred":"prefers","s":"?T","o":"AvoidHills"},{"pred":"hasTag","s":"?S","o":"Hilly"},{"pred":"partOf","s":"?S","o":"?T"}],
|
|
321
|
-
"then":[{"pred":"requiresTask","s":"?T","o":"UseLowGradientRoute"}],
|
|
322
|
-
"explain":"Trip ?T prefers to avoid hills; since segment ?S is hilly, use a low-gradient route."
|
|
323
|
-
},
|
|
324
|
-
{ "id":"Pref-ProtectedLanes",
|
|
325
|
-
"if":[{"pred":"prefers","s":"?T","o":"PreferProtectedLanes"},{"pred":"hasTag","s":"?S","o":"NoBikeLane"},{"pred":"partOf","s":"?S","o":"?T"}],
|
|
326
|
-
"then":[{"pred":"requiresTask","s":"?T","o":"UseProtectedLaneRoute"}],
|
|
327
|
-
"explain":"Trip ?T prefers protected lanes; since segment ?S lacks bike lanes, choose protected lanes."
|
|
328
|
-
}
|
|
329
|
-
]</textarea
|
|
330
|
-
>
|
|
331
|
-
</div>
|
|
332
|
-
|
|
333
|
-
<div class="card">
|
|
334
|
-
<h2>Checks (JSON)</h2>
|
|
335
|
-
<textarea id="checksTA" spellcheck="false">
|
|
336
|
-
[
|
|
337
|
-
{"name":"ObsRain1 about T1", "pattern":{"pred":"aboutTrip","s":"ObsRain1","o":"T1"}},
|
|
338
|
-
{"name":"ObsWind2 about T1", "pattern":{"pred":"aboutTrip","s":"ObsWind2","o":"T1"}},
|
|
339
|
-
{"name":"ObsLowLight2 about T1", "pattern":{"pred":"aboutTrip","s":"ObsLowLight2","o":"T1"}},
|
|
340
|
-
{"name":"ObsConstr3 about T2", "pattern":{"pred":"aboutTrip","s":"ObsConstr3","o":"T2"}},
|
|
341
|
-
{"name":"ObsClosed3 about T2", "pattern":{"pred":"aboutTrip","s":"ObsClosed3","o":"T2"}},
|
|
342
|
-
|
|
343
|
-
{"name":"T1 requires PackRainGear", "pattern":{"pred":"requiresTask","s":"T1","o":"PackRainGear"}},
|
|
344
|
-
{"name":"T1 requires ChooseSheltered", "pattern":{"pred":"requiresTask","s":"T1","o":"ChooseShelteredPath"}},
|
|
345
|
-
{"name":"T1 requires AdjustStartTime", "pattern":{"pred":"requiresTask","s":"T1","o":"AdjustStartTime"}},
|
|
346
|
-
{"name":"T1 requires AddLights", "pattern":{"pred":"requiresTask","s":"T1","o":"AddLights"}},
|
|
347
|
-
|
|
348
|
-
{"name":"T2 requires Reroute", "pattern":{"pred":"requiresTask","s":"T2","o":"Reroute"}},
|
|
349
|
-
{"name":"T2 requires AvoidRoad", "pattern":{"pred":"requiresTask","s":"T2","o":"AvoidRoad"}},
|
|
350
|
-
{"name":"T2 requires UseProtectedLane","pattern":{"pred":"requiresTask","s":"T2","o":"UseProtectedLaneRoute"}},
|
|
351
|
-
{"name":"T1 requires LowGradient", "pattern":{"pred":"requiresTask","s":"T1","o":"UseLowGradientRoute"}},
|
|
352
|
-
|
|
353
|
-
{"name":"T1 priority Caution", "pattern":{"pred":"priority","s":"T1","o":"Caution"}},
|
|
354
|
-
{"name":"T2 priority Blocked", "pattern":{"pred":"priority","s":"T2","o":"Blocked"}}
|
|
355
|
-
]</textarea
|
|
356
|
-
>
|
|
357
|
-
</div>
|
|
358
|
-
|
|
359
|
-
<div class="card">
|
|
360
|
-
<h2>Controls</h2>
|
|
361
|
-
<div class="row">
|
|
362
|
-
<button id="runBtn">▶ Run ARC</button>
|
|
363
|
-
<button id="reasonBtn" class="secondary">Show Reason only</button>
|
|
364
|
-
<span id="status" class="muted tiny" style="margin-left: auto"></span>
|
|
365
|
-
</div>
|
|
366
|
-
<div id="diag" class="diag"></div>
|
|
367
|
-
</div>
|
|
368
|
-
|
|
369
|
-
<div class="card">
|
|
370
|
-
<h2>Answer (newly derived facts)</h2>
|
|
371
|
-
<div id="answer" class="output tall">computing…</div>
|
|
372
|
-
</div>
|
|
373
|
-
|
|
374
|
-
<div class="card">
|
|
375
|
-
<h2>Reason Why (mathematical English)</h2>
|
|
376
|
-
<div id="reason" class="output tall">(click “Run ARC”)</div>
|
|
377
|
-
</div>
|
|
378
|
-
|
|
379
|
-
<div class="card">
|
|
380
|
-
<h2>Check</h2>
|
|
381
|
-
<div id="checks" class="output tall">computing…</div>
|
|
382
|
-
</div>
|
|
383
|
-
|
|
384
|
-
<div class="card" id="summaryCard">
|
|
385
|
-
<h2>Trip Plan Summary</h2>
|
|
386
|
-
<div id="summary" class="output">(run to populate)</div>
|
|
387
|
-
</div>
|
|
388
|
-
</div>
|
|
389
|
-
</div>
|
|
390
|
-
|
|
391
|
-
<script>
|
|
392
|
-
const $ = (id) => document.getElementById(id);
|
|
393
|
-
const els = {
|
|
394
|
-
dataTA: $('dataTA'),
|
|
395
|
-
policyTA: $('policyTA'),
|
|
396
|
-
checksTA: $('checksTA'),
|
|
397
|
-
runBtn: $('runBtn'),
|
|
398
|
-
reasonBtn: $('reasonBtn'),
|
|
399
|
-
status: $('status'),
|
|
400
|
-
diag: $('diag'),
|
|
401
|
-
answer: $('answer'),
|
|
402
|
-
reason: $('reason'),
|
|
403
|
-
checks: $('checks'),
|
|
404
|
-
summary: $('summary'),
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
// Auto-grow textareas
|
|
408
|
-
function autoResize(el) {
|
|
409
|
-
el.style.height = 'auto';
|
|
410
|
-
el.style.height = el.scrollHeight + 'px';
|
|
411
|
-
}
|
|
412
|
-
['dataTA', 'policyTA', 'checksTA'].forEach((id) => {
|
|
413
|
-
const el = $(id);
|
|
414
|
-
el.addEventListener('input', () => autoResize(el));
|
|
415
|
-
setTimeout(() => autoResize(el), 0);
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
// ---- KB helpers
|
|
419
|
-
function key(f) {
|
|
420
|
-
return `${f.pred}|${f.s}|${f.o}`;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function buildFactsFromData(spec) {
|
|
424
|
-
const facts = [];
|
|
425
|
-
(spec.subclassOf || []).forEach(([a, b]) => facts.push({ pred: 'subClass', s: a, o: b, _base: true }));
|
|
426
|
-
// asset typing + partOf
|
|
427
|
-
for (const [id, info] of Object.entries(spec.assets || {})) {
|
|
428
|
-
if (info.type) facts.push({ pred: 'isA', s: id, o: info.type, _base: true });
|
|
429
|
-
if (info.partOf) facts.push({ pred: 'partOf', s: id, o: info.partOf, _base: true });
|
|
430
|
-
}
|
|
431
|
-
// segment tags (as 'tag' facts, later exposed as hasTag via rule)
|
|
432
|
-
(spec.segmentTags || []).forEach(([s, t]) => facts.push({ pred: 'tag', s: s, o: t, _base: true }));
|
|
433
|
-
// preferences at trip level
|
|
434
|
-
(spec.preferences || []).forEach(([trip, p]) => facts.push({ pred: 'prefers', s: trip, o: p, _base: true }));
|
|
435
|
-
// observations
|
|
436
|
-
(spec.observations || []).forEach((o) => {
|
|
437
|
-
facts.push({ pred: 'isA', s: o.id, o: o.type, _base: true });
|
|
438
|
-
facts.push({ pred: 'about', s: o.id, o: o.about, _base: true });
|
|
439
|
-
});
|
|
440
|
-
return facts;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function indexFacts(facts) {
|
|
444
|
-
const byPred = new Map();
|
|
445
|
-
for (const f of facts) {
|
|
446
|
-
if (!byPred.has(f.pred)) byPred.set(f.pred, []);
|
|
447
|
-
byPred.get(f.pred).push(f);
|
|
448
|
-
}
|
|
449
|
-
return { byPred };
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
function isVar(x) {
|
|
453
|
-
return typeof x === 'string' && x.startsWith('?');
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
function unifyAtomWithFact(atom, f, env) {
|
|
457
|
-
const out = { ...env };
|
|
458
|
-
const slots = ['pred', 's', 'o'];
|
|
459
|
-
for (const slot of slots) {
|
|
460
|
-
const val = atom[slot];
|
|
461
|
-
const factVal = f[slot] ?? (slot === 'pred' ? f.pred : slot === 's' ? f.s : f.o);
|
|
462
|
-
if (isVar(val)) {
|
|
463
|
-
if (out[val] !== undefined && out[val] !== factVal) return null;
|
|
464
|
-
out[val] = factVal;
|
|
465
|
-
} else {
|
|
466
|
-
if (val !== factVal) return null;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
return out;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function matchPositives(rule, factsIdx, env = {}, i = 0) {
|
|
473
|
-
if (i >= rule.if.length) return [env];
|
|
474
|
-
const atom = rule.if[i];
|
|
475
|
-
if (atom.not) return matchPositives(rule, factsIdx, env, i + 1);
|
|
476
|
-
const candidates = factsIdx.byPred.get(atom.pred) || [];
|
|
477
|
-
const out = [];
|
|
478
|
-
for (const f of candidates) {
|
|
479
|
-
const env2 = unifyAtomWithFact(atom, f, env);
|
|
480
|
-
if (env2) out.push(...matchPositives(rule, factsIdx, env2, i + 1));
|
|
481
|
-
}
|
|
482
|
-
return out;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
function negHolds(atom, factsIdx, env) {
|
|
486
|
-
const candidates = factsIdx.byPred.get(atom.pred) || [];
|
|
487
|
-
for (const f of candidates) {
|
|
488
|
-
if (unifyAtomWithFact(atom, f, env)) return true;
|
|
489
|
-
}
|
|
490
|
-
return false;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function substitute(t, env) {
|
|
494
|
-
const sub = (v) => (isVar(v) ? env[v] : v);
|
|
495
|
-
return { pred: t.pred, s: sub(t.s), o: sub(t.o) };
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// ---- Priority handling (Blocked > Replan > Caution > Go)
|
|
499
|
-
const PRIORITY_RANK = { Blocked: 4, Replan: 3, Caution: 2, Go: 1 };
|
|
500
|
-
|
|
501
|
-
function bestPriorities(facts) {
|
|
502
|
-
const best = new Map();
|
|
503
|
-
for (const f of facts) {
|
|
504
|
-
if (f.pred !== 'priority') continue;
|
|
505
|
-
const cur = best.get(f.s);
|
|
506
|
-
if (!cur || (PRIORITY_RANK[f.o] || 0) > (PRIORITY_RANK[cur] || 0)) {
|
|
507
|
-
best.set(f.s, f.o);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
return best;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
function derive(data, policies) {
|
|
514
|
-
let facts = buildFactsFromData(data);
|
|
515
|
-
const baseSet = new Set(facts.map(key));
|
|
516
|
-
const factSet = new Set(baseSet);
|
|
517
|
-
const factsIdx = indexFacts(facts);
|
|
518
|
-
const proofs = new Map(); // key -> text
|
|
519
|
-
|
|
520
|
-
let changed = true,
|
|
521
|
-
guard = 0;
|
|
522
|
-
while (changed && guard++ < 200) {
|
|
523
|
-
changed = false;
|
|
524
|
-
for (const rule of policies) {
|
|
525
|
-
const posBindings = matchPositives(rule, factsIdx, {}, 0);
|
|
526
|
-
for (const env of posBindings) {
|
|
527
|
-
const negs = rule.if.filter((a) => a.not);
|
|
528
|
-
let ok = true,
|
|
529
|
-
negUsed = [];
|
|
530
|
-
for (const n of negs) {
|
|
531
|
-
const nSub = substitute(n, env);
|
|
532
|
-
if (negHolds(nSub, factsIdx, env)) {
|
|
533
|
-
ok = false;
|
|
534
|
-
break;
|
|
535
|
-
}
|
|
536
|
-
negUsed.push(nSub);
|
|
537
|
-
}
|
|
538
|
-
if (!ok) continue;
|
|
539
|
-
|
|
540
|
-
for (const t of rule.then) {
|
|
541
|
-
const concl = substitute(t, env);
|
|
542
|
-
const k = key(concl);
|
|
543
|
-
if (!factSet.has(k)) {
|
|
544
|
-
facts.push({ ...concl, _base: false });
|
|
545
|
-
factSet.add(k);
|
|
546
|
-
changed = true;
|
|
547
|
-
if (!factsIdx.byPred.has(concl.pred)) factsIdx.byPred.set(concl.pred, []);
|
|
548
|
-
factsIdx.byPred.get(concl.pred).push({ ...concl, _base: false });
|
|
549
|
-
const exp = buildExplanation(rule, env, negUsed);
|
|
550
|
-
proofs.set(k, exp);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
const derived = facts.filter((f) => !f._base);
|
|
558
|
-
|
|
559
|
-
// Keep only strongest priority per trip for Answer/Reason
|
|
560
|
-
const best = bestPriorities(facts);
|
|
561
|
-
const derivedFiltered = derived.filter((f) => f.pred !== 'priority' || best.get(f.s) === f.o);
|
|
562
|
-
|
|
563
|
-
const answerLines = formatFacts(derivedFiltered);
|
|
564
|
-
|
|
565
|
-
const reasonLines = [];
|
|
566
|
-
for (const f of derivedFiltered) {
|
|
567
|
-
const k = key(f);
|
|
568
|
-
const exp = proofs.get(k);
|
|
569
|
-
if (exp) reasonLines.push(`• ${exp} ⇒ therefore ${prettyFact(f)}.`);
|
|
570
|
-
else reasonLines.push(`• Derived ${prettyFact(f)}.`);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
return { facts, derived, answerText: answerLines.join('\n'), reasonText: reasonLines.join('\n') };
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
function buildExplanation(rule, env, negUsed) {
|
|
577
|
-
const fill = (s) => s.replace(/\?[A-Za-z0-9_]+/g, (m) => env[m] ?? m);
|
|
578
|
-
let text = fill(rule.explain || `Applied ${rule.id}`);
|
|
579
|
-
if (negUsed && negUsed.length) {
|
|
580
|
-
const negBits = negUsed.map((n) => `no fact ${n.pred}(${n.s}, ${n.o})`).join(' and ');
|
|
581
|
-
text += `, and ${negBits}`;
|
|
582
|
-
}
|
|
583
|
-
return text;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
function prettyFact(f) {
|
|
587
|
-
const tri = {
|
|
588
|
-
isA: (s, o) => `${s} ∈ ${o}`,
|
|
589
|
-
subClass: (s, o) => `${s} ⊑ ${o}`,
|
|
590
|
-
partOf: (s, o) => `${s} partOf ${o}`,
|
|
591
|
-
about: (s, o) => `${s} about ${o}`,
|
|
592
|
-
aboutTrip: (s, o) => `${s} aboutTrip ${o}`,
|
|
593
|
-
tag: (s, o) => `${s} tag ${o}`,
|
|
594
|
-
hasTag: (s, o) => `${s} hasTag ${o}`,
|
|
595
|
-
prefers: (s, o) => `${s} prefers ${o}`,
|
|
596
|
-
requiresTask: (s, o) => `${s} requires ${o}`,
|
|
597
|
-
priority: (s, o) => `${s} priority ${o}`,
|
|
598
|
-
};
|
|
599
|
-
const fn = tri[f.pred] || ((s, o) => `${s} ${f.pred} ${o}`);
|
|
600
|
-
return fn(f.s, f.o);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
function formatFacts(facts) {
|
|
604
|
-
const groups = {};
|
|
605
|
-
for (const f of facts) {
|
|
606
|
-
(groups[f.pred] ||= []).push(f);
|
|
607
|
-
}
|
|
608
|
-
const order = [
|
|
609
|
-
'isA',
|
|
610
|
-
'subClass',
|
|
611
|
-
'partOf',
|
|
612
|
-
'tag',
|
|
613
|
-
'hasTag',
|
|
614
|
-
'prefers',
|
|
615
|
-
'about',
|
|
616
|
-
'aboutTrip',
|
|
617
|
-
'requiresTask',
|
|
618
|
-
'priority',
|
|
619
|
-
];
|
|
620
|
-
const lines = [];
|
|
621
|
-
for (const pred of order) {
|
|
622
|
-
if (!groups[pred]) continue;
|
|
623
|
-
const sorted = groups[pred].slice().sort((a, b) => (a.s + a.o).localeCompare(b.s + b.o));
|
|
624
|
-
for (const f of sorted) lines.push(prettyFact(f));
|
|
625
|
-
}
|
|
626
|
-
for (const pred of Object.keys(groups)) {
|
|
627
|
-
if (order.includes(pred)) continue;
|
|
628
|
-
const sorted = groups[pred].slice().sort((a, b) => (a.s + a.o).localeCompare(b.s + b.o));
|
|
629
|
-
for (const f of sorted) lines.push(prettyFact(f));
|
|
630
|
-
}
|
|
631
|
-
return lines;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// ---- Checks
|
|
635
|
-
function runChecks(facts, checksSpec) {
|
|
636
|
-
const factSet = new Set(facts.map(key));
|
|
637
|
-
const out = [];
|
|
638
|
-
for (let i = 0; i < checksSpec.length; i++) {
|
|
639
|
-
const chk = checksSpec[i];
|
|
640
|
-
const k = key(chk.pattern);
|
|
641
|
-
const passed = factSet.has(k);
|
|
642
|
-
out.push({ i: i + 1, name: chk.name, passed });
|
|
643
|
-
}
|
|
644
|
-
return out;
|
|
645
|
-
}
|
|
646
|
-
function renderChecks(results) {
|
|
647
|
-
if (!results.length) return '(no checks)';
|
|
648
|
-
const lines = [];
|
|
649
|
-
for (const r of results) {
|
|
650
|
-
lines.push(`${r.passed ? '✅' : '❌'} ${String(r.i).padStart(2, ' ')} — ${r.name}`);
|
|
651
|
-
}
|
|
652
|
-
const passCt = results.filter((r) => r.passed).length;
|
|
653
|
-
lines.push(`\nSummary: ${passCt}/${results.length} PASS`);
|
|
654
|
-
return lines.join('\n');
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// ---- Summary (uses strongest priority per trip)
|
|
658
|
-
function summarizeTrips(facts) {
|
|
659
|
-
const tasksByT = new Map();
|
|
660
|
-
const segCountByT = new Map();
|
|
661
|
-
for (const f of facts) {
|
|
662
|
-
if (f.pred === 'requiresTask') {
|
|
663
|
-
if (!tasksByT.has(f.s)) tasksByT.set(f.s, new Set());
|
|
664
|
-
tasksByT.get(f.s).add(f.o);
|
|
665
|
-
} else if (f.pred === 'partOf') {
|
|
666
|
-
segCountByT.set(f.o, (segCountByT.get(f.o) || 0) + 1);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
const best = bestPriorities(facts);
|
|
670
|
-
const trips = new Set([...tasksByT.keys(), ...best.keys(), ...segCountByT.keys()]);
|
|
671
|
-
if (!trips.size) return '(no trips)';
|
|
672
|
-
const lines = [];
|
|
673
|
-
for (const t of [...trips].sort()) {
|
|
674
|
-
const pr = best.get(t) || 'Go';
|
|
675
|
-
const nSeg = segCountByT.get(t) || 0;
|
|
676
|
-
const tasks = [...(tasksByT.get(t) || [])].sort().join(', ');
|
|
677
|
-
lines.push(`${t}: priority ${pr} — segments ${nSeg}${tasks ? ' — tasks: ' + tasks : ''}`);
|
|
678
|
-
}
|
|
679
|
-
return lines.join('\n');
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
// ---- Orchestration
|
|
683
|
-
function parseJSON(text, label) {
|
|
684
|
-
try {
|
|
685
|
-
return JSON.parse(text);
|
|
686
|
-
} catch (e) {
|
|
687
|
-
throw new Error(`${label} JSON error: ${e.message}`);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
function toPolicies(spec) {
|
|
691
|
-
return spec.map((r) => ({
|
|
692
|
-
id: r.id || '(unnamed)',
|
|
693
|
-
if: r.if || [],
|
|
694
|
-
then: r.then || [],
|
|
695
|
-
explain: r.explain || '',
|
|
696
|
-
}));
|
|
697
|
-
}
|
|
698
|
-
function buildDataObj(spec) {
|
|
699
|
-
return spec;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
async function runARC() {
|
|
703
|
-
els.status.textContent = 'Parsing JSON…';
|
|
704
|
-
els.answer.textContent = els.reason.textContent = els.checks.textContent = 'computing…';
|
|
705
|
-
els.summary.textContent = '(run to populate)';
|
|
706
|
-
els.diag.textContent = '';
|
|
707
|
-
try {
|
|
708
|
-
const dataSpec = parseJSON(els.dataTA.value, 'Data');
|
|
709
|
-
const polSpec = parseJSON(els.policyTA.value, 'Policies');
|
|
710
|
-
const checksSpec = parseJSON(els.checksTA.value, 'Checks');
|
|
711
|
-
const data = buildDataObj(dataSpec);
|
|
712
|
-
const policies = toPolicies(polSpec);
|
|
713
|
-
|
|
714
|
-
els.status.textContent = 'Reasoning…';
|
|
715
|
-
const { facts, derived, answerText, reasonText } = derive(data, policies);
|
|
716
|
-
|
|
717
|
-
els.answer.textContent = answerText || '(no new derivations)';
|
|
718
|
-
els.reason.textContent = reasonText || '(no explanations available)';
|
|
719
|
-
|
|
720
|
-
els.status.textContent = 'Running checks…';
|
|
721
|
-
const checkResults = runChecks(facts, checksSpec);
|
|
722
|
-
els.checks.textContent = renderChecks(checkResults);
|
|
723
|
-
|
|
724
|
-
els.summary.textContent = summarizeTrips(facts);
|
|
725
|
-
|
|
726
|
-
els.status.textContent = 'Done.';
|
|
727
|
-
} catch (e) {
|
|
728
|
-
console.error(e);
|
|
729
|
-
els.status.textContent = 'Error';
|
|
730
|
-
els.answer.textContent = '(failed)';
|
|
731
|
-
els.reason.textContent = '(failed)';
|
|
732
|
-
els.checks.textContent = '(failed)';
|
|
733
|
-
els.summary.textContent = '(failed)';
|
|
734
|
-
els.diag.textContent = e.message;
|
|
735
|
-
}
|
|
736
|
-
autoResize(els.dataTA);
|
|
737
|
-
autoResize(els.policyTA);
|
|
738
|
-
autoResize(els.checksTA);
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
function showReasonOnly() {
|
|
742
|
-
runARC().then(() => {
|
|
743
|
-
window.scrollTo({ top: $('reason').getBoundingClientRect().top + window.scrollY - 12, behavior: 'smooth' });
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
els.runBtn.addEventListener('click', runARC);
|
|
748
|
-
els.reasonBtn.addEventListener('click', showReasonOnly);
|
|
749
|
-
window.addEventListener('DOMContentLoaded', runARC);
|
|
750
|
-
</script>
|
|
751
|
-
</body>
|
|
752
|
-
</html>
|