/** * MiningState - Autonomous mining with intelligent exploration * Mines ores, explores caves, manages inventory */ import { BaseState } from './BaseState.js'; const VALUABLE_BLOCKS = new Set([ 'minecraft:coal_ore', 'minecraft:iron_ore', 'minecraft:gold_ore', 'minecraft:diamond_ore', 'minecraft:emerald_ore', 'minecraft:redstone_ore', 'minecraft:lapis_ore', 'minecraft:copper_ore', 'minecraft:deepslate_coal_ore', 'minecraft:deepslate_iron_ore', 'minecraft:deepslate_gold_ore', 'minecraft:deepslate_diamond_ore', 'minecraft:deepslate_emerald_ore', 'minecraft:deepslate_redstone_ore', 'minecraft:deepslate_lapis_ore', 'minecraft:deepslate_copper_ore', 'minecraft:nether_gold_ore', 'minecraft:nether_quartz_ore', 'minecraft:ancient_debris', ]); export class MiningState extends BaseState { constructor(turtle, data = {}) { super(turtle, data); this.maxDistance = data.maxDistance || 200; this.minFuel = data.minFuel || 500; this.blocksMined = 0; this.oresFound = 0; this.stuckCounter = 0; this.visitedPositions = new Set(); this.miningArea = data.miningArea || null; // Optional bounded area } get name() { return 'mining'; } get description() { return `Mining - ${this.blocksMined} mined, ${this.oresFound} ores found`; } async *act() { console.log(`[${this.turtle.id}] Starting mining operation`); while (!this.cancelled) { try { // Safety checks const fuel = await this.checkFuel(); if (fuel !== 'unlimited' && fuel < this.minFuel) { console.log(`[${this.turtle.id}] Low fuel (${fuel}), attempting refuel`); const refueled = await this.tryRefuel(); if (!refueled) { console.log(`[${this.turtle.id}] Cannot refuel, going home`); this.turtle.setState('goHome', { reason: 'low_fuel' }); return; } } // Check inventory const isFull = await this.isInventoryFull(); if (isFull) { console.log(`[${this.turtle.id}] Inventory full, going home to dump`); this.turtle.setState('goHome', { reason: 'inventory_full', returnState: 'mining', returnData: this.data }); return; } // Check distance from home if (this._isTooFar()) { console.log(`[${this.turtle.id}] Too far from home, returning`); this.turtle.setState('goHome', { reason: 'too_far' }); return; } // Mark current position as visited const pos = this.turtle.position; if (pos) { this.visitedPositions.add(`${pos.x},${pos.y},${pos.z}`); } // Scan surroundings for ores const scanResult = await this.scanSurroundings(); // Mine any ores found in surroundings yield* this._mineAdjacentOres(); // Explore step - try to find new areas yield* this._exploreStep(); } catch (error) { const isTimeout = error.message?.includes('timed out'); if (isTimeout) { console.warn(`[${this.turtle.id}] Mining exec timeout, will retry next iteration`); await this._sleep(3000); } else { throw error; } } yield; await this._sleep(200); } } /** * Mine ores in adjacent positions */ async *_mineAdjacentOres() { // Check all 6 directions for ores const directions = [ { check: 'turtle.inspect()', dig: 'turtle.dig()', dir: 'forward' }, { check: 'turtle.inspectUp()', dig: 'turtle.digUp()', dir: 'up' }, { check: 'turtle.inspectDown()', dig: 'turtle.digDown()', dir: 'down' }, ]; for (const { check, dig, dir } of directions) { if (this.cancelled) return; const result = await this.exec(` local hasBlock, data = ${check} if hasBlock then return {name = data.name, metadata = data.metadata or 0} end return nil `); if (result && VALUABLE_BLOCKS.has(result.name)) { console.log(`[${this.turtle.id}] Found ore: ${result.name} (${dir})`); await this.exec(dig); this.blocksMined++; this.oresFound++; // Report mined block to server this.turtle.emit('blockMined', { blockType: result.name, direction: dir }); yield; } } } /** * Explore step - move to unvisited positions, favor horizontal movement */ async *_exploreStep() { const pos = this.turtle.position; if (!pos) return; const r = Math.random() * 100; // 75%: horizontal exploration if (r < 75) { // Try to find an unvisited forward direction let moved = false; // Try current facing first const forwardPos = this.turtle.getBlockPositionInDirection('forward'); if (forwardPos && !this.visitedPositions.has(`${forwardPos.x},${forwardPos.y},${forwardPos.z}`)) { const success = await this.moveForward(true); if (success) { this.stuckCounter = 0; moved = true; } } // If forward is visited or blocked, try other directions if (!moved) { for (let i = 0; i < 4; i++) { await this.exec('turtle.turnRight()'); this.turtle.facing = (this.turtle.facing + 1) % 4; const newForwardPos = this.turtle.getBlockPositionInDirection('forward'); if (newForwardPos && !this.visitedPositions.has(`${newForwardPos.x},${newForwardPos.y},${newForwardPos.z}`)) { const success = await this.moveForward(true); if (success) { this.stuckCounter = 0; moved = true; break; } } } } if (!moved) { // All directions visited, just move forward const success = await this.moveForward(true); if (!success) { this.stuckCounter++; await this.exec('turtle.turnRight()'); this.turtle.facing = (this.turtle.facing + 1) % 4; } } // 15%: go down (if not too deep) } else if (r < 90 && pos.y > -50) { const downPos = { x: pos.x, y: pos.y - 1, z: pos.z }; if (!this.visitedPositions.has(`${downPos.x},${downPos.y},${downPos.z}`)) { await this.moveDown(true); } // 10%: go up } else { const upPos = { x: pos.x, y: pos.y + 1, z: pos.z }; if (!this.visitedPositions.has(`${upPos.x},${upPos.y},${upPos.z}`)) { await this.moveUp(true); } } // Handle being stuck if (this.stuckCounter > 5) { console.log(`[${this.turtle.id}] Stuck! Trying to escape`); 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, miningArea: this.miningArea, }, }; } }