1
This commit is contained in:
2025-10-07 14:52:17 +02:00
parent b71267bf42
commit a5b159fca4
10 changed files with 1169 additions and 1178 deletions

9
.luarc.json Normal file
View File

@ -0,0 +1,9 @@
{
"diagnostics.globals": [
"printError",
"turtle",
"shell",
"peripheral",
"parallel"
]
}

View File

@ -1,5 +1,4 @@
local dfpwm = require("cc.audio.dfpwm") local dfpwm = require("cc.audio.dfpwm")
local Direction = require("lib.direction")
---@alias AudioControllerConfig {buffer_length_seconds: number} ---@alias AudioControllerConfig {buffer_length_seconds: number}
@ -9,88 +8,80 @@ local Direction = require("lib.direction")
local AudioController = {} local AudioController = {}
AudioController.__index = AudioController AudioController.__index = AudioController
---@return AudioController ---@return AudioController
function AudioController:Create() function AudioController:Create()
local t = {} local t = {}
setmetatable(t, AudioController) setmetatable(t, AudioController)
-----------------------------------------------------------------------------------------------
-- Fields
-----------------------------------------------------------------------------------------------
t.config = {
buffer_length_seconds = 1,
}
t.play = false
----------------------------------------------------------------------------------------------- return t
-- Fields
-----------------------------------------------------------------------------------------------
t.config = {
buffer_length_seconds = 1
}
t.play = false
return t
end end
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
-- Audio Methods -- Audio Methods
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
function AudioController:PlayAudio(filename) function AudioController:PlayAudio(filename)
self.play = true self.play = true
local decoder = dfpwm.make_decoder() local decoder = dfpwm.make_decoder()
while self.play do while self.play do
for chunk in io.lines(("audio/%s.dfpwm"):format(filename), self.config.buffer_length_seconds * 1024) do for chunk in io.lines(("audio/%s.dfpwm"):format(filename), self.config.buffer_length_seconds * 1024) do
if not self.play then if not self.play then
break break
end end
local buffer = decoder(chunk) local buffer = decoder(chunk)
while not self:GetSpeaker().playAudio(buffer) do while not self:GetSpeaker().playAudio(buffer) do
---@diagnostic disable-next-line: undefined-field if not self.play then
os.pullEvent("speaker_audio_empty") break
end end
end
end ---@diagnostic disable-next-line: undefined-field
os.pullEvent("speaker_audio_empty")
end
end
end
end end
function AudioController:PlayAudioFactory(filename) function AudioController:PlayAudioFactory(filename)
local function play() local function play()
self:PlayAudio(filename) self:PlayAudio(filename)
end end
return play return play
end end
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
-- Management Methods -- Management Methods
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
---@return table | nil ---@return table | nil
function AudioController:GetSpeaker() function AudioController:GetSpeaker()
return peripheral.find("speaker") return peripheral.find("speaker")
end end
function AudioController:StopPlaying() function AudioController:StopPlaying()
self.play = false self.play = false
self:GetSpeaker().stop() self:GetSpeaker().stop()
end end
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
-- Main Method -- Main Method
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
function AudioController:Run() function AudioController:Run()
-- self:Configure() -- self:Configure()
self:PlayAudio("bangarang") self:PlayAudio("bangarang")
end end
return AudioController return AudioController

View File

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

View File

