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": "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": "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);
|
convoManager.endConversation(player_name);
|
||||||
return `Converstaion with ${player_name} ended.`;
|
return `Converstaion with ${player_name} ended.`;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
// { // commented for now, causes confusion with goal command
|
// { // commented for now, causes confusion with goal command
|
||||||
// name: '!npcGoal',
|
// name: '!npcGoal',
|
||||||
// description: 'Set a simple goal for an item or building to automatically work towards. Do not use for complex goals.',
|
// 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) {
|
perform: async function (agent) {
|
||||||
return "Saved place names: " + agent.memory_bank.getKeys();
|
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) {
|
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) {
|
if (placedTable) {
|
||||||
await collectBlock(bot, 'crafting_table', 1);
|
await collectBlock(bot, 'crafting_table', 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,7 +204,7 @@ class ItemWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
createChildren() {
|
createChildren() {
|
||||||
let recipes = mc.getItemCraftingRecipes(this.name);
|
let recipes = mc.getItemCraftingRecipes(this.name).map(([recipe, craftedCount]) => recipe);
|
||||||
if (recipes) {
|
if (recipes) {
|
||||||
for (let recipe of recipes) {
|
for (let recipe of recipes) {
|
||||||
let includes_blacklisted = false;
|
let includes_blacklisted = false;
|
||||||
|
|
|
@ -190,7 +190,10 @@ export function getItemCraftingRecipes(itemName) {
|
||||||
recipe[ingredientName] = 0;
|
recipe[ingredientName] = 0;
|
||||||
recipe[ingredientName]++;
|
recipe[ingredientName]++;
|
||||||
}
|
}
|
||||||
recipes.push(recipe);
|
recipes.push([
|
||||||
|
recipe,
|
||||||
|
{craftedCount : r.result.count}
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return recipes;
|
return recipes;
|
||||||
|
@ -327,4 +330,156 @@ export function calculateLimitingResource(availableItems, requiredItems, discret
|
||||||
}
|
}
|
||||||
if(discrete) num = Math.floor(num);
|
if(discrete) num = Math.floor(num);
|
||||||
return {num, limitingResource}
|
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