feat: Implement ExploringState class for turtle state machine to autonomously explore and discover the world map

This commit is contained in:
MayaTheShy
2026-02-20 01:45:39 -05:00
parent 1b08777f88
commit fa98e86055

View File

@@ -0,0 +1,177 @@
/**
* ExploringState - Autonomous exploration to discover the world map
* Similar to mining but focused on discovering blocks rather than mining them
*/
import { BaseState } from './BaseState.js';
export class ExploringState extends BaseState {
constructor(turtle, data = {}) {
super(turtle, data);
this.maxDistance = data.maxDistance || 200;
this.minFuel = data.minFuel || 500;
this.blocksDiscovered = 0;
this.stuckCounter = 0;
this.visitedPositions = new Set();
}
get name() {
return 'exploring';
}
get description() {
return `Exploring - ${this.blocksDiscovered} blocks discovered`;
}
async *act() {
console.log(`[${this.turtle.id}] Starting exploration`);
while (!this.cancelled) {
// Safety checks
const fuel = await this.checkFuel();
if (fuel !== 'unlimited' && fuel < this.minFuel) {
const refueled = await this.tryRefuel();
if (!refueled) {
this.turtle.setState('goHome', { reason: 'low_fuel' });
return;
}
}
// Check distance
if (this._isTooFar()) {
this.turtle.setState('goHome', { reason: 'too_far' });
return;
}
// Check inventory
const isFull = await this.isInventoryFull();
if (isFull) {
this.turtle.setState('goHome', { reason: 'inventory_full', returnState: 'exploring' });
return;
}
// Mark position
const pos = this.turtle.position;
if (pos) {
this.visitedPositions.add(`${pos.x},${pos.y},${pos.z}`);
}
// Comprehensive scan
const scanResult = await this.scanSurroundings();
if (scanResult) {
this.blocksDiscovered += Object.keys(scanResult).length;
}
// Mine any valuable ores we find
yield* this._checkAndMineOres();
// Exploration movement
yield* this._exploreStep();
yield;
await this._sleep(300);
}
}
async *_checkAndMineOres() {
const valuableOres = new Set([
'minecraft:diamond_ore', 'minecraft:emerald_ore',
'minecraft:deepslate_diamond_ore', 'minecraft:deepslate_emerald_ore',
]);
const directions = [
{ check: 'turtle.inspect()', dig: 'turtle.dig()' },
{ check: 'turtle.inspectUp()', dig: 'turtle.digUp()' },
{ check: 'turtle.inspectDown()', dig: 'turtle.digDown()' },
];
for (const { check, dig } of directions) {
if (this.cancelled) return;
const result = await this.exec(`
local hasBlock, data = ${check}
if hasBlock then return {name = data.name} end
return nil
`);
if (result && valuableOres.has(result.name)) {
await this.exec(dig);
this.turtle.emit('blockMined', { blockType: result.name });
yield;
}
}
}
async *_exploreStep() {
const pos = this.turtle.position;
if (!pos) return;
// Favor horizontal movement heavily (85%)
const r = Math.random() * 100;
if (r < 85) {
// Try to find unvisited direction
let moved = false;
for (let i = 0; i < 4; i++) {
const fwdPos = this.turtle.getBlockPositionInDirection('forward');
if (fwdPos && !this.visitedPositions.has(`${fwdPos.x},${fwdPos.y},${fwdPos.z}`)) {
const canMove = await this.exec('local h = turtle.inspect(); return not h');
if (canMove) {
await this.moveForward(false);
moved = true;
this.stuckCounter = 0;
break;
} else {
// Block in the way - dig through if exploring
const success = await this.moveForward(true);
if (success) {
moved = true;
this.stuckCounter = 0;
break;
}
}
}
await this.exec('turtle.turnRight()');
this.turtle.facing = (this.turtle.facing + 1) % 4;
}
if (!moved) {
this.stuckCounter++;
const success = await this.moveForward(true);
if (!success) {
await this.exec('turtle.turnRight()');
this.turtle.facing = (this.turtle.facing + 1) % 4;
}
}
} else if (r < 93 && pos.y > 10) {
await this.moveDown(true);
} else {
await this.moveUp(true);
}
if (this.stuckCounter > 6) {
await this.moveUp(true);
this.stuckCounter = 0;
}
yield;
}
_isTooFar() {
const pos = this.turtle.position;
const home = this.turtle.homePosition;
if (!pos || !home) return false;
const dist = Math.abs(pos.x - home.x) + Math.abs(pos.y - home.y) + Math.abs(pos.z - home.z);
return dist > this.maxDistance;
}
getRecoveryData() {
return {
...super.getRecoveryData(),
data: {
maxDistance: this.maxDistance,
minFuel: this.minFuel,
},
};
}
}