@ -6,206 +6,215 @@ local TurtleController = require("controller.turtle_controller")
local TestingController = {} local TestingController = {}
TestingController.__index = TestingController TestingController.__index = TestingController
---@return TestingController ---@return TestingController
function TestingController:Create() function TestingController:Create()
local t = {} local t = {}
setmetatable(t, TestingController) setmetatable(t, TestingController)
t.controller = TurtleController:Create() t.controller = TurtleController:Create()
return t return t
end end
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
-- Behavior Methods -- Behavior Methods
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
function TestingController:TestRelativeMovementWithRelativeRotation() function TestingController:TestRelativeMovementWithRelativeRotation()
print("Testing relative movement with relative rotation...") print("Testing relative movement with relative rotation...")
-- N: BackCenter (Center) -- N: BackCenter (Center)
self.controller:MoveForward(3) self.controller:MoveForward(3)
self.controller:TurnRelative(1) -- E: FrontCenter (Center) self.controller:TurnRelative(1) -- E: FrontCenter (Center)
self.controller:MoveForward(3) self.controller:MoveForward(3)
self.controller:TurnRelative(2) -- W: FrontRight (Center) self.controller:TurnRelative(2) -- W: FrontRight (Center)
self.controller:MoveVertical(3) self.controller:MoveVertical(3)
self.controller:TurnRelative(8) -- W: FrontRight (Top) self.controller:TurnRelative(8) -- W: FrontRight (Top)
self.controller:MoveForward(3) self.controller:MoveForward(3)
self.controller:TurnRelative(3) -- S: FrontCenter (Top) self.controller:TurnRelative(3) -- S: FrontCenter (Top)
self.controller:MoveForward(3) self.controller:MoveForward(3)
self.controller:TurnRelative(9) -- W: BackCenter (Top) self.controller:TurnRelative(9) -- W: BackCenter (Top)
self.controller:MoveVertical(-3) self.controller:MoveVertical(-3)
self.controller:TurnRelative(1) -- N: BackCenter (Center) self.controller:TurnRelative(1) -- N: BackCenter (Center)
print("The turtle should be in its original location.") print("The turtle should be in its original location.")
print(("The turtle has internal position (%d, %d, %d) and internal direction %d"):format( print(
self.controller.position.x, self.controller.position.y, self.controller.position.z, self.controller.position.dir ("The turtle has internal position (%d, %d, %d) and internal direction %d"):format(
)) self.controller.position.x,
print("Press Enter to continue") self.controller.position.y,
_ = io.read() self.controller.position.z,
self.controller.position.dir
)
)
print("Press Enter to continue")
_ = io.read()
end end
function TestingController:TestRelativeMovementWithAbsoluteRotation() function TestingController:TestRelativeMovementWithAbsoluteRotation()
print("Testing relative movement with absolute rotation...") print("Testing relative movement with absolute rotation...")
-- N: BackCenter (Center) -- N: BackCenter (Center)
self.controller:MoveForward(3) self.controller:MoveForward(3)
self.controller:TurnToDirection(Direction.EAST) -- E: FrontCenter (Center) self.controller:TurnToDirection(Direction.EAST) -- E: FrontCenter (Center)
self.controller:MoveForward(3) self.controller:MoveForward(3)
self.controller:TurnToDirection(Direction.WEST) -- W: FrontRight (Center) self.controller:TurnToDirection(Direction.WEST) -- W: FrontRight (Center)
self.controller:MoveVertical(3) self.controller:MoveVertical(3)
self.controller:TurnToDirection(Direction.WEST) -- W: FrontRight (Top) self.controller:TurnToDirection(Direction.WEST) -- W: FrontRight (Top)
self.controller:MoveForward(3) self.controller:MoveForward(3)
self.controller:TurnToDirection(Direction.SOUTH) -- S: FrontCenter (Top) self.controller:TurnToDirection(Direction.SOUTH) -- S: FrontCenter (Top)
self.controller:MoveForward(3) self.controller:MoveForward(3)
self.controller:TurnToDirection(Direction.WEST) -- W: BackCenter (Top) self.controller:TurnToDirection(Direction.WEST) -- W: BackCenter (Top)
self.controller:MoveVertical(-3) self.controller:MoveVertical(-3)
self.controller:TurnToDirection(Direction.NORTH) -- N: BackCenter (Center) self.controller:TurnToDirection(Direction.NORTH) -- N: BackCenter (Center)
print("The turtle should be in its original location.") print("The turtle should be in its original location.")
print(("The turtle has internal position (%d, %d, %d) and internal direction %d"):format( print(
self.controller.position.x, self.controller.position.y, self.controller.position.z, self.controller.position.dir ("The turtle has internal position (%d, %d, %d) and internal direction %d"):format(
)) self.controller.position.x,
print("Press Enter to continue") self.controller.position.y,
_ = io.read() self.controller.position.z,
self.controller.position.dir
)
)
print("Press Enter to continue")
_ = io.read()
end end
function TestingController:TestAbsoluteMovementWithoutStack() function TestingController:TestAbsoluteMovementWithoutStack()
print("Testing absolute movement without stack...") print("Testing absolute movement without stack...")
-- N: BackCenter (Center) -- N: BackCenter (Center)
self.controller:MoveToPosition(0, 0, 3, Direction.EAST) -- E: FrontCenter (Center) self.controller:MoveToPosition(0, 0, 3, Direction.EAST) -- E: FrontCenter (Center)
self.controller:MoveToPosition(3, 0, 3, Direction.WEST) -- W: FrontRight (Center) self.controller:MoveToPosition(3, 0, 3, Direction.WEST) -- W: FrontRight (Center)
self.controller:MoveToPosition(3, 3, 3, Direction.WEST) -- W: FrontRight (Top) self.controller:MoveToPosition(3, 3, 3, Direction.WEST) -- W: FrontRight (Top)
self.controller:MoveToPosition(0, 3, 3, Direction.SOUTH) -- S: FrontCenter (Top) self.controller:MoveToPosition(0, 3, 3, Direction.SOUTH) -- S: FrontCenter (Top)
self.controller:MoveToPosition(0, 3, 0, Direction.WEST) -- W: BackCenter (Top) self.controller:MoveToPosition(0, 3, 0, Direction.WEST) -- W: BackCenter (Top)
self.controller:MoveToPosition(0, 0, 0, Direction.NORTH) -- N: BackCenter (Center) self.controller:MoveToPosition(0, 0, 0, Direction.NORTH) -- N: BackCenter (Center)
print("The turtle should be in its original location.") print("The turtle should be in its original location.")
print(("The turtle has internal position (%d, %d, %d) and internal direction %d"):format( print(
self.controller.position.x, self.controller.position.y, self.controller.position.z, self.controller.position.dir ("The turtle has internal position (%d, %d, %d) and internal direction %d"):format(
)) self.controller.position.x,
print("Press Enter to continue") self.controller.position.y,
_ = io.read() self.controller.position.z,
self.controller.position.dir
)
)
print("Press Enter to continue")
_ = io.read()
end end
function TestingController:TestAbsoluteMovementWithStack() function TestingController:TestAbsoluteMovementWithStack()
print("Testing absolute movement with stack...") print("Testing absolute movement with stack...")
-- N: BotCenter (Center) -- N: BotCenter (Center)
self.controller:MoveToPosition(0, 0, 3, Direction.EAST) -- E: TopCenter (Center) self.controller:MoveToPosition(0, 0, 3, Direction.EAST) -- E: TopCenter (Center)
self.controller:MoveBack() -- N: BotCenter (Center) self.controller:MoveBack() -- N: BotCenter (Center)
self.controller:MoveToPosition(3, 3, 3, Direction.WEST) -- W: TopRight (Top) self.controller:MoveToPosition(3, 3, 3, Direction.WEST) -- W: TopRight (Top)
self.controller:MoveBack() -- N: BotCenter (Center) self.controller:MoveBack() -- N: BotCenter (Center)
print("The turtle should be in its original location.") print("The turtle should be in its original location.")
print(("The turtle has internal position (%d, %d, %d) and internal direction %d"):format( print(
self.controller.position.x, self.controller.position.y, self.controller.position.z, self.controller.position.dir ("The turtle has internal position (%d, %d, %d) and internal direction %d"):format(
)) self.controller.position.x,
print("Press Enter to continue") self.controller.position.y,
_ = io.read() self.controller.position.z,
self.controller.position.dir
)
)
print("Press Enter to continue")
_ = io.read()
end end
function TestingController:TestUnstocking() function TestingController:TestUnstocking()
print("Testing inventory unloading...") print("Testing inventory unloading...")
self.controller:MoveToPosition(3, 0, 3, Direction.WEST) self.controller:MoveToPosition(3, 0, 3, Direction.WEST)
print("Please fill the entire inventory with items.") print("Please fill the entire inventory with items.")
print("Press Enter to continue") print("Press Enter to continue")
_ = io.read() _ = io.read()
self.controller:UnstockIfFull(true) self.controller:UnstockIfFull(true)
print("Press Enter to continue") print("Press Enter to continue")
_ = io.read() _ = io.read()
self.controller:MoveBack() self.controller:MoveBack()
print("The turtle should be in its original location.") print("The turtle should be in its original location.")
print("Press Enter to continue") print("Press Enter to continue")
_ = io.read() _ = io.read()
end end
function TestingController:TestRefueling() function TestingController:TestRefueling()
print("Testing refueling...") print("Testing refueling...")
self.controller:MoveToPosition(3, 0, 3, Direction.WEST) self.controller:MoveToPosition(3, 0, 3, Direction.WEST)
print("Please fill multiple inventory slots with items.") print("Please fill multiple inventory slots with items.")
print("Press Enter to continue") print("Press Enter to continue")
_ = io.read() _ = io.read()
self.controller:RefuelIfEmpty(true) self.controller:RefuelIfEmpty(true)
print("Press Enter to continue") print("Press Enter to continue")
_ = io.read() _ = io.read()
self.controller:MoveBack() self.controller:MoveBack()
print("The turtle should be in its original location.") print("The turtle should be in its original location.")
print("Press Enter to continue") print("Press Enter to continue")
_ = io.read() _ = io.read()
end end
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
-- Main Method -- Main Method
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
function TestingController:Run() function TestingController:Run()
self.controller:Configure() self.controller:Configure()
self.controller:DisableMiningForward() self.controller:DisableMiningForward()
self.controller:DisableMiningAbove() self.controller:DisableMiningAbove()
self.controller:DisableMiningBelow() self.controller:DisableMiningBelow()
print("There are multiple tests available:") print("There are multiple tests available:")
print("1: Relative movement with relative rotation") print("1: Relative movement with relative rotation")
print("2: Relative movement with absolute rotation") print("2: Relative movement with absolute rotation")
print("3: Absolute movement without stack") print("3: Absolute movement without stack")
print("4: Absolute movement with stack") print("4: Absolute movement with stack")
print("5: All movement tests") print("5: All movement tests")
print("6: Unloading") print("6: Unloading")
print("7: Refueling") print("7: Refueling")
print("8: All non-movement tests") print("8: All non-movement tests")
print("9: All tests") print("9: All tests")
local choice = 0 local choice = 0
while choice < 1 or choice > 9 do while choice < 1 or choice > 9 do
print("Choose a test by entering its number:") print("Choose a test by entering its number:")
choice = tonumber(io.read()) or 0 choice = tonumber(io.read()) or 0
end end
if choice == 1 then if choice == 1 then
self:TestRelativeMovementWithRelativeRotation() self:TestRelativeMovementWithRelativeRotation()
elseif choice == 2 then elseif choice == 2 then
self:TestRelativeMovementWithAbsoluteRotation() self:TestRelativeMovementWithAbsoluteRotation()
elseif choice == 3 then elseif choice == 3 then
self:TestAbsoluteMovementWithoutStack() self:TestAbsoluteMovementWithoutStack()
elseif choice == 4 then elseif choice == 4 then
self:TestAbsoluteMovementWithStack() self:TestAbsoluteMovementWithStack()
elseif choice == 5 then elseif choice == 5 then
self:TestRelativeMovementWithRelativeRotation() self:TestRelativeMovementWithRelativeRotation()
self:TestRelativeMovementWithAbsoluteRotation() self:TestRelativeMovementWithAbsoluteRotation()
self:TestAbsoluteMovementWithoutStack() self:TestAbsoluteMovementWithoutStack()
self:TestAbsoluteMovementWithStack() self:TestAbsoluteMovementWithStack()
elseif choice == 6 then elseif choice == 6 then
self:TestUnstocking() self:TestUnstocking()
elseif choice == 7 then elseif choice == 7 then
self:TestRefueling() self:TestRefueling()
elseif choice == 8 then elseif choice == 8 then
self:TestUnstocking() self:TestUnstocking()
self:TestRefueling() self:TestRefueling()
elseif choice == 9 then elseif choice == 9 then
self:TestRelativeMovementWithRelativeRotation() self:TestRelativeMovementWithRelativeRotation()
self:TestRelativeMovementWithAbsoluteRotation() self:TestRelativeMovementWithAbsoluteRotation()
self:TestAbsoluteMovementWithoutStack() self:TestAbsoluteMovementWithoutStack()
self:TestAbsoluteMovementWithStack() self:TestAbsoluteMovementWithStack()
self:TestUnstocking() self:TestUnstocking()
self:TestRefueling() self:TestRefueling()
end end
end end
return TestingController return TestingController

