1

Compare commits

..

5 Commits

13 changed files with 1348 additions and 1071 deletions

9
.luarc.json Normal file
View File

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

View File

@ -2,6 +2,8 @@
"Lua.diagnostics.globals": [
"printError",
"turtle",
"shell"
"shell",
"peripheral",
"parallel"
]
}

BIN
audio/bangarang.dfpwm Normal file

Binary file not shown.

View File

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

View File

@ -0,0 +1,82 @@
local TurtleController = require("controller.turtle_controller")
local AudioController = require("controller.audio_controller")
---@class AudioTestingController
---@field controller TurtleController
---@field audio AudioController
local AudioTestingController = {}
AudioTestingController.__index = AudioTestingController
---@return AudioTestingController
function AudioTestingController:Create()
local t = {}
setmetatable(t, AudioTestingController)
-----------------------------------------------------------------------------------------------
-- Fields
-----------------------------------------------------------------------------------------------
t.controller = TurtleController:Create()
t.audio = AudioController:Create()
return t
end
-----------------------------------------------------------------------------------------------
-- Behavior Methods
-----------------------------------------------------------------------------------------------
function AudioTestingController:TestAudioWithoutMovement()
local function dancing()
for _ = 1, 256 do
self.controller:TurnRelative(1)
end
self.audio:StopPlaying()
end
parallel.waitForAll(dancing, self.audio:PlayAudioFactory("bangarang"))
end
function AudioTestingController:TestAudioWithMovement()
local function movement()
for _ = 1, 10 do
self.controller:MoveForward(10)
self.controller:MoveVertical(3)
self.controller:TurnRelative(2)
self.controller:MoveForward(10)
self.controller:MoveVertical(-3)
self.controller:TurnRelative(2)
end
self.audio:StopPlaying()
end
parallel.waitForAll(movement, self.audio:PlayAudioFactory("bangarang"))
end
-----------------------------------------------------------------------------------------------
-- Main Method
-----------------------------------------------------------------------------------------------
function AudioTestingController:Run()
self.controller:Configure()
self.controller:DisableMiningForward()
self.controller:DisableMiningAbove()
self.controller:DisableMiningBelow()
print("There are multiple tests available:")
print("1: Audio without movement")
print("2: Audio with movement")
local choice = 0
while choice < 1 or choice > 2 do
print("Choose a test by entering its number:")
choice = tonumber(io.read()) or 0
end
if choice == 1 then
self:TestAudioWithoutMovement()
elseif choice == 2 then
self:TestAudioWithMovement()
end
end
return AudioTestingController

View File

