/** * 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, }, }; } }