new skills

This commit is contained in:
Kolby Nottingham 2023-09-29 15:53:16 -07:00
parent a6b1f4cb81
commit 54da4c9032
6 changed files with 375 additions and 55 deletions

26
act.js
View file

@ -13,7 +13,7 @@ function buildSystemMessage(bot) {
function buildExamples() { function buildExamples() {
return[ return [
`mr_steve2: Will you help me collect wood? `mr_steve2: Will you help me collect wood?
!blocks !blocks
@ -25,19 +25,19 @@ NEARBY_BLOCKS
\`\`\` \`\`\`
Me: I'd be glad to help you collect wood.`, Me: I'd be glad to help you collect wood.`,
`I'm going to help mr_steve2 collect wood. The type of wood block nearby is 'oak_log'. I'll adjust my code to collect 10 'oak_log' for mr_steve2. `I'm going to help mr_steve2 collect wood. The type of wood block nearby is 'oak_log'. I'll adjust my code to collect an 'oak_log' for mr_steve2.
\`\`\` \`\`\`
await skills.CollectBlock(bot, 'oak_log', 10); await skills.collectBlock(bot, 'oak_log');
await skills.GoToPlayer(bot, 'mr_steve2'); await skills.giveToPlayer(bot, 'oak_log', 'mr_steve2');
await skills.DropItem(bot, 'oak_log', 10);
\`\`\``, \`\`\``,
`sally32: What are you doing? `sally32: What are you doing?
!action !action
\`\`\` \`\`\`
await skills.ExploreToFind(bot, 'coal_ore'); await skills.equipItem(bot, 'wooden_pickaxe');
await skills.EquipItem(bot, 'wooden_pickaxe'); while (world.getInventory(bot).coal_ore < 10) {
await skills.CollectBlock(bot, 'coal_ore', 10); await skills.collectBlock(bot, 'coal_ore');
}
\`\`\` \`\`\`
Me: I'm looking for coal. Have you seen any? Me: I'm looking for coal. Have you seen any?
@ -46,20 +46,22 @@ sally32: Yes, there's some in this cave, follow me.`,
`I'm going to follow sally32 to the cave and collect coal. I'll adjust my code to follow sally32 until I find coal_ore and then I'll mine it. `I'm going to follow sally32 to the cave and collect coal. I'll adjust my code to follow sally32 until I find coal_ore and then I'll mine it.
\`\`\` \`\`\`
while (true) { while (true) {
await skills.GoToPlayer(bot, 'sally32'); await skills.goToPlayer(bot, 'sally32');
if (world.getNearbyBlocks(bot).includes('coal_ore')) { if (world.getNearbyBlocks(bot).includes('coal_ore')) {
break; break;
} }
} }
await skills.EquipItem(bot, 'wooden_pickaxe'); await skills.equipItem(bot, 'wooden_pickaxe');
await skills.CollectBlock(bot, 'coal_ore', 10); while (world.getInventory(bot).coal_ore < 10) {
await skills.collectBlock(bot, 'coal_ore');
}
\`\`\``, \`\`\``,
`user42: come here `user42: come here
Me: Sure! I'm on my way.`, Me: Sure! I'm on my way.`,
`I'm going to navigate to user42. `I'm going to navigate to user42.
\`\`\` \`\`\`
await skills.GoToPlayer(bot, 'user42'); await skills.goToPlayer(bot, 'user42');
\`\`\``, \`\`\``,
] ]
} }

View file

@ -10,7 +10,7 @@ function buildSystemMessage() {
message += ' Do not give instructions unless asked, and always be brief in your responses.'; message += ' Do not give instructions unless asked, and always be brief in your responses.';
message += '\n\nYou can use the following commands followed by to query for information about the world.'; message += '\n\nYou can use the following commands followed by to query for information about the world.';
message += ' The query response will be returned between sets of "\`\`\`":'; message += ' The query response will be returned between sets of "\`\`\`":';
message += '\n!stats - get player and world stats (e.g. current health and time of day)'; message += '\n!stats - get player and world stats (current health, time of day, etc.)';
message += '\n!inventory - get your current inventory'; message += '\n!inventory - get your current inventory';
message += '\n!blocks - get a list of nearby blocks'; message += '\n!blocks - get a list of nearby blocks';
message += '\n!craftable - get a list of craftable items with your current inventory'; message += '\n!craftable - get a list of craftable items with your current inventory';

View file

@ -1,6 +1,6 @@
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { getNearbyBlocks, getNearbyBlockTypes } from './world.js'; import { getCraftingTable, getNearbyMobTypes, getNearbyPlayerNames, getNearbyBlockTypes, getInventoryCounts } from './world.js';
import { getAllItems } from './mcdata.js'; import { getAllItems } from './mcdata.js';
@ -20,22 +20,14 @@ export function getStats(bot) {
export function getInventory(bot) { export function getInventory(bot) {
let inventory = getInventoryCounts(bot);
let res = 'INVENTORY'; let res = 'INVENTORY';
let allItems = new Map(); for (const item in inventory) {
for (const item of bot.inventory.slots.values()) { if (inventory[item] > 0)
if (item != null) { res += `\n- ${item}: ${inventory[item]}`;
if (allItems.has(item.name)) {
allItems.set(item.name, allItems.get(item.name) + item.count);
} else {
allItems.set(item.name, item.count);
} }
} if (res == 'INVENTORY') {
} res += ': none';
for (const [item, count] of allItems.entries()) {
res += `\n- ${item}: ${count}`;
}
if (allItems.size == 0) {
res += ': empty';
} }
return res; return res;
} }
@ -56,14 +48,11 @@ export function getBlocks(bot) {
export function getNearbyEntities(bot) { export function getNearbyEntities(bot) {
let res = 'NEARBY_ENTITIES'; let res = 'NEARBY_ENTITIES';
for (const entity of Object.values(bot.entities)) { for (const entity of getNearbyPlayerNames(bot)) {
const distance = entity.position.distanceTo(bot.entity.position); res += `\n- player: ${entity}`;
if (distance > 50) continue;
if (entity.type == 'mob') {
res += `\n- mob: ${entity.mobType}`;
} else if (entity.type == 'player' && entity.username != bot.username) {
res += `\n- player: ${entity.username}`;
} }
for (const entity of getNearbyMobTypes(bot)) {
res += `\n- mob: ${entity}`;
} }
if (res == 'NEARBY_ENTITIES') { if (res == 'NEARBY_ENTITIES') {
res += ': none'; res += ': none';
@ -73,14 +62,7 @@ export function getNearbyEntities(bot) {
export function getCraftable(bot) { export function getCraftable(bot) {
const blocks = getNearbyBlocks(bot, 50); const table = getCraftingTable(bot);
let table = null;
for (const block of blocks) {
if (block.name == 'crafting_table') {
table = block;
break;
}
}
let res = 'CRAFTABLE_ITEMS'; let res = 'CRAFTABLE_ITEMS';
for (const item of getAllItems()) { for (const item of getAllItems()) {
let recipes = bot.recipesFor(item.id, null, 1, table); let recipes = bot.recipesFor(item.id, null, 1, table);

View file

@ -26,7 +26,7 @@ Sure, do you want oak or birch?`},
\`\`\` \`\`\`
while (true) { while (true) {
await skills.CollectBlock(bot, 'oak_log', 1); await skills.CollectBlock(bot, 'oak_log', 1);
await skills.GoToPlayer(bot, 'username'); await skills.goToPlayer(bot, 'username');
await skills.DropItem(bot, 'oak_log', 1); await skills.DropItem(bot, 'oak_log', 1);
} }
\`\`\``} \`\`\``}

View file

@ -1,4 +1,5 @@
import { getItemId } from "./mcdata.js"; import { getItemId } from "./mcdata.js";
import { getCraftingTable, getInventoryCounts, getInventoryStacks, getNearbyMobs, getNearbyBlocks } from "./world.js";
import pf from 'mineflayer-pathfinder'; import pf from 'mineflayer-pathfinder';
@ -8,24 +9,207 @@ import pf from 'mineflayer-pathfinder';
* @param {string} item_name, the item name to craft. * @param {string} item_name, the item name to craft.
* @returns {Promise<boolean>} true if the item was crafted, false otherwise. * @returns {Promise<boolean>} true if the item was crafted, false otherwise.
* @example * @example
* await skills.CraftItem(bot, "wooden_pickaxe"); * await skills.craftItem(bot, "wooden_pickaxe");
**/ **/
export async function CraftItem(bot, itemName) { export async function craftItem(bot, itemName) {
let recipes = bot.recipesFor(getItemId(itemName), null, 1, null); // TODO add crafting table as final arg const table = getCraftingTable(bot);
let recipes = bot.recipesFor(getItemId(itemName), null, 1, table);
await bot.craft(recipes[0], 1, null); await bot.craft(recipes[0], 1, null);
return true; return true;
} }
/**
* Attack mob of the given type.
* @param {MinecraftBot} bot, reference to the minecraft bot.
* @param {string} mobType, the type of mob to attack.
* @returns {Promise<boolean>} true if the mob was attacked, false if the mob type was not found.
* @example
* await skills.attackMob(bot, "zombie");
**/
export async function attackMob(bot, mobType) {
const mobs = getNearbyMobs(bot);
for (let i = 0; i < mobs.length; i++) {
if (mobs[i].mobType == mobType) {
bot.attack(mobs[i]);
return true;
}
}
return false;
}
/**
* Collect one of the given block type.
* @param {MinecraftBot} bot, reference to the minecraft bot.
* @param {string} blockType, the type of block to collect.
* @returns {Promise<boolean>} true if the block was collected, false if the block type was not found.
* @example
* await skills.collectBlock(bot, "oak_log");
**/
export async function collectBlock(bot, blockType) {
const blocks = getNearbyBlocks(bot);
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].name == blockType) {
await bot.collectBlock.collect(blocks[i]);
return true;
}
}
return false;
}
/**
* Break the block at the given position. Will use the bot's equipped item.
* @param {MinecraftBot} bot, reference to the minecraft bot.
* @param {number} x, the x coordinate of the block to break.
* @param {number} y, the y coordinate of the block to break.
* @param {number} z, the z coordinate of the block to break.
* @returns {Promise<boolean>} true if the block was broken, false otherwise.
* @example
* let position = getPosition(bot);
* await skills.breakBlockAt(bot, position.x, position.y - 1, position.x);
**/
export async function breakBlockAt(bot, x, y, z) {
let current = bot.blockAt({ x: x, y: y, z: z });
if (current.name != 'air')
await bot.dig(current, true);
return true;
}
/**
* Place the given block type at the given position.
* @param {MinecraftBot} bot, reference to the minecraft bot.
* @param {string} blockType, the type of block to place.
* @param {number} x, the x coordinate to place the block at.
* @param {number} y, the y coordinate to place the block at.
* @param {number} z, the z coordinate to place the block at.
* @returns {Promise<boolean>} true if the block was placed, false otherwise.
* @example
* let position = getPosition(bot);
* await skills.placeBlock(bot, "oak_log", position.x + 1, position.y, position.x);
**/
export async function placeBlock(bot, blockType, x, y, z) {
let referenceBlock = null;
let refVec = null;
if (bot.blockAt({ x: x + 1, y: y, z: z }).name != "air") {
referenceBlock = bot.blockAt({ x: x + 1, y: y, z: z });
refVec = { x: x - 1, y: y, z: z };
} else if (bot.blockAt({ x: x - 1, y: y, z: z }).name != "air") {
referenceBlock = bot.blockAt({ x: x - 1, y: y, z: z });
refVec = { x: x + 1, y: y, z: z };
} else if (bot.blockAt({ x: x, y: y + 1, z: z }).name != "air") {
referenceBlock = bot.blockAt({ x: x, y: y + 1, z: z });
refVec = { x: x, y: y - 1, z: z };
} else if (bot.blockAt({ x: x, y: y - 1, z: z }).name != "air") {
referenceBlock = bot.blockAt({ x: x, y: y - 1, z: z });
refVec = { x: x, y: y + 1, z: z };
} else if (bot.blockAt({ x: x, y: y, z: z + 1 }).name != "air") {
referenceBlock = bot.blockAt({ x: x, y: y, z: z + 1 });
refVec = { x: x, y: y, z: z - 1 };
} else if (bot.blockAt({ x: x, y: y, z: z - 1 }).name != "air") {
referenceBlock = bot.blockAt({ x: x, y: y, z: z - 1 });
refVec = { x: x, y: y, z: z + 1 };
} else {
return false;
}
let block = null;
for (let stack of getInventoryStacks(bot)) {
if (stack.name == blockType) {
block = stack;
break;
}
}
if (block == null)
return false;
breakBlockAt(bot, x, y, z);
await bot.equip(block, 'hand');
await bot.placeBlock(referenceBlock, refVec);
return true;
}
/**
* Equip the given item or block.
* @param {MinecraftBot} bot, reference to the minecraft bot.
* @param {string} itemName, the item or block name to equip.
* @returns {Promise<boolean>} true if the item was equipped, false otherwise.
* @example
* await skills.equipItem(bot, "wooden_pickaxe");
**/
export async function equipItem(bot, itemName) {
let item = null;
for (let stack of getInventoryStacks(bot)) {
if (stack.name == itemName) {
item = stack;
break;
}
}
if (item == null)
return false;
await bot.equip(item, 'hand');
return true;
}
/**
* Navigate to the given position.
* @param {MinecraftBot} bot, reference to the minecraft bot.
* @param {number} x, the x coordinate to navigate to. If null, the bot's current x coordinate will be used.
* @param {number} y, the y coordinate to navigate to. If null, the bot's current y coordinate will be used.
* @param {number} z, the z coordinate to navigate to. If null, the bot's current z coordinate will be used.
* @returns {Promise<boolean>} true if the position was reached, false otherwise.
* @example
* let position = getPosition(bot);
* await skills.goToPosition(bot, position.x, position.y, position.x + 20);
**/
export async function goToPosition(bot, x, y, z) {
if (x == null) x = bot.entity.position.x;
if (y == null) y = bot.entity.position.y;
if (z == null) z = bot.entity.position.z;
bot.pathfinder.setMovements(new pf.Movements(bot));
let pos = { x: x, y: y, z: z };
bot.pathfinder.setGoal(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 1));
return true;
}
/**
* Give one of the specified item to the specified player
* @param {MinecraftBot} bot, reference to the minecraft bot.
* @param {string} itemType, the name of the item to give.
* @param {string} username, the username of the player to give the item to.
* @returns {Promise<boolean>} true if the item was given, false otherwise.
* @example
* await skills.giveToPlayer(bot, "oak_log", "player1");
**/
export async function giveToPlayer(bot, itemType, username) {
let player = bot.players[username].entity
if (!player)
return false;
if (getInventoryCounts(bot)[itemType] == 0)
return false;
goToPlayer(bot, username);
let pos = player.position;
await bot.lookAt(pos);
await bot.toss(getItemId(itemType), null, 1);
return true;
}
/** /**
* Navigate to the given player. * Navigate to the given player.
* @param {MinecraftBot} bot, reference to the minecraft bot. * @param {MinecraftBot} bot, reference to the minecraft bot.
* @param {string} username, the username of the player to navigate to. * @param {string} username, the username of the player to navigate to.
* @returns {Promise<boolean>} true if the player was found, false otherwise. * @returns {Promise<boolean>} true if the player was found, false otherwise.
* @example * @example
* await skills.GoToPlayer(bot, "player"); * await skills.goToPlayer(bot, "player");
**/ **/
export async function GoToPlayer(bot, username) { export async function goToPlayer(bot, username) {
let player = bot.players[username].entity let player = bot.players[username].entity
if (!player) if (!player)
return false; return false;

View file

@ -1,12 +1,164 @@
import { getAllBlockIds } from './mcdata.js'; import { getAllBlockIds, getAllBlocks, getAllItems } from './mcdata.js';
export function getNearbyBlocks(bot, distance) { export function getCraftingTable(bot) {
let positions = bot.findBlocks({matching: getAllBlockIds(['air']), maxDistance: distance, count: 10000}); const blocks = getNearbyBlocks(bot, 50);
let found = []; let table = null;
for (const block of blocks) {
if (block.name == 'crafting_table') {
table = block;
break;
}
}
return table;
}
export function getNearbyBlocks(bot, maxDistance) {
if (maxDistance == null) maxDistance = 16;
let positions = bot.findBlocks({matching: getAllBlockIds(['air']), maxDistance: maxDistance, count: 10000});
let blocks = [];
for (let i = 0; i < positions.length; i++) { for (let i = 0; i < positions.length; i++) {
let block = bot.blockAt(positions[i]); let block = bot.blockAt(positions[i]);
found.push(block); let distance = positions[i].distanceTo(bot.entity.position);
blocks.push({ block: block, distance: distance });
}
blocks.sort((a, b) => a.distance - b.distance);
let res = [];
for (let i = 0; i < blocks.length; i++) {
res.push(blocks[i].block);
}
return res;
}
export function getNearbyMobs(bot, maxDistance) {
if (maxDistance == null) maxDistance = 16;
let entities = [];
for (const entity of Object.values(bot.entities)) {
const distance = entity.position.distanceTo(bot.entity.position);
if (distance > maxDistance) continue;
if (entity.type == 'mob') {
entities.push({ entity: entity, distance: distance });
}
}
entities.sort((a, b) => a.distance - b.distance);
let res = [];
for (let i = 0; i < entities.length; i++) {
res.push(entities[i].entity);
}
return res;
}
export function getNearbyPlayers(bot, maxDistance) {
if (maxDistance == null) maxDistance = 16;
let players = [];
for (const entity of Object.values(bot.entities)) {
const distance = entity.position.distanceTo(bot.entity.position);
if (distance > maxDistance) continue;
if (entity.type == 'player' && entity.username != bot.username) {
players.push({ entity: entity, distance: distance });
}
}
players.sort((a, b) => a.distance - b.distance);
let res = [];
for (let i = 0; i < players.length; i++) {
res.push(players[i].entity);
}
return res;
}
export function getInventoryStacks(bot) {
let inventory = [];
for (const item of bot.inventory.slots.values()) {
if (item != null) {
inventory.push(item);
}
}
return inventory;
}
/**
* Get an object representing the bot's inventory.
* @param {Bot} bot - The bot to get the inventory for.
* @returns {object} - An object with item names as keys and counts as values.
* @example
* let inventory = world.getInventoryCounts(bot);
* let oakLogCount = inventory['oak_log'];
* let hasWoodenPickaxe = inventory['wooden_pickaxe'] > 0;
**/
export function getInventoryCounts(bot) {
let inventory = {};
for (const item of getInventoryStacks(bot)) {
if (inventory.hasOwnProperty(item.name)) {
inventory[item.name] = inventory[item.name] + item.count;
} else {
inventory[item.name] = item.count;
}
}
for (const item of getAllItems()) {
if (!inventory.hasOwnProperty(item.name)) {
inventory[item.name] = 0;
}
}
for (const item of getAllBlocks()) {
if (!inventory.hasOwnProperty(item.name)) {
inventory[item.name] = 0;
}
}
return inventory;
}
/**
* Get your position in the world (Note that y is vertical).
* @param {Bot} bot - The bot to get the position for.
* @returns {Vec3} - An object with x, y, and x attributes representing the position of the bot.
* @example
* let position = world.getPosition(bot);
* let x = position.x;
**/
export function getPosition(bot) {
return bot.entity.position;
}
/**
* Get a list of all nearby mob types.
* @param {Bot} bot - The bot to get nearby mobs for.
* @returns {string[]} - A list of all nearby mobs.
* @example
* let mobs = world.getNearbyMobTypes(bot);
**/
export function getNearbyMobTypes(bot) {
let mobs = getNearbyMobs(bot, 16);
let found = [];
for (let i = 0; i < mobs.length; i++) {
if (!found.includes(mobs[i].mobType)) {
found.push(mobs[i].mobType);
}
}
return found;
}
/**
* Get a list of all nearby player names.
* @param {Bot} bot - The bot to get nearby players for.
* @returns {string[]} - A list of all nearby players.
* @example
* let players = world.getNearbyPlayerNames(bot);
**/
export function getNearbyPlayerNames(bot) {
let players = getNearbyPlayers(bot, 16);
let found = [];
for (let i = 0; i < players.length; i++) {
if (!found.includes(players[i].username) && players[i].username != bot.username) {
found.push(players[i].username);
}
} }
return found; return found;
} }