Skip to content

Commit 5642b1e

Browse files
shinohara-rinnekomeowww
authored andcommitted
feat(minecraft): add query.self/snapshot helpers and inventory count/has/summary methods
Add query.self() returning one-shot self state (pos/health/food/heldItem/gameMode/isRaining/timeOfDay), add query.snapshot(range) returning combined self/inventory/nearby snapshot with counts/summary/emptySlots/totalStacks and blocks/entities/ores within range, add inventory.count(name)/has(name, atLeast)/summary() helpers for item queries, register query.self/snapshot as readonly functions in JavaScriptPlanner sandbox
1 parent a9f95ae commit 5642b1e

File tree

3 files changed

+97
-0
lines changed

3 files changed

+97
-0
lines changed

services/minecraft/src/cognitive/conscious/js-planner.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ export class JavaScriptPlanner {
207207
{ name: 'llmUserMessage', kind: 'string', readonly: true },
208208
{ name: 'llmConversationHistory', kind: 'object', readonly: true },
209209
{ name: 'query', kind: 'object', readonly: true },
210+
{ name: 'query.self', kind: 'function', readonly: true },
211+
{ name: 'query.snapshot', kind: 'function', readonly: true },
210212
{ name: 'bot', kind: 'object', readonly: true },
211213
{ name: 'mineflayer', kind: 'object', readonly: true },
212214
{ name: 'mem', kind: 'object', readonly: false },

services/minecraft/src/cognitive/conscious/query-dsl.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,29 @@ describe('query DSL', () => {
7373
const pickaxes = query.craftable().whereIncludes('pickaxe').uniq().list()
7474
expect(pickaxes).toEqual(['wooden_pickaxe', 'stone_pickaxe'])
7575
})
76+
77+
it('supports inventory helper methods for count/has/summary', () => {
78+
const query = createQueryRuntime(createMineflayerStub())
79+
expect(query.inventory().count('bread')).toBe(5)
80+
expect(query.inventory().has('bread', 5)).toBe(true)
81+
expect(query.inventory().has('bread', 6)).toBe(false)
82+
expect(query.inventory().summary()).toEqual([
83+
{ name: 'cobblestone', count: 16 },
84+
{ name: 'bread', count: 5 },
85+
])
86+
})
87+
88+
it('returns self and snapshot views for one-shot state reads', () => {
89+
const query = createQueryRuntime(createMineflayerStub())
90+
const self = query.self()
91+
expect(self.pos).toEqual({ x: 0, y: 64, z: 0 })
92+
expect(self.heldItem).toBeNull()
93+
94+
const snap = query.snapshot(12)
95+
expect(snap.inventory.counts).toEqual({
96+
bread: 5,
97+
cobblestone: 16,
98+
})
99+
expect(snap.nearby.ores.map(b => b.name)).toEqual(['coal_ore', 'coal_ore', 'ancient_debris'])
100+
})
76101
})

services/minecraft/src/cognitive/conscious/query-dsl.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,21 @@ interface InventoryRecord {
3131
displayName?: string
3232
}
3333

34+
interface InventorySummaryRecord {
35+
name: string
36+
count: number
37+
}
38+
39+
interface SelfQueryRecord {
40+
pos: { x: number, y: number, z: number }
41+
health: number
42+
food: number
43+
heldItem: string | null
44+
gameMode: string
45+
isRaining: boolean
46+
timeOfDay: number | null
47+
}
48+
3449
type NamePredicate = (value: string) => boolean
3550

3651
class NameQueryChain {
@@ -199,6 +214,28 @@ class InventoryQueryChain {
199214
}, {} as Record<string, number>)
200215
}
201216

217+
public count(name: string): number {
218+
const needle = name.toLowerCase()
219+
return this.list()
220+
.filter(item => item.name.toLowerCase() === needle)
221+
.reduce((sum, item) => sum + item.count, 0)
222+
}
223+
224+
public has(name: string, atLeast = 1): boolean {
225+
return this.count(name) >= Math.max(1, Math.floor(atLeast))
226+
}
227+
228+
public summary(): InventorySummaryRecord[] {
229+
const counts = this.countByName()
230+
return Object.entries(counts)
231+
.map(([name, count]) => ({ name, count }))
232+
.sort((a, b) => {
233+
if (b.count !== a.count)
234+
return b.count - a.count
235+
return a.name.localeCompare(b.name)
236+
})
237+
}
238+
202239
public list(): InventoryRecord[] {
203240
return this.mineflayer.bot.inventory
204241
.items()
@@ -296,8 +333,41 @@ function clamp(value: number, min: number, max: number): number {
296333
return Math.min(max, Math.max(min, value))
297334
}
298335

336+
function toSelfRecord(mineflayer: Mineflayer): SelfQueryRecord {
337+
return {
338+
pos: toPos(mineflayer.bot.entity.position),
339+
health: mineflayer.bot.health,
340+
food: mineflayer.bot.food,
341+
heldItem: mineflayer.bot.heldItem?.name ?? null,
342+
gameMode: mineflayer.bot.game?.gameMode ?? 'unknown',
343+
isRaining: Boolean(mineflayer.bot.isRaining),
344+
timeOfDay: typeof mineflayer.bot.time?.timeOfDay === 'number' ? mineflayer.bot.time.timeOfDay : null,
345+
}
346+
}
347+
299348
export function createQueryRuntime(mineflayer: Mineflayer) {
300349
return {
350+
self: () => toSelfRecord(mineflayer),
351+
snapshot: (range = 16) => {
352+
const normalizedRange = clamp(Math.floor(range), 1, 64)
353+
const inventory = new InventoryQueryChain(mineflayer)
354+
return {
355+
self: toSelfRecord(mineflayer),
356+
inventory: {
357+
counts: inventory.countByName(),
358+
summary: inventory.summary(),
359+
emptySlots: typeof mineflayer.bot.inventory.emptySlotCount === 'function'
360+
? mineflayer.bot.inventory.emptySlotCount()
361+
: Math.max(0, 36 - mineflayer.bot.inventory.items().length),
362+
totalStacks: mineflayer.bot.inventory.items().length,
363+
},
364+
nearby: {
365+
blocks: new BlockQueryChain(mineflayer).within(normalizedRange).limit(20).list(),
366+
entities: new EntityQueryChain(mineflayer).within(normalizedRange).limit(20).list(),
367+
ores: new BlockQueryChain(mineflayer).within(normalizedRange).isOre().limit(20).list(),
368+
},
369+
}
370+
},
301371
blocks: () => new BlockQueryChain(mineflayer),
302372
blockAt: ({ x, y, z }: { x: number, y: number, z: number }) => {
303373
const block = mineflayer.bot.blockAt(new Vec3(Math.floor(x), Math.floor(y), Math.floor(z)))

0 commit comments

Comments
 (0)