@polycode-projects/the-mechanical-code-talker 4.1.9 → 5.0.1
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 +1 -1
- package/corpus/worlds/index.json.gz +0 -0
- package/corpus/worlds/manifest.json +35 -5
- package/corpus/worlds/shards/town-square-chapel.jsonl.gz +0 -0
- package/corpus/worlds/shards/town-square-market.jsonl.gz +0 -0
- package/corpus/worlds/shards/town-square.jsonl.gz +0 -0
- package/corpus/worlds/src/town-square-chapel.jsonl +929 -0
- package/corpus/worlds/src/town-square-market.jsonl +455 -0
- package/corpus/worlds/src/town-square.jsonl +687 -0
- package/data/mudiii-assets.json +201 -0
- package/package.json +8 -2
- package/src/domain/agent-belief.mjs +96 -0
- package/src/domain/answer-variants.json +1 -1
- package/src/domain/game-config.mjs +65 -0
- package/src/domain/memory/compaction.mjs +2 -0
- package/src/domain/town-square-world.mjs +416 -0
- package/src/services/adventure-editor.mjs +36 -0
- package/src/services/adventure.mjs +45 -15
- package/src/services/chat.mjs +25 -1
- package/src/services/mud-editor.mjs +80 -0
- package/src/services/mudiii-scene.mjs +766 -0
- package/src/services/mudiii-turn.mjs +670 -0
- package/src/services/mudiii-viz.mjs +1247 -0
- package/src/services/pill-complete.mjs +495 -0
- package/src/services/predator-prey.mjs +916 -0
- package/src/services/spider-fly.mjs +14 -64
- package/src/services/world-teach.mjs +214 -0
- package/src/surfaces/web/adventure-browser-entry.mjs +18 -3
- package/src/surfaces/web/memory-ask-browser.bundle.js +126 -121
- package/src/surfaces/web/mudiii-browser-entry.mjs +224 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_readme": [
|
|
3
|
+
"Closed allowlist. A later copy agent may copy ONLY the exact sourcePath -> destPath pairs listed in `assets` below, and must not read, list, or copy anything else out of ../world-of-claudecraft. If a model isn't a row here, it doesn't get copied, no matter how well it fits.",
|
|
4
|
+
"The eastbrook_* trap: those files sit alphabetically right next to the correct ones and look better, but CREDITS.md licenses the whole eastbrook_* kit 'With the project only' (ships with that project, not extractable into a standalone demo like mudiii.html). Take the unprefixed sibling every time: market_stand_1.glb not eastbrook_market_stall.glb, inn.glb not eastbrook_inn.glb, blacksmith.glb not eastbrook_smithy.glb, well.glb not eastbrook_civic_well_beacon.glb.",
|
|
5
|
+
"Every GLB listed here uses EXT_meshopt_compression and KHR_mesh_quantization (verified by parsing each file's JSON chunk directly, not by trusting docs). Three additionally use EXT_texture_webp: creatures/goblin.glb, foliage/oak_1.glb, foliage/bush.glb. three.js's GLTFLoader decodes KHR_mesh_quantization and EXT_texture_webp natively, so nobody needs to hunt for a decoder for either of those. EXT_meshopt_compression is the one exception and needs exactly one line: loader.setMeshoptDecoder(MeshoptDecoder). See commands.vendorMeshoptDecoderLine below, and commands.fallbackTranscodeToPlainGlb if vendoring the decoder turns out to be undesirable.",
|
|
6
|
+
"Food markers (cheese, apples, apple crate) are primitive geometry in v1, not models. The KayKit food props (food_cheese, food_apple_*, food_crate_large_apples) are almost certainly CC0 at source, but no CREDITS.md row names a KayKit resource pack, so they're unrecorded rather than free to take and are excluded here. Fetching the genuine KayKit Resource Bits pack from source (https://kaykit.itch.io) and adding a CREDITS.md row for it is a clean later upgrade, not a wall.",
|
|
7
|
+
"The predator is creatures/fox.glb, not creatures/wolf_basic.glb. wolf_basic.glb looks like the obvious predator model and is close in name to the CREDITS 'Animated creatures' row, but wolf_basic.glb itself is an unrecorded custom Tripo-generated project asset, not the CC0 Quaternius wolf. fox.glb is covered by the CC0 Quaternius creatures row by name, shares the same quadruped rig (Idle/Walk/Gallop/Attack/hit-react/Death via manifest.ts's animal() clip map), and additionally ships an 'Eating' clip verified in the GLB itself -- useful for an eaten-flourish. goblin.glb is the prey/scavenger, matching world-of-claudecraft's own mob_kobold casting (Idle/Walk/Run/Attack/HitRecieve/Death) -- note HitRecieve is misspelled in the asset itself and is reproduced verbatim below, not corrected.",
|
|
8
|
+
"targetHeight is the model's own measured bounding-box height in metres (computed from each GLB's POSITION accessor bounds through its node transform chain, dequantizing normalized SHORT/BYTE components first), not an arbitrary guess -- these packs already ship in a consistent real-world scale, so no further per-item normalization judgment call was needed for the static props. The two character rigs (fox, goblin) use world-of-claudecraft's own manifest.ts height values instead, since their POSITION accessors are in skinned bind-pose space and a plain node-transform bbox walk (as used for the static props) does not account for joint transforms."
|
|
9
|
+
],
|
|
10
|
+
"commands": {
|
|
11
|
+
"verifyBytes": "stat -f '%z' <file> # BSD/macOS; use `stat -c '%s' <file>` on GNU/Linux",
|
|
12
|
+
"verifySha256": "shasum -a 256 <file>",
|
|
13
|
+
"vendorMeshoptDecoderLine": "loader.setMeshoptDecoder(MeshoptDecoder) // three.js GLTFLoader; the only line meshopt compression needs -- quantization and webp need nothing",
|
|
14
|
+
"fallbackTranscodeToPlainGlb": "npx gltf-transform copy <in>.glb <out>.glb # decodes EXT_meshopt_compression on read; since no compress step is re-applied, the written GLB carries plain (uncompressed) buffers -- use only if vendoring the decoder is undesirable"
|
|
15
|
+
},
|
|
16
|
+
"assets": [
|
|
17
|
+
{
|
|
18
|
+
"key": "fox",
|
|
19
|
+
"sourcePath": "public/models/creatures/fox.glb",
|
|
20
|
+
"destPath": "public/models/creatures/fox.glb",
|
|
21
|
+
"licence": "CC0-1.0",
|
|
22
|
+
"author": "Quaternius",
|
|
23
|
+
"source": "https://poly.pizza/u/Quaternius · https://quaternius.com",
|
|
24
|
+
"bytes": 332748,
|
|
25
|
+
"sha256": "2694028b47e84bad3b9d985aa7743dae02a3bcfddd6becd73b46c747a1e067b3",
|
|
26
|
+
"targetHeight": 1.0,
|
|
27
|
+
"clips": {
|
|
28
|
+
"idle": "Idle",
|
|
29
|
+
"walk": "Walk",
|
|
30
|
+
"run": "Gallop",
|
|
31
|
+
"attack": "Attack",
|
|
32
|
+
"hit": ["Idle_HitReact_Left", "Idle_HitReact_Right"],
|
|
33
|
+
"death": "Death",
|
|
34
|
+
"eat": "Eating"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"key": "goblin",
|
|
39
|
+
"sourcePath": "public/models/creatures/goblin.glb",
|
|
40
|
+
"destPath": "public/models/creatures/goblin.glb",
|
|
41
|
+
"licence": "CC0-1.0",
|
|
42
|
+
"author": "Quaternius",
|
|
43
|
+
"source": "https://poly.pizza/u/Quaternius · https://quaternius.com",
|
|
44
|
+
"bytes": 47924,
|
|
45
|
+
"sha256": "2f02ce927a8871554f3931655d75f1abb8237d36be8a19a35b09920d0812bc3c",
|
|
46
|
+
"targetHeight": 2.1,
|
|
47
|
+
"clips": {
|
|
48
|
+
"idle": "Idle",
|
|
49
|
+
"walk": "Walk",
|
|
50
|
+
"run": "Run",
|
|
51
|
+
"attack": "Attack",
|
|
52
|
+
"hit": ["HitRecieve"],
|
|
53
|
+
"death": "Death"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"key": "well",
|
|
58
|
+
"sourcePath": "public/models/props/well.glb",
|
|
59
|
+
"destPath": "public/models/props/well.glb",
|
|
60
|
+
"licence": "CC0-1.0",
|
|
61
|
+
"author": "Quaternius",
|
|
62
|
+
"source": "https://quaternius.com/packs/medievalvillage.html",
|
|
63
|
+
"bytes": 27788,
|
|
64
|
+
"sha256": "9308ba09ea3bed048984eeb14307b0557c5ea11404556d33afa9ed904b6ee123",
|
|
65
|
+
"targetHeight": 1.25,
|
|
66
|
+
"clips": null
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"key": "house-1",
|
|
70
|
+
"sourcePath": "public/models/props/house_1.glb",
|
|
71
|
+
"destPath": "public/models/props/house_1.glb",
|
|
72
|
+
"licence": "CC0-1.0",
|
|
73
|
+
"author": "Quaternius",
|
|
74
|
+
"source": "https://quaternius.com/packs/medievalvillage.html",
|
|
75
|
+
"bytes": 84484,
|
|
76
|
+
"sha256": "87c8306f95e335ce01150ef3fb90b52cabc15e047077f98d1f25df8d76a95b9e",
|
|
77
|
+
"targetHeight": 3.39,
|
|
78
|
+
"clips": null
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"key": "house-2",
|
|
82
|
+
"sourcePath": "public/models/props/house_2.glb",
|
|
83
|
+
"destPath": "public/models/props/house_2.glb",
|
|
84
|
+
"licence": "CC0-1.0",
|
|
85
|
+
"author": "Quaternius",
|
|
86
|
+
"source": "https://quaternius.com/packs/medievalvillage.html",
|
|
87
|
+
"bytes": 103180,
|
|
88
|
+
"sha256": "ede3299a2b09bdf66fc56cb56187ae1cdd673ee23eb44b75a40aa38cd8855e93",
|
|
89
|
+
"targetHeight": 3.25,
|
|
90
|
+
"clips": null
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"key": "house-3",
|
|
94
|
+
"sourcePath": "public/models/props/house_3.glb",
|
|
95
|
+
"destPath": "public/models/props/house_3.glb",
|
|
96
|
+
"licence": "CC0-1.0",
|
|
97
|
+
"author": "Quaternius",
|
|
98
|
+
"source": "https://quaternius.com/packs/medievalvillage.html",
|
|
99
|
+
"bytes": 43696,
|
|
100
|
+
"sha256": "3b426a1d8894d3423c0a32c61527dfcbf79e9c0727fa9b83889cf6fa93d9b969",
|
|
101
|
+
"targetHeight": 1.07,
|
|
102
|
+
"clips": null
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"key": "inn",
|
|
106
|
+
"sourcePath": "public/models/props/inn.glb",
|
|
107
|
+
"destPath": "public/models/props/inn.glb",
|
|
108
|
+
"licence": "CC0-1.0",
|
|
109
|
+
"author": "Quaternius",
|
|
110
|
+
"source": "https://quaternius.com/packs/medievalvillage.html",
|
|
111
|
+
"bytes": 92748,
|
|
112
|
+
"sha256": "a1386fe4db504f5110df670b91f4e5f6fdc3baf545cc023fb21ebcc457867a04",
|
|
113
|
+
"targetHeight": 3.49,
|
|
114
|
+
"clips": null
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"key": "blacksmith",
|
|
118
|
+
"sourcePath": "public/models/props/blacksmith.glb",
|
|
119
|
+
"destPath": "public/models/props/blacksmith.glb",
|
|
120
|
+
"licence": "CC0-1.0",
|
|
121
|
+
"author": "Quaternius",
|
|
122
|
+
"source": "https://quaternius.com/packs/medievalvillage.html",
|
|
123
|
+
"bytes": 97444,
|
|
124
|
+
"sha256": "893c49e06a5c7e5967caad0a666666fb898f3d6823e81dec66b8cf96b3aca3ac",
|
|
125
|
+
"targetHeight": 3.0,
|
|
126
|
+
"clips": null
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"key": "market-stall-1",
|
|
130
|
+
"sourcePath": "public/models/props/market_stand_1.glb",
|
|
131
|
+
"destPath": "public/models/props/market_stand_1.glb",
|
|
132
|
+
"licence": "CC0-1.0",
|
|
133
|
+
"author": "Quaternius",
|
|
134
|
+
"source": "https://quaternius.com/packs/medievalvillage.html",
|
|
135
|
+
"bytes": 25184,
|
|
136
|
+
"sha256": "d59645bb16549882280e091e41804cca09dac4a693772960bde0418e5d0229b5",
|
|
137
|
+
"targetHeight": 1.05,
|
|
138
|
+
"clips": null
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"key": "market-stall-2",
|
|
142
|
+
"sourcePath": "public/models/props/market_stand_2.glb",
|
|
143
|
+
"destPath": "public/models/props/market_stand_2.glb",
|
|
144
|
+
"licence": "CC0-1.0",
|
|
145
|
+
"author": "Quaternius",
|
|
146
|
+
"source": "https://quaternius.com/packs/medievalvillage.html",
|
|
147
|
+
"bytes": 18868,
|
|
148
|
+
"sha256": "b81f32ab47d17e5b76386814554fe5c1419189049c727d5b84a46a2278ce3597",
|
|
149
|
+
"targetHeight": 1.04,
|
|
150
|
+
"clips": null
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"key": "cart",
|
|
154
|
+
"sourcePath": "public/models/props/cart.glb",
|
|
155
|
+
"destPath": "public/models/props/cart.glb",
|
|
156
|
+
"licence": "CC0-1.0",
|
|
157
|
+
"author": "Quaternius",
|
|
158
|
+
"source": "https://quaternius.com/packs/medievalvillage.html",
|
|
159
|
+
"bytes": 30972,
|
|
160
|
+
"sha256": "8bb07d235482439d0e4f1d9152d932b6487cdb09b3331c668f882461401f43d9",
|
|
161
|
+
"targetHeight": 0.8,
|
|
162
|
+
"clips": null
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"key": "fence",
|
|
166
|
+
"sourcePath": "public/models/props/fence.glb",
|
|
167
|
+
"destPath": "public/models/props/fence.glb",
|
|
168
|
+
"licence": "CC0-1.0",
|
|
169
|
+
"author": "Quaternius",
|
|
170
|
+
"source": "https://quaternius.com/packs/medievalvillage.html",
|
|
171
|
+
"bytes": 4836,
|
|
172
|
+
"sha256": "ff22bf2593398774f20e7e6f82f3a9184e326cc4c45cd780a0981590a54172c0",
|
|
173
|
+
"targetHeight": 0.33,
|
|
174
|
+
"clips": null
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"key": "oak-tree",
|
|
178
|
+
"sourcePath": "public/models/foliage/oak_1.glb",
|
|
179
|
+
"destPath": "public/models/foliage/oak_1.glb",
|
|
180
|
+
"licence": "CC0-1.0",
|
|
181
|
+
"author": "Quaternius",
|
|
182
|
+
"source": "https://quaternius.itch.io/stylized-nature-megakit",
|
|
183
|
+
"bytes": 184216,
|
|
184
|
+
"sha256": "e8c8b7e02173490bfe5e802fad47c1b7824b787bb6c9274163bde7fda065c102",
|
|
185
|
+
"targetHeight": 7.26,
|
|
186
|
+
"clips": null
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"key": "bush",
|
|
190
|
+
"sourcePath": "public/models/foliage/bush.glb",
|
|
191
|
+
"destPath": "public/models/foliage/bush.glb",
|
|
192
|
+
"licence": "CC0-1.0",
|
|
193
|
+
"author": "Quaternius",
|
|
194
|
+
"source": "https://quaternius.itch.io/stylized-nature-megakit",
|
|
195
|
+
"bytes": 49064,
|
|
196
|
+
"sha256": "614172ada35fc6048487a6af6d77059a5c031e0a93eff9d58036649ea0d16c12",
|
|
197
|
+
"targetHeight": 1.57,
|
|
198
|
+
"clips": null
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The Mechanical Code Talker (tmct) — a tolerant, offline, $0 chat surface that guides you toward precision queries about a software repository. ELIZA/PARRY-style but domain-obsessed with code. No model calls; indexes a repo on request (tmct index) or reads any producer's graph.",
|
|
@@ -101,11 +101,14 @@
|
|
|
101
101
|
"check:pii": "node scripts/pii-lint.mjs",
|
|
102
102
|
"check:pack": "node scripts/check-pack-manifest.mjs",
|
|
103
103
|
"check:licences": "node scripts/check-licences.mjs",
|
|
104
|
+
"check:models": "node scripts/check-model-manifest.mjs",
|
|
105
|
+
"check:model-credits": "node scripts/gen-model-credits.mjs --check",
|
|
106
|
+
"gen:model-credits": "node scripts/gen-model-credits.mjs",
|
|
104
107
|
"check:publint": "npx --no-install publint",
|
|
105
108
|
"check:tool-docs": "node scripts/generate-tool-docs.mjs --check",
|
|
106
109
|
"check:budgets": "node scripts/check-tier-budgets.mjs",
|
|
107
110
|
"check:publish": "node scripts/check-publish.mjs",
|
|
108
|
-
"check:all": "npm run check:links && npm run check:pii && npm run check:licences && npm run check:publint && npm run check:tool-docs && npm run check:budgets",
|
|
111
|
+
"check:all": "npm run check:links && npm run check:pii && npm run check:licences && npm run check:models && npm run check:model-credits && npm run check:publint && npm run check:tool-docs && npm run check:budgets",
|
|
109
112
|
"smoke:deploy": "node scripts/post-deploy-smoke.mjs",
|
|
110
113
|
"wait:site": "node scripts/wait-for-site.mjs",
|
|
111
114
|
"chat": "node --disable-warning=ExperimentalWarning bin/tmct.mjs",
|
|
@@ -162,11 +165,13 @@
|
|
|
162
165
|
"build:research-bundle": "node scripts/build-research-bundle.mjs",
|
|
163
166
|
"build:code-explorer-bundle": "node scripts/build-code-explorer-bundle.mjs",
|
|
164
167
|
"build:mud-bundle": "node scripts/build-mud-bundle.mjs",
|
|
168
|
+
"build:mudiii-bundle": "node scripts/build-mudiii-bundle.mjs",
|
|
165
169
|
"build:electron": "node scripts/build-electron-app.mjs",
|
|
166
170
|
"electron": "electron electron/main.mjs",
|
|
167
171
|
"test:electron": "node --test electron/smoke.test.mjs",
|
|
168
172
|
"build:sprites-bundle": "node scripts/build-sprites-bundle.mjs",
|
|
169
173
|
"build:wink-vendor": "node scripts/build-wink-vendor.mjs",
|
|
174
|
+
"build:three-vendor": "node scripts/build-three-vendor.mjs",
|
|
170
175
|
"build:chat-seed": "node scripts/build-chat-seed.mjs",
|
|
171
176
|
"build:demo-graph": "node scripts/build-demo-graph.mjs",
|
|
172
177
|
"build:demo-pack": "node scripts/build-demo-pack.mjs",
|
|
@@ -190,6 +195,7 @@
|
|
|
190
195
|
"ink-testing-library": "4.0.0",
|
|
191
196
|
"playwright": "1.61.1",
|
|
192
197
|
"publint": "0.3.21",
|
|
198
|
+
"three": "0.185.1",
|
|
193
199
|
"yaml": "2.9.0"
|
|
194
200
|
}
|
|
195
201
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// agent-belief.mjs — what one agent believes about where another agent or
|
|
2
|
+
// item is, this tick. Ground truth inside its own vision radius, else the
|
|
3
|
+
// newest position it was told, else nothing at all. The "nothing at all" rung
|
|
4
|
+
// is the point: an agent that has not seen and has not been told believes
|
|
5
|
+
// nothing, and a deceived agent stays visibly wrong until it looks.
|
|
6
|
+
//
|
|
7
|
+
// Vision is a plain Chebyshev radius with NO line of sight. Buildings block
|
|
8
|
+
// movement (a world pack omits its mgx:has-exit-* edges into an occupied cell)
|
|
9
|
+
// and do not block sight. Anyone tempted to add a raycast here should know
|
|
10
|
+
// that nothing else in the engine models occlusion, so belief would then
|
|
11
|
+
// disagree with the planner, the goal line and the belief panel at once.
|
|
12
|
+
//
|
|
13
|
+
// The module imports nothing, deliberately. Every world that runs on the
|
|
14
|
+
// planning engine shares it, and each world sizes its own board, so belief
|
|
15
|
+
// must not inherit any one world's grid constants. Asking a world for its
|
|
16
|
+
// clipped list of visible cells is what once bound this test to a 10x10 board
|
|
17
|
+
// and made every cell outside it invisible on a larger one.
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_VISION_RADIUS = 4;
|
|
20
|
+
|
|
21
|
+
const CELL_ID_RE = /^cell-(\d+)-(\d+)$/;
|
|
22
|
+
|
|
23
|
+
const cellCoordsOf = (id) => {
|
|
24
|
+
const m = CELL_ID_RE.exec(String(id ?? ""));
|
|
25
|
+
return m ? { x: Number(m[1]), y: Number(m[2]) } : null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const cellIdOf = (x, y) => `cell-${x}-${y}`;
|
|
29
|
+
|
|
30
|
+
const chebyshevSteps = (ax, ay, bx, by) => Math.max(Math.abs(ax - bx), Math.abs(ay - by));
|
|
31
|
+
|
|
32
|
+
/** Where `observerSubject` currently believes `targetSubject` to be, as an
|
|
33
|
+
* `{ x, y }` cell, or null for no belief at all.
|
|
34
|
+
*
|
|
35
|
+
* Four rungs, in order:
|
|
36
|
+
*
|
|
37
|
+
* 1. A target the board has removed (eaten, starved, hatched) is believed
|
|
38
|
+
* nowhere, and no told fact can put it back. A claim about an id that was
|
|
39
|
+
* never placed at all is a different case and still lands, which is what
|
|
40
|
+
* keeps deception working.
|
|
41
|
+
* 2. Ground truth, when the target's real cell sits within `visionRadius`
|
|
42
|
+
* Chebyshev steps of `observerCell`.
|
|
43
|
+
* 3. The newest told fact addressed to this observer about this target —
|
|
44
|
+
* `opts.toldFacts` rows shaped `{ subject, toAgent, cell, turn }`.
|
|
45
|
+
* 4. Otherwise null.
|
|
46
|
+
*
|
|
47
|
+
* `state` is a folded world state: `.placements` (subject -> `{ cell }`) and
|
|
48
|
+
* `.removed` (a Set of subjects off the board). Pure. */
|
|
49
|
+
export function believedCellOf(targetSubject, observerSubject, observerCell, state, opts = {}) {
|
|
50
|
+
if (state.removed.has(targetSubject)) return null;
|
|
51
|
+
const { visionRadius = DEFAULT_VISION_RADIUS, toldFacts = [] } = opts;
|
|
52
|
+
const place = state.placements.get(targetSubject);
|
|
53
|
+
const at = place ? cellCoordsOf(place.cell) : null;
|
|
54
|
+
if (at && chebyshevSteps(observerCell.x, observerCell.y, at.x, at.y) <= visionRadius) return at;
|
|
55
|
+
const told = toldFacts
|
|
56
|
+
.filter((f) => f.toAgent === observerSubject && f.subject === targetSubject)
|
|
57
|
+
.sort((a, b) => (b.turn ?? 0) - (a.turn ?? 0))[0];
|
|
58
|
+
return told ? cellCoordsOf(told.cell) : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The nearest candidate (by believed Chebyshev distance) an observer has
|
|
62
|
+
* any belief about at all — null when the observer believes nothing about
|
|
63
|
+
* any candidate. `candidates` must already be in a deterministic order
|
|
64
|
+
* (ties favor the earlier candidate). */
|
|
65
|
+
export function nearestBelievedTarget(observerSubject, observerCell, candidates, state, opts = {}) {
|
|
66
|
+
let best = null;
|
|
67
|
+
let bestDist = Infinity;
|
|
68
|
+
for (const subject of candidates) {
|
|
69
|
+
const cell = believedCellOf(subject, observerSubject, observerCell, state, opts);
|
|
70
|
+
if (!cell) continue;
|
|
71
|
+
const dist = chebyshevSteps(observerCell.x, observerCell.y, cell.x, cell.y);
|
|
72
|
+
if (dist < bestDist) { bestDist = dist; best = { subject, cell }; }
|
|
73
|
+
}
|
|
74
|
+
return best;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** A full "world knowledge graph" snapshot for one observer this tick: every
|
|
78
|
+
* OTHER named candidate's believed cell (believedCellOf — ground truth
|
|
79
|
+
* inside vision, else the newest told fact addressed to this observer,
|
|
80
|
+
* else null for "unknown") — feeds a per-agent belief panel. Deliberately
|
|
81
|
+
* never ground truth: showing the observer's own honest gap
|
|
82
|
+
* between belief and reality (visibly widened by a deceiving pill or a fed
|
|
83
|
+
* false fact) is the whole point of that panel. Returns a plain
|
|
84
|
+
* `{ [candidateId]: cellId | null }` map. Exported as the read path for
|
|
85
|
+
* what one agent can currently observe — every caller (a tick loop, a viz
|
|
86
|
+
* panel, a chat lane) reads the same computation, never a re-derived copy
|
|
87
|
+
* of it. */
|
|
88
|
+
export function beliefSnapshotFor(observerSubject, observerCell, candidateIds, state, opts) {
|
|
89
|
+
const belief = {};
|
|
90
|
+
for (const candidateId of candidateIds) {
|
|
91
|
+
if (candidateId === observerSubject) continue;
|
|
92
|
+
const cell = believedCellOf(candidateId, observerSubject, observerCell, state, opts);
|
|
93
|
+
belief[candidateId] = cell ? cellIdOf(cell.x, cell.y) : null;
|
|
94
|
+
}
|
|
95
|
+
return belief;
|
|
96
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"ask.mjs metaFallbackEntityAnswer (\"X is a Y in this codebase, defined in Z ...\")"
|
|
11
11
|
],
|
|
12
12
|
"deliberatelyNotUsedBy": [
|
|
13
|
-
"ask.mjs renderCore whereShape (\"X is defined in Y at line(s) Z\" / \"X is defined in Y (no line span...)\") -- test-benchmarks/chatbench/graded-pool-max.jsonl pins this exact substring as ground truth for 11 'where is X defined' cases (2 in the always-run promoted subset), and that pool is append-only/never-edited-mid-arc per
|
|
13
|
+
"ask.mjs renderCore whereShape (\"X is defined in Y at line(s) Z\" / \"X is defined in Y (no line span...)\") -- test-benchmarks/chatbench/graded-pool-max.jsonl pins this exact substring as ground truth for 11 'where is X defined' cases (2 in the always-run promoted subset), and that pool is append-only/never-edited-mid-arc per .claude/skills/benchmark-cefr-english/SKILL.md."
|
|
14
14
|
]
|
|
15
15
|
},
|
|
16
16
|
"is-dated": {
|
|
@@ -56,11 +56,43 @@ export const DEFAULT_GAME_CONFIG = Object.freeze({
|
|
|
56
56
|
meerkatSpeed: 1,
|
|
57
57
|
meerkatDigReach: 1,
|
|
58
58
|
}),
|
|
59
|
+
// The town square's knobs are keyed by role, not by species: its cast is a
|
|
60
|
+
// predator/prey pair by construction, so a later pair is a new roles object
|
|
61
|
+
// and no edit here. (mud above went the other way because its cast is
|
|
62
|
+
// authored per world.) The drains sit on mud's scale against the deck's own
|
|
63
|
+
// 400-turn default: a prey that never eats starves after about 133 turns, a
|
|
64
|
+
// predator after 250.
|
|
65
|
+
//
|
|
66
|
+
// maxPreyPopulation and maxFoodItems are the two knobs that keep that
|
|
67
|
+
// arithmetic from running away. Prey arrive every 5 turns and live ~133, so
|
|
68
|
+
// an uncapped 400-turn run ends with roughly 80 of them on a 144-cell board
|
|
69
|
+
// — a solid carpet of goblins, and every tick paying for their planning.
|
|
70
|
+
// Food spawns faster still. Both caps are checked before a spawn, so
|
|
71
|
+
// reaching one simply skips that turn's arrival.
|
|
72
|
+
mudiii: Object.freeze({
|
|
73
|
+
predatorInitialMass: 20,
|
|
74
|
+
predatorMassDecrementPerTurn: 0.08,
|
|
75
|
+
predatorVisionRadius: 4,
|
|
76
|
+
preyInitialMass: 8,
|
|
77
|
+
preyMassDecrementPerTurn: 0.06,
|
|
78
|
+
preyVisionRadius: 3,
|
|
79
|
+
preySpawnIntervalTurns: 5,
|
|
80
|
+
foodSpawnIntervalTurns: 3,
|
|
81
|
+
spawnedFoodMass: 1,
|
|
82
|
+
placedFoodMass: 2,
|
|
83
|
+
maxPreyPopulation: 6,
|
|
84
|
+
maxFoodItems: 8,
|
|
85
|
+
}),
|
|
59
86
|
guessNumber: Object.freeze({
|
|
60
87
|
defaultLo: 1,
|
|
61
88
|
defaultHi: 100,
|
|
62
89
|
maxBound: 1_000_000_000,
|
|
63
90
|
}),
|
|
91
|
+
// Whether the adventure surface accepts teaching. One knob behind a page
|
|
92
|
+
// checkbox, the CLI flag and the corpus rows, so all three agree.
|
|
93
|
+
adventure: Object.freeze({
|
|
94
|
+
teach: false,
|
|
95
|
+
}),
|
|
64
96
|
planning: Object.freeze({
|
|
65
97
|
maxDepth: 300,
|
|
66
98
|
}),
|
|
@@ -107,6 +139,25 @@ const MUD_KEY_MAP = Object.freeze({
|
|
|
107
139
|
meerkat_dig_reach: "meerkatDigReach",
|
|
108
140
|
});
|
|
109
141
|
|
|
142
|
+
const MUDIII_KEY_MAP = Object.freeze({
|
|
143
|
+
predator_initial_mass: "predatorInitialMass",
|
|
144
|
+
predator_mass_decrement_per_turn: "predatorMassDecrementPerTurn",
|
|
145
|
+
predator_vision_radius: "predatorVisionRadius",
|
|
146
|
+
prey_initial_mass: "preyInitialMass",
|
|
147
|
+
prey_mass_decrement_per_turn: "preyMassDecrementPerTurn",
|
|
148
|
+
prey_vision_radius: "preyVisionRadius",
|
|
149
|
+
prey_spawn_interval_turns: "preySpawnIntervalTurns",
|
|
150
|
+
food_spawn_interval_turns: "foodSpawnIntervalTurns",
|
|
151
|
+
spawned_food_mass: "spawnedFoodMass",
|
|
152
|
+
placed_food_mass: "placedFoodMass",
|
|
153
|
+
max_prey_population: "maxPreyPopulation",
|
|
154
|
+
max_food_items: "maxFoodItems",
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const ADVENTURE_KEY_MAP = Object.freeze({
|
|
158
|
+
teach: "teach",
|
|
159
|
+
});
|
|
160
|
+
|
|
110
161
|
const GUESS_NUMBER_KEY_MAP = Object.freeze({
|
|
111
162
|
default_lo: "defaultLo",
|
|
112
163
|
default_hi: "defaultHi",
|
|
@@ -154,6 +205,18 @@ export function massScaleFor(cls, config) {
|
|
|
154
205
|
return null;
|
|
155
206
|
}
|
|
156
207
|
|
|
208
|
+
/** The mass denominator a town-square HUD bar scales against, from a resolved
|
|
209
|
+
* `mudiii` section. `role` is "predator" or "prey"; anything else — a crumb,
|
|
210
|
+
* a prop, a role a later cast adds — has no denominator and gets null, never
|
|
211
|
+
* a guessed number. Separate from massScaleFor above on purpose: that one
|
|
212
|
+
* takes a species literal off the spider-and-fly page, and folding a second
|
|
213
|
+
* vocabulary into it would turn a two-line read into a registry. Pure. */
|
|
214
|
+
export function mudiiiMassScaleFor(role, config) {
|
|
215
|
+
if (role === "predator") return config?.predatorInitialMass ?? null;
|
|
216
|
+
if (role === "prey") return config?.preyInitialMass ?? null;
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
157
220
|
/**
|
|
158
221
|
* Fold a normalized tmct.toml's `games`/`planning` tables (the raw sparse
|
|
159
222
|
* pass-through src/adapters/toml-config.mjs's normalizeConfig produces —
|
|
@@ -168,7 +231,9 @@ export function resolveGameConfig(toml) {
|
|
|
168
231
|
return {
|
|
169
232
|
spiderFly: mergeSection(DEFAULT_GAME_CONFIG.spiderFly, games["spider-fly"], SPIDER_FLY_KEY_MAP),
|
|
170
233
|
mud: mergeSection(DEFAULT_GAME_CONFIG.mud, games.mud, MUD_KEY_MAP),
|
|
234
|
+
mudiii: mergeSection(DEFAULT_GAME_CONFIG.mudiii, games.mudiii, MUDIII_KEY_MAP),
|
|
171
235
|
guessNumber: mergeSection(DEFAULT_GAME_CONFIG.guessNumber, games["guess-number"], GUESS_NUMBER_KEY_MAP),
|
|
236
|
+
adventure: mergeSection(DEFAULT_GAME_CONFIG.adventure, games.adventure, ADVENTURE_KEY_MAP),
|
|
172
237
|
planning: mergeSection(DEFAULT_GAME_CONFIG.planning, toml?.planning, PLANNING_KEY_MAP),
|
|
173
238
|
};
|
|
174
239
|
}
|