@polycode-projects/the-mechanical-code-talker 5.0.5 → 5.0.6

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.
@@ -6,14 +6,6 @@
6
6
  // on `{ as: character }` — the addressee is in the sentence, "@fox-1 look"),
7
7
  // and the simulation itself advances as ONE whole-world tick
8
8
  // (`runTownSquareTick`) rather than mud's per-character `autoplayTick`.
9
- //
10
- // `src/services/predator-prey.mjs` — the engine this file drives — does not
11
- // exist in every worktree yet; a concurrent track owns it. The import below
12
- // is guarded (dynamic, try/catch) so this module still loads, and every
13
- // test that imports mudiii-viz.mjs (which never imports this file) is
14
- // unaffected either way. `createMudiiiSession` throws a clear "the engine
15
- // isn't built yet" error if actually called before that track lands, rather
16
- // than a bare ERR_MODULE_NOT_FOUND with no context.
17
9
  import {
18
10
  createInMemoryStore, appendFacts, appendRule, loadMemory, readFactRows, removeFacts,
19
11
  } from "../../adapters/memory/core.mjs";
@@ -22,26 +14,21 @@ import { memoryFactGraphPayload } from "../../domain/memory-facts.mjs";
22
14
  import { loadLexicon } from "../../domain/grammar/lexicon.mjs";
23
15
  import { worldProvenanceTag } from "../../domain/worlds-pack.mjs";
24
16
  import {
25
- COMPASS_POINTS, DEFAULT_FACING, layoutNamed, reverseFacing, stepCellFrom, turnedFacing,
17
+ COMPASS_POINTS, DEFAULT_FACING, cellId, layoutNamed, parseCellId, reverseFacing, stepCellFrom, turnedFacing,
26
18
  } from "../../domain/town-square-world.mjs";
19
+ import { findActionPath } from "../../domain/planning.mjs";
20
+ import { DEFAULT_GAME_CONFIG } from "../../domain/game-config.mjs";
27
21
  import { parseMudEditorText, planMudEditorSync, gridWorldEditorState } from "../../services/mud-editor.mjs";
22
+ import { pillsForMudiii } from "../../services/mudiii-turn.mjs";
23
+ import { relatedForTerm } from "../../domain/skos-view.mjs";
24
+ import { classAncestorChain } from "../../domain/sprite-map.mjs";
28
25
  import { createTurnSession } from "./turn-session.mjs";
29
26
  import { publishTmctSurface } from "./tmct-surface.mjs";
30
27
  import { graphAsk, enginePlan } from "./engine-surface.mjs";
31
-
32
- let engine = null;
33
- async function loadEngine() {
34
- if (engine) return engine;
35
- try {
36
- engine = await import("../../services/predator-prey.mjs");
37
- } catch (err) {
38
- throw new Error(
39
- "mudiii's engine (src/services/predator-prey.mjs) is not built in this worktree yet "
40
- + `(${err && err.message ? err.message : err})`,
41
- );
42
- }
43
- return engine;
44
- }
28
+ import {
29
+ foldTownSquareState, gridApplyActions, pathStateKey, placeFood as engPlaceFood, recastTownSquare,
30
+ roleOfId, runTownSquareTick, startTownSquareGame, townSquareBoard,
31
+ } from "../../services/predator-prey.mjs";
45
32
 
