diff --git a/src/agent/item_goal.js b/src/agent/item_goal.js index 1f76364..f3db618 100644 --- a/src/agent/item_goal.js +++ b/src/agent/item_goal.js @@ -4,11 +4,10 @@ import * as mc from '../utils/mcdata.js'; class ItemNode { - constructor(bot, name, quantity, wrapper) { - this.bot = bot; - this.name = name; - this.quantity = quantity; + constructor(manager, wrapper, name) { + this.manager = manager; this.wrapper = wrapper; + this.name = name; this.type = ''; this.source = null; this.prereq = null; @@ -20,11 +19,15 @@ class ItemNode { this.type = 'craft'; let size = 0; for (let [key, value] of Object.entries(recipe)) { - this.recipe.push(new ItemWrapper(this.bot, key, value * this.quantity, this.wrapper)); + if (this.manager.nodes[key] === undefined) + this.manager.nodes[key] = new ItemWrapper(this.manager, this.wrapper, key); + this.recipe.push([this.manager.nodes[key], value]); size += value; } if (size > 4) { - this.prereq = new ItemWrapper(this.bot, 'crafting_table', 1, this.wrapper); + if (this.manager.nodes['crafting_table'] === undefined) + this.manager.nodes['crafting_table'] = new ItemWrapper(this.manager, this.wrapper, 'crafting_table'); + this.prereq = this.manager.nodes['crafting_table']; } return this; } @@ -35,15 +38,22 @@ class ItemNode { this.source = source; else this.source = this.name; - if (tool) - this.prereq = new ItemWrapper(this.bot, tool, 1, this.wrapper); + if (tool) { + if (this.manager.nodes[tool] === undefined) + this.manager.nodes[tool] = new ItemWrapper(this.manager, this.wrapper, tool); + this.prereq = this.manager.nodes[tool]; + } return this; } - setSmeltable(source) { + setSmeltable(source_item) { this.type = 'smelt'; - this.prereq = new ItemWrapper(this.bot, 'furnace', 1, this.wrapper); - this.source = new ItemWrapper(this.bot, source, this.quantity, this.wrapper); + if (this.manager.nodes['furnace'] === undefined) + this.manager.nodes['furnace'] = new ItemWrapper(this.manager, this.wrapper, 'furnace'); + this.prereq = this.manager.nodes['furnace']; + if (this.manager.nodes[source_item] === undefined) + this.manager.nodes[source_item] = new ItemWrapper(this.manager, this.wrapper, source_item); + this.source = this.manager.nodes[source_item]; return this; } @@ -56,26 +66,26 @@ class ItemNode { getChildren() { let children = []; for (let child of this.recipe) { - if (child instanceof ItemWrapper && child.methods.length > 0) { + if (child[0] instanceof ItemWrapper && child[0].methods.length > 0) { children.push(child); } } if (this.prereq && this.prereq instanceof ItemWrapper && this.prereq.methods.length > 0) { - children.push(this.prereq); + children.push([this.prereq, 1]); } return children; } isReady() { - for (let child of this.getChildren()) { - if (!child.isDone()) { + for (let [child, quantity] of this.getChildren()) { + if (!child.isDone(quantity)) { return false; } } return true; } - isDone() { + isDone(quantity=1) { let qualifying = [this.name]; if (this.name.includes('pickaxe') || this.name.includes('axe') || @@ -101,31 +111,31 @@ class ItemNode { } } for (let item of qualifying) { - if (world.getInventoryCounts(this.bot)[item] >= this.quantity) { + if (world.getInventoryCounts(this.manager.agent.bot)[item] >= quantity) { return true; } } return false; } - getDepth() { - if (this.isDone()) { + getDepth(quantity=1) { + if (this.isDone(quantity)) { return 0; } let depth = 0; - for (let child of this.getChildren()) { - depth = Math.max(depth, child.getDepth()); + for (let [child, quantity] of this.getChildren()) { + depth = Math.max(depth, child.getDepth(quantity)); } return depth + 1; } - getFails() { - if (this.isDone()) { + getFails(quantity=1) { + if (this.isDone(quantity)) { return 0; } let fails = 0; - for (let child of this.getChildren()) { - fails += child.getFails(); + for (let [child, quantity] of this.getChildren()) { + fails += child.getFails(quantity); } return fails + this.fails; } @@ -136,7 +146,7 @@ class ItemNode { } let furthest_depth = -1; let furthest_child = null; - for (let child of this.getChildren()) { + for (let [child, quantity] of this.getChildren()) { let depth = child.getDepth(); if (depth > furthest_depth) { furthest_depth = depth; @@ -151,19 +161,18 @@ class ItemNode { this.fails += 1; return; } + let init_quantity = world.getInventoryCounts(this.manager.agent.bot)[this.name] || 0; if (this.type === 'block') { - await skills.collectBlock(this.bot, this.source, this.quantity); + await skills.collectBlock(this.manager.agent.bot, this.source); } else if (this.type === 'smelt') { - await skills.smeltItem(this.bot, this.name, this.quantity); + await skills.smeltItem(this.manager.agent.bot, this.name); } else if (this.type === 'hunt') { - for (let i = 0; i < this.quantity; i++) { - let res = await skills.attackNearest(this.bot, this.source); - if (!res) break; - } + await skills.attackNearest(this.manager.agent.bot, this.source); } else if (this.type === 'craft') { - await skills.craftRecipe(this.bot, this.name, this.quantity); + await skills.craftRecipe(this.manager.agent.bot, this.name); } - if (!this.isDone()) { + let final_quantity = world.getInventoryCounts(this.manager.agent.bot)[this.name] || 0; + if (final_quantity <= init_quantity) { this.fails += 1; } } @@ -171,10 +180,9 @@ class ItemNode { class ItemWrapper { - constructor(bot, name, quantity, parent=null) { - this.bot = bot; + constructor(manager, parent, name) { + this.manager = manager; this.name = name; - this.quantity = quantity; this.parent = parent; this.methods = []; @@ -187,24 +195,24 @@ class ItemWrapper { let recipes = mc.getItemCraftingRecipes(this.name); if (recipes) { for (let recipe of recipes) { - this.methods.push(new ItemNode(this.bot, this.name, this.quantity, this).setRecipe(recipe)); + this.methods.push(new ItemNode(this.manager, this, this.name).setRecipe(recipe)); } } let block_source = mc.getItemBlockSource(this.name); if (block_source) { let tool = mc.getBlockTool(block_source); - this.methods.push(new ItemNode(this.bot, this.name, this.quantity, this).setCollectable(block_source, tool)); + this.methods.push(new ItemNode(this.manager, this, this.name).setCollectable(block_source, tool)); } let smeltingIngredient = mc.getItemSmeltingIngredient(this.name); if (smeltingIngredient) { - this.methods.push(new ItemNode(this.bot, this.name, this.quantity, this).setSmeltable(smeltingIngredient)); + this.methods.push(new ItemNode(this.manager, this, this.name).setSmeltable(smeltingIngredient)); } let animal_source = mc.getItemAnimalSource(this.name); if (animal_source) { - this.methods.push(new ItemNode(this.bot, this.name, this.quantity, this).setHuntable(animal_source)); + this.methods.push(new ItemNode(this.manager, this, this.name).setHuntable(animal_source)); } } @@ -244,10 +252,10 @@ class ItemWrapper { return this.getBestMethod().isReady(); } - isDone() { + isDone(quantity=1) { if (this.methods.length === 0) return true; - return this.getBestMethod().isDone(); + return this.getBestMethod().isDone(quantity); } getDepth() { @@ -275,24 +283,37 @@ export class ItemGoal { this.agent = agent; this.timeout = timeout; this.goal = null; + this.quantity = 1; + this.nodes = {}; } setGoal(goal, quantity=1) { - this.goal = new ItemWrapper(this.agent.bot, goal, quantity); + this.quantity = quantity; + if (this.nodes[goal] === undefined) + this.nodes[goal] = new ItemWrapper(this, null, goal); + this.goal = this.nodes[goal]; } async executeNext() { - await new Promise(resolve => setTimeout(resolve, 500)); let next = this.goal.getNext(); + // Prevent unnecessary attempts to obtain blocks that are not nearby + if (next.type === 'block' && !world.getNearbyBlockTypes(this.agent.bot).includes(next.source)) { + next.fails += 1; + await new Promise((resolve) => setTimeout(resolve, 500)); + this.agent.bot.emit('idle'); + return; + } + let init_quantity = world.getInventoryCounts(this.agent.bot)[next.name] || 0; await this.agent.coder.execute(async () => { await next.execute(); }, this.timeout); + let final_quantity = world.getInventoryCounts(this.agent.bot)[next.name] || 0; - if (next.isDone()) { - console.log(`Successfully obtained ${next.quantity} ${next.name} for goal ${this.goal.name}`); + if (final_quantity > init_quantity) { + console.log(`Successfully obtained ${next.name} for goal ${this.goal.name}`); } else { - console.log(`Failed to obtain ${next.quantity} ${next.name} for goal ${this.goal.name}`); + console.log(`Failed to obtain ${next.name} for goal ${this.goal.name}`); } } }