mindcraft/tasks/construction_tasks/generate_multiagent_construction_tasks.js
2025-04-21 12:20:06 -05:00

178 lines
6.1 KiB
JavaScript

import fs from 'fs';
import path from 'path';
import {proceduralGeneration} from "../../src/agent/tasks/construction_tasks.js";
//note 'main' (script to run generation of tasks) is at bottom of page
/**
* Helper function to initalize agent inventories
* @param blueprint
* @param agents
* @param evenlySplit - When true, splits materials evenly across inventories
* @returns {{}}
*/
function createInitialInventory(blueprint, agents, evenlySplit = true) {
const inventories = {};
const materialCounts = {};
let currentAgent = 0;
// Initialize inventories
for (let i = 0; i < agents; i++) {
inventories[i] = {'diamond_pickaxe': 1};
}
// Count materials in blueprint and replace ladder variants with "ladder"
for (const level of blueprint.levels) {
for (let rowIndex = 0; rowIndex < level.placement.length; rowIndex++) {
for (let blockIndex = 0; blockIndex < level.placement[rowIndex].length; blockIndex++) {
let block = level.placement[rowIndex][blockIndex];
if (block !== 'air') {
// Check if material contains 'door' or 'ladder' and convert appropriately
let materialKey = block;
if (block.includes('dark_oak_door')) {
materialKey = 'dark_oak_door';
} else if (block.includes('oak_door')) {
materialKey = 'oak_door';
} else if (block.includes('ladder')) {
materialKey = 'ladder';
level.placement[rowIndex][blockIndex] = 'ladder'; // Replace in blueprint
}
materialCounts[materialKey] = (materialCounts[materialKey] || 0) + 1;
}
}
}
}
if (evenlySplit) {
// Distribute materials evenly among agents
for (const [material, count] of Object.entries(materialCounts)) {
const baseAmount = Math.floor(count / agents);
const remainder = count % agents;
// Give each agent the base amount
for (let i = 0; i < agents; i++) {
inventories[i][material] = baseAmount;
}
// Distribute remainder one by one to agents
for (let i = 0; i < remainder; i++) {
inventories[i][material]++;
}
}
} else {
// Original distribution - one material type to one agent
for (const [material, count] of Object.entries(materialCounts)) {
inventories[currentAgent][material] = count;
currentAgent = (currentAgent + 1) % agents;
}
}
for (let i = 0; i < agents; i++) {
inventories[i]['dirt'] = 128;
}
return inventories;
}
/**
* Helper function to allocate space for the blueprint based on the number of rooms
* @param rooms
* @returns {number}
*/
function calculateSpaceNeeded(rooms) {
const baseSize = 10;
const scaleFactor = Math.floor(rooms / 4) * 5;
return baseSize + scaleFactor;
}
/**
* MAIN GENERATION FUNCTION
*
* Varies agents, materials, room count, windows and carpets to create different complexities of construction tasks.
* @param variants is the number of variants within each complexity level you want.
* @returns The tasks as nested JSON {{}}
*/
function generateConstructionTasks(variants, agents) {
const materialLevels = 5;
const roomCounts = [4, 6, 8];
const windowStyles = [0, 1, 2];
const carpetStyles = [0, 1, 2];
const timeout = 600; // 10 min base
const tasks = {};
for (let m = 0; m < materialLevels; m++) {
for (let r = 0; r < roomCounts.length; r++) {
for (let w = 0; w < windowStyles.length; w++) {
for (let c = 0; c < carpetStyles.length; c++) {
for (let variant = 0; variant < variants; variant++) {
const rooms = roomCounts[r];
const spaceSize = calculateSpaceNeeded(rooms);
const blueprint = proceduralGeneration(
spaceSize,
spaceSize,
spaceSize,
rooms,
5,
5,
4,
5,
"air",
carpetStyles[c],
windowStyles[w],
m + 1
);
const taskName = `materials_${m}_rooms_${r}_window_${w}_carpet_${c}_variant_${variant}`;
tasks[taskName] = {
type: "construction",
goal: "Make a house with the blueprint",
conversation: "Let's share materials and make a house with the blueprint",
agent_count: agents,
initial_inventory: createInitialInventory(blueprint, agents),
timeout: timeout + (300 * r), // 5 minute per additional level of complexity
blueprint: blueprint,
};
}
}
}
}
}
return tasks;
}
// const fs = require('fs');
// const path = require('path');
// Function to create directories and generate tasks
function setupTaskFiles(baseDirs, agentCounts, variants) {
baseDirs.forEach(base => {
if (!fs.existsSync(base)) {
fs.mkdirSync(base, { recursive: true });
}
agentCounts.forEach(count => {
const tasks = generateConstructionTasks(variants, count);
const filePath = path.join(base, `${count}agents.json`);
fs.writeFileSync(filePath, JSON.stringify(tasks, null, 2));
console.log(`Generated tasks saved to ${filePath}`);
});
});
}
const baseDirs = ['./train', './test'];
const agentCounts = [2, 3, 4, 5];
const variants = 5;
setupTaskFiles(baseDirs, agentCounts, variants);