local Direction = require("lib.direction") local TurtleController = require("controller.turtle_controller") local AudioController = require("controller.audio_controller") ---@alias ExcavationControllerConfig {slice_length: number, slice_height: number, center_slice_width: number, slice_width: number, slice_padding: number, slices_left: number, slices_right: number} ---@class ExcavationController ---@field controller TurtleController ---@field audio AudioController ---@field config ExcavationControllerConfig local ExcavationController = {} ExcavationController.__index = ExcavationController ---@return ExcavationController function ExcavationController:Create() local t = {} setmetatable(t, ExcavationController) ----------------------------------------------------------------------------------------------- -- Fields ----------------------------------------------------------------------------------------------- t.controller = TurtleController:Create() t.audio = AudioController:Create() t.config = { slice_length = 0, slice_height = 0, center_slice_width = 0, slice_width = 0, slice_padding = 0, slices_left = 0, slices_right = 0, } return t end ----------------------------------------------------------------------------------------------- -- Behavior Methods ----------------------------------------------------------------------------------------------- ---Excavates a single 1x1/1x2 tunnel of configured length. ---Will leave the turtle wherever it ends up. ---Only unstocks/refuels if required. ---@param mine_above boolean ---@param mine_below boolean function ExcavationController:Excavate_1x1or2or3xL(mine_above, mine_below) self.controller:EnableMiningForward() if mine_above then self.controller:EnableMiningAbove() end if mine_below then self.controller:EnableMiningBelow() end self.controller:MoveForward(self.config.slice_length) self.controller:DisableMiningForward() self.controller:DisableMiningAbove() self.controller:DisableMiningBelow() end ---Excavates a single 1xH partial slice of configured length. ---Will leave the turtle in its starting position. ---Only unstocks/refuels if required. function ExcavationController:Excavate_1xHxL() self.controller:StorePosition() -- We can mine 2 blocks heigh in a single go local number_of_1x3xL = math.floor(self.config.slice_height / 3) -- Prepare for mining the blocks below the turtle if number_of_1x3xL > 0 then self.controller:EnableMiningAbove() self.controller:MoveVertical(1) self.controller:DisableMiningAbove() end -- Mine 1x3xL for i = 1,number_of_1x3xL do self:Excavate_1x1or2or3xL(true, true) self.controller:TurnRelative(2) -- Don't move upwards in the last iteration so we can decide -- how much to move depending on how much we still have to excavate if i == number_of_1x3xL then break end self.controller:EnableMiningAbove() self.controller:MoveVertical(4) self.controller:MoveVertical(-1) self.controller:DisableMiningAbove() end -- Mine remaining 1x2xL or 1x1xL if self.config.slice_height % 3 == 2 then self.controller:EnableMiningAbove() self.controller:MoveVertical(3) self.controller:MoveVertical(-1) self.controller:DisableMiningAbove() self:Excavate_1x1or2or3xL(true, false) elseif self.config.slice_height % 3 == 1 then self.controller:EnableMiningAbove() self.controller:MoveVertical(3) self.controller:MoveVertical(-1) self.controller:DisableMiningAbove() self:Excavate_1x1or2or3xL(false, false) end self.controller:EnableMiningBelow() self.controller:MoveBack() self.controller:DisableMiningBelow() end ---Excavates a single WxH slice of configured length (EAST to WEST). ---Will leave the turtle in its starting position. ---Only unstocks/refuels if required. ---@param center_slice boolean | nil function ExcavationController:Excavate_WxHxL(center_slice) self.controller:StorePosition() center_slice = center_slice or false local width = center_slice and self.config.center_slice_width or self.config.slice_width for i = 1,width do self:Excavate_1xHxL() if i == width then break end self.controller:TurnToDirection(Direction.WEST) self.controller:EnableMiningForward() self.controller:EnableMiningAbove() self.controller:MoveForward(1) self.controller:DisableMiningForward() self.controller:DisableMiningAbove() self.controller:TurnToDirection(Direction.NORTH) end self.controller:MoveBack() end ---Excavates all slices. ---Will leave the turtle refueled and unstocked in its 0x0x0 position. function ExcavationController:Excavate() -- Enter excavation area self.controller:EnableMiningForward() self.controller:EnableMiningAbove() self.controller:MoveForward(1) self.controller:DisableMiningForward() self.controller:DisableMiningAbove() -- Excavate center slice (start from right/back/bottom) -- Uneven center widths will be centered, even center widths will have a block more on the right print("Excavating center slice...") local center_width_right = math.floor(self.config.center_slice_width / 2) self.controller:StorePosition() self.controller:TurnToDirection(Direction.EAST) self.controller:EnableMiningForward() self.controller:EnableMiningAbove() self.controller:MoveForward(center_width_right) self.controller:DisableMiningForward() self.controller:DisableMiningAbove() self.controller:TurnToDirection(Direction.NORTH) self:Excavate_WxHxL(true) self.controller:MoveBack() -- (0, 0, 1) if self.config.slices_right > 0 then -- Move to right slices starting location local padded_width_right = self.config.slices_right * (self.config.slice_width + self.config.slice_padding) self.controller:StorePosition() self.controller:TurnToDirection(Direction.EAST) self.controller:EnableMiningForward() self.controller:EnableMiningAbove() self.controller:MoveForward(center_width_right + padded_width_right) self.controller:DisableMiningForward() self.controller:DisableMiningAbove() self.controller:TurnToDirection(Direction.NORTH) -- Excavate right slices print("Excavating right slices") for i = 1,self.config.slices_right do self:Excavate_WxHxL() if i == self.config.slices_right then break end -- Move to the next slice self.controller:TurnToDirection(Direction.WEST) self.controller:EnableMiningForward() self.controller:EnableMiningAbove() self.controller:MoveForward(self.config.slice_width + self.config.slice_padding) self.controller:DisableMiningForward() self.controller:DisableMiningAbove() self.controller:TurnToDirection(Direction.NORTH) end self.controller:MoveBack() -- (0, 0, 1) end if self.config.slices_left > 0 then -- Move to left slices starting location local center_width_left = self.config.center_slice_width - center_width_right self.controller:StorePosition() self.controller:TurnToDirection(Direction.WEST) self.controller:EnableMiningForward() self.controller:EnableMiningAbove() self.controller:MoveForward(center_width_left) self.controller:DisableMiningForward() self.controller:DisableMiningAbove() self.controller:TurnToDirection(Direction.NORTH) -- Excavate left slices print("Excavate left slices") for i = 1,self.config.slices_left do -- Move to the next slice self.controller:TurnToDirection(Direction.WEST) self.controller:EnableMiningForward() self.controller:EnableMiningAbove() self.controller:MoveForward(self.config.slice_padding) self.controller:DisableMiningForward() self.controller:DisableMiningAbove() self.controller:TurnToDirection(Direction.NORTH) self:Excavate_WxHxL() if i == self.config.slices_left then break end self.controller:TurnToDirection(Direction.WEST) self.controller:EnableMiningForward() self.controller:EnableMiningAbove() self.controller:MoveForward(self.config.slice_width) self.controller:DisableMiningForward() self.controller:DisableMiningAbove() self.controller:TurnToDirection(Direction.NORTH) end self.controller:MoveBack() -- (0, 0, 1) end -- Finish up self.controller:MoveToPosition(0, 0, 0, self.controller.config.storage_direction) self.controller:DropInventory() self.controller:TurnToDirection(Direction.NORTH) self.audio:StopPlaying() end ----------------------------------------------------------------------------------------------- -- Management Methods ----------------------------------------------------------------------------------------------- function ExcavationController:Configure() local config_complete = false while not config_complete do print("How long should each slice be?") self.config.slice_length = tonumber(io.read()) or 3 print("How high should each slice be?") self.config.slice_height = tonumber(io.read()) or 3 print("How wide should the center slice be?") self.config.center_slice_width = tonumber(io.read()) or 1 print("How wide should the other slices be?") self.config.slice_width = tonumber(io.read()) or 1 print("How many blocks should remain between each slice?") self.config.slice_padding = tonumber(io.read()) or 0 print("How many slices should the turtle mine to its right?") self.config.slices_right = tonumber(io.read()) or 1 print("How many slices should the turtle mine to its left?") self.config.slices_left = tonumber(io.read()) or 1 local padded_width_left = self.config.slices_left * (self.config.slice_width + self.config.slice_padding) local padded_width_right = self.config.slices_right * (self.config.slice_width + self.config.slice_padding) local padded_width = padded_width_left + padded_width_right + self.config.center_slice_width local mined_width_left = self.config.slices_left * self.config.slice_width local mined_width_right = self.config.slices_right * self.config.slice_width local mined_width = mined_width_left + mined_width_right + self.config.center_slice_width print("Configuration complete!") print(("Mining area spans %d x %d x %d (width x height x forward), totalling %d blocks."):format( padded_width, self.config.slice_height, self.config.slice_length, padded_width * self.config.slice_height * self.config.slice_length )) print(("Turtle will mine %d block wide slices (%d wide in the center) with %d blocks of padding, totalling %d mined blocks."):format( self.config.slice_width, self.config.center_slice_width, self.config.slice_padding, mined_width * self.config.slice_height * self.config.slice_length )) print("Do you want to accept the configuration (enter 1, otherwise 0)?") config_complete = tonumber(io.read()) == 1 end end ----------------------------------------------------------------------------------------------- -- Main Method ----------------------------------------------------------------------------------------------- function ExcavationController:Run() self.controller:Configure() self:Configure() -- Consume our starting fuel and refuel to the full amount turtle.select(1) turtle.refuel() self.controller:RefuelIfEmpty() parallel.waitForAll(function() self:Excavate() end, self.audio:PlayAudioFactory("bangarang")) end return ExcavationController