diff --git a/bots/andy/assist.json b/bots/andy/assist.json index 680b4be..eb47577 100644 --- a/bots/andy/assist.json +++ b/bots/andy/assist.json @@ -2,5 +2,6 @@ "name": "andy", "bio": "You are playing minecraft and assisting other players in tasks.", "memory": "", + "goals": [], "turns": [] } \ No newline at end of file diff --git a/bots/andy/survive.json b/bots/andy/survive.json new file mode 100644 index 0000000..159bc0b --- /dev/null +++ b/bots/andy/survive.json @@ -0,0 +1,19 @@ +{ + "name": "andy", + "bio": "You are playing minecraft and assisting other players in tasks.", + "memory": "", + "goals": [ + "wooden_pickaxe", + "torch", + "stone_pickaxe", + "stone_sword", + "leather_tunic", + "iron_pickaxe", + "iron_sword", + "iron_helmet", + "iron_boots", + "iron_leggings", + "iron_chestplate" + ], + "turns": [] +} \ No newline at end of file diff --git a/src/agent/agent.js b/src/agent/agent.js index ff7f1af..5fe4633 100644 --- a/src/agent/agent.js +++ b/src/agent/agent.js @@ -15,11 +15,11 @@ export class Agent { this.history = new History(this); this.coder = new Coder(this); this.item_goal = new ItemGoal(this); - this.item_goal.setGoal('iron_pickaxe', 1); console.log('Loading examples...'); this.history.load(profile); + this.item_goal.setGoals(this.history.goals); await this.examples.load('./src/examples.json'); await this.coder.load(); diff --git a/src/agent/history.js b/src/agent/history.js index 1e020d6..2614531 100644 --- a/src/agent/history.js +++ b/src/agent/history.js @@ -13,6 +13,7 @@ export class History { // These define an agent's long term memory this.bio = ''; this.memory = ''; + this.goals = []; // Variables for controlling the agent's memory and knowledge this.max_messages = 20; @@ -88,6 +89,7 @@ export class History { 'name': this.name, 'bio': this.bio, 'memory': this.memory, + 'goals': this.goals, 'turns': this.turns }; const json_data = JSON.stringify(data, null, 4); @@ -108,6 +110,7 @@ export class History { this.bio = obj.bio; this.memory = obj.memory; this.turns = obj.turns; + this.goals = obj.goals; } catch (err) { console.error(`No file for profile '${load_path}' for agent ${this.name}.`); } diff --git a/src/agent/item_goal.js b/src/agent/item_goal.js index 5db9bce..c130c07 100644 --- a/src/agent/item_goal.js +++ b/src/agent/item_goal.js @@ -18,10 +18,11 @@ class ItemNode { setRecipe(recipe) { this.type = 'craft'; let size = 0; + this.recipe = []; for (let [key, value] of Object.entries(recipe)) { 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]); + this.recipe.push({node: this.manager.nodes[key], quantity: value}); size += value; } if (size > 4) { @@ -64,21 +65,16 @@ class ItemNode { } getChildren() { - let children = []; - for (let child of this.recipe) { - 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, 1]); + let children = [...this.recipe]; + if (this.prereq) { + children.push({node: this.prereq, quantity: 1}); } return children; } isReady() { - for (let [child, quantity] of this.getChildren()) { - if (!child.isDone(quantity)) { + for (let child of this.getChildren()) { + if (!child.node.isDone(child.quantity)) { return false; } } @@ -123,8 +119,8 @@ class ItemNode { return 0; } let depth = 0; - for (let [child, quantity] of this.getChildren()) { - depth = Math.max(depth, child.getDepth(quantity)); + for (let child of this.getChildren()) { + depth = Math.max(depth, child.node.getDepth(child.quantity)); } return depth + 1; } @@ -134,18 +130,19 @@ class ItemNode { return 0; } let fails = 0; - for (let [child, quantity] of this.getChildren()) { - fails += child.getFails(quantity); + for (let child of this.getChildren()) { + fails += child.node.getFails(child.quantity); } return fails + this.fails; } - getNext() { - if (this.isReady()) { + getNext(q=1) { + if (this.isDone(q)) + return null; + if (this.isReady()) return this; - } - for (let [child, quantity] of this.getChildren()) { - let res = child.getNext(); + for (let child of this.getChildren()) { + let res = child.node.getNext(child.quantity); if (res) return res; } @@ -187,28 +184,37 @@ class ItemWrapper { } } + add_method(method) { + for (let child of method.getChildren()) { + if (child.node.methods.length === 0) + return; + } + this.methods.push(method); + } + + createChildren() { let recipes = mc.getItemCraftingRecipes(this.name); if (recipes) { for (let recipe of recipes) { - this.methods.push(new ItemNode(this.manager, this, this.name).setRecipe(recipe)); + this.add_method(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.manager, this, this.name).setCollectable(block_source, tool)); + this.add_method(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.manager, this, this.name).setSmeltable(smeltingIngredient)); + this.add_method(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.manager, this, this.name).setHuntable(animal_source)); + this.add_method(new ItemNode(this.manager, this, this.name).setHuntable(animal_source)); } } @@ -223,11 +229,11 @@ class ItemWrapper { return false; } - getBestMethod() { + getBestMethod(q=1) { let best_cost = -1; let best_method = null; for (let method of this.methods) { - let cost = method.getDepth() + method.getFails(); + let cost = method.getDepth(q) + method.getFails(q); if (best_cost == -1 || cost < best_cost) { best_cost = cost; best_method = method; @@ -236,40 +242,28 @@ class ItemWrapper { return best_method } - getChildren() { - if (this.methods.length === 0) - return []; - return this.getBestMethod().getChildren(); - } - - isReady() { + isDone(q=1) { if (this.methods.length === 0) return false; - return this.getBestMethod().isReady(); - } - - isDone(quantity=1) { - if (this.methods.length === 0) - return true; - return this.getBestMethod().isDone(quantity); + return this.getBestMethod(q).isDone(q); } getDepth(q=1) { if (this.methods.length === 0) return 0; - return this.getBestMethod().getDepth(q); + return this.getBestMethod(q).getDepth(q); } getFails(q=1) { if (this.methods.length === 0) return 0; - return this.getBestMethod().getFails(q); + return this.getBestMethod(q).getFails(q); } - getNext() { + getNext(q=1) { if (this.methods.length === 0) return null; - return this.getBestMethod().getNext(); + return this.getBestMethod(q).getNext(q); } } @@ -278,20 +272,34 @@ export class ItemGoal { constructor(agent, timeout=-1) { this.agent = agent; this.timeout = timeout; - this.goal = null; - this.quantity = 1; + this.goals = []; this.nodes = {}; } - setGoal(goal, quantity=1) { - this.quantity = quantity; - if (this.nodes[goal] === undefined) - this.nodes[goal] = new ItemWrapper(this, null, goal); - this.goal = this.nodes[goal]; + setGoals(goals) { + this.goals = [] + for (let goal of goals) { + this.goals.push({name: goal, quantity: 1}) + } } async executeNext() { - let next = this.goal.getNext(); + // Get goal by priority + let goal = null; + let inventory = world.getInventoryCounts(this.agent.bot); + for (let g of this.goals) { + if (inventory[g.name] === undefined || inventory[g.name] < g.quantity) { + if (this.nodes[g.name] === undefined) + this.nodes[g.name] = new ItemWrapper(this, null, g.name); + goal = this.nodes[g.name]; + break; + } + } + if (goal === null) + return; + + // Get next goal to execute + let next = goal.getNext(); // Prevent unnecessary attempts to obtain blocks that are not nearby if (next.type === 'block' && !world.getNearbyBlockTypes(this.agent.bot).includes(next.source)) { @@ -302,11 +310,10 @@ export class ItemGoal { } // Wait for the bot to be idle before attempting to execute the next goal - if (!this.agent.isIdle()) { - await new Promise((resolve) => setTimeout(resolve, 500)); + if (!this.agent.isIdle()) return; - } + // Execute the next goal let init_quantity = world.getInventoryCounts(this.agent.bot)[next.name] || 0; this.agent.coder.interruptible = true; await this.agent.coder.execute(async () => { @@ -315,10 +322,11 @@ export class ItemGoal { this.agent.coder.interruptible = false; let final_quantity = world.getInventoryCounts(this.agent.bot)[next.name] || 0; + // Log the result of the goal attempt if (final_quantity > init_quantity) { - console.log(`Successfully obtained ${next.name} for goal ${this.goal.name}`); + console.log(`Successfully obtained ${next.name} for goal ${goal.name}`); } else { - console.log(`Failed to obtain ${next.name} for goal ${this.goal.name}`); + console.log(`Failed to obtain ${next.name} for goal ${goal.name}`); } } }