Merge pull request #103 from kolbytn/cheat-mode

Cheat mode
This commit is contained in:
Max Robinson 2024-06-03 19:14:27 -05:00 committed by GitHub
commit 6f1f79f3c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 117 additions and 45 deletions

View file

@ -9,6 +9,17 @@
"saving_memory": "You are a minecraft bot named $NAME that has been talking and playing minecraft by using commands. Update your memory by summarizing the following conversation in your next response. Store information that will help you improve as a Minecraft bot. Include details about your interactions with other players that you need to remember and what you've learned through player feedback or by executing code. Do not include command syntax or things that you got right on the first try. Be extremely brief and use as few words as possible.\nOld Memory: '$MEMORY'\nRecent conversation: \n$TO_SUMMARIZE\nSummarize your old memory and recent conversation into a new memory, and respond only with the memory text: ", "saving_memory": "You are a minecraft bot named $NAME that has been talking and playing minecraft by using commands. Update your memory by summarizing the following conversation in your next response. Store information that will help you improve as a Minecraft bot. Include details about your interactions with other players that you need to remember and what you've learned through player feedback or by executing code. Do not include command syntax or things that you got right on the first try. Be extremely brief and use as few words as possible.\nOld Memory: '$MEMORY'\nRecent conversation: \n$TO_SUMMARIZE\nSummarize your old memory and recent conversation into a new memory, and respond only with the memory text: ",
"modes": {
"self_preservation": true,
"cowardice": true,
"self_defense": true,
"hunting": true,
"item_collecting": true,
"torch_placing": true,
"idle_staring": true,
"cheat": false
},
"conversation_examples": [ "conversation_examples": [
[ [
{"role": "user", "content": "miner_32: Hey! What are you up to?"}, {"role": "user", "content": "miner_32: Hey! What are you up to?"},

View file

@ -79,9 +79,6 @@ export class Agent {
} }
async handleMessage(source, message) { async handleMessage(source, message) {
if (!!source && !!message)
await this.history.add(source, message);
const user_command_name = containsCommand(message); const user_command_name = containsCommand(message);
if (user_command_name) { if (user_command_name) {
if (!commandExists(user_command_name)) { if (!commandExists(user_command_name)) {
@ -101,6 +98,8 @@ export class Agent {
return; return;
} }
await this.history.add(source, message);
for (let i=0; i<5; i++) { for (let i=0; i<5; i++) {
let history = this.history.getHistory(); let history = this.history.getHistory();
let res = await this.prompter.promptConvo(history); let res = await this.prompter.promptConvo(history);

View file

@ -11,18 +11,11 @@ export function log(bot, message, chat=false) {
} }
async function autoLight(bot) { async function autoLight(bot) {
if (bot.modes.isOn('torch_placing') && !bot.interrupt_code) { if (world.shouldPlaceTorch(bot)) {
let nearest_torch = world.getNearestBlock(bot, 'torch', 6); try {
if (!nearest_torch) { const pos = world.getPosition(bot);
let has_torch = bot.inventory.items().find(item => item.name === 'torch'); return await placeBlock(bot, 'torch', pos.x, pos.y, pos.z, true);
const curr_block = agent.bot.blockAt(pos); } catch (err) {return false;}
if (has_torch && curr_block.name === 'air') {
try {
log(bot, `Placing torch at ${bot.entity.position}.`);
return await placeBlock(bot, 'torch', bot.entity.position.x, bot.entity.position.y, bot.entity.position.z);
} catch (err) {return true;}
}
}
} }
return false; return false;
} }
@ -455,6 +448,13 @@ export async function breakBlockAt(bot, x, y, z) {
if (x == null || y == null || z == null) throw new Error('Invalid position to break block at.'); if (x == null || y == null || z == null) throw new Error('Invalid position to break block at.');
let block = bot.blockAt(Vec3(x, y, z)); let block = bot.blockAt(Vec3(x, y, z));
if (block.name !== 'air' && block.name !== 'water' && block.name !== 'lava') { if (block.name !== 'air' && block.name !== 'water' && block.name !== 'lava') {
if (bot.modes.isOn('cheat')) {
let msg = '/setblock ' + Math.floor(x) + ' ' + Math.floor(y) + ' ' + Math.floor(z) + ' air';
bot.chat(msg);
log(bot, `Used /setblock to break block at ${x}, ${y}, ${z}.`);
return true;
}
if (bot.entity.position.distanceTo(block.position) > 4.5) { if (bot.entity.position.distanceTo(block.position) > 4.5) {
let pos = block.position; let pos = block.position;
let movements = new pf.Movements(bot); let movements = new pf.Movements(bot);
@ -482,7 +482,7 @@ export async function breakBlockAt(bot, x, y, z) {
} }
export async function placeBlock(bot, blockType, x, y, z) { export async function placeBlock(bot, blockType, x, y, z, no_cheat=false) {
/** /**
* Place the given block type at the given position. It will build off from any adjacent blocks. Will fail if there is a block in the way or nothing to build off of. * Place the given block type at the given position. It will build off from any adjacent blocks. Will fail if there is a block in the way or nothing to build off of.
* @param {MinecraftBot} bot, reference to the minecraft bot. * @param {MinecraftBot} bot, reference to the minecraft bot.
@ -490,12 +490,24 @@ export async function placeBlock(bot, blockType, x, y, z) {
* @param {number} x, the x coordinate of the block to place. * @param {number} x, the x coordinate of the block to place.
* @param {number} y, the y coordinate of the block to place. * @param {number} y, the y coordinate of the block to place.
* @param {number} z, the z coordinate of the block to place. * @param {number} z, the z coordinate of the block to place.
* @param {boolean} no_cheat, overrides cheat mode to place the block normally. Defaults to false.
* @returns {Promise<boolean>} true if the block was placed, false otherwise. * @returns {Promise<boolean>} true if the block was placed, false otherwise.
* @example * @example
* let position = world.getPosition(bot); * let position = world.getPosition(bot);
* await skills.placeBlock(bot, "oak_log", position.x + 1, position.y - 1, position.x); * await skills.placeBlock(bot, "oak_log", position.x + 1, position.y - 1, position.x);
**/ **/
console.log('placing block...') if (!mc.getBlockId(blockType)) {
log(bot, `Invalid block type: ${blockType}.`);
return false;
}
if (bot.modes.isOn('cheat') && !no_cheat) {
let msg = '/setblock ' + Math.floor(x) + ' ' + Math.floor(y) + ' ' + Math.floor(z) + ' ' + blockType;
bot.chat(msg);
log(bot, `Used /setblock to place ${blockType} at ${x}, ${y}, ${z}.`);
return true;
}
let block = bot.inventory.items().find(item => item.name === blockType); let block = bot.inventory.items().find(item => item.name === blockType);
if (!block) { if (!block) {
log(bot, `Don't have any ${blockType} to place.`); log(bot, `Don't have any ${blockType} to place.`);
@ -688,6 +700,11 @@ export async function goToPosition(bot, x, y, z, min_distance=2) {
log(bot, `Missing coordinates, given x:${x} y:${y} z:${z}`); log(bot, `Missing coordinates, given x:${x} y:${y} z:${z}`);
return false; return false;
} }
if (bot.modes.isOn('cheat')) {
bot.chat('/tp @s ' + x + ' ' + y + ' ' + z);
log(bot, `Teleported to ${x}, ${y}, ${z}.`);
return true;
}
bot.pathfinder.setMovements(new pf.Movements(bot)); bot.pathfinder.setMovements(new pf.Movements(bot));
await bot.pathfinder.goto(new pf.goals.GoalNear(x, y, z, min_distance)); await bot.pathfinder.goto(new pf.goals.GoalNear(x, y, z, min_distance));
log(bot, `You have reached at ${x}, ${y}, ${z}.`); log(bot, `You have reached at ${x}, ${y}, ${z}.`);
@ -705,6 +722,13 @@ export async function goToPlayer(bot, username, distance=3) {
* @example * @example
* await skills.goToPlayer(bot, "player"); * await skills.goToPlayer(bot, "player");
**/ **/
if (bot.modes.isOn('cheat')) {
bot.chat('/tp @s ' + username);
log(bot, `Teleported to ${username}.`);
return true;
}
bot.modes.pause('self_defense'); bot.modes.pause('self_defense');
bot.modes.pause('cowardice'); bot.modes.pause('cowardice');
let player = bot.players[username].entity let player = bot.players[username].entity
@ -759,6 +783,20 @@ export async function moveAway(bot, distance) {
let goal = new pf.goals.GoalNear(pos.x, pos.y, pos.z, distance); let goal = new pf.goals.GoalNear(pos.x, pos.y, pos.z, distance);
let inverted_goal = new pf.goals.GoalInvert(goal); let inverted_goal = new pf.goals.GoalInvert(goal);
bot.pathfinder.setMovements(new pf.Movements(bot)); bot.pathfinder.setMovements(new pf.Movements(bot));
if (bot.modes.isOn('cheat')) {
const path = await bot.pathfinder.getPathTo(move, inverted_goal, 10000);
let last_move = path.path[path.path.length-1];
console.log(last_move);
if (last_move) {
let x = Math.floor(last_move.x);
let y = Math.floor(last_move.y);
let z = Math.floor(last_move.z);
bot.chat('/tp @s ' + x + ' ' + y + ' ' + z);
return true;
}
}
await bot.pathfinder.goto(inverted_goal); await bot.pathfinder.goto(inverted_goal);
let new_pos = bot.entity.position; let new_pos = bot.entity.position;
log(bot, `Moved away from nearest entity to ${new_pos}.`); log(bot, `Moved away from nearest entity to ${new_pos}.`);

View file

@ -256,6 +256,19 @@ export async function isClearPath(bot, target) {
return path.status === 'success'; return path.status === 'success';
} }
export function shouldPlaceTorch(bot) {
if (!bot.modes.isOn('torch_placing') || bot.interrupt_code) return false;
const pos = getPosition(bot);
// TODO: check light level instead of nearby torches, block.light is broken
let nearest_torch = getNearestBlock(bot, 'torch', 6);
if (!nearest_torch) {
const block = bot.blockAt(pos);
let has_torch = bot.inventory.items().find(item => item.name === 'torch');
return has_torch && block.name === 'air';
}
return false;
}
export function getBiomeName(bot) { export function getBiomeName(bot) {
/** /**
* Get the name of the biome the bot is in. * Get the name of the biome the bot is in.

View file

@ -149,21 +149,16 @@ const modes = [
interrupts: ['followPlayer'], interrupts: ['followPlayer'],
on: true, on: true,
active: false, active: false,
cooldown: 5,
last_place: Date.now(),
update: function (agent) { update: function (agent) {
// TODO: check light level instead of nearby torches, block.light is broken if (world.shouldPlaceTorch(agent.bot)) {
const near_torch = world.getNearestBlock(agent.bot, 'torch', 6); if (Date.now() - this.last_place < this.cooldown * 1000) return;
if (!near_torch) { execute(this, agent, async () => {
let torches = agent.bot.inventory.items().filter(item => item.name === 'torch');
if (torches.length > 0) {
const torch = torches[0];
const pos = agent.bot.entity.position; const pos = agent.bot.entity.position;
const curr_block = agent.bot.blockAt(pos); await skills.placeBlock(agent.bot, 'torch', pos.x, pos.y, pos.z, true);
if (curr_block.name === 'air') { });
execute(this, agent, async () => { this.last_place = Date.now();
await skills.placeBlock(agent.bot, torch.name, pos.x, pos.y, pos.z);
});
}
}
} }
} }
}, },
@ -204,6 +199,14 @@ const modes = [
} }
} }
}, },
{
name: 'cheat',
description: 'Use cheats to instantly place blocks and teleport.',
interrupts: [],
on: false,
active: false,
update: function (agent) { /* do nothing */ }
}
]; ];
async function execute(mode, agent, func, timeout=-1) { async function execute(mode, agent, func, timeout=-1) {
@ -291,4 +294,8 @@ class ModeController {
export function initModes(agent) { export function initModes(agent) {
// the mode controller is added to the bot object so it is accessible from anywhere the bot is used // the mode controller is added to the bot object so it is accessible from anywhere the bot is used
agent.bot.modes = new ModeController(agent); agent.bot.modes = new ModeController(agent);
let modes = agent.prompter.getInitModes();
if (modes) {
agent.bot.modes.loadJson(modes);
}
} }

View file

@ -11,7 +11,7 @@ import * as mc from '../../utils/mcdata.js';
export class NPCContoller { export class NPCContoller {
constructor(agent) { constructor(agent) {
this.agent = agent; this.agent = agent;
this.data = NPCData.fromObject(agent.prompter.prompts.npc); this.data = NPCData.fromObject(agent.prompter.profile.npc);
this.temp_goals = []; this.temp_goals = [];
this.item_goal = new ItemGoal(agent, this.data); this.item_goal = new ItemGoal(agent, this.data);
this.build_goal = new BuildGoal(agent); this.build_goal = new BuildGoal(agent);

View file

@ -15,12 +15,12 @@ import { Local } from '../models/local.js';
export class Prompter { export class Prompter {
constructor(agent, fp) { constructor(agent, fp) {
this.agent = agent; this.agent = agent;
this.prompts = JSON.parse(readFileSync(fp, 'utf8')); this.profile = JSON.parse(readFileSync(fp, 'utf8'));
this.convo_examples = null; this.convo_examples = null;
this.coding_examples = null; this.coding_examples = null;
let name = this.prompts.name; let name = this.profile.name;
let chat = this.prompts.model; let chat = this.profile.model;
if (typeof chat === 'string' || chat instanceof String) { if (typeof chat === 'string' || chat instanceof String) {
chat = {model: chat}; chat = {model: chat};
if (chat.model.includes('gemini')) if (chat.model.includes('gemini'))
@ -50,7 +50,7 @@ export class Prompter {
else else
throw new Error('Unknown API:', api); throw new Error('Unknown API:', api);
let embedding = this.prompts.embedding; let embedding = this.profile.embedding;
if (embedding === undefined) { if (embedding === undefined) {
if (chat.api !== 'ollama') if (chat.api !== 'ollama')
embedding = {api: chat.api}; embedding = {api: chat.api};
@ -76,7 +76,7 @@ export class Prompter {
} }
mkdirSync(`./bots/${name}`, { recursive: true }); mkdirSync(`./bots/${name}`, { recursive: true });
writeFileSync(`./bots/${name}/last_profile.json`, JSON.stringify(this.prompts, null, 4), (err) => { writeFileSync(`./bots/${name}/last_profile.json`, JSON.stringify(this.profile, null, 4), (err) => {
if (err) { if (err) {
throw err; throw err;
} }
@ -85,15 +85,19 @@ export class Prompter {
} }
getName() { getName() {
return this.prompts.name; return this.profile.name;
}
getInitModes() {
return this.profile.modes;
} }
async initExamples() { async initExamples() {
console.log('Loading examples...') console.log('Loading examples...')
this.convo_examples = new Examples(this.embedding_model); this.convo_examples = new Examples(this.embedding_model);
await this.convo_examples.load(this.prompts.conversation_examples); await this.convo_examples.load(this.profile.conversation_examples);
this.coding_examples = new Examples(this.embedding_model); this.coding_examples = new Examples(this.embedding_model);
await this.coding_examples.load(this.prompts.coding_examples); await this.coding_examples.load(this.profile.coding_examples);
console.log('Examples loaded.'); console.log('Examples loaded.');
} }
@ -149,25 +153,25 @@ export class Prompter {
} }
async promptConvo(messages) { async promptConvo(messages) {
let prompt = this.prompts.conversing; let prompt = this.profile.conversing;
prompt = await this.replaceStrings(prompt, messages, this.convo_examples); prompt = await this.replaceStrings(prompt, messages, this.convo_examples);
return await this.chat_model.sendRequest(messages, prompt); return await this.chat_model.sendRequest(messages, prompt);
} }
async promptCoding(messages) { async promptCoding(messages) {
let prompt = this.prompts.coding; let prompt = this.profile.coding;
prompt = await this.replaceStrings(prompt, messages, this.coding_examples); prompt = await this.replaceStrings(prompt, messages, this.coding_examples);
return await this.chat_model.sendRequest(messages, prompt); return await this.chat_model.sendRequest(messages, prompt);
} }
async promptMemSaving(prev_mem, to_summarize) { async promptMemSaving(prev_mem, to_summarize) {
let prompt = this.prompts.saving_memory; let prompt = this.profile.saving_memory;
prompt = await this.replaceStrings(prompt, null, null, prev_mem, to_summarize); prompt = await this.replaceStrings(prompt, null, null, prev_mem, to_summarize);
return await this.chat_model.sendRequest([], prompt); return await this.chat_model.sendRequest([], prompt);
} }
async promptGoalSetting(messages, last_goals) { async promptGoalSetting(messages, last_goals) {
let system_message = this.prompts.goal_setting; let system_message = this.profile.goal_setting;
system_message = await this.replaceStrings(system_message, messages); system_message = await this.replaceStrings(system_message, messages);
let user_message = 'Use the below info to determine what goal to target next\n\n'; let user_message = 'Use the below info to determine what goal to target next\n\n';

View file

@ -20,7 +20,7 @@ export class Claude {
let res = null; let res = null;
try { try {
console.log('Awaiting anthropic api response...') console.log('Awaiting anthropic api response...')
console.log('Messages:', messages); // console.log('Messages:', messages);
const resp = await this.anthropic.messages.create({ const resp = await this.anthropic.messages.create({
model: this.model_name || "claude-3-sonnet-20240229", model: this.model_name || "claude-3-sonnet-20240229",
system: systemMessage, system: systemMessage,

View file

@ -24,7 +24,7 @@ export class GPT {
let res = null; let res = null;
try { try {
console.log('Awaiting openai api response...') console.log('Awaiting openai api response...')
console.log('Messages:', messages); // console.log('Messages:', messages);
let completion = await this.openai.chat.completions.create({ let completion = await this.openai.chat.completions.create({
model: this.model_name || "gpt-3.5-turbo", model: this.model_name || "gpt-3.5-turbo",
messages: messages, messages: messages,