View File

@ -14,125 +14,117 @@ local Stack = require("lib.stack")
local TurtleController = {} local TurtleController = {}
TurtleController.__index = TurtleController TurtleController.__index = TurtleController
---@return TurtleController ---@return TurtleController
function TurtleController:Create() function TurtleController:Create()
local t = {} local t = {}
setmetatable(t, TurtleController) setmetatable(t, TurtleController)
-----------------------------------------------------------------------------------------------
-- Fields
-----------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------- t.config = {
-- Fields fuel_direction = Direction.EAST,
----------------------------------------------------------------------------------------------- fuel_name = turtle.getItemDetail(1) == nil and "" or turtle.getItemDetail(1).name,
refuel_amount = 500,
refuel_safety_margin = 160,
storage_direction = Direction.WEST,
}
t.position = Position:Empty()
t.last_positions = Stack:Create()
t.mine_forward = false
t.mine_above = false
t.mine_below = false
return t
t.config = {
fuel_direction = Direction.EAST,
fuel_name = turtle.getItemDetail(1) == nil and "" or turtle.getItemDetail(1).name,
refuel_amount = 500,
refuel_safety_margin = 160,
storage_direction = Direction.WEST,
}
t.position = Position:Empty()
t.last_positions = Stack:Create()
t.mine_forward = false
t.mine_above = false
t.mine_below = false
return t
end end
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
-- Movement Methods -- Movement Methods
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
---Positive numbers turn clockwise, negative numbers turn counterclockwise ---Positive numbers turn clockwise, negative numbers turn counterclockwise
---@param number_of_turns number ---@param number_of_turns number
function TurtleController:TurnRelative(number_of_turns) function TurtleController:TurnRelative(number_of_turns)
if number_of_turns % 4 == 0 then if number_of_turns % 4 == 0 then
return return
end end
-- Turn turtle -- Turn turtle
local turns = number_of_turns % 4 local turns = number_of_turns % 4
if turns == 3 then if turns == 3 then
-- print(("Turtle is turning by %d (shortened to %d)..."):format(number_of_turns, -1)) -- print(("Turtle is turning by %d (shortened to %d)..."):format(number_of_turns, -1))
-- If we're rotating by 3, we could turn faster by rotating by 1 in the opposite direction -- If we're rotating by 3, we could turn faster by rotating by 1 in the opposite direction
turtle.turnLeft() turtle.turnLeft()
else else
-- print(("Turtle is turning by %d (shortened to %d)..."):format(number_of_turns, turns)) -- print(("Turtle is turning by %d (shortened to %d)..."):format(number_of_turns, turns))
for _ = 1,turns do for _ = 1, turns do
turtle.turnRight() turtle.turnRight()
end end
end end
-- Update current_position -- Update current_position
self.position.dir = (self.position.dir + number_of_turns) % 4 self.position.dir = (self.position.dir + number_of_turns) % 4
end end
---@param direction Direction ---@param direction Direction
function TurtleController:TurnToDirection(direction) function TurtleController:TurnToDirection(direction)
if self.position.dir == direction then if self.position.dir == direction then
return return
end end
self:TurnRelative(direction - self.position.dir) self:TurnRelative(direction - self.position.dir)
end end
---Move forward by a number of blocks depending on the current rotation ---Move forward by a number of blocks depending on the current rotation
---@param number_of_blocks number ---@param number_of_blocks number
---@param skip_unstocking boolean | nil ---@param skip_unstocking boolean | nil
---@param skip_refueling boolean | nil ---@param skip_refueling boolean | nil
function TurtleController:MoveForward(number_of_blocks, skip_unstocking, skip_refueling) function TurtleController:MoveForward(number_of_blocks, skip_unstocking, skip_refueling)
skip_unstocking = skip_unstocking or false skip_unstocking = skip_unstocking or false
skip_refueling = skip_refueling or false skip_refueling = skip_refueling or false
if number_of_blocks == 0 then if number_of_blocks == 0 then
return return
end end
-- print(("Turtle is moving forward by %d blocks..."):format(number_of_blocks)) -- print(("Turtle is moving forward by %d blocks..."):format(number_of_blocks))
for _ = 1,math.abs(number_of_blocks) do for _ = 1, math.abs(number_of_blocks) do
if not skip_refueling then if not skip_refueling then
self:RefuelIfEmpty() self:RefuelIfEmpty()
end end
if not skip_unstocking then if not skip_unstocking then
self:UnstockIfFull() self:UnstockIfFull()
end end
-- Mine/Move -- Mine/Move
if self.mine_forward then if self.mine_forward then
while not turtle.forward() do while not turtle.forward() do
turtle.dig() turtle.dig()
end end
elseif not turtle.forward() then elseif not turtle.forward() then
error("Turtle failed to move forward!") error("Turtle failed to move forward!")
end end
if self.mine_above then if self.mine_above then
turtle.digUp() turtle.digUp()
end end
if self.mine_below then if self.mine_below then
turtle.digDown() turtle.digDown()
end end
-- Update current position -- Update current position
if self.position.dir == Direction.NORTH then if self.position.dir == Direction.NORTH then
self.position.z = self.position.z + 1 self.position.z = self.position.z + 1
elseif self.position.dir == Direction.SOUTH then elseif self.position.dir == Direction.SOUTH then
self.position.z = self.position.z - 1 self.position.z = self.position.z - 1
elseif self.position.dir == Direction.EAST then elseif self.position.dir == Direction.EAST then
self.position.x = self.position.x + 1 self.position.x = self.position.x + 1
elseif self.position.dir == Direction.WEST then elseif self.position.dir == Direction.WEST then
self.position.x = self.position.x - 1 self.position.x = self.position.x - 1
end end
end end
end end
---Positive numbers move up, negative numbers move down. ---Positive numbers move up, negative numbers move down.
@ -140,52 +132,50 @@ end
---@param skip_unstocking boolean | nil ---@param skip_unstocking boolean | nil
---@param skip_refueling boolean | nil ---@param skip_refueling boolean | nil
function TurtleController:MoveVertical(number_of_blocks, skip_unstocking, skip_refueling) function TurtleController:MoveVertical(number_of_blocks, skip_unstocking, skip_refueling)
skip_unstocking = skip_unstocking or false skip_unstocking = skip_unstocking or false
skip_refueling = skip_refueling or false skip_refueling = skip_refueling or false
if number_of_blocks == 0 then if number_of_blocks == 0 then
return return
end end
-- print(("Turtle is moving vertically by %d blocks..."):format(number_of_blocks)) -- print(("Turtle is moving vertically by %d blocks..."):format(number_of_blocks))
local move_function = number_of_blocks > 0 and turtle.up or turtle.down local move_function = number_of_blocks > 0 and turtle.up or turtle.down
local mine_function = number_of_blocks > 0 and turtle.digUp or turtle.digDown local mine_function = number_of_blocks > 0 and turtle.digUp or turtle.digDown
local mine_enabled = number_of_blocks > 0 and self.mine_above or self.mine_below local mine_enabled = number_of_blocks > 0 and self.mine_above or self.mine_below
for _ = 1,math.abs(number_of_blocks) do for _ = 1, math.abs(number_of_blocks) do
if not skip_refueling then if not skip_refueling then
self:RefuelIfEmpty() self:RefuelIfEmpty()
end end
if not skip_unstocking then if not skip_unstocking then
self:UnstockIfFull() self:UnstockIfFull()
end end
-- Mine -- Mine
if mine_enabled then if mine_enabled then
mine_function() mine_function()
end end
-- Move -- Move
if not move_function() then if not move_function() then
error("Turtle failed to move vertically!") error("Turtle failed to move vertically!")
end end
-- Update current position -- Update current position
if number_of_blocks > 0 then if number_of_blocks > 0 then
self.position.y = self.position.y + 1 self.position.y = self.position.y + 1
elseif number_of_blocks < 0 then elseif number_of_blocks < 0 then
self.position.y = self.position.y - 1 self.position.y = self.position.y - 1
end end
end end
end end
---Stores the current position on the stack so we can return using TurtleController:MoveBack() ---Stores the current position on the stack so we can return using TurtleController:MoveBack()
function TurtleController:StorePosition() function TurtleController:StorePosition()
self.last_positions:Push(Position:Copy(self.position)) self.last_positions:Push(Position:Copy(self.position))
end end
---Move to an absolute position. Stores the current position on the stack so we can return using TurtleController:MoveBack() ---Move to an absolute position. Stores the current position on the stack so we can return using TurtleController:MoveBack()
---@param x number The EAST/WEST axis (grows from WEST to EAST) ---@param x number The EAST/WEST axis (grows from WEST to EAST)
---@param y number The UP/DOWN axis (grows from DOWN to UP) ---@param y number The UP/DOWN axis (grows from DOWN to UP)
@ -194,40 +184,39 @@ end
---@param skip_unstocking boolean | nil ---@param skip_unstocking boolean | nil
---@param skip_refueling boolean | nil ---@param skip_refueling boolean | nil
function TurtleController:MoveToPosition(x, y, z, dir, skip_unstocking, skip_refueling) function TurtleController:MoveToPosition(x, y, z, dir, skip_unstocking, skip_refueling)
-- print(("Turtle is moving to (%d, %d, %d)..."):format(x, y, z)) -- print(("Turtle is moving to (%d, %d, %d)..."):format(x, y, z))
-- Store the current position on the stack, so we can return using TurtleController:MoveBack() -- Store the current position on the stack, so we can return using TurtleController:MoveBack()
self.last_positions:Push(Position:Copy(self.position)) self.last_positions:Push(Position:Copy(self.position))
-- EAST/WEST axis (do first to not interfere with chests) -- EAST/WEST axis (do first to not interfere with chests)
if self.position.x > x then if self.position.x > x then
self:TurnToDirection(Direction.WEST) self:TurnToDirection(Direction.WEST)
elseif self.position.x < x then elseif self.position.x < x then
self:TurnToDirection(Direction.EAST) self:TurnToDirection(Direction.EAST)
end end
self:MoveForward(math.abs(x - self.position.x), skip_unstocking, skip_refueling) self:MoveForward(math.abs(x - self.position.x), skip_unstocking, skip_refueling)
-- NORTH/SOUTH axis -- NORTH/SOUTH axis
if self.position.z > z then if self.position.z > z then
self:TurnToDirection(Direction.SOUTH) self:TurnToDirection(Direction.SOUTH)
elseif self.position.z < z then elseif self.position.z < z then
self:TurnToDirection(Direction.NORTH) self:TurnToDirection(Direction.NORTH)
end end
self:MoveForward(math.abs(z - self.position.z), skip_unstocking, skip_refueling) self:MoveForward(math.abs(z - self.position.z), skip_unstocking, skip_refueling)
-- UP/DOWN axis -- UP/DOWN axis
self:MoveVertical(y - self.position.y, skip_unstocking, skip_refueling) self:MoveVertical(y - self.position.y, skip_unstocking, skip_refueling)
-- Direction -- Direction
self:TurnToDirection(dir or Direction.NORTH) self:TurnToDirection(dir or Direction.NORTH)
-- Sanity check -- Sanity check
if not(self.position.x == x and self.position.y == y and self.position.z == z and self.position.dir == dir) then if not (self.position.x == x and self.position.y == y and self.position.z == z and self.position.dir == dir) then
error("TurtleController:MoveToPosition failed to move to target position!") error("TurtleController:MoveToPosition failed to move to target position!")
end end
end end
---Move by a vector. Stores the current position on the stack so we can return using TurtleController:MoveBack() ---Move by a vector. Stores the current position on the stack so we can return using TurtleController:MoveBack()
---@param x number The EAST/WEST axis (grows from WEST to EAST) ---@param x number The EAST/WEST axis (grows from WEST to EAST)
---@param y number The UP/DOWN axis (grows from DOWN to UP) ---@param y number The UP/DOWN axis (grows from DOWN to UP)
@ -236,227 +225,233 @@ end
---@param skip_unstocking boolean | nil ---@param skip_unstocking boolean | nil
---@param skip_refueling boolean | nil ---@param skip_refueling boolean | nil
function TurtleController:MoveByVector(x, y, z, dir, skip_unstocking, skip_refueling) function TurtleController:MoveByVector(x, y, z, dir, skip_unstocking, skip_refueling)
self:MoveToPosition(self.position.x + x, self.position.y + y, self.position.z + z, dir, skip_unstocking, skip_refueling) self:MoveToPosition(
self.position.x + x,
self.position.y + y,
self.position.z + z,
dir,
skip_unstocking,
skip_refueling
)
end end
---Move to the previously stored position and pop this position from the stack. ---Move to the previously stored position and pop this position from the stack.
---@param skip_unstocking boolean | nil ---@param skip_unstocking boolean | nil
---@param skip_refueling boolean | nil ---@param skip_refueling boolean | nil
function TurtleController:MoveBack(skip_unstocking, skip_refueling) function TurtleController:MoveBack(skip_unstocking, skip_refueling)
local last_position = self.last_positions:Pop() local last_position = self.last_positions:Pop()
if last_position == nil then if last_position == nil then
error("Failed to obtain last_position to move back!") error("Failed to obtain last_position to move back!")
else else
self:MoveToPosition(last_position.x, last_position.y, last_position.z, last_position.dir, skip_unstocking, skip_refueling) self:MoveToPosition(
last_position.x,
last_position.y,
last_position.z,
last_position.dir,
skip_unstocking,
skip_refueling
)
-- Pop the stack because MoveToPosition pushes our current position -- Pop the stack because MoveToPosition pushes our current position
self.last_positions:Pop() self.last_positions:Pop()
end end
end end
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
-- Inventory Methods -- Inventory Methods
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
---@return boolean ---@return boolean
function TurtleController:HasFuel() function TurtleController:HasFuel()
local level = turtle.getFuelLevel() local level = turtle.getFuelLevel()
local distance_home = self.position.x + self.position.y + self.position.z local distance_home = self.position.x + self.position.y + self.position.z
return level > distance_home + self.config.refuel_safety_margin return level > distance_home + self.config.refuel_safety_margin
end end
---@return boolean ---@return boolean
function TurtleController:HasInventorySpace() function TurtleController:HasInventorySpace()
for slot = 1,16 do for slot = 1, 16 do
if turtle.getItemDetail(slot) == nil then if turtle.getItemDetail(slot) == nil then
return true return true
end end
end end
return false return false
end end
---@param slot number ---@param slot number
---@param count number ---@param count number
---@return boolean ---@return boolean
function TurtleController:SuckItem(slot, count) function TurtleController:SuckItem(slot, count)
local previous_slot = turtle.getSelectedSlot() local previous_slot = turtle.getSelectedSlot()
turtle.select(slot or 1) turtle.select(slot or 1)
local sucked = turtle.suck(count) local sucked = turtle.suck(count)
turtle.select(previous_slot) turtle.select(previous_slot)
return sucked return sucked
end end
---@param slot number ---@param slot number
---@param count number ---@param count number
---@return boolean ---@return boolean
function TurtleController:DropItem(slot, count) function TurtleController:DropItem(slot, count)
local previous_slot = turtle.getSelectedSlot() local previous_slot = turtle.getSelectedSlot()
turtle.select(slot or 1) turtle.select(slot or 1)
local dropped = turtle.drop(count) local dropped = turtle.drop(count)
turtle.select(previous_slot) turtle.select(previous_slot)
return dropped return dropped
end end
function TurtleController:DropInventory() function TurtleController:DropInventory()
print("Dropping inventory into chest...") print("Dropping inventory into chest...")
for slot = 1,16 do for slot = 1, 16 do
turtle.select(slot) turtle.select(slot)
turtle.drop() turtle.drop()
end end
turtle.select(1) turtle.select(1)
end end
---@param skip_inventory_check boolean | nil ---@param skip_inventory_check boolean | nil
function TurtleController:UnstockIfFull(skip_inventory_check) function TurtleController:UnstockIfFull(skip_inventory_check)
skip_inventory_check = skip_inventory_check or false skip_inventory_check = skip_inventory_check or false
if not skip_inventory_check and self:HasInventorySpace() then if not skip_inventory_check and self:HasInventorySpace() then
return return
end end
if skip_inventory_check then if skip_inventory_check then
print(("HasInventorySpace() returned %s"):format(self:HasInventorySpace())) print(("HasInventorySpace() returned %s"):format(self:HasInventorySpace()))
end end
print("Turtle is unstocking...") print("Turtle is unstocking...")
self:MoveToPosition(0, 0, 0, self.config.storage_direction, true, true) self:MoveToPosition(0, 0, 0, self.config.storage_direction, true, true)
self:DropInventory() self:DropInventory()
self:RefuelIfEmpty() self:RefuelIfEmpty()
self:MoveBack(true, true) self:MoveBack(true, true)
end end
---@param skip_fuel_check boolean | nil ---@param skip_fuel_check boolean | nil
function TurtleController:RefuelIfEmpty(skip_fuel_check) function TurtleController:RefuelIfEmpty(skip_fuel_check)
skip_fuel_check = skip_fuel_check or false skip_fuel_check = skip_fuel_check or false
if not skip_fuel_check and self:HasFuel() then if not skip_fuel_check and self:HasFuel() then
return return
end end
if skip_fuel_check then if skip_fuel_check then
print(("HasFuel() returned %s"):format(self:HasFuel())) print(("HasFuel() returned %s"):format(self:HasFuel()))
end end
print("Turtle is refueling...") print("Turtle is refueling...")
-- Clear our inventory into the storage chest -- Clear our inventory into the storage chest
self:MoveToPosition(0, 0, 0, self.config.storage_direction, true, true) self:MoveToPosition(0, 0, 0, self.config.storage_direction, true, true)
self:DropInventory() self:DropInventory()
-- Prepare refueling -- Prepare refueling
self:TurnToDirection(self.config.fuel_direction) self:TurnToDirection(self.config.fuel_direction)
turtle.select(1) turtle.select(1)
-- Include the distance to the last position when refueling -- Include the distance to the last position when refueling
-- to keep the amount of work done between refuelings constant -- to keep the amount of work done between refuelings constant
local target_fuel_level = self.config.refuel_amount local target_fuel_level = self.config.refuel_amount
local last_position = self.last_positions:Peek() local last_position = self.last_positions:Peek()
if last_position == nil then if last_position == nil then
error("Failed to obtain last_position while refueling!") error("Failed to obtain last_position while refueling!")
else else
target_fuel_level = target_fuel_level + last_position.x + last_position.y + last_position.z target_fuel_level = target_fuel_level + last_position.x + last_position.y + last_position.z
end end
-- Refuel until we hit the refuel_amount -- Refuel until we hit the refuel_amount
local before_level = turtle.getFuelLevel() local before_level = turtle.getFuelLevel()
repeat repeat
if not self:SuckItem(1, 1) then if not self:SuckItem(1, 1) then
error("Failed to suck fuel out of fuel chest!") error("Failed to suck fuel out of fuel chest!")
end end
turtle.refuel() turtle.refuel()
until turtle.getFuelLevel() >= target_fuel_level until turtle.getFuelLevel() >= target_fuel_level
local after_level = turtle.getFuelLevel() local after_level = turtle.getFuelLevel()
self:MoveBack(true, true) self:MoveBack(true, true)
print(("Refuelled %d units, current level is %d (old level was %d)"):format(after_level - before_level, after_level, before_level)) print(
("Refuelled %d units, current level is %d (old level was %d)"):format(
after_level - before_level,
after_level,
before_level
)
)
end end
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
-- Management Methods -- Management Methods
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
function TurtleController:Configure() function TurtleController:Configure()
local config_complete = false local config_complete = false
while not config_complete do while not config_complete do
-- Check for valid fuel -- Check for valid fuel
while self.config.fuel_name == "" do while self.config.fuel_name == "" do
print("Please put valid fuel into the first slot and press Enter!") print("Please put valid fuel into the first slot and press Enter!")
local _ = io.read() local _ = io.read()
self.config.fuel_name = turtle.getItemDetail(1) == nil and "" or turtle.getItemDetail(1).name self.config.fuel_name = turtle.getItemDetail(1) == nil and "" or turtle.getItemDetail(1).name
end end
print("A chest with fuel has to be placed beside the turtle's starting position. Is this chest...") print("A chest with fuel has to be placed beside the turtle's starting position. Is this chest...")
print(("...right of the turtle (enter %d)?"):format(Direction.EAST)) print(("...right of the turtle (enter %d)?"):format(Direction.EAST))
print(("...behind the turtle (enter %d)?"):format(Direction.SOUTH)) print(("...behind the turtle (enter %d)?"):format(Direction.SOUTH))
print(("...left of the turtle (enter %d)?"):format(Direction.WEST)) print(("...left of the turtle (enter %d)?"):format(Direction.WEST))
print("By default, the chest is assumed on the left.") print("By default, the chest is assumed on the left.")
self.config.fuel_direction = tonumber(io.read()) or Direction.WEST self.config.fuel_direction = tonumber(io.read()) or Direction.WEST
print("A chest to store mined blocks has to be placed beside the turtle's starting position. Is this chest...") print("A chest to store mined blocks has to be placed beside the turtle's starting position. Is this chest...")
print(("...right of the turtle (enter %d)?"):format(Direction.EAST)) print(("...right of the turtle (enter %d)?"):format(Direction.EAST))
print(("...behind the turtle (enter %d)?"):format(Direction.SOUTH)) print(("...behind the turtle (enter %d)?"):format(Direction.SOUTH))
print(("...left of the turtle (enter %d)?"):format(Direction.WEST)) print(("...left of the turtle (enter %d)?"):format(Direction.WEST))
print("By default, the chest is assumed on the right.") print("By default, the chest is assumed on the right.")
self.config.storage_direction = tonumber(io.read()) or Direction.EAST self.config.storage_direction = tonumber(io.read()) or Direction.EAST
print("Configuration complete!") print("Configuration complete!")
print(("Turtle will consume \"%s\" for fuel. Put the desired fuel into the first slot to change."):format(self.config.fuel_name)) print(
print("Do you want to accept the configuration (enter 1, otherwise 0)?") ('Turtle will consume "%s" for fuel. Put the desired fuel into the first slot to change.'):format(
config_complete = tonumber(io.read()) == 1 self.config.fuel_name
end )
)
print("Do you want to accept the configuration (enter 1, otherwise 0)?")
config_complete = tonumber(io.read()) == 1
end
end end
function TurtleController:EnableMiningForward() function TurtleController:EnableMiningForward()
self.mine_forward = true self.mine_forward = true
end end
function TurtleController:DisableMiningForward() function TurtleController:DisableMiningForward()
self.mine_forward = false self.mine_forward = false
end end
function TurtleController:EnableMiningAbove() function TurtleController:EnableMiningAbove()
self.mine_above = true self.mine_above = true
end end
function TurtleController:DisableMiningAbove() function TurtleController:DisableMiningAbove()
self.mine_above = false self.mine_above = false
end end
function TurtleController:EnableMiningBelow() function TurtleController:EnableMiningBelow()
self.mine_below = true self.mine_below = true
end end
function TurtleController:DisableMiningBelow() function TurtleController:DisableMiningBelow()
self.mine_below = false self.mine_below = false
end end
return TurtleController return TurtleController

