diff --git a/docs/agent-log.md b/docs/agent-log.md index 4199e47..bbd2fbd 100644 --- a/docs/agent-log.md +++ b/docs/agent-log.md @@ -6,6 +6,18 @@ open or not verified. Keep each entry short; the git history has the details. +## 2026-10-29: t82 dev console material table lookup (Codex) + +- Display-name discovery scans MD ids 1..NMAT-1 through devGiveable; DEV_MATERIALS + supplies internal aliases. Listing, suggestions and Tab share the real-table list. +- Added regression removing CUORE temporarily (restored in finally): lookup/give, + prefixes, filtering, suggestions and completion failed before the fix and pass after. + Corrected the new prefix expectation because Copper ore vein also matches. +- Files: src/sim/devconsole.js, test/t_dev_console.js, tasks/t82_dev_console_finds.md + and this log. Node console suite and python3 test/run_all.py passed (log: + /tmp/t82-headless.log). Browser/screenshots skipped at the owner's request; game + playtesting remains open. No simulation/worldgen changes; no commit. + ## 2026-10-28: t94 dev console discovery and keyboard ease of use (Codex) - Added case/space-insensitive material lookup, three close giveable suggestions and diff --git a/src/sim/devconsole.js b/src/sim/devconsole.js index 58a3871..ed9fa13 100644 --- a/src/sim/devconsole.js +++ b/src/sim/devconsole.js @@ -26,7 +26,13 @@ function devError(text) { return Object.assign([text], { error: true }); } function devName(text) { return text.toLowerCase().replace(/\s+/g, ''); } function devGiveable(id) { return id !== EMPTY && PH[id] !== PH_GAS && (PH[id] !== PH_SOLID || !!YLD[id]); } function devMaterialList(text = '') { - return Object.entries(DEV_MATERIALS).filter(([key, id]) => devGiveable(id) && (devName(key).includes(devName(text)) || devName(MD[id].name).includes(devName(text)))); + const keys = new Map(Object.entries(DEV_MATERIALS).map(([key, id]) => [id, key])); + const list = [], name = devName(text); + for (let id = 1; id < NMAT; id++) { + const key = keys.get(id) || ''; + if (devGiveable(id) && (devName(key).includes(name) || devName(MD[id].name).includes(name))) list.push([key, id]); + } + return list; } // Edit distance is only used on a console error, never in the simulation tick. function devDistance(a, b) { @@ -83,12 +89,12 @@ function devComplete(text, previous) { const DEV_MATERIALS = { EMPTY, GRASS, DIRT, LDIRT, SAND, GRAVEL, CLAY, STONE, GRANITE, BASALT, WOOD, LEAVES, COALV, COAL, CUV, CUORE, FEV, FEORE, ASH, SLAG, BRICK, GLASS, COPPER, IRON, STEEL, WATER, MAGMA, MCU, MFE, MST, MGL, STEAM, SMOKE, FIRE, BELTL, BELTR, SHAFT, GATE, HULL, SPRING, RSTONE, RGRAN, RBAS, LCLAY, LWOOD, RBRICK, CULLET, SCRCU, SCRFE, SCRST, BARCU, BARFE, BARST, ACORN, CHARCOAL, SNOW, ICE, SNV, SNORE, TIN, MSN, SCRSN, BARSN, ZNV, ZNORE, ZINC, MZN, SCRZN, BARZN, AGV, AGORE, SILVER, MAG, SCRAG, BARAG, AUV, AUORE, GOLD, MAU, SCRAU, BARAU, BRONZE, MBZ, SCRBZ, BARBZ, BRASS, MBS, SCRBS, BARBS, DOCKW, MSLAG, PIPE, BARGL, CUTST, BARSB, GRINDER, SAWB, PLANK, PRESSB, GREENB, BARBR, RACKB }; function devMaterial(name) { name = devName(name); - const ids = Object.entries(DEV_MATERIALS); - const internal = ids.find(([key]) => devName(key) === name); + const internal = Object.entries(DEV_MATERIALS).find(([key]) => devName(key) === name); if (internal) return internal[1]; + const ids = devMaterialList(); const exact = ids.filter(([, id]) => devName(MD[id].name) === name); const matches = [...new Set((exact.length ? exact : ids.filter(([key, id]) => devName(key).startsWith(name) || devName(MD[id].name).startsWith(name))).map(([, id]) => id))]; - return matches.length === 1 ? matches[0] : matches.length ? `Ambiguous material: ${matches.map(id => MD[id].name + ' (' + ids.find(([, m]) => m === id)[0] + ')').join(', ')}.` : 'Unknown material. Did you mean: ' + devSuggestions(name) + '? Use materials to list names.'; + return matches.length === 1 ? matches[0] : matches.length ? `Ambiguous material: ${matches.map(id => { const key = ids.find(([, m]) => m === id)[0]; return MD[id].name + (key ? ' (' + key + ')' : ''); }).join(', ')}.` : 'Unknown material. Did you mean: ' + devSuggestions(name) + '? Use materials to list names.'; } function devCommand(text, cursor) { if (typeof text !== 'string' || !text.trim()) return ['Type help to list commands.']; @@ -105,7 +111,7 @@ function devCommand(text, cursor) { return ['Usage: ' + def[0], 'Example: ' + def[1]]; } if (cmd === 'materials') { - const lines = devMaterialList(args.join(' ')).map(([key, id]) => MD[id].name + ' (' + key + ')'); + const lines = devMaterialList(args.join(' ')).map(([key, id]) => MD[id].name + (key ? ' (' + key + ')' : '')); return lines.length ? lines : ['No matching materials.']; } if (cmd === 'clear') return args.length ? usage() : Object.assign([], { clear: true }); diff --git a/test/t_dev_console.js b/test/t_dev_console.js index 874c8b4..77f75a2 100644 --- a/test/t_dev_console.js +++ b/test/t_dev_console.js @@ -74,6 +74,24 @@ check('give turns rock into its rubble and says so', run(`(() => { enableDevCons check('give refuses things that cannot be in a bag', run(`(() => { enableDevConsole(); const out = devCommand('give steam 5').join(' '); return /cannot go in the bag/.test(out) && bag[STEAM] === 0; })()`)); check('material matching ignores spaces and case', run("devMaterial('CoPpErOrE') === CUORE && devMaterial('copper bar') === BARCU")); +const withoutAlias = run(`(() => { + const saved = DEV_MATERIALS.CUORE; + delete DEV_MATERIALS.CUORE; + try { + const before = bag[CUORE]; + devCommand('give CoPpEr OrE 7'); + return { + lookup: devMaterial('CoPpEr OrE') === CUORE && bag[CUORE] === before + 7, + prefix: devMaterial('copperor').startsWith('Ambiguous material:') && devMaterial('copperor').includes(', Copper ore.'), + list: devMaterialList('CoPpErOrE').some(([, id]) => id === CUORE), + suggestions: devSuggestions('coperore').includes(MD[CUORE].name), + completion: devComplete('give copperore 25').matches.includes('give Copper ore 25') + }; + } finally { DEV_MATERIALS.CUORE = saved; } +})()`); +for (const [feature, works] of Object.entries(withoutAlias)) { + check('display-name ' + feature + ' works without an internal alias', works); +} check('unknown material suggests close giveable names', /Did you mean:.*Copper ore/i.test(command('give coperore')[0])); check('materials lists giveable names with internal names', command('materials').some(s => /Copper ore.*CUORE/.test(s)) && !command('materials').some(s => /STEAM/.test(s))); check('materials filtering ignores spaces and case', command('materials CoPpErOrE').every(s => /Copper ore/i.test(s)) && command('materials copperore').some(s => /CUORE/.test(s)));