@ -1,27 +1,27 @@
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,
@ -32,16 +32,13 @@ function ExcavationController:Create()
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.
@ -63,7 +60,6 @@ function ExcavationController:Excavate_1x1or2or3xL(mine_above, mine_below)
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.
@ -120,7 +116,6 @@ function ExcavationController:Excavate_1xHxL()
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.
@ -150,7 +145,6 @@ function ExcavationController:Excavate_WxHxL(center_slice)
self.controller:MoveBack()
end
---Excavates all slices.
---Will leave the turtle refueled and unstocked in its 0x0x0 position.
function ExcavationController:Excavate()
@ -176,6 +170,7 @@ function ExcavationController:Excavate()
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()
@ -206,7 +201,9 @@ function ExcavationController:Excavate()
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()
@ -245,19 +242,19 @@ function ExcavationController:Excavate()
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:DropInventoryIntoChest()
self.controller:TurnToDirection(Direction.NORTH)
self.audio:StopPlaying()
end
-----------------------------------------------------------------------------------------------
-- Management Methods
-----------------------------------------------------------------------------------------------
function ExcavationController:Configure()
local config_complete = false
@ -291,23 +288,31 @@ function ExcavationController:Configure()
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(
("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()
@ -317,8 +322,9 @@ function ExcavationController:Run()
turtle.refuel()
self.controller:RefuelIfEmpty()
parallel.waitForAll(function()
self:Excavate()
end, self.audio:PlayAudioFactory("bangarang"))
end
return ExcavationController

View File

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

View File

@ -14,18 +14,15 @@ local Stack = require("lib.stack")
local TurtleController = {}
TurtleController.__index = TurtleController
---@return TurtleController
function TurtleController:Create()
local t = {}
setmetatable(t, TurtleController)
-----------------------------------------------------------------------------------------------
-- Fields
-----------------------------------------------------------------------------------------------
t.config = {
fuel_direction = Direction.EAST,
fuel_name = turtle.getItemDetail(1) == nil and "" or turtle.getItemDetail(1).name,
@ -39,16 +36,13 @@ function TurtleController:Create()
t.mine_above = false
t.mine_below = false
return t
end
-----------------------------------------------------------------------------------------------
-- Movement Methods
-----------------------------------------------------------------------------------------------
---Positive numbers turn clockwise, negative numbers turn counterclockwise
---@param number_of_turns number
function TurtleController:TurnRelative(number_of_turns)
@ -75,7 +69,6 @@ function TurtleController:TurnRelative(number_of_turns)
self.position.dir = (self.position.dir + number_of_turns) % 4
end
---@param direction Direction
function TurtleController:TurnToDirection(direction)
if self.position.dir == direction then
@ -85,7 +78,6 @@ function TurtleController:TurnToDirection(direction)
self:TurnRelative(direction - self.position.dir)
end
---Move forward by a number of blocks depending on the current rotation
---@param number_of_blocks number
---@param skip_unstocking boolean | nil
@ -110,15 +102,24 @@ function TurtleController:MoveForward(number_of_blocks, skip_unstocking, skip_re
-- Mine/Move
if self.mine_forward then
while not turtle.forward() do
if self:TestForBlock("minecraft:chest") then
error("Won't mine because a chest is in the way!")
end
turtle.dig()
end
elseif not turtle.forward() then
error("Turtle failed to move forward!")
end
if self.mine_above then
if self:TestForBlock("minecraft:chest", true, false) then
error("Won't mine because a chest is in the way!")
end
turtle.digUp()
end
if self.mine_below then
if self:TestForBlock("minecraft:chest", false, true) then
error("Won't mine because a chest is in the way!")
end
turtle.digDown()
end
@ -160,13 +161,15 @@ function TurtleController:MoveVertical(number_of_blocks, skip_unstocking, skip_r
self:UnstockIfFull()
end
-- Mine
-- Mine/Move
if mine_enabled then
while not move_function() do
if self:TestForBlock("minecraft:chest", number_of_blocks > 0, number_of_blocks < 0) then
error("Won't mine because a chest is in the way!")
end
mine_function()
end
-- Move
if not move_function() then
elseif not move_function() then
error("Turtle failed to move vertically!")
end
@ -179,13 +182,11 @@ function TurtleController:MoveVertical(number_of_blocks, skip_unstocking, skip_r
end
end
---Stores the current position on the stack so we can return using TurtleController:MoveBack()
function TurtleController:StorePosition()
self.last_positions:Push(Position:Copy(self.position))
end
---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 y number The UP/DOWN axis (grows from DOWN to UP)
@ -199,7 +200,15 @@ function TurtleController:MoveToPosition(x, y, z, dir, skip_unstocking, skip_ref
-- Store the current position on the stack, so we can return using TurtleController:MoveBack()
self.last_positions:Push(Position:Copy(self.position))
-- Store mining config so we can restore it later
local mine_forward = self.mine_forward
local mine_above = self.mine_above
local mine_below = self.mine_below
-- EAST/WEST axis (do first to not interfere with chests)
self:EnableMiningForward()
self:DisableMiningAbove()
self:DisableMiningBelow()
if self.position.x > x then
self:TurnToDirection(Direction.WEST)
elseif self.position.x < x then
@ -216,18 +225,40 @@ function TurtleController:MoveToPosition(x, y, z, dir, skip_unstocking, skip_ref
self:MoveForward(math.abs(z - self.position.z), skip_unstocking, skip_refueling)
-- UP/DOWN axis
self:DisableMiningForward()
if y < self.position.y then
self:EnableMiningBelow()
elseif y > self.position.y then
self:EnableMiningAbove()
end
self:MoveVertical(y - self.position.y, skip_unstocking, skip_refueling)
-- Direction
self:TurnToDirection(dir or Direction.NORTH)
-- Restore mining config
if mine_forward then
self:EnableMiningForward()
else
self:DisableMiningForward()
end
if mine_above then
self:EnableMiningAbove()
else
self:DisableMiningAbove()
end
if mine_below then
self:EnableMiningBelow()
else
self:DisableMiningBelow()
end
-- Sanity check
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!")
end
end
---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 y number The UP/DOWN axis (grows from DOWN to UP)
@ -236,10 +267,16 @@ end
---@param skip_unstocking boolean | nil
---@param skip_refueling boolean | nil
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
---Move to the previously stored position and pop this position from the stack.
---@param skip_unstocking boolean | nil
---@param skip_refueling boolean | nil
@ -249,19 +286,55 @@ function TurtleController:MoveBack(skip_unstocking, skip_refueling)
if last_position == nil then
error("Failed to obtain last_position to move back!")
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
self.last_positions:Pop()
end
end
-----------------------------------------------------------------------------------------------
-- Testing Methods
-----------------------------------------------------------------------------------------------
---@param block_name string
---@param above boolean | nil
---@param below boolean | nil
---@return boolean
function TurtleController:TestForBlock(block_name, above, below)
above = above or false
below = below or false
if above and below then
error("Can only test for blocks in one direction!")
end
local inspect_function = turtle.inspect
if above then
inspect_function = turtle.inspectUp
elseif below then
inspect_function = turtle.inspectDown
end
local has_block, block_data = inspect_function()
if not has_block then
return false
end
return block_data.name == block_name
end
-----------------------------------------------------------------------------------------------
-- Inventory Methods
-----------------------------------------------------------------------------------------------
---@return boolean
function TurtleController:HasFuel()
local level = turtle.getFuelLevel()
@ -270,7 +343,6 @@ function TurtleController:HasFuel()
return level > distance_home + self.config.refuel_safety_margin
end
---@return boolean
function TurtleController:HasInventorySpace()
for slot = 1, 16 do
@ -282,11 +354,14 @@ function TurtleController:HasInventorySpace()
return false
end
---@param slot number
---@param count number
---@return boolean
function TurtleController:SuckItem(slot, count)
function TurtleController:SuckItemFromChest(slot, count)
if not self:TestForBlock("minecraft:chest") then
error("Can't suck item: no chest!")
end
local previous_slot = turtle.getSelectedSlot()
turtle.select(slot or 1)
@ -297,11 +372,14 @@ function TurtleController:SuckItem(slot, count)
return sucked
end
---@param slot number
---@param count number
---@return boolean
function TurtleController:DropItem(slot, count)
function TurtleController:DropItemIntoChest(slot, count)
if not self:TestForBlock("minecraft:chest") then
error("Can't drop item: no chest!")
end
local previous_slot = turtle.getSelectedSlot()
turtle.select(slot or 1)
@ -312,8 +390,11 @@ function TurtleController:DropItem(slot, count)
return dropped
end
function TurtleController:DropInventoryIntoChest()
if not self:TestForBlock("minecraft:chest") then
error("Can't drop item: no chest!")
end
function TurtleController:DropInventory()
print("Dropping inventory into chest...")
for slot = 1, 16 do
@ -324,7 +405,6 @@ function TurtleController:DropInventory()
turtle.select(1)
end
---@param skip_inventory_check boolean | nil
function TurtleController:UnstockIfFull(skip_inventory_check)
skip_inventory_check = skip_inventory_check or false
@ -338,13 +418,14 @@ function TurtleController:UnstockIfFull(skip_inventory_check)
print("Turtle is unstocking...")
self:MoveToPosition(0, 0, 0, self.config.storage_direction, true, true)
self:DropInventory()
self:DropInventoryIntoChest()
self:RefuelIfEmpty()
self:TurnToDirection(Direction.NORTH)
self:MoveForward(1)
self:MoveBack(true, true)
end
---@param skip_fuel_check boolean | nil
function TurtleController:RefuelIfEmpty(skip_fuel_check)
skip_fuel_check = skip_fuel_check or false
@ -359,7 +440,7 @@ function TurtleController:RefuelIfEmpty(skip_fuel_check)
-- Clear our inventory into the storage chest
self:MoveToPosition(0, 0, 0, self.config.storage_direction, true, true)
self:DropInventory()
self:DropInventoryIntoChest()
-- Prepare refueling
self:TurnToDirection(self.config.fuel_direction)
@ -378,24 +459,30 @@ function TurtleController:RefuelIfEmpty(skip_fuel_check)
-- Refuel until we hit the refuel_amount
local before_level = turtle.getFuelLevel()
repeat
if not self:SuckItem(1, 1) then
if not self:SuckItemFromChest(1, 1) then
error("Failed to suck fuel out of fuel chest!")
end
turtle.refuel()
until turtle.getFuelLevel() >= target_fuel_level
local after_level = turtle.getFuelLevel()
self:TurnToDirection(Direction.NORTH)
self:MoveForward(1)
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
-----------------------------------------------------------------------------------------------
-- Management Methods
-----------------------------------------------------------------------------------------------
function TurtleController:Configure()
local config_complete = false
@ -422,41 +509,38 @@ function TurtleController:Configure()
self.config.storage_direction = tonumber(io.read()) or Direction.EAST
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(
('Turtle will consume "%s" for fuel. Put the desired fuel into the first slot to change.'):format(
self.config.fuel_name
)
)
print("Do you want to accept the configuration (enter 1, otherwise 0)?")
config_complete = tonumber(io.read()) == 1
end
end
function TurtleController:EnableMiningForward()
self.mine_forward = true
end
function TurtleController:DisableMiningForward()
self.mine_forward = false
end
function TurtleController:EnableMiningAbove()
self.mine_above = true
end
function TurtleController:DisableMiningAbove()
self.mine_above = false
end
function TurtleController:EnableMiningBelow()
self.mine_below = true
end
function TurtleController:DisableMiningBelow()
self.mine_below = false
end
return TurtleController

View File

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

View File

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

View File

@ -8,7 +8,6 @@ local Direction = require("lib.direction")
local Position = {}
Position.__index = Position
---@return Position
function Position:Empty()
local t = {}

View File

@ -1,11 +1,8 @@
local Position = require("lib.position")
---@class Stack
---@field elements Position[]
local Stack = {}
Stack.__index = Stack
---@return Stack
function Stack:Create()
-- stack table
@ -18,7 +15,6 @@ function Stack:Create()
return t
end
---@param element Position
function Stack:Push(element)
if element == nil or element == {} then
@ -29,7 +25,6 @@ function Stack:Push(element)
table.insert(self.elements, element)
end
---@return Position | nil
function Stack:Pop()
if self:Count() == 0 then
@ -40,7 +35,6 @@ function Stack:Pop()
return table.remove(self.elements)
end
---@return Position | nil
function Stack:Peek()
if self:Count() == 0 then
@ -51,11 +45,9 @@ function Stack:Peek()
return self.elements[#self.elements]
end
---@return number
function Stack:Count()
return #self.elements
end
return Stack

View File

@ -1,15 +1,20 @@
local TestingController = require("controller.testing_controller")
local TurtleTestingController = require("controller.turtle_testing_controller")
local AudioTestingController = require("controller.audio_testing_controller")
local ExcavationController = require("controller.excavation_controller")
local AudioController = require("controller.audio_controller")
local controllers = {
TestingController:Create(),
TurtleTestingController:Create(),
AudioTestingController:Create(),
ExcavationController:Create(),
AudioController:Create(),
}
print("Multiple controllers are available:")
print("1: Testing Mode")
print("2: Volume Excavation")
print("1: Turtle Testing Mode")
print("2: Audio Testing Mode")
print("3: Volume Excavation")
print("4: Play Bangarang")
local choice = 0
while choice < 1 or choice > #controllers do
@ -19,11 +24,13 @@ end
controllers[choice]:Run()
-- TODO: When refueling but chest is missing, wait there until the user puts a chest with fuel
-- TODO: Improve error handling: Don't abort the program in all cases,
-- but wait until the user fixes the issue (e.g. places a chest)
-- TODO: StackOverflow once the inventory is full, unstocking doesn't work...
-- Is the InventoryFull check wrong?
-- TODO: Test if there's a chest when dropping/sucking/mining (don't mine chests!)
-- TODO: Add shortcuts for the volume excavation:
-- - Tunnel forward 3x2 or 3x3 with specified length
-- - Excavate downwards (quarry) in a square, specify side length
-- TODO: Add controller to build rooms with walls of a specified material
-- TODO: Support specifying the mining height
-- TODO: Add a stair building mode (specify side length, min 2x2)
-- TODO: Add configurable trash_list with items that won't be picked up
-- TODO: Add a speaker to the turtle (listen to music while mining)