Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- move_lib.lua v1.0
- A comprehensive movement and positioning library for ComputerCraft turtles.
- Manages position, orientation, and provides movement commands with basic obstacle avoidance.
- ]]
- local move_lib = {}
- --#region Configuration and State
- local AUTOMATA_PERIPHERAL_NAME = "end_automata" -- Corrected from "endAutomata" as CC peripherals are snake_case
- local POSITION_FILE = "turtle_pos.json" -- File to save/load position and orientation
- local REFUEL_SLOT = 16 -- Standard fuel slot
- local FUEL_ITEM_NAME_PART = "coal" -- Used to identify fuel items for refueling logic (can be broader if needed)
- -- Internal state
- local automata = peripheral.find(AUTOMATA_PERIPHERAL_NAME)
- local current_pos = { x = nil, y = nil, z = nil }
- local current_dir = nil -- 0: North (+Z), 1: East (+X), 2: South (-Z), 3: West (-X)
- -- Direction vectors (dx, dy, dz)
- local DIR_VECTORS = {
- [0] = {x = 0, y = 0, z = 1}, -- North (+Z)
- [1] = {x = 1, y = 0, z = 0}, -- East (+X)
- [2] = {x = 0, y = 0, z = -1}, -- South (-Z)
- [3] = {x = -1, y = 0, z = 0} -- West (-X)
- }
- local DIR_NAMES = {
- [0] = "North (+Z)",
- [1] = "East (+X)",
- [2] = "South (-Z)",
- [3] = "West (-X)"
- }
- --#endregion
- --#region Helper Functions (Internal)
- local function log(message)
- print("[MoveLib] " .. message)
- -- If you had a global debug logger for the main bot, you could call it here
- end
- local function savePosition()
- if current_pos.x == nil or current_dir == nil then
- log("Cannot save position, state is unknown.")
- return
- end
- local data = {
- x = current_pos.x,
- y = current_pos.y,
- z = current_pos.z,
- dir = current_dir
- }
- local file, err = fs.open(POSITION_FILE, "w")
- if file then
- file.write(textutils.serialiseJSON(data))
- file.close()
- log("Position saved: X:" .. data.x .. " Y:" .. data.y .. " Z:" .. data.z .. " Dir:" .. DIR_NAMES[data.dir])
- else
- log("Error saving position: " .. (err or "unknown"))
- end
- end
- local function loadPosition()
- if fs.exists(POSITION_FILE) then
- local file, err = fs.open(POSITION_FILE, "r")
- if file then
- local sData = file.readAll()
- file.close()
- local success, data = pcall(textutils.unserialiseJSON, sData)
- if success and data and data.x ~= nil and data.dir ~= nil then
- current_pos.x = data.x
- current_pos.y = data.y
- current_pos.z = data.z
- current_dir = data.dir
- log("Position loaded: X:" .. current_pos.x .. " Y:" .. current_pos.y .. " Z:" .. current_pos.z .. " Dir:" .. DIR_NAMES[current_dir])
- return true
- else
- log("Failed to parse position file or data invalid: " .. tostring(data))
- end
- else
- log("Error opening position file for reading: " .. (err or "unknown"))
- end
- else
- log("Position file not found.")
- end
- return false
- end
- local function dirNameToNumber(name)
- name = string.lower(name or "")
- if name == "n" or name == "north" then return 0
- elseif name == "e" or name == "east" then return 1
- elseif name == "s" or name == "south" then return 2
- elseif name == "w" or name == "west" then return 3
- end
- log("Warning: Invalid direction name '" .. (name or "nil") .. "'. Defaulting to North.")
- return 0 -- Default to North if invalid
- end
- local function numberToDirName(num)
- return DIR_NAMES[num] or "Unknown"
- end
- --#endregion
- --#region Core Movement and Action Functions
- function move_lib.turnLeft()
- if turtle.turnLeft() then
- current_dir = (current_dir - 1 + 4) % 4
- log("Turned Left. New Dir: " .. DIR_NAMES[current_dir])
- savePosition()
- return true
- else
- log("Failed to turn left.")
- return false
- end
- end
- move_lib.l = move_lib.turnLeft
- function move_lib.turnRight()
- if turtle.turnRight() then
- current_dir = (current_dir + 1) % 4
- log("Turned Right. New Dir: " .. DIR_NAMES[current_dir])
- savePosition()
- return true
- else
- log("Failed to turn right.")
- return false
- end
- end
- move_lib.r = move_lib.turnRight
- function move_lib.turnAround()
- log("Turning around...")
- if move_lib.turnRight() and move_lib.turnRight() then
- log("Turned Around. New Dir: " .. DIR_NAMES[current_dir])
- return true
- else
- log("Failed to turn around completely.")
- -- State might be inconsistent if one turn fails
- return false
- end
- end
- move_lib.a = move_lib.turnAround
- function move_lib.forward()
- if turtle.forward() then
- local vec = DIR_VECTORS[current_dir]
- current_pos.x = current_pos.x + vec.x
- current_pos.y = current_pos.y + vec.y -- Forward/back doesn't change Y unless flying
- current_pos.z = current_pos.z + vec.z
- log("Moved Forward. New Pos: X:" .. current_pos.x .. " Y:" .. current_pos.y .. " Z:" .. current_pos.z)
- savePosition()
- return true
- else
- log("Failed to move forward (blocked).")
- return false
- end
- end
- move_lib.f = move_lib.forward
- function move_lib.back()
- if turtle.back() then
- local vec = DIR_VECTORS[current_dir]
- current_pos.x = current_pos.x - vec.x
- current_pos.y = current_pos.y - vec.y
- current_pos.z = current_pos.z - vec.z
- log("Moved Back. New Pos: X:" .. current_pos.x .. " Y:" .. current_pos.y .. " Z:" .. current_pos.z)
- savePosition()
- return true
- else
- log("Failed to move back (blocked).")
- return false
- end
- end
- move_lib.b = move_lib.back
- function move_lib.up()
- if turtle.up() then
- current_pos.y = current_pos.y + 1
- log("Moved Up. New Pos: X:" .. current_pos.x .. " Y:" .. current_pos.y .. " Z:" .. current_pos.z)
- savePosition()
- return true
- else
- log("Failed to move up (blocked or no fuel).")
- return false
- end
- end
- move_lib.u = move_lib.up
- function move_lib.down()
- if turtle.down() then
- current_pos.y = current_pos.y - 1
- log("Moved Down. New Pos: X:" .. current_pos.x .. " Y:" .. current_pos.y .. " Z:" .. current_pos.z)
- savePosition()
- return true
- else
- log("Failed to move down (blocked).")
- return false
- end
- end
- move_lib.d = move_lib.down
- function move_lib.home()
- if not automata then
- log("Error: End Automata peripheral not found for home warp.")
- return false
- end
- log("Attempting to warp to 'home'...")
- local success, reason = automata.warpToPoint("home")
- if success then
- log("Warped to home successfully.")
- current_pos.x = 0
- current_pos.y = 0
- current_pos.z = 0
- current_dir = 0 -- Assume facing North after home warp, can be adjusted
- log("Position reset to home (0,0,0,N).")
- savePosition()
- return true
- else
- log("Failed to warp to home: " .. (reason or "unknown error"))
- return false
- end
- end
- move_lib.h = move_lib.home
- function move_lib.refuel()
- log("Starting refuel process...")
- local initialFuel = turtle.getFuelLevel()
- log("Initial fuel level: " .. initialFuel)
- -- 1. Ensure slot 16 is selected and try to consolidate fuel there
- turtle.select(REFUEL_SLOT)
- local fuelInSlot = turtle.getItemCount(REFUEL_SLOT)
- if fuelInSlot > 0 then
- log("Fuel found in slot " .. REFUEL_SLOT .. ": " .. fuelInSlot)
- end
- -- 2. Suck from chest in front until slot 16 is full or chest is empty
- local suckedCount = 0
- local maxStackSize = turtle.getItemSpace(REFUEL_SLOT) -- Or 64 if not specific
- if maxStackSize == 0 and fuelInSlot > 0 then maxStackSize = 64 - fuelInSlot -- Estimate if slot has item
- elseif maxStackSize == 0 and fuelInSlot == 0 then maxStackSize = 64
- else maxStackSize = turtle.getItemSpace(REFUEL_SLOT) end -- Available space
- log("Attempting to suck fuel into slot " .. REFUEL_SLOT .. ". Space available: " .. maxStackSize)
- while turtle.getItemSpace(REFUEL_SLOT) > 0 do -- While there is space in slot 16
- local itemDetail = turtle.getItemDetail(REFUEL_SLOT)
- if itemDetail and not string.find(string.lower(itemDetail.name), FUEL_ITEM_NAME_PART) then
- log("Item in slot " .. REFUEL_SLOT .. " (" .. itemDetail.name .. ") is not fuel. Stopping suck.")
- break -- Slot has non-fuel item
- end
- if turtle.suck() then
- suckedCount = suckedCount + 1
- log("Sucked 1 fuel item. Total sucked: " .. suckedCount)
- if turtle.getItemCount(REFUEL_SLOT) >= 64 then
- log("Slot " .. REFUEL_SLOT .. " is full.")
- break
- end
- else
- log("Failed to suck or chest empty.")
- break
- end
- sleep(0.1) -- Small delay
- end
- -- 3. Refuel from slot 16
- turtle.select(REFUEL_SLOT)
- if turtle.getItemCount(REFUEL_SLOT) > 0 then
- log("Refueling from slot " .. REFUEL_SLOT .. "...")
- local refueled = turtle.refuel(0) -- Refuel all from selected slot
- if refueled then
- log("Successfully refueled. Amount: " .. (turtle.getFuelLevel() - initialFuel - suckedCount)) -- approximation
- else
- log("Refuel command failed, or no fuel items in slot " .. REFUEL_SLOT .. ".")
- end
- else
- log("No fuel items in slot " .. REFUEL_SLOT .. " to refuel with.")
- end
- log("Refuel process finished. Current fuel: " .. turtle.getFuelLevel())
- if turtle.getFuelLevel() == "unlimited" or turtle.getFuelLevel() > initialFuel then
- return true
- else
- return false
- end
- end
- function move_lib.setPos(x, y, z, dir_name_or_num)
- if type(x) ~= "number" or type(y) ~= "number" or type(z) ~= "number" then
- log("Error: setPos requires x, y, z to be numbers.")
- return false
- end
- current_pos.x = x
- current_pos.y = y
- current_pos.z = z
- if type(dir_name_or_num) == "number" then
- current_dir = dir_name_or_num % 4
- else
- current_dir = dirNameToNumber(dir_name_or_num)
- end
- log("Position manually set to: X:" .. x .. " Y:" .. y .. " Z:" .. z .. " Dir:" .. DIR_NAMES[current_dir])
- savePosition()
- return true
- end
- --#endregion
- --#region Advanced Movement (Pathfinding)
- local function turnToDir(target_dir_num)
- if current_dir == target_dir_num then return true end
- log("Turning to target direction: " .. DIR_NAMES[target_dir_num])
- local diff = (target_dir_num - current_dir + 4) % 4
- if diff == 1 then return move_lib.turnRight()
- elseif diff == 2 then return move_lib.turnAround()
- elseif diff == 3 then return move_lib.turnLeft()
- end
- return false -- Should not happen
- end
- function move_lib.moveTo(targetX, targetY, targetZ, targetDir_name_or_num)
- log(string.format("MoveTo called: Target X:%s Y:%s Z:%s Dir:%s",
- tostring(targetX), tostring(targetY), tostring(targetZ), tostring(targetDir_name_or_num)))
- local target_dir_num
- if type(targetDir_name_or_num) == "number" then
- target_dir_num = targetDir_name_or_num % 4
- else
- target_dir_num = dirNameToNumber(targetDir_name_or_num)
- end
- local attempts = 0
- local max_attempts_per_block = 3 -- How many random moves to try if stuck on one axis
- while (current_pos.x ~= targetX or current_pos.y ~= targetY or current_pos.z ~= targetZ) and attempts < 100 do
- attempts = attempts + 1
- local moved_this_cycle = false
- local stuck_on_axis_attempts = 0
- -- Try Y first (often requires fuel and can be tricky)
- if current_pos.y < targetY then
- if move_lib.up() then moved_this_cycle = true else
- log("moveTo: Cannot move UP towards target Y.")
- -- Basic avoidance: try moving on X/Z if also needed, then retry Y
- if (current_pos.x ~= targetX or current_pos.z ~= targetZ) and stuck_on_axis_attempts < max_attempts_per_block then
- stuck_on_axis_attempts = stuck_on_axis_attempts + 1
- log("moveTo: Stuck on Y, trying X/Z move (" .. stuck_on_axis_attempts .. "/" .. max_attempts_per_block .. ")")
- -- (Implementation of X/Z preference here before random)
- elseif stuck_on_axis_attempts < max_attempts_per_block then -- Only Y is wrong, but can't move
- stuck_on_axis_attempts = stuck_on_axis_attempts + 1
- log("moveTo: Stuck only on Y, trying random move (" .. stuck_on_axis_attempts .. "/" .. max_attempts_per_block .. ")")
- local rand_dir = math.random(0,3)
- if turnToDir(rand_dir) and move_lib.forward() then moved_this_cycle = true else log("moveTo: Random move failed.") end
- else
- log("moveTo: Persistently stuck on Y axis. Aborting move for this axis.")
- targetY = current_pos.y -- Give up on Y
- end
- end
- elseif current_pos.y > targetY then
- if move_lib.down() then moved_this_cycle = true else
- log("moveTo: Cannot move DOWN towards target Y.")
- if (current_pos.x ~= targetX or current_pos.z ~= targetZ) and stuck_on_axis_attempts < max_attempts_per_block then
- stuck_on_axis_attempts = stuck_on_axis_attempts + 1
- -- (X/Z preference)
- elseif stuck_on_axis_attempts < max_attempts_per_block then
- stuck_on_axis_attempts = stuck_on_axis_attempts + 1
- local rand_dir = math.random(0,3)
- if turnToDir(rand_dir) and move_lib.forward() then moved_this_cycle = true else log("moveTo: Random move failed.") end
- else
- log("moveTo: Persistently stuck on Y axis. Aborting move for this axis.")
- targetY = current_pos.y
- end
- end
- end
- if moved_this_cycle then stuck_on_axis_attempts = 0; goto continue_main_loop end
- -- Try X
- if current_pos.x < targetX then
- if turnToDir(1) and move_lib.forward() then moved_this_cycle = true else -- Turn East (+X)
- log("moveTo: Cannot move EAST towards target X.")
- if current_pos.z ~= targetZ and stuck_on_axis_attempts < max_attempts_per_block then stuck_on_axis_attempts = stuck_on_axis_attempts + 1 -- (Z preference)
- elseif stuck_on_axis_attempts < max_attempts_per_block then stuck_on_axis_attempts = stuck_on_axis_attempts + 1; local r_dir = (math.random(0,1)*2); if turnToDir(r_dir) and move_lib.forward() then moved_this_cycle = true end -- Try N/S
- else log("moveTo: Persistently stuck on X axis. Aborting move for this axis."); targetX = current_pos.x end
- end
- elseif current_pos.x > targetX then
- if turnToDir(3) and move_lib.forward() then moved_this_cycle = true else -- Turn West (-X)
- log("moveTo: Cannot move WEST towards target X.")
- if current_pos.z ~= targetZ and stuck_on_axis_attempts < max_attempts_per_block then stuck_on_axis_attempts = stuck_on_axis_attempts + 1 -- (Z preference)
- elseif stuck_on_axis_attempts < max_attempts_per_block then stuck_on_axis_attempts = stuck_on_axis_attempts + 1; local r_dir = (math.random(0,1)*2); if turnToDir(r_dir) and move_lib.forward() then moved_this_cycle = true end
- else log("moveTo: Persistently stuck on X axis. Aborting move for this axis."); targetX = current_pos.x end
- end
- end
- if moved_this_cycle then stuck_on_axis_attempts = 0; goto continue_main_loop end
- -- Try Z
- if current_pos.z < targetZ then
- if turnToDir(0) and move_lib.forward() then moved_this_cycle = true else -- Turn North (+Z)
- log("moveTo: Cannot move NORTH towards target Z.")
- if current_pos.x ~= targetX and stuck_on_axis_attempts < max_attempts_per_block then stuck_on_axis_attempts = stuck_on_axis_attempts + 1 -- (X preference)
- elseif stuck_on_axis_attempts < max_attempts_per_block then stuck_on_axis_attempts = stuck_on_axis_attempts + 1; local r_dir = (math.random(0,1)*2)+1; if turnToDir(r_dir) and move_lib.forward() then moved_this_cycle = true end -- Try E/W
- else log("moveTo: Persistently stuck on Z axis. Aborting move for this axis."); targetZ = current_pos.z end
- end
- elseif current_pos.z > targetZ then
- if turnToDir(2) and move_lib.forward() then moved_this_cycle = true else -- Turn South (-Z)
- log("moveTo: Cannot move SOUTH towards target Z.")
- if current_pos.x ~= targetX and stuck_on_axis_attempts < max_attempts_per_block then stuck_on_axis_attempts = stuck_on_axis_attempts + 1 -- (X preference)
- elseif stuck_on_axis_attempts < max_attempts_per_block then stuck_on_axis_attempts = stuck_on_axis_attempts + 1; local r_dir = (math.random(0,1)*2)+1; if turnToDir(r_dir) and move_lib.forward() then moved_this_cycle = true end
- else log("moveTo: Persistently stuck on Z axis. Aborting move for this axis."); targetZ = current_pos.z end
- end
- end
- if moved_this_cycle then stuck_on_axis_attempts = 0; goto continue_main_loop end
- if not moved_this_cycle then
- log("moveTo: No successful primary move this cycle. Attempts: " .. attempts)
- -- This means it's likely truly stuck or has given up on an axis.
- if stuck_on_axis_attempts >= max_attempts_per_block then
- log("moveTo: Max stuck attempts reached for current axis strategy. Breaking loop.")
- break
- end
- end
- ::continue_main_loop::
- sleep(0.05) -- Small delay to prevent cpu overload if stuck
- end
- if attempts >= 100 then
- log("moveTo: Reached max attempts (100). Giving up on reaching exact coordinates.")
- end
- -- Finally, turn to the target direction
- if not turnToDir(target_dir_num) then
- log("moveTo: Failed to turn to final target direction.")
- end
- if current_pos.x == targetX and current_pos.y == targetY and current_pos.z == targetZ and current_dir == target_dir_num then
- log("moveTo: Successfully reached target position and orientation.")
- return true
- else
- log(string.format("moveTo: Finished. Final Pos X:%s Y:%s Z:%s Dir:%s. Target was X:%s Y:%s Z:%s Dir:%s",
- current_pos.x, current_pos.y, current_pos.z, DIR_NAMES[current_dir],
- targetX, targetY, targetZ, DIR_NAMES[target_dir_num]))
- return false
- end
- end
- move_lib.m = move_lib.moveTo
- --#endregion
- --#region Initialization
- function move_lib.init()
- log("Initializing Move Library...")
- if not automata then
- log("CRITICAL WARNING: End Automata peripheral ('"..AUTOMATA_PERIPHERAL_NAME.."') not found. Home warp and auto-init will fail.")
- end
- if not loadPosition() then
- log("Position unknown or load failed. Warping to home and setting default position.")
- if move_lib.home() then -- home() already sets pos to 0,0,0,N and saves
- log("Successfully initialized at home.")
- else
- log("CRITICAL: Failed to warp home for initial setup. Position remains unknown.")
- -- Bot is in an undefined state here. Might need manual setPos.
- current_pos.x = nil -- Ensure it's marked as unknown
- end
- end
- log("Move Library Initialized. Current Pos: X:" .. tostring(current_pos.x) .. " Y:" .. tostring(current_pos.y) .. " Z:" .. tostring(current_pos.z) .. " Dir:" .. DIR_NAMES[current_dir or -1])
- end
- move_lib.getPosition = function() return current_pos end
- move_lib.getDirection = function() return current_dir end
- move_lib.getDirectionName = function() return DIR_NAMES[current_dir] end
- -- Call init when library is loaded
- move_lib.init()
- --#endregion
- return move_lib
Add Comment
Please, Sign In to add comment