View File

@ -1,9 +1,9 @@
---@enum Direction ---@enum Direction
local Direction = { local Direction = {
NORTH = 0, NORTH = 0,
EAST = 1, EAST = 1,
SOUTH = 2, SOUTH = 2,
WEST = 3 WEST = 3,
} }
return Direction return Direction

View File

@ -8,18 +8,17 @@ local Direction = require("lib.direction")
local Position = {} local Position = {}
Position.__index = Position Position.__index = Position
---@return Position ---@return Position
function Position:Empty() function Position:Empty()
local t = {} local t = {}
setmetatable(t, Position) setmetatable(t, Position)
t.x = 0 t.x = 0
t.y = 0 t.y = 0
t.z = 0 t.z = 0
t.dir = Direction.NORTH t.dir = Direction.NORTH
return t return t
end end
---@param x number ---@param x number
@ -28,43 +27,43 @@ end
---@param dir Direction ---@param dir Direction
---@return Position ---@return Position
function Position:Create(x, y, z, dir) function Position:Create(x, y, z, dir)
local t = {} local t = {}
setmetatable(t, Position) setmetatable(t, Position)
t.x = x t.x = x
t.y = y t.y = y
t.z = z t.z = z
t.dir = dir or Direction.NORTH t.dir = dir or Direction.NORTH
return t return t
end end
---@param other Position ---@param other Position
---@return Position ---@return Position
function Position:Copy(other) function Position:Copy(other)
local t = {} local t = {}
setmetatable(t, Position) setmetatable(t, Position)
t.x = other.x t.x = other.x
t.y = other.y t.y = other.y
t.z = other.z t.z = other.z
t.dir = other.dir t.dir = other.dir
return t return t
end end
---@param other Position ---@param other Position
function Position:Add(other) function Position:Add(other)
self.x = self.x + other.x self.x = self.x + other.x
self.y = self.y + other.y self.y = self.y + other.y
self.z = self.z + other.z self.z = self.z + other.z
end end
---@param other Position ---@param other Position
function Position:Subtract(other) function Position:Subtract(other)
self.x = self.x - other.x self.x = self.x - other.x
self.y = self.y - other.y self.y = self.y - other.y
self.z = self.z - other.z self.z = self.z - other.z
end end
return Position return Position

