Myros27

move_lib.lua

May 18th, 2025
24
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 20.13 KB | None | 0 0
  1. --[[
  2.     move_lib.lua v1.0
  3.     A comprehensive movement and positioning library for ComputerCraft turtles.
  4.     Manages position, orientation, and provides movement commands with basic obstacle avoidance.
  5. ]]
  6.  
  7. local move_lib = {}
  8.  
  9. --#region Configuration and State
  10. local AUTOMATA_PERIPHERAL_NAME = "end_automata" -- Corrected from "endAutomata" as CC peripherals are snake_case
  11. local POSITION_FILE = "turtle_pos.json" -- File to save/load position and orientation
  12. local REFUEL_SLOT = 16 -- Standard fuel slot
  13. local FUEL_ITEM_NAME_PART = "coal" -- Used to identify fuel items for refueling logic (can be broader if needed)
  14.  
  15. -- Internal state
  16. local automata = peripheral.find(AUTOMATA_PERIPHERAL_NAME)
  17. local current_pos = { x = nil, y = nil, z = nil }
  18. local current_dir = nil -- 0: North (+Z), 1: East (+X), 2: South (-Z), 3: West (-X)
  19.  
  20. -- Direction vectors (dx, dy, dz)
  21. local DIR_VECTORS = {
  22.     [0] = {x = 0, y = 0, z = 1},  -- North (+Z)
  23.     [1] = {x = 1, y = 0, z = 0},  -- East (+X)
  24.     [2] = {x = 0, y = 0, z = -1}, -- South (-Z)
  25.     [3] = {x = -1, y = 0, z = 0}  -- West (-X)
  26. }
  27.  
  28. local DIR_NAMES = {
  29.     [0] = "North (+Z)",
  30.     [1] = "East (+X)",
  31.     [2] = "South (-Z)",
  32.     [3] = "West (-X)"
  33. }
  34. --#endregion
  35.  
  36. --#region Helper Functions (Internal)
  37. local function log(message)
  38.     print("[MoveLib] " .. message)
  39.     -- If you had a global debug logger for the main bot, you could call it here
  40. end
  41.  
  42. local function savePosition()
  43.     if current_pos.x == nil or current_dir == nil then
  44.         log("Cannot save position, state is unknown.")
  45.         return
  46.     end
  47.     local data = {
  48.         x = current_pos.x,
  49.         y = current_pos.y,
  50.         z = current_pos.z,
  51.         dir = current_dir
  52.     }
  53.     local file, err = fs.open(POSITION_FILE, "w")
  54.     if file then
  55.         file.write(textutils.serialiseJSON(data))
  56.         file.close()
  57.         log("Position saved: X:" .. data.x .. " Y:" .. data.y .. " Z:" .. data.z .. " Dir:" .. DIR_NAMES[data.dir])
  58.     else
  59.         log("Error saving position: " .. (err or "unknown"))
  60.     end
  61. end
  62.  
  63. local function loadPosition()
  64.     if fs.exists(POSITION_FILE) then
  65.         local file, err = fs.open(POSITION_FILE, "r")
  66.         if file then
  67.             local sData = file.readAll()
  68.             file.close()
  69.             local success, data = pcall(textutils.unserialiseJSON, sData)
  70.             if success and data and data.x ~= nil and data.dir ~= nil then
  71.                 current_pos.x = data.x
  72.                 current_pos.y = data.y
  73.                 current_pos.z = data.z
  74.                 current_dir = data.dir
  75.                 log("Position loaded: X:" .. current_pos.x .. " Y:" .. current_pos.y .. " Z:" .. current_pos.z .. " Dir:" .. DIR_NAMES[current_dir])
  76.                 return true
  77.             else
  78.                 log("Failed to parse position file or data invalid: " .. tostring(data))
  79.             end
  80.         else
  81.             log("Error opening position file for reading: " .. (err or "unknown"))
  82.         end
  83.     else
  84.         log("Position file not found.")
  85.     end
  86.     return false
  87. end
  88.  
  89. local function dirNameToNumber(name)
  90.     name = string.lower(name or "")
  91.     if name == "n" or name == "north" then return 0
  92.     elseif name == "e" or name == "east" then return 1
  93.     elseif name == "s" or name == "south" then return 2
  94.     elseif name == "w" or name == "west" then return 3
  95.     end
  96.     log("Warning: Invalid direction name '" .. (name or "nil") .. "'. Defaulting to North.")
  97.     return 0 -- Default to North if invalid
  98. end
  99.  
  100. local function numberToDirName(num)
  101.     return DIR_NAMES[num] or "Unknown"
  102. end
  103. --#endregion
  104.  
  105. --#region Core Movement and Action Functions
  106. function move_lib.turnLeft()
  107.     if turtle.turnLeft() then
  108.         current_dir = (current_dir - 1 + 4) % 4
  109.         log("Turned Left. New Dir: " .. DIR_NAMES[current_dir])
  110.         savePosition()
  111.         return true
  112.     else
  113.         log("Failed to turn left.")
  114.         return false
  115.     end
  116. end
  117. move_lib.l = move_lib.turnLeft
  118.  
  119. function move_lib.turnRight()
  120.     if turtle.turnRight() then
  121.         current_dir = (current_dir + 1) % 4
  122.         log("Turned Right. New Dir: " .. DIR_NAMES[current_dir])
  123.         savePosition()
  124.         return true
  125.     else
  126.         log("Failed to turn right.")
  127.         return false
  128.     end
  129. end
  130. move_lib.r = move_lib.turnRight
  131.  
  132. function move_lib.turnAround()
  133.     log("Turning around...")
  134.     if move_lib.turnRight() and move_lib.turnRight() then
  135.         log("Turned Around. New Dir: " .. DIR_NAMES[current_dir])
  136.         return true
  137.     else
  138.         log("Failed to turn around completely.")
  139.         -- State might be inconsistent if one turn fails
  140.         return false
  141.     end
  142. end
  143. move_lib.a = move_lib.turnAround
  144.  
  145. function move_lib.forward()
  146.     if turtle.forward() then
  147.         local vec = DIR_VECTORS[current_dir]
  148.         current_pos.x = current_pos.x + vec.x
  149.         current_pos.y = current_pos.y + vec.y -- Forward/back doesn't change Y unless flying
  150.         current_pos.z = current_pos.z + vec.z
  151.         log("Moved Forward. New Pos: X:" .. current_pos.x .. " Y:" .. current_pos.y .. " Z:" .. current_pos.z)
  152.         savePosition()
  153.         return true
  154.     else
  155.         log("Failed to move forward (blocked).")
  156.         return false
  157.     end
  158. end
  159. move_lib.f = move_lib.forward
  160.  
  161. function move_lib.back()
  162.     if turtle.back() then
  163.         local vec = DIR_VECTORS[current_dir]
  164.         current_pos.x = current_pos.x - vec.x
  165.         current_pos.y = current_pos.y - vec.y
  166.         current_pos.z = current_pos.z - vec.z
  167.         log("Moved Back. New Pos: X:" .. current_pos.x .. " Y:" .. current_pos.y .. " Z:" .. current_pos.z)
  168.         savePosition()
  169.         return true
  170.     else
  171.         log("Failed to move back (blocked).")
  172.         return false
  173.     end
  174. end
  175. move_lib.b = move_lib.back
  176.  
  177. function move_lib.up()
  178.     if turtle.up() then
  179.         current_pos.y = current_pos.y + 1
  180.         log("Moved Up. New Pos: X:" .. current_pos.x .. " Y:" .. current_pos.y .. " Z:" .. current_pos.z)
  181.         savePosition()
  182.         return true
  183.     else
  184.         log("Failed to move up (blocked or no fuel).")
  185.         return false
  186.     end
  187. end
  188. move_lib.u = move_lib.up
  189.  
  190. function move_lib.down()
  191.     if turtle.down() then
  192.         current_pos.y = current_pos.y - 1
  193.         log("Moved Down. New Pos: X:" .. current_pos.x .. " Y:" .. current_pos.y .. " Z:" .. current_pos.z)
  194.         savePosition()
  195.         return true
  196.     else
  197.         log("Failed to move down (blocked).")
  198.         return false
  199.     end
  200. end
  201. move_lib.d = move_lib.down
  202.  
  203. function move_lib.home()
  204.     if not automata then
  205.         log("Error: End Automata peripheral not found for home warp.")
  206.         return false
  207.     end
  208.     log("Attempting to warp to 'home'...")
  209.     local success, reason = automata.warpToPoint("home")
  210.     if success then
  211.         log("Warped to home successfully.")
  212.         current_pos.x = 0
  213.         current_pos.y = 0
  214.         current_pos.z = 0
  215.         current_dir = 0 -- Assume facing North after home warp, can be adjusted
  216.         log("Position reset to home (0,0,0,N).")
  217.         savePosition()
  218.         return true
  219.     else
  220.         log("Failed to warp to home: " .. (reason or "unknown error"))
  221.         return false
  222.     end
  223. end
  224. move_lib.h = move_lib.home
  225.  
  226. function move_lib.refuel()
  227.     log("Starting refuel process...")
  228.     local initialFuel = turtle.getFuelLevel()
  229.     log("Initial fuel level: " .. initialFuel)
  230.  
  231.     -- 1. Ensure slot 16 is selected and try to consolidate fuel there
  232.     turtle.select(REFUEL_SLOT)
  233.     local fuelInSlot = turtle.getItemCount(REFUEL_SLOT)
  234.     if fuelInSlot > 0 then
  235.         log("Fuel found in slot " .. REFUEL_SLOT .. ": " .. fuelInSlot)
  236.     end
  237.  
  238.     -- 2. Suck from chest in front until slot 16 is full or chest is empty
  239.     local suckedCount = 0
  240.     local maxStackSize = turtle.getItemSpace(REFUEL_SLOT) -- Or 64 if not specific
  241.     if maxStackSize == 0 and fuelInSlot > 0 then maxStackSize = 64 - fuelInSlot -- Estimate if slot has item
  242.     elseif maxStackSize == 0 and fuelInSlot == 0 then maxStackSize = 64
  243.     else maxStackSize = turtle.getItemSpace(REFUEL_SLOT) end -- Available space
  244.  
  245.     log("Attempting to suck fuel into slot " .. REFUEL_SLOT .. ". Space available: " .. maxStackSize)
  246.     while turtle.getItemSpace(REFUEL_SLOT) > 0 do -- While there is space in slot 16
  247.         local itemDetail = turtle.getItemDetail(REFUEL_SLOT)
  248.         if itemDetail and not string.find(string.lower(itemDetail.name), FUEL_ITEM_NAME_PART) then
  249.             log("Item in slot " .. REFUEL_SLOT .. " (" .. itemDetail.name .. ") is not fuel. Stopping suck.")
  250.             break -- Slot has non-fuel item
  251.         end
  252.         if turtle.suck() then
  253.             suckedCount = suckedCount + 1
  254.             log("Sucked 1 fuel item. Total sucked: " .. suckedCount)
  255.             if turtle.getItemCount(REFUEL_SLOT) >= 64 then
  256.                 log("Slot " .. REFUEL_SLOT .. " is full.")
  257.                 break
  258.             end
  259.         else
  260.             log("Failed to suck or chest empty.")
  261.             break
  262.         end
  263.         sleep(0.1) -- Small delay
  264.     end
  265.  
  266.     -- 3. Refuel from slot 16
  267.     turtle.select(REFUEL_SLOT)
  268.     if turtle.getItemCount(REFUEL_SLOT) > 0 then
  269.         log("Refueling from slot " .. REFUEL_SLOT .. "...")
  270.         local refueled = turtle.refuel(0) -- Refuel all from selected slot
  271.         if refueled then
  272.             log("Successfully refueled. Amount: " .. (turtle.getFuelLevel() - initialFuel - suckedCount)) -- approximation
  273.         else
  274.             log("Refuel command failed, or no fuel items in slot " .. REFUEL_SLOT .. ".")
  275.         end
  276.     else
  277.         log("No fuel items in slot " .. REFUEL_SLOT .. " to refuel with.")
  278.     end
  279.  
  280.     log("Refuel process finished. Current fuel: " .. turtle.getFuelLevel())
  281.     if turtle.getFuelLevel() == "unlimited" or turtle.getFuelLevel() > initialFuel then
  282.         return true
  283.     else
  284.         return false
  285.     end
  286. end
  287.  
  288. function move_lib.setPos(x, y, z, dir_name_or_num)
  289.     if type(x) ~= "number" or type(y) ~= "number" or type(z) ~= "number" then
  290.         log("Error: setPos requires x, y, z to be numbers.")
  291.         return false
  292.     end
  293.     current_pos.x = x
  294.     current_pos.y = y
  295.     current_pos.z = z
  296.     if type(dir_name_or_num) == "number" then
  297.         current_dir = dir_name_or_num % 4
  298.     else
  299.         current_dir = dirNameToNumber(dir_name_or_num)
  300.     end
  301.     log("Position manually set to: X:" .. x .. " Y:" .. y .. " Z:" .. z .. " Dir:" .. DIR_NAMES[current_dir])
  302.     savePosition()
  303.     return true
  304. end
  305.  
  306. --#endregion
  307.  
  308. --#region Advanced Movement (Pathfinding)
  309. local function turnToDir(target_dir_num)
  310.     if current_dir == target_dir_num then return true end
  311.     log("Turning to target direction: " .. DIR_NAMES[target_dir_num])
  312.     local diff = (target_dir_num - current_dir + 4) % 4
  313.     if diff == 1 then return move_lib.turnRight()
  314.     elseif diff == 2 then return move_lib.turnAround()
  315.     elseif diff == 3 then return move_lib.turnLeft()
  316.     end
  317.     return false -- Should not happen
  318. end
  319.  
  320. function move_lib.moveTo(targetX, targetY, targetZ, targetDir_name_or_num)
  321.     log(string.format("MoveTo called: Target X:%s Y:%s Z:%s Dir:%s",
  322.         tostring(targetX), tostring(targetY), tostring(targetZ), tostring(targetDir_name_or_num)))
  323.  
  324.     local target_dir_num
  325.     if type(targetDir_name_or_num) == "number" then
  326.         target_dir_num = targetDir_name_or_num % 4
  327.     else
  328.         target_dir_num = dirNameToNumber(targetDir_name_or_num)
  329.     end
  330.  
  331.     local attempts = 0
  332.     local max_attempts_per_block = 3 -- How many random moves to try if stuck on one axis
  333.  
  334.     while (current_pos.x ~= targetX or current_pos.y ~= targetY or current_pos.z ~= targetZ) and attempts < 100 do
  335.         attempts = attempts + 1
  336.         local moved_this_cycle = false
  337.         local stuck_on_axis_attempts = 0
  338.  
  339.         -- Try Y first (often requires fuel and can be tricky)
  340.         if current_pos.y < targetY then
  341.             if move_lib.up() then moved_this_cycle = true else
  342.                 log("moveTo: Cannot move UP towards target Y.")
  343.                 -- Basic avoidance: try moving on X/Z if also needed, then retry Y
  344.                 if (current_pos.x ~= targetX or current_pos.z ~= targetZ) and stuck_on_axis_attempts < max_attempts_per_block then
  345.                     stuck_on_axis_attempts = stuck_on_axis_attempts + 1
  346.                     log("moveTo: Stuck on Y, trying X/Z move (" .. stuck_on_axis_attempts .. "/" .. max_attempts_per_block .. ")")
  347.                     -- (Implementation of X/Z preference here before random)
  348.                 elseif stuck_on_axis_attempts < max_attempts_per_block then -- Only Y is wrong, but can't move
  349.                     stuck_on_axis_attempts = stuck_on_axis_attempts + 1
  350.                     log("moveTo: Stuck only on Y, trying random move (" .. stuck_on_axis_attempts .. "/" .. max_attempts_per_block .. ")")
  351.                     local rand_dir = math.random(0,3)
  352.                     if turnToDir(rand_dir) and move_lib.forward() then moved_this_cycle = true else log("moveTo: Random move failed.") end
  353.                 else
  354.                     log("moveTo: Persistently stuck on Y axis. Aborting move for this axis.")
  355.                     targetY = current_pos.y -- Give up on Y
  356.                 end
  357.             end
  358.         elseif current_pos.y > targetY then
  359.             if move_lib.down() then moved_this_cycle = true else
  360.                 log("moveTo: Cannot move DOWN towards target Y.")
  361.                  if (current_pos.x ~= targetX or current_pos.z ~= targetZ) and stuck_on_axis_attempts < max_attempts_per_block then
  362.                     stuck_on_axis_attempts = stuck_on_axis_attempts + 1
  363.                     -- (X/Z preference)
  364.                 elseif stuck_on_axis_attempts < max_attempts_per_block then
  365.                      stuck_on_axis_attempts = stuck_on_axis_attempts + 1
  366.                     local rand_dir = math.random(0,3)
  367.                     if turnToDir(rand_dir) and move_lib.forward() then moved_this_cycle = true else log("moveTo: Random move failed.") end
  368.                 else
  369.                     log("moveTo: Persistently stuck on Y axis. Aborting move for this axis.")
  370.                     targetY = current_pos.y
  371.                 end
  372.             end
  373.         end
  374.         if moved_this_cycle then stuck_on_axis_attempts = 0; goto continue_main_loop end
  375.  
  376.         -- Try X
  377.         if current_pos.x < targetX then
  378.             if turnToDir(1) and move_lib.forward() then moved_this_cycle = true else -- Turn East (+X)
  379.                 log("moveTo: Cannot move EAST towards target X.")
  380.                 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)
  381.                 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
  382.                 else log("moveTo: Persistently stuck on X axis. Aborting move for this axis."); targetX = current_pos.x end
  383.             end
  384.         elseif current_pos.x > targetX then
  385.             if turnToDir(3) and move_lib.forward() then moved_this_cycle = true else -- Turn West (-X)
  386.                 log("moveTo: Cannot move WEST towards target X.")
  387.                 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)
  388.                 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
  389.                 else log("moveTo: Persistently stuck on X axis. Aborting move for this axis."); targetX = current_pos.x end
  390.             end
  391.         end
  392.         if moved_this_cycle then stuck_on_axis_attempts = 0; goto continue_main_loop end
  393.  
  394.         -- Try Z
  395.         if current_pos.z < targetZ then
  396.             if turnToDir(0) and move_lib.forward() then moved_this_cycle = true else -- Turn North (+Z)
  397.                 log("moveTo: Cannot move NORTH towards target Z.")
  398.                 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)
  399.                 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
  400.                 else log("moveTo: Persistently stuck on Z axis. Aborting move for this axis."); targetZ = current_pos.z end
  401.             end
  402.         elseif current_pos.z > targetZ then
  403.             if turnToDir(2) and move_lib.forward() then moved_this_cycle = true else -- Turn South (-Z)
  404.                 log("moveTo: Cannot move SOUTH towards target Z.")
  405.                 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)
  406.                 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
  407.                 else log("moveTo: Persistently stuck on Z axis. Aborting move for this axis."); targetZ = current_pos.z end
  408.             end
  409.         end
  410.         if moved_this_cycle then stuck_on_axis_attempts = 0; goto continue_main_loop end
  411.  
  412.         if not moved_this_cycle then
  413.             log("moveTo: No successful primary move this cycle. Attempts: " .. attempts)
  414.             -- This means it's likely truly stuck or has given up on an axis.
  415.             if stuck_on_axis_attempts >= max_attempts_per_block then
  416.                  log("moveTo: Max stuck attempts reached for current axis strategy. Breaking loop.")
  417.                  break
  418.             end
  419.         end
  420.         ::continue_main_loop::
  421.         sleep(0.05) -- Small delay to prevent cpu overload if stuck
  422.     end
  423.  
  424.     if attempts >= 100 then
  425.         log("moveTo: Reached max attempts (100). Giving up on reaching exact coordinates.")
  426.     end
  427.  
  428.     -- Finally, turn to the target direction
  429.     if not turnToDir(target_dir_num) then
  430.         log("moveTo: Failed to turn to final target direction.")
  431.     end
  432.  
  433.     if current_pos.x == targetX and current_pos.y == targetY and current_pos.z == targetZ and current_dir == target_dir_num then
  434.         log("moveTo: Successfully reached target position and orientation.")
  435.         return true
  436.     else
  437.         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",
  438.             current_pos.x, current_pos.y, current_pos.z, DIR_NAMES[current_dir],
  439.             targetX, targetY, targetZ, DIR_NAMES[target_dir_num]))
  440.         return false
  441.     end
  442. end
  443. move_lib.m = move_lib.moveTo
  444. --#endregion
  445.  
  446. --#region Initialization
  447. function move_lib.init()
  448.     log("Initializing Move Library...")
  449.     if not automata then
  450.         log("CRITICAL WARNING: End Automata peripheral ('"..AUTOMATA_PERIPHERAL_NAME.."') not found. Home warp and auto-init will fail.")
  451.     end
  452.  
  453.     if not loadPosition() then
  454.         log("Position unknown or load failed. Warping to home and setting default position.")
  455.         if move_lib.home() then -- home() already sets pos to 0,0,0,N and saves
  456.             log("Successfully initialized at home.")
  457.         else
  458.             log("CRITICAL: Failed to warp home for initial setup. Position remains unknown.")
  459.             -- Bot is in an undefined state here. Might need manual setPos.
  460.             current_pos.x = nil -- Ensure it's marked as unknown
  461.         end
  462.     end
  463.     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])
  464. end
  465.  
  466. move_lib.getPosition = function() return current_pos end
  467. move_lib.getDirection = function() return current_dir end
  468. move_lib.getDirectionName = function() return DIR_NAMES[current_dir] end
  469.  
  470. -- Call init when library is loaded
  471. move_lib.init()
  472. --#endregion
  473.  
  474. return move_lib
Add Comment
Please, Sign In to add comment