46
33
  /** `count` entries drawn at random from `roster`, in random order, without
47
34
  * repeats — mirrors mud-browser-entry.mjs's own `pickMudRoster`. `random` is
@@ -127,6 +114,29 @@ export function driveRequest(direction, { cell, facing = DEFAULT_FACING } = {})
127
114
  return COMPASS_POINTS.includes(press) ? { facing: press } : null;
128
115
  }
129
116
 
117
+ /** The shortest route from `fromCell` to `toCell` over `factRows`' own exit
118
+ * facts, as `{ cells, directions }` — `cells` runs from one end to the other
119
+ * inclusive, `directions` names one hop each. Null when nothing connects
120
+ * them, so a caller declines visibly rather than drawing a line through a
121
+ * building the board would never let anyone walk through.
122
+ *
123
+ * The exit table is the one legality answer, the same rows the engine's own
124
+ * chase and forage searches read, so a route drawn here is a route the world
125
+ * agrees with. */
126
+ export function routeBetweenCells(factRows, fromCell, toCell) {
127
+ const from = parseCellId(fromCell);
128
+ const to = parseCellId(toCell);
129
+ if (!from || !to) return null;
130
+ const found = findActionPath(
131
+ from,
132
+ (searchState) => searchState.x === to.x && searchState.y === to.y,
133
+ gridApplyActions(factRows),
134
+ { stateKey: pathStateKey },
135
+ );
136
+ if (!found) return null;
137
+ return { cells: found.states.map((s) => cellId(s.x, s.y)), directions: found.actions };
138
+ }
139
+
130
140
  /** A live, shared town-square world one visitor watches and talks over.
131
141
  * `worldPayload` is `{ name, facts, rules, opening }`, read once at build
132
142
  * time the same way every other viz page's world payload is. `agents` is
@@ -137,15 +147,14 @@ export function driveRequest(direction, { cell, facing = DEFAULT_FACING } = {})
137
147
  * (`runTownSquareTick`) and its conversation is a single shared dock.
138
148
  *
139
149
  * Returns `{ memoryDir, codeGraph, graph, refreshGraph, turn, tick, board,
140
- * snapshot, applyEdit, placeFood, driveAgent }`. A reset is not a method here: the
141
- * page re-opens a whole session for it, which is what a reset means when the
142
- * store is in memory and belongs to one visitor. */
143
- export async function createMudiiiSession(worldPayload, { agents = [], epoch = 0 } = {}) {
144
- const {
145
- startTownSquareGame, runTownSquareTick, townSquareBoard, foldTownSquareState,
146
- placeFood: engPlaceFood, roleOfId,
147
- } = await loadEngine();
148
-
150
+ * snapshot, applyEdit, placeFood, driveAgent, recast }`.
151
+ *
152
+ * `getTeachEnabled` is read fresh on every turn, so a visitor can tick the
153
+ * page's teach box mid-session and have the very next line read as a fact to
154
+ * store rather than a command to run. */
155
+ export async function createMudiiiSession(
156
+ worldPayload, { agents = [], epoch = 0, getTeachEnabled = () => false } = {},
157
+ ) {
149
158
  // Every engine call is layout-scoped: the world pack ships the BOARD, and
150
159
  // the layout carries the geometry plus the cast counts the engine mints the
151
160
  // animals from. Resolved once here so no call site has to remember it.
@@ -184,8 +193,11 @@ export async function createMudiiiSession(worldPayload, { agents = [], epoch = 0
184
193
 
185
194
  const turnSession = createTurnSession({
186
195
  memoryDir, graph: codeGraph, lexicon, sessionId: "town-square",
187
- vocabHint: 'Try "@fox-1 look", or "what does fox-1 believe".',
188
- buildExtraOptions: () => ({ planState: planHolder.state }),
196
+ vocabHint: 'Try "@fox the goblin is east", or "what does the fox see".',
197
+ buildExtraOptions: () => ({
198
+ planState: planHolder.state,
199
+ gameConfig: { ...DEFAULT_GAME_CONFIG, mudiii: { ...DEFAULT_GAME_CONFIG.mudiii, teach: getTeachEnabled() } },
200
+ }),
189
201
  captureExtraState: async (result, state) => {
190
202
  if ("planState" in result) planHolder.state = state.planState;
191
203
  },
@@ -233,6 +245,22 @@ export async function createMudiiiSession(worldPayload, { agents = [], epoch = 0
233
245
  };
234
246
  }
235
247
 
248
+ /** Re-cast this same store onto a fresh epoch and hand back the opening
249
+ * board. This is what a Reset means once a store is live: the world's own
250
+ * facts, everything taught into it and everything the editor changed all
251
+ * stand, and only the animals are minted again. Re-opening a whole session
252
+ * instead would throw away the taught facts along with the cast, which is
253
+ * not what "reset the board" says.
254
+ *
255
+ * `agents` sizes the new cast the same way `createMudiiiSession`'s own
256
+ * roster does; `epoch` defaults to one past whatever the store is on. */
257
+ async function recast({ agents: nextAgents = [], epoch: nextEpoch = null } = {}) {
258
+ await recastTownSquare(memoryDir, {
259
+ layout, epoch: nextEpoch, ...townSquareRosterArgs(nextAgents, (id) => roleOfId(id)),
260
+ });
261
+ return townSquareBoard(memoryDir, { layout });
262
+ }
263
+
236
264
  /** The board as it stands, in the same payload shape `tick` returns, with no
237
265
  * turn spent. A page opens a session and then draws THIS — otherwise its
238
266
  * first sight of where anything stands is the first tick, and every mesh
@@ -285,6 +313,7 @@ export async function createMudiiiSession(worldPayload, { agents = [], epoch = 0
285
313
  turn: turnSession.turn,
286
314
  tick,
287
315
  driveAgent,
316
+ recast,
288
317
  board,
289
318
  snapshot,
290
319
  applyEdit,
@@ -304,5 +333,12 @@ publishTmctSurface({
304
333
  plan: enginePlan,
305
334
  page: {
306
335
  pickMudiiiRoster,
336
+ // pillsForMudiii closes over two other modules' bindings, so the page
337
+ // reaches it through this bag rather than a `.toString()` splice.
338
+ pillsForMudiii,
339
+ // What the editor's own suggestion rail reads for the word under the
340
+ // cursor: the lateral SKOS neighbourhood and the vertical is-a chain.
341
+ relatedForTerm, classAncestorChain,
342
+ routeBetweenCells,
307
343
  },
308
344
  });