View File

@ -1,61 +1,53 @@
local Position = require("lib.position")
---@class Stack ---@class Stack
---@field elements Position[] ---@field elements Position[]
local Stack = {} local Stack = {}
Stack.__index = Stack Stack.__index = Stack
---@return Stack ---@return Stack
function Stack:Create() function Stack:Create()
-- stack table -- stack table
local t = {} local t = {}
setmetatable(t, Stack) setmetatable(t, Stack)
-- entry table -- entry table
t.elements = {} t.elements = {}
return t return t
end end
---@param element Position ---@param element Position
function Stack:Push(element) function Stack:Push(element)
if element == nil or element == {} then if element == nil or element == {} then
printError("Failed to push empty element to the stack!") printError("Failed to push empty element to the stack!")
return return
end end
table.insert(self.elements, element) table.insert(self.elements, element)
end end
---@return Position | nil ---@return Position | nil
function Stack:Pop() function Stack:Pop()
if self:Count() == 0 then if self:Count() == 0 then
printError("Failed to pop element from the empty stack!") printError("Failed to pop element from the empty stack!")
return nil return nil
end end
return table.remove(self.elements) return table.remove(self.elements)
end end
---@return Position | nil ---@return Position | nil
function Stack:Peek() function Stack:Peek()
if self:Count() == 0 then if self:Count() == 0 then
printError("Failed to peek element from the empty stack!") printError("Failed to peek element from the empty stack!")
return nil return nil
end end
return self.elements[#self.elements] return self.elements[#self.elements]
end end
---@return number ---@return number
function Stack:Count() function Stack:Count()
return #self.elements return #self.elements
end end
return Stack return Stack

View File

@ -2,11 +2,10 @@ local TestingController = require("controller.testing_controller")
local ExcavationController = require("controller.excavation_controller") local ExcavationController = require("controller.excavation_controller")
local AudioController = require("controller.audio_controller") local AudioController = require("controller.audio_controller")
local controllers = { local controllers = {
TestingController:Create(), TestingController:Create(),
ExcavationController:Create(), ExcavationController:Create(),
AudioController:Create(), AudioController:Create(),
} }
print("Multiple controllers are available:") print("Multiple controllers are available:")
@ -16,13 +15,12 @@ print("3: Play Bangarang")
local choice = 0 local choice = 0
while choice < 1 or choice > #controllers do while choice < 1 or choice > #controllers do
print("Choose a controller by entering its number:") print("Choose a controller by entering its number:")
choice = tonumber(io.read()) or 0 choice = tonumber(io.read()) or 0
end end
controllers[choice]:Run() controllers[choice]:Run()
-- TODO: StackOverflow once the inventory is full, unstocking doesn't work... -- TODO: StackOverflow once the inventory is full, unstocking doesn't work...
-- Is the InventoryFull check wrong? -- Is the InventoryFull check wrong?
-- TODO: Test if there's a chest when dropping/sucking/mining (don't mine chests!) -- TODO: Test if there's a chest when dropping/sucking/mining (don't mine chests!)