178 lines
4.7 KiB
JavaScript
178 lines
4.7 KiB
JavaScript
/**
|
|
* 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,
|
|
},
|
|
};
|
|
}
|
|
}
|