mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-04-22 14:12:07 +02:00
Add getCraftingPlan tool for making agents faster and resource efficient
This commit is contained in:
parent
43cdcc6879
commit
a14d4af65f
6 changed files with 209 additions and 5 deletions
|
@ -200,6 +200,18 @@
|
|||
[
|
||||
{"role": "system", "content": "You are self-prompting with the goal: 'Build a house'. Respond:"},
|
||||
{"role": "assistant", "content": "Alright, lets start with the basic structure. !newAction(\"Build an 8x8 base for the house.\")"}
|
||||
],
|
||||
[
|
||||
{"role": "user", "content": "trex56: Lets make a wooden pickaxe together"},
|
||||
{"role": "assistant", "content": "Sure, let me check how to make a wooden pickaxe !getCraftingPlan(\"wooden_pickaxe\",1)"},
|
||||
{"role": "system", "content": "You are missing the following items:\n- 2 oak_log\n\nOnce you have these items, here's your crafting plan:\n\nCraft 1 oak_log -> 4 oak_planks\nCraft 1 oak_log -> 4 oak_planks\nCraft 2 oak_planks -> 4 stick\nCraft 3 oak_planks + 2 stick -> 1 wooden_pickaxe"},
|
||||
{"role": "assistant", "content": "I need 2 oak logs to make a wooden pickaxe. Do you have any?"}
|
||||
],
|
||||
[
|
||||
{"role": "user", "content": "terrance: (FROM OTHER BOT) I want you to craft 5 chains"},
|
||||
{"role": "assistant", "content": "Sure, first let me check how to build a chain, !getCraftingPlan(\"chain\",5)"},
|
||||
{"role": "system", "content": "You are missing the following items:\n- 7 iron_ingot\n\nOnce you have these items, here's your crafting plan:\n\nCraft 2 iron_ingot -> 18 iron_nugget\nCraft 10 iron_nugget + 5 iron_ingot -> 5 chain"},
|
||||
{"role": "assistant", "content": "Seems like I need 7 iron_ingots for 5 chains. Do you have any?"}
|
||||
]
|
||||
],
|
||||
|
||||
|
|
|
@ -406,7 +406,7 @@ export const actionsList = [
|
|||
convoManager.endConversation(player_name);
|
||||
return `Converstaion with ${player_name} ended.`;
|
||||
}
|
||||
}
|
||||
},
|
||||
// { // commented for now, causes confusion with goal command
|
||||
// name: '!npcGoal',
|
||||
// description: 'Set a simple goal for an item or building to automatically work towards. Do not use for complex goals.',
|
||||
|
|
|
@ -176,5 +176,42 @@ export const queryList = [
|
|||
perform: async function (agent) {
|
||||
return "Saved place names: " + agent.memory_bank.getKeys();
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '!getCraftingPlan',
|
||||
description: "Provides a comprehensive crafting plan for a specified item. This includes a breakdown of required ingredients, the exact quantities needed, and an analysis of missing ingredients or extra items needed based on the bot's current inventory.",
|
||||
params: {
|
||||
targetItem: {
|
||||
type: 'string',
|
||||
description: 'The item that we are trying to craft'
|
||||
},
|
||||
quantity: {
|
||||
type: 'int',
|
||||
description: 'The quantity of the item that we are trying to craft',
|
||||
optional: true,
|
||||
domain: [1, Infinity, '[)'], // Quantity must be at least 1,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
perform: function (agent, targetItem, quantity = 1) {
|
||||
let bot = agent.bot;
|
||||
|
||||
// Fetch the bot's inventory
|
||||
const curr_inventory = world.getInventoryCounts(bot);
|
||||
const target_item = targetItem;
|
||||
let existingCount = curr_inventory[target_item] || 0;
|
||||
var prefixMessage = '';
|
||||
if (existingCount > 0) {
|
||||
curr_inventory[target_item] -= existingCount;
|
||||
prefixMessage = `You already have ${existingCount} ${target_item} in your inventory. If you need to craft more,\n`;
|
||||
}
|
||||
|
||||
// Generate crafting plan
|
||||
var craftingPlan = mc.getDetailedCraftingPlan(target_item, quantity, curr_inventory);
|
||||
craftingPlan = prefixMessage + craftingPlan;
|
||||
console.log('\n\n\n\n\n\n\n\n\n\n\n');
|
||||
console.log(craftingPlan);
|
||||
return pad(craftingPlan);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
|
|
@ -79,7 +79,7 @@ export async function craftRecipe(bot, itemName, num=1) {
|
|||
}
|
||||
}
|
||||
if (!recipes || recipes.length === 0) {
|
||||
log(bot, `You do not have the resources to craft a ${itemName}. It requires: ${Object.entries(mc.getItemCraftingRecipes(itemName)[0]).map(([key, value]) => `${key}: ${value}`).join(', ')}.`);
|
||||
log(bot, `You do not have the resources to craft a ${itemName}. It requires: ${Object.entries(mc.getItemCraftingRecipes(itemName)[0][0]).map(([key, value]) => `${key}: ${value}`).join(', ')}.`);
|
||||
if (placedTable) {
|
||||
await collectBlock(bot, 'crafting_table', 1);
|
||||
}
|
||||
|
|
|
@ -204,7 +204,7 @@ class ItemWrapper {
|
|||
}
|
||||
|
||||
createChildren() {
|
||||
let recipes = mc.getItemCraftingRecipes(this.name);
|
||||
let recipes = mc.getItemCraftingRecipes(this.name).map(([recipe, craftedCount]) => recipe);
|
||||
if (recipes) {
|
||||
for (let recipe of recipes) {
|
||||
let includes_blacklisted = false;
|
||||
|
|
|
@ -190,7 +190,10 @@ export function getItemCraftingRecipes(itemName) {
|
|||
recipe[ingredientName] = 0;
|
||||
recipe[ingredientName]++;
|
||||
}
|
||||
recipes.push(recipe);
|
||||
recipes.push([
|
||||
recipe,
|
||||
{craftedCount : r.result.count}
|
||||
]);
|
||||
}
|
||||
|
||||
return recipes;
|
||||
|
@ -328,3 +331,155 @@ export function calculateLimitingResource(availableItems, requiredItems, discret
|
|||
if(discrete) num = Math.floor(num);
|
||||
return {num, limitingResource}
|
||||
}
|
||||
|
||||
let loopingItems = new Set();
|
||||
|
||||
export function initializeLoopingItems() {
|
||||
|
||||
loopingItems = new Set(['coal',
|
||||
'wheat',
|
||||
'diamond',
|
||||
'emerald',
|
||||
'raw_iron',
|
||||
'raw_gold',
|
||||
'redstone',
|
||||
'blue_wool',
|
||||
'packed_mud',
|
||||
'raw_copper',
|
||||
'iron_ingot',
|
||||
'dried_kelp',
|
||||
'gold_ingot',
|
||||
'slime_ball',
|
||||
'black_wool',
|
||||
'quartz_slab',
|
||||
'copper_ingot',
|
||||
'lapis_lazuli',
|
||||
'honey_bottle',
|
||||
'rib_armor_trim_smithing_template',
|
||||
'eye_armor_trim_smithing_template',
|
||||
'vex_armor_trim_smithing_template',
|
||||
'dune_armor_trim_smithing_template',
|
||||
'host_armor_trim_smithing_template',
|
||||
'tide_armor_trim_smithing_template',
|
||||
'wild_armor_trim_smithing_template',
|
||||
'ward_armor_trim_smithing_template',
|
||||
'coast_armor_trim_smithing_template',
|
||||
'spire_armor_trim_smithing_template',
|
||||
'snout_armor_trim_smithing_template',
|
||||
'shaper_armor_trim_smithing_template',
|
||||
'netherite_upgrade_smithing_template',
|
||||
'raiser_armor_trim_smithing_template',
|
||||
'sentry_armor_trim_smithing_template',
|
||||
'silence_armor_trim_smithing_template',
|
||||
'wayfinder_armor_trim_smithing_template']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a detailed plan for crafting an item considering current inventory
|
||||
*/
|
||||
export function getDetailedCraftingPlan(targetItem, count = 1, current_inventory = {}) {
|
||||
initializeLoopingItems();
|
||||
if (!targetItem || count <= 0 || !getItemId(targetItem)) {
|
||||
return "Invalid input. Please provide a valid item name and positive count.";
|
||||
}
|
||||
|
||||
if (isBaseItem(targetItem)) {
|
||||
const available = current_inventory[targetItem] || 0;
|
||||
if (available >= count) return "You have all required items already in your inventory!";
|
||||
return `${targetItem} is a base item, you need to find ${count - available} more in the world`;
|
||||
}
|
||||
|
||||
const inventory = { ...current_inventory };
|
||||
const leftovers = {};
|
||||
const plan = craftItem(targetItem, count, inventory, leftovers);
|
||||
return formatPlan(plan);
|
||||
}
|
||||
|
||||
function isBaseItem(item) {
|
||||
return loopingItems.has(item) || getItemCraftingRecipes(item) === null;
|
||||
}
|
||||
|
||||
function craftItem(item, count, inventory, leftovers, crafted = { required: {}, steps: [], leftovers: {} }) {
|
||||
// Check available inventory and leftovers first
|
||||
const availableInv = inventory[item] || 0;
|
||||
const availableLeft = leftovers[item] || 0;
|
||||
const totalAvailable = availableInv + availableLeft;
|
||||
|
||||
if (totalAvailable >= count) {
|
||||
// Use leftovers first, then inventory
|
||||
const useFromLeft = Math.min(availableLeft, count);
|
||||
leftovers[item] = availableLeft - useFromLeft;
|
||||
|
||||
const remainingNeeded = count - useFromLeft;
|
||||
if (remainingNeeded > 0) {
|
||||
inventory[item] = availableInv - remainingNeeded;
|
||||
}
|
||||
return crafted;
|
||||
}
|
||||
|
||||
// Use whatever is available
|
||||
const stillNeeded = count - totalAvailable;
|
||||
if (availableLeft > 0) leftovers[item] = 0;
|
||||
if (availableInv > 0) inventory[item] = 0;
|
||||
|
||||
if (isBaseItem(item)) {
|
||||
crafted.required[item] = (crafted.required[item] || 0) + stillNeeded;
|
||||
return crafted;
|
||||
}
|
||||
|
||||
const recipe = getItemCraftingRecipes(item)?.[0];
|
||||
if (!recipe) {
|
||||
crafted.required[item] = stillNeeded;
|
||||
return crafted;
|
||||
}
|
||||
|
||||
const [ingredients, result] = recipe;
|
||||
const craftedPerRecipe = result.craftedCount;
|
||||
const batchCount = Math.ceil(stillNeeded / craftedPerRecipe);
|
||||
const totalProduced = batchCount * craftedPerRecipe;
|
||||
|
||||
// Add excess to leftovers
|
||||
if (totalProduced > stillNeeded) {
|
||||
leftovers[item] = (leftovers[item] || 0) + (totalProduced - stillNeeded);
|
||||
}
|
||||
|
||||
// Process each ingredient
|
||||
for (const [ingredientName, ingredientCount] of Object.entries(ingredients)) {
|
||||
const totalIngredientNeeded = ingredientCount * batchCount;
|
||||
craftItem(ingredientName, totalIngredientNeeded, inventory, leftovers, crafted);
|
||||
}
|
||||
|
||||
// Add crafting step
|
||||
const stepIngredients = Object.entries(ingredients)
|
||||
.map(([name, amount]) => `${amount * batchCount} ${name}`)
|
||||
.join(' + ');
|
||||
crafted.steps.push(`Craft ${stepIngredients} -> ${totalProduced} ${item}`);
|
||||
|
||||
return crafted;
|
||||
}
|
||||
|
||||
function formatPlan({ required, steps, leftovers }) {
|
||||
const lines = [];
|
||||
|
||||
if (Object.keys(required).length > 0) {
|
||||
lines.push('You are missing the following items:');
|
||||
Object.entries(required).forEach(([item, count]) =>
|
||||
lines.push(`- ${count} ${item}`));
|
||||
lines.push('\nOnce you have these items, here\'s your crafting plan:');
|
||||
} else {
|
||||
lines.push('You have all items required to craft this item!');
|
||||
lines.push('Here\'s your crafting plan:');
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
lines.push(...steps);
|
||||
|
||||
if (Object.keys(leftovers).length > 0) {
|
||||
lines.push('\nYou will have leftover:');
|
||||
Object.entries(leftovers).forEach(([item, count]) =>
|
||||
lines.push(`- ${count} ${item}`));
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
Loading…
Add table
Reference in a new issue