mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-08-09 08:45:33 +02:00
Merge branch 'main' of https://github.com/icwhite/mindcraft
This commit is contained in:
commit
fce23becb2
4 changed files with 3941 additions and 1679 deletions
|
@ -485,7 +485,7 @@ export class Agent {
|
||||||
if (res) {
|
if (res) {
|
||||||
await this.history.add('system', `Task ended with score : ${res.score}`);
|
await this.history.add('system', `Task ended with score : ${res.score}`);
|
||||||
await this.history.save();
|
await this.history.save();
|
||||||
await new Promise(resolve => setTimeout(resolve, 3000)); // Wait 3 second for save to complete
|
// await new Promise(resolve => setTimeout(resolve, 3000)); // Wait 3 second for save to complete
|
||||||
console.log('Task finished:', res.message);
|
console.log('Task finished:', res.message);
|
||||||
this.killAll();
|
this.killAll();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync , writeFileSync, existsSync} from 'fs';
|
||||||
import { executeCommand } from './commands/index.js';
|
import { executeCommand } from './commands/index.js';
|
||||||
import { getPosition } from './library/world.js';
|
import { getPosition } from './library/world.js';
|
||||||
import settings from '../../settings.js';
|
import settings from '../../settings.js';
|
||||||
|
@ -6,6 +6,61 @@ import { Vec3 } from 'vec3';
|
||||||
import { ConstructionTaskValidator, Blueprint } from './task_types/construction_tasks.js';
|
import { ConstructionTaskValidator, Blueprint } from './task_types/construction_tasks.js';
|
||||||
import { CookingTaskInitiator } from './task_types/cooking_tasks.js';
|
import { CookingTaskInitiator } from './task_types/cooking_tasks.js';
|
||||||
|
|
||||||
|
const PROGRESS_FILE = './hells_kitchen_progress.json';
|
||||||
|
|
||||||
|
const hellsKitchenProgressManager = {
|
||||||
|
readProgress: function() {
|
||||||
|
try {
|
||||||
|
if (existsSync(PROGRESS_FILE)) {
|
||||||
|
const data = readFileSync(PROGRESS_FILE, 'utf8');
|
||||||
|
return JSON.parse(data);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error reading progress file:', err);
|
||||||
|
}
|
||||||
|
return { taskId: null, agent0Complete: false, agent1Complete: false };
|
||||||
|
},
|
||||||
|
|
||||||
|
writeProgress: function(progress) {
|
||||||
|
try {
|
||||||
|
writeFileSync(PROGRESS_FILE, JSON.stringify(progress), 'utf8');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error writing progress file:', err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
resetTask: function(taskId) {
|
||||||
|
const progress = { taskId, agent0Complete: false, agent1Complete: false };
|
||||||
|
this.writeProgress(progress);
|
||||||
|
return progress;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAgentProgress: function(taskId, agentId, isComplete) {
|
||||||
|
const progress = this.readProgress();
|
||||||
|
|
||||||
|
// If it's a different task, reset first
|
||||||
|
if (progress.taskId !== taskId) {
|
||||||
|
progress.taskId = taskId;
|
||||||
|
progress.agent0Complete = false;
|
||||||
|
progress.agent1Complete = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the specific agent's status
|
||||||
|
if (agentId === 0) progress.agent0Complete = isComplete;
|
||||||
|
if (agentId === 1) progress.agent1Complete = isComplete;
|
||||||
|
|
||||||
|
this.writeProgress(progress);
|
||||||
|
return progress;
|
||||||
|
},
|
||||||
|
|
||||||
|
isTaskComplete: function(taskId) {
|
||||||
|
const progress = this.readProgress();
|
||||||
|
if (progress.taskId !== taskId) return false;
|
||||||
|
return progress.agent0Complete && progress.agent1Complete;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
//todo: modify validator code to return an object with valid and score -> do more testing hahah
|
//todo: modify validator code to return an object with valid and score -> do more testing hahah
|
||||||
//todo: figure out how to log these things to the same place as bots/histories
|
//todo: figure out how to log these things to the same place as bots/histories
|
||||||
// export class CraftTaskValidator {
|
// export class CraftTaskValidator {
|
||||||
|
@ -14,7 +69,6 @@ import { CookingTaskInitiator } from './task_types/cooking_tasks.js';
|
||||||
// this.number_of_target = data.number_of_target;
|
// this.number_of_target = data.number_of_target;
|
||||||
// this.agent = agent;
|
// this.agent = agent;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the presence of required items in an agent's inventory
|
* Validates the presence of required items in an agent's inventory
|
||||||
* @param {Object} data - Task data containing target and quantity information
|
* @param {Object} data - Task data containing target and quantity information
|
||||||
|
@ -22,94 +76,47 @@ import { CookingTaskInitiator } from './task_types/cooking_tasks.js';
|
||||||
* @returns {Object} Validation result with success status and missing items
|
* @returns {Object} Validation result with success status and missing items
|
||||||
*/
|
*/
|
||||||
function checkItemPresence(data, agent) {
|
function checkItemPresence(data, agent) {
|
||||||
// Helper function to check if target is a dictionary with quantities
|
|
||||||
function isTargetDictionaryWithQuantities(target) {
|
|
||||||
return typeof target === 'object' &&
|
|
||||||
!Array.isArray(target) &&
|
|
||||||
|
|
||||||
target !== null &&
|
|
||||||
Object.values(target).every(value => typeof value === 'number');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert any target format into a standardized dictionary
|
|
||||||
function normalizeTargets(target) {
|
|
||||||
if (typeof target === 'string') {
|
|
||||||
// Single target case
|
|
||||||
return { [target]: 1 };
|
|
||||||
} else if (Array.isArray(target)) {
|
|
||||||
// Array case - convert to dictionary with default quantity 1
|
|
||||||
return target.reduce((acc, item) => {
|
|
||||||
acc[item] = 1;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
} else if (typeof target === 'object' && target !== null) {
|
|
||||||
// Already a dictionary - return as is
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
throw new Error('Invalid target format');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize quantities to match target format
|
|
||||||
function normalizeQuantities(targets, quantities) {
|
|
||||||
if (quantities === undefined) {
|
|
||||||
// If no quantities specified, default to 1 for each target
|
|
||||||
return Object.keys(targets).reduce((acc, key) => {
|
|
||||||
acc[key] = 1;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
} else if (typeof quantities === 'number') {
|
|
||||||
// If single number provided, apply to all targets
|
|
||||||
return Object.keys(targets).reduce((acc, key) => {
|
|
||||||
acc[key] = quantities;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
} else if (typeof quantities === 'object' && quantities !== null) {
|
|
||||||
// If quantities dictionary provided, use it directly
|
|
||||||
return quantities;
|
|
||||||
}
|
|
||||||
throw new Error('Invalid number_of_target format');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// First normalize targets to always have a consistent format
|
// Special handling for hells_kitchen tasks
|
||||||
const targets = normalizeTargets(data.target);
|
if (data.task_id && data.task_id.endsWith('hells_kitchen') && Array.isArray(data.target) && data.target.length === 2) {
|
||||||
|
|
||||||
// Determine the required quantities
|
// Get agent ID and target for this agent
|
||||||
const requiredQuantities = isTargetDictionaryWithQuantities(data.target)
|
const agentId = agent.count_id;
|
||||||
? data.target
|
|
||||||
: normalizeQuantities(targets, data.number_of_target);
|
|
||||||
|
|
||||||
// Count items in inventory
|
if (agentId === 0 || agentId === 1) {
|
||||||
const inventoryCount = {};
|
// Use only the corresponding element from the target list
|
||||||
agent.bot.inventory.slots.forEach((slot) => {
|
const targetForThisAgent = data.target[agentId];
|
||||||
if (slot) {
|
const modifiedData = {
|
||||||
const itemName = slot.name.toLowerCase();
|
...data,
|
||||||
inventoryCount[itemName] = (inventoryCount[itemName] || 0) + slot.count;
|
target: targetForThisAgent
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
// Check if all required items are present in sufficient quantities
|
// Check if this agent has their required item
|
||||||
const missingItems = [];
|
const agentResult = checkItemForSingleAgent(modifiedData, agent);
|
||||||
let allTargetsMet = true;
|
|
||||||
|
|
||||||
for (const [item, requiredCount] of Object.entries(requiredQuantities)) {
|
// Update the file-based progress tracker
|
||||||
const itemName = item.toLowerCase();
|
const progress = hellsKitchenProgressManager.updateAgentProgress(
|
||||||
const currentCount = inventoryCount[itemName] || 0;
|
data.task_id,
|
||||||
if (currentCount < requiredCount) {
|
agentId,
|
||||||
allTargetsMet = false;
|
agentResult.success
|
||||||
missingItems.push({
|
);
|
||||||
item: itemName,
|
|
||||||
required: requiredCount,
|
// // Log the current state
|
||||||
current: currentCount,
|
// console.log(`Agent ${agentId} has item: ${agentResult.success}`);
|
||||||
missing: requiredCount - currentCount
|
// console.log(`Task state: Agent0=${progress.agent0Complete}, Agent1=${progress.agent1Complete}`);
|
||||||
});
|
|
||||||
|
// Return combined result - success only if both agents have their items
|
||||||
|
return {
|
||||||
|
success: progress.agent0Complete && progress.agent1Complete,
|
||||||
|
missingItems: agentResult.missingItems,
|
||||||
|
agentComplete: agentResult.success // Individual agent status for debugging
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
// Non-hells_kitchen tasks use the standard check
|
||||||
success: allTargetsMet,
|
return checkItemForSingleAgent(data, agent);
|
||||||
missingItems: missingItems
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error checking item presence:', error);
|
console.error('Error checking item presence:', error);
|
||||||
|
@ -121,6 +128,93 @@ function checkItemPresence(data, agent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to check a single agent's inventory
|
||||||
|
* Extracted from the original checkItemPresence logic
|
||||||
|
*/
|
||||||
|
function checkItemForSingleAgent(data, agent) {
|
||||||
|
function isTargetDictionaryWithQuantities(target) {
|
||||||
|
return typeof target === 'object' &&
|
||||||
|
!Array.isArray(target) &&
|
||||||
|
target !== null &&
|
||||||
|
Object.values(target).every(value => typeof value === 'number');
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeTargets(target) {
|
||||||
|
if (typeof target === 'string') {
|
||||||
|
return { [target]: 1 };
|
||||||
|
} else if (Array.isArray(target)) {
|
||||||
|
return target.reduce((acc, item) => {
|
||||||
|
acc[item] = 1;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
} else if (typeof target === 'object' && target !== null) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
throw new Error('Invalid target format');
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeQuantities(targets, quantities) {
|
||||||
|
if (quantities === undefined) {
|
||||||
|
return Object.keys(targets).reduce((acc, key) => {
|
||||||
|
acc[key] = 1;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
} else if (typeof quantities === 'number') {
|
||||||
|
return Object.keys(targets).reduce((acc, key) => {
|
||||||
|
acc[key] = quantities;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
} else if (typeof quantities === 'object' && quantities !== null) {
|
||||||
|
return quantities;
|
||||||
|
}
|
||||||
|
throw new Error('Invalid number_of_target format');
|
||||||
|
}
|
||||||
|
|
||||||
|
// First normalize targets to always have a consistent format
|
||||||
|
const targets = normalizeTargets(data.target);
|
||||||
|
|
||||||
|
// Determine the required quantities
|
||||||
|
const requiredQuantities = isTargetDictionaryWithQuantities(data.target)
|
||||||
|
? data.target
|
||||||
|
: normalizeQuantities(targets, data.number_of_target);
|
||||||
|
|
||||||
|
// Count items in inventory
|
||||||
|
const inventoryCount = {};
|
||||||
|
agent.bot.inventory.slots.forEach((slot) => {
|
||||||
|
if (slot) {
|
||||||
|
const itemName = slot.name.toLowerCase();
|
||||||
|
inventoryCount[itemName] = (inventoryCount[itemName] || 0) + slot.count;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if all required items are present in sufficient quantities
|
||||||
|
const missingItems = [];
|
||||||
|
let allTargetsMet = true;
|
||||||
|
|
||||||
|
for (const [item, requiredCount] of Object.entries(requiredQuantities)) {
|
||||||
|
const itemName = item.toLowerCase();
|
||||||
|
const currentCount = inventoryCount[itemName] || 0;
|
||||||
|
if (currentCount < requiredCount) {
|
||||||
|
allTargetsMet = false;
|
||||||
|
missingItems.push({
|
||||||
|
item: itemName,
|
||||||
|
required: requiredCount,
|
||||||
|
current: currentCount,
|
||||||
|
missing: requiredCount - currentCount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: allTargetsMet,
|
||||||
|
missingItems: missingItems
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CookingCraftingTaskValidator {
|
class CookingCraftingTaskValidator {
|
||||||
constructor(data, agent) {
|
constructor(data, agent) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
@ -155,6 +249,14 @@ export class Task {
|
||||||
this.blocked_actions = [];
|
this.blocked_actions = [];
|
||||||
this.task_id = task_id;
|
this.task_id = task_id;
|
||||||
console.log('Task ID:', task_id);
|
console.log('Task ID:', task_id);
|
||||||
|
|
||||||
|
// Reset hells_kitchen progress when a new task starts
|
||||||
|
if (task_id && task_id.endsWith('hells_kitchen')) {
|
||||||
|
hellsKitchenProgressManager.resetTask(task_id);
|
||||||
|
console.log('Reset Hells Kitchen progress for new task');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (task_path && task_id) {
|
if (task_path && task_id) {
|
||||||
this.data = this.loadTask(task_path, task_id);
|
this.data = this.loadTask(task_path, task_id);
|
||||||
this.task_type = this.data.type;
|
this.task_type = this.data.type;
|
||||||
|
@ -195,6 +297,14 @@ export class Task {
|
||||||
this.available_agents = settings.profiles.map((p) => JSON.parse(readFileSync(p, 'utf8')).name);
|
this.available_agents = settings.profiles.map((p) => JSON.parse(readFileSync(p, 'utf8')).name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add this method if you want to manually reset the hells_kitchen progress
|
||||||
|
resetHellsKitchenProgress() {
|
||||||
|
if (this.task_id && this.task_id.endsWith('hells_kitchen')) {
|
||||||
|
hellsKitchenProgressManager.resetTask(this.task_id);
|
||||||
|
console.log('Hells Kitchen progress reset manually');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getAgentGoal() {
|
getAgentGoal() {
|
||||||
if (!this.data || !this.data.goal) {
|
if (!this.data || !this.data.goal) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -204,18 +314,23 @@ export class Task {
|
||||||
|
|
||||||
if (this.task_type === 'cooking') {
|
if (this.task_type === 'cooking') {
|
||||||
|
|
||||||
if (this.data.agent_count > 2) {
|
if (this.data.agent_count > 2) {
|
||||||
|
|
||||||
if (this.name.toLowerCase().startsWith('andy')) {
|
if (this.name.toLowerCase().startsWith('andy')) {
|
||||||
add_string = '\nIn the end, all the food items should be given to you by other bots. Make sure to talk to all the agents using startConversation command to coordinate the task instead of talking to just one agent. You can even end current conversation with any agent using endConversation command and then talk to a new agent using startConversation command.';
|
add_string = '\nIn the end, all the food items should be given to you by other bots. Make sure to talk to all the agents using startConversation command to coordinate the task instead of talking to just one agent. You can even end current conversation with any agent using endConversation command and then talk to a new agent using startConversation command.';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
add_string = '\nIn the end, all the food items should be given to one single bot whose name starts with andy or Andy. Make sure to talk to all the agents using startConversation command to coordinate the task instead of talking to just one agent. You can even end current conversation with any agent using endConversation command and then talk to a new agent using startConversation command.';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
add_string = '\nIn the end, all the food items should be given to one single bot whose name starts with andy or Andy. Make sure to talk to all the agents using startConversation command to coordinate the task instead of talking to just one agent. You can even end current conversation with any agent using endConversation command and then talk to a new agent using startConversation command.';
|
if (this.data.task_id && this.data.task_id.endsWith('hells_kitchen')) {
|
||||||
|
add_string = '';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
add_string = '\nIn the end, all the food items should be given to one single bot.';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
add_string = '\nIn the end, all the food items should be given to one single bot.';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If goal is a string, all agents share the same goal
|
// If goal is a string, all agents share the same goal
|
||||||
|
@ -237,6 +352,7 @@ export class Task {
|
||||||
const tasksFile = readFileSync(task_path, 'utf8');
|
const tasksFile = readFileSync(task_path, 'utf8');
|
||||||
const tasks = JSON.parse(tasksFile);
|
const tasks = JSON.parse(tasksFile);
|
||||||
let task = tasks[task_id];
|
let task = tasks[task_id];
|
||||||
|
task['task_id'] = task_id;
|
||||||
console.log(task);
|
console.log(task);
|
||||||
console.log(this.agent.count_id);
|
console.log(this.agent.count_id);
|
||||||
if (!task) {
|
if (!task) {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue