Blackhome

Tree Farmer

Jun 22nd, 2025 (edited)
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.61 KB | Gaming | 0 0
  1. -- pastebin get Fue55B2k TreeFarmer
  2.  
  3. local numberTrees = ...
  4.  
  5. if numberTrees then
  6.     numberTrees = tonumber(numberTrees)
  7. else
  8.     numberTrees = 1
  9. end
  10.  
  11. local turtlePos, direction
  12. local startPos, startDir
  13.  
  14. local Settings = {
  15.     horDiff = 5,
  16.     vertDiff = 7,           -- 9 for Spruce (8 + 1)
  17.     vertDiffToCollector = 4
  18. }
  19.  
  20.  
  21.  
  22. local function getGPSPosition()
  23.     local x, y, z = gps.locate()
  24.     if not x then print("Error: GPS not available") return nil end
  25.     return {x = math.floor(x), y = math.floor(y), z = math.floor(z)}
  26. end
  27.  
  28. local function negative(vec)
  29.     return {x = -vec.x, y = -vec.y, z = -vec.z}
  30. end
  31.  
  32. local function add(vec1, vec2)
  33.     return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z}
  34. end
  35. local function sub(vec1, vec2)
  36.     return add(vec1, negative(vec2))
  37. end
  38.  
  39. local function multiply(s, vec)
  40.     return {x = s * vec.x, y = s * vec.y, z = s * vec.z}
  41. end
  42.  
  43. local function getSign(number)
  44.     return number / math.abs(number)
  45. end
  46.  
  47. local function isEqual(vec1, vec2)
  48.     return vec1.x == vec2.x and vec1.y == vec2.y and vec1.z == vec2.z
  49. end
  50.  
  51.  
  52. -- === Basic Movement ===
  53.  
  54. local function moveForward()
  55.     if turtle.forward() then
  56.         turtlePos = add(turtlePos, direction)
  57.         return true
  58.     end
  59.     return false
  60. end
  61. local function moveUp()
  62.     if turtle.up() then
  63.         turtlePos.y = turtlePos.y + 1
  64.         return true
  65.     end
  66.     return false
  67. end
  68.  
  69. local function moveDown()
  70.     if turtle.down() then
  71.         turtlePos.y = turtlePos.y - 1
  72.         return true
  73.     end
  74.     return false
  75. end
  76.  
  77. local function forceMoveForward()
  78.     while not moveForward() do turtle.dig() end
  79. end
  80.  
  81. local function forceMoveUp()
  82.     while not moveUp() do turtle.digUp() end
  83. end
  84.  
  85. local function forceMoveDown()
  86.     while not moveDown() do turtle.digDown() end
  87. end
  88.  
  89. -- === Basic Turning ===
  90.  
  91. local function turnInDirection(dir)
  92.     if not direction then return end
  93.     if dir == "left" then
  94.         turtle.turnLeft()
  95.         direction = {x = direction.z, y = 0, z = -direction.x}
  96.     elseif dir == "right" then
  97.         turtle.turnRight()
  98.         direction = {x = -direction.z, y = 0, z = direction.x}
  99.     elseif dir == "around" then
  100.         turtle.turnLeft()
  101.         turtle.turnLeft()
  102.         direction = {x = -direction.x, y = 0, z = -direction.z}
  103.     end
  104. end
  105.  
  106. local function getTurnDirection(from, to)
  107.     local function vectorsEqual(a, b)
  108.         return a.x == b.x and a.z == b.z
  109.     end
  110.  
  111.     if vectorsEqual(from, to) then return nil end
  112.     local left  = {x = from.z, y = 0, z = -from.x}
  113.     local right = {x = -from.z, y = 0, z = from.x}
  114.     local back  = {x = -from.x, y = 0, z = -from.z}
  115.     if vectorsEqual(to, left) then
  116.         return "left"
  117.     elseif vectorsEqual(to, right) then
  118.         return "right"
  119.     elseif vectorsEqual(to, back) then
  120.         return "around"
  121.     else
  122.         error("Invalid target direction")
  123.     end
  124. end
  125.  
  126. local function getDirection()
  127.     local pos1 = getGPSPosition()
  128.     if not pos1 then error("GPS failure, cannot get direction") end
  129.  
  130.     for i = 1, 4 do
  131.         if turtle.forward() then
  132.             local pos2 = getGPSPosition()
  133.             if not pos2 then error("GPS failure after move") end
  134.             -- turtlePos = pos2
  135.             -- Richtung berechnen
  136.             direction = sub(pos2, pos1)
  137.  
  138.             -- zurück zur ursprünglichen Position
  139.             turnInDirection("around")
  140.             forceMoveForward()
  141.  
  142.             -- Sicherheitsprüfung
  143.             local posBack = getGPSPosition()
  144.             if not isEqual(pos1, posBack) then
  145.                 error("Warnung: Position hat sich beim Rückweg verschoben!")
  146.                 turtlePos = posBack -- explizit wieder auf GPS setzen
  147.             else
  148.                 turtlePos = pos1
  149.             end
  150.  
  151.             turnInDirection("around")
  152.             return
  153.         else
  154.             turtle.turnLeft()
  155.         end
  156.     end
  157.  
  158.     error("Unable to move forward in any direction for direction detection")
  159. end
  160.  
  161.  
  162.  
  163. -- === Positioning ===
  164.  
  165. local function goToLift()
  166.     local coord = add(startPos, startDir)
  167.  
  168.     local deltaX = coord.x - turtlePos.x
  169.     local deltaZ = coord.z - turtlePos.z
  170.  
  171.     local dirX = startDir.x
  172.     local dirZ = startDir.z
  173.  
  174.     local function moveToEqualX()
  175.         while not (deltaX == 0) do
  176.             turnInDirection(getTurnDirection(direction, {x = getSign(deltaX), y = 0, z = 0}))
  177.             forceMoveForward()
  178.             deltaX = coord.x - turtlePos.x
  179.         end
  180.     end
  181.  
  182.     local function moveToEqualZ()
  183.         while not (deltaZ == 0) do
  184.             turnInDirection(getTurnDirection(direction, {x = 0, y = 0, z = getSign(deltaZ)}))
  185.             forceMoveForward()
  186.             deltaZ = coord.z - turtlePos.z
  187.         end
  188.     end
  189.  
  190.     if dirX == 0 then
  191.         moveToEqualX()
  192.         moveToEqualZ()
  193.     elseif dirZ == 0 then
  194.         moveToEqualZ()
  195.         moveToEqualX()
  196.     end
  197. end
  198.  
  199. local function goToPos(coord)
  200.     local deltaX = coord.x - turtlePos.x
  201.     local deltaY = coord.y - turtlePos.y
  202.     local deltaZ = coord.z - turtlePos.z
  203.  
  204.     local dirX = startDir.x
  205.     local dirZ = startDir.z
  206.  
  207.     local function moveToEqualX()
  208.         while not (deltaX == 0) do
  209.             turnInDirection(getTurnDirection(direction, {x = getSign(deltaX), y = 0, z = 0}))
  210.             forceMoveForward()
  211.             deltaX = coord.x - turtlePos.x
  212.         end
  213.     end
  214.  
  215.     local function moveToEqualZ()
  216.         while not (deltaZ == 0) do
  217.             turnInDirection(getTurnDirection(direction, {x = 0, y = 0, z = getSign(deltaZ)}))
  218.             forceMoveForward()
  219.             deltaZ = coord.z - turtlePos.z
  220.         end
  221.     end
  222.  
  223.     local function moveToEqualY()
  224.         while not (deltaY == 0) do
  225.             if deltaY > 0 then
  226.                 forceMoveUp()
  227.             elseif deltaY < 0 then
  228.                 forceMoveDown()
  229.             end
  230.             deltaY = coord.y - turtlePos.y
  231.         end
  232.     end
  233.  
  234.     if not (deltaY == 0) then
  235.         goToLift()
  236.         moveToEqualY()
  237.         deltaX = coord.x - turtlePos.x
  238.         deltaZ = coord.z - turtlePos.z
  239.     end
  240.     if dirX == 0 then
  241.         moveToEqualZ()
  242.         moveToEqualX()
  243.     elseif dirZ == 0 then
  244.         moveToEqualX()
  245.         moveToEqualZ()
  246.     end
  247. end
  248.  
  249. -- === Tasks ===
  250.  
  251.  
  252. local function isSapling(item)
  253.     if not item or not item.name then return false end
  254.  
  255.     local saplingSuffix = "_sapling"
  256.     return item.name:match(":.*" .. saplingSuffix .. "$") ~= nil
  257. end
  258.  
  259. local function isBoneMeal(item)
  260.     if not item or not item.name then return false end
  261.     return item.name == "minecraft:bone_meal"
  262. end
  263.  
  264. local function getSaplingsCount()
  265.     local numSaplings = 0
  266.     for i = 1, 16 do
  267.         local item = turtle.getItemDetail(i)
  268.        
  269.         if isSapling(item) then
  270.             numSaplings = numSaplings + turtle.getItemCount(i)
  271.         end
  272.     end
  273.     return numSaplings
  274. end
  275.  
  276. local function getBoneMealCount()
  277.     local num = 0
  278.     for i = 1, 16 do
  279.         local item = turtle.getItemDetail(i)
  280.        
  281.         if isBoneMeal(item) then
  282.             num = num + turtle.getItemCount(i)
  283.         end
  284.     end
  285.     return num
  286. end
  287.  
  288. local function selectSaplingSlot()
  289.     for i = 1, 16 do
  290.         local item = turtle.getItemDetail(i)
  291.        
  292.         if isSapling(item) then
  293.             turtle.select(i)
  294.             return true
  295.         end
  296.     end
  297.     return false
  298. end
  299.  
  300. local function selectBoneMealSlot()
  301.     for i = 1, 16 do
  302.         local item = turtle.getItemDetail(i)
  303.        
  304.         if isBoneMeal(item) then
  305.             turtle.select(i)
  306.             return true
  307.         end
  308.     end
  309.     return false
  310. end
  311.  
  312. local function plantSapling()
  313.     if not selectSaplingSlot() then return false end
  314.     if not turtle.place() then return false end
  315.     return true
  316. end
  317.  
  318. local function isLog(item)
  319.     if not item or not item.name then return false end
  320.  
  321.     local saplingSuffix = "_log"
  322.     return item.name:match(":.*" .. saplingSuffix .. "$") ~= nil
  323. end
  324.  
  325.  
  326. local function fellTree()
  327.     local success, item = turtle.inspect()
  328.  
  329.     if success and isLog(item) then
  330.         forceMoveForward()
  331.         local cnt = 0
  332.         while true do
  333.             local suc, itemUp = turtle.inspectUp()
  334.             if not isLog(itemUp) then
  335.                 break
  336.             end
  337.             forceMoveUp()
  338.             cnt = cnt + 1
  339.         end
  340.         while cnt > 0 do
  341.             forceMoveDown()
  342.             cnt = cnt - 1
  343.         end
  344.         if not fellTree() then
  345.             turnInDirection("right")
  346.             fellTree()
  347.         end
  348.         return true
  349.     end
  350.     return false
  351. end
  352.  
  353. local function fellAndPlantTree()
  354.     local bNeedBoneMeal = true
  355.  
  356.     local prevPos = turtlePos
  357.     local prevDirection = direction
  358.  
  359.     local bFelledOrPlanted = false
  360.     if fellTree() then
  361.         goToPos(prevPos)
  362.         turnInDirection(getTurnDirection(direction, prevDirection))
  363.         moveForward()
  364.         if getSaplingsCount() >= 4 and plantSapling() then      -- plant (1, 1)
  365.             turnInDirection("right")
  366.             moveForward()
  367.             turnInDirection("left")
  368.             plantSapling()                                      -- plant (1, 2)
  369.             turnInDirection("left")
  370.             moveForward()
  371.             turnInDirection("around")
  372.             plantSapling()                                      -- plant (2, 2)
  373.         end
  374.         turnInDirection(getTurnDirection(direction, negative(prevDirection)))
  375.         moveForward()
  376.         turnInDirection("around")
  377.         bNeedBoneMeal = not plantSapling()                      -- plant (2, 1)
  378.         bFelledOrPlanted = true
  379.     elseif moveForward() then
  380.         if getSaplingsCount() >= 4 and plantSapling() then      -- plant (1, 1)
  381.             turnInDirection("right")
  382.             moveForward()
  383.             turnInDirection("left")
  384.             plantSapling()                                      -- plant (1, 2)
  385.             turnInDirection("left")
  386.             moveForward()
  387.             turnInDirection("around")
  388.             plantSapling()                                      -- plant (2, 2)
  389.         end
  390.         turnInDirection(getTurnDirection(direction, negative(prevDirection)))
  391.         moveForward()
  392.         turnInDirection("around")
  393.         bNeedBoneMeal = not plantSapling()                      -- plant (2, 1)
  394.         bFelledOrPlanted = true
  395.     end
  396.     local cnt1 = 0
  397.     while selectBoneMealSlot() and bNeedBoneMeal and cnt1 < 5 do
  398.         if not turtle.place() then
  399.             if fellTree() then
  400.                 goToPos(prevPos)
  401.                 turnInDirection(getTurnDirection(direction, prevDirection))
  402.                 moveForward()
  403.                 if getSaplingsCount() >= 4 and plantSapling() then      -- plant (1, 1)
  404.                     turnInDirection("right")
  405.                     moveForward()
  406.                     turnInDirection("left")
  407.                     plantSapling()                                      -- plant (1, 2)
  408.                     turnInDirection("left")
  409.                     moveForward()
  410.                     turnInDirection("around")
  411.                     plantSapling()                                      -- plant (2, 2)
  412.                 end
  413.                 turnInDirection(getTurnDirection(direction, negative(prevDirection)))
  414.                 moveForward()
  415.                 turnInDirection("around")
  416.                 plantSapling()                                          -- plant (2, 1)
  417.                 bFelledOrPlanted = true
  418.             end
  419.             break
  420.         end
  421.         cnt1 = cnt1 + 1
  422.     end
  423.     return bFelledOrPlanted
  424. end
  425.  
  426.  
  427. -- === Inventory Management ===
  428.  
  429. local function compactInventory()
  430.     for i = 1, 16 do
  431.         local itemI = turtle.getItemDetail(i)
  432.         if itemI and itemI.count < 64 then
  433.             for j = i + 1, 16 do
  434.                 local itemJ = turtle.getItemDetail(j)
  435.                 if itemJ and itemJ.name == itemI.name then
  436.                     turtle.select(j)
  437.                     turtle.transferTo(i)
  438.                    
  439.                     -- Aktuellen Slot updaten
  440.                     itemI = turtle.getItemDetail(i)
  441.                     if not itemI or itemI.count == 64 then
  442.                         break
  443.                     end
  444.                 end
  445.             end
  446.         end
  447.     end
  448.     turtle.select(1) -- zum Standard zurückkehren
  449. end
  450.  
  451. local function pullAllFromChest()
  452.     local success, chest = pcall(peripheral.call, "front", "list")
  453.     if not success or not chest then
  454.         print("No chest found or cannot read from chest.")
  455.         return true  -- Treat as successful (no reason to return)
  456.     end
  457.  
  458.     -- Step 1: Count item stacks BEFORE pulling
  459.     local chestStacksBefore = 0
  460.     for _, stack in pairs(chest) do
  461.         if stack and stack.count > 0 then
  462.             chestStacksBefore = chestStacksBefore + 1
  463.         end
  464.     end
  465.  
  466.     -- Step 2: Count free slots BEFORE pulling
  467.     local freeSlotsBefore = 0
  468.     for i = 1, 16 do
  469.         if turtle.getItemDetail(i) == nil then
  470.             freeSlotsBefore = freeSlotsBefore + 1
  471.         end
  472.     end
  473.  
  474.     -- Step 3: Pull items (use all empty slots)
  475.     for i = 1, 16 do
  476.         if turtle.getItemDetail(i) == nil then
  477.             turtle.select(i)
  478.             turtle.suck()
  479.         end
  480.     end
  481.     turtle.select(1)
  482.  
  483.     -- Step 4: Final decision
  484.     if chestStacksBefore > freeSlotsBefore then
  485.         return false  -- Couldn’t fit everything
  486.     else
  487.         return true   -- Everything fit
  488.     end
  489. end
  490.  
  491. -- Keep saplings
  492. local function dumpInventoryKeepSaplings(n)
  493.     local success, chest = turtle.inspect()
  494.     while not (success and chest.name == "minecraft:chest") do
  495.         print("No chest found. Pls place one in front.")
  496.         print("Press enter to continue.")
  497.         read()
  498.         success, chest = turtle.inspect()
  499.     end
  500.  
  501.     local saplingsKept = 0
  502.  
  503.     for slot = 1, 16 do
  504.         local item = turtle.getItemDetail(slot)
  505.         if item then
  506.             turtle.select(slot)
  507.             if isSapling(item) and saplingsKept < n then
  508.                 local keep = math.min(item.count, n - saplingsKept)
  509.                 local drop = item.count - keep
  510.                 if drop > 0 then
  511.                     turtle.drop(drop)
  512.                 end
  513.                 saplingsKept = saplingsKept + keep
  514.             elseif not isBoneMeal(item) then
  515.                 turtle.drop()
  516.             end
  517.         end
  518.     end
  519.  
  520.     turtle.select(1)
  521. end
  522.  
  523. local function emptyCollectorChest(posCollector)
  524.     goToPos(posCollector)
  525.        
  526.     while not pullAllFromChest() do
  527.         goToPos(startPos)
  528.  
  529.         turnInDirection(getTurnDirection(direction, negative(startDir)))
  530.         dumpInventoryKeepSaplings(2 * numberTrees)
  531.         turnInDirection(getTurnDirection(direction, startDir))
  532.  
  533.         goToPos(posCollector)
  534.     end
  535.  
  536.     -- collects BoneMeal
  537.     compactInventory()
  538.     local success, chest = turtle.inspectDown()
  539.     if success and chest.name == "minecraft:chest" and getBoneMealCount() == 0 then
  540.         turtle.suckDown()
  541.     end
  542.  
  543.     goToPos(startPos)
  544.  
  545.     turnInDirection(getTurnDirection(direction, negative(startDir)))
  546.     dumpInventoryKeepSaplings(math.max(2 * numberTrees, 4))
  547.     turnInDirection(getTurnDirection(direction, startDir))
  548. end
  549.  
  550.  
  551. local function turtleNeedsFuel()
  552.     local fuelLevel = turtle.getFuelLevel()
  553.  
  554.     if fuelLevel < 1000 then
  555.         if not isEqual(startPos, turtlePos) then
  556.             goToPos(startPos)
  557.         end
  558.         print("Turtle needs more fuel")
  559.         return true
  560.     end
  561.     return false
  562. end
  563.  
  564. -- === Initialization ===
  565.  
  566. local function saveStartingParameters()
  567.     local parameterList = {pos = turtlePos, dir = direction, numTrees = numberTrees}
  568.  
  569.     local file = fs.open("startingParameters.txt", "w")
  570.     file.write(textutils.serialize(parameterList))
  571.     file.close()
  572. end
  573.  
  574. local function loadStartingParameters()
  575.     if not fs.exists("startingParameters.txt") then return false end
  576.  
  577.     local file = fs.open("startingParameters.txt", "r")
  578.     local data = textutils.unserialize(file.readAll())
  579.  
  580.     file.close()
  581.  
  582.     startPos = data.pos
  583.     startDir = data.dir
  584.     numberTrees = data.numTrees
  585.     return true
  586. end
  587.  
  588. local function initProgram()
  589.     turtlePos = getGPSPosition()
  590.     if not turtlePos then error("GPS failure, cannot run the program") end
  591.    
  592.     getDirection()
  593.  
  594.     if not loadStartingParameters() then
  595.         startPos = turtlePos
  596.         startDir = direction
  597.         saveStartingParameters()
  598.     end
  599. end
  600.  
  601.  
  602. local function main()
  603.     if turtleNeedsFuel() then      
  604.         return
  605.     end
  606.  
  607.     local dirUp = {x = 0, y = 1, z = 0}
  608.     local posTree_XZ = add(startPos, multiply(Settings.horDiff, startDir))
  609.  
  610.     local posCollector = sub(posTree_XZ, multiply(Settings.vertDiffToCollector, dirUp))
  611.    
  612.     local numSaplings = getSaplingsCount()
  613.  
  614.     if numSaplings < math.max(numberTrees, 4) then
  615.         emptyCollectorChest(posCollector)
  616.         numSaplings = getSaplingsCount()
  617.         while numSaplings < numberTrees and numSaplings < 4 do
  618.             print("Not enough saplings. Please put at least " .. tostring(math.max(numberTrees, 4)) .. " in the inventory.")
  619.             read()
  620.             numSaplings = getSaplingsCount()
  621.         end
  622.     end
  623.  
  624.     while true do
  625.         local bCollect = false
  626.         if turtleNeedsFuel() then
  627.             return
  628.         end
  629.  
  630.         numSaplings = getSaplingsCount()
  631.         local bFirstLoop = true
  632.         while numSaplings < numberTrees and numSaplings < 4 do
  633.             emptyCollectorChest(posCollector)
  634.             numSaplings = getSaplingsCount()
  635.             if numSaplings < numberTrees and numSaplings < 4 then
  636.                 if bFirstLoop then
  637.                     bFirstLoop = false
  638.                     print("Not enough Saplings. Waiting for more.")
  639.                 end
  640.                 sleep(30)
  641.             elseif not bFirstLoop then
  642.                 print("Found enough Saplings, continuing work.\n")
  643.             end
  644.         end
  645.  
  646.         for cnt = 0, numberTrees - 1 do
  647.             local posNextTree = add(posTree_XZ, multiply(cnt * Settings.vertDiff, dirUp))
  648.  
  649.             goToPos(posNextTree)
  650.             turnInDirection(getTurnDirection(direction, startDir))
  651.             if fellAndPlantTree() then
  652.                 bCollect = true
  653.             end
  654.         end
  655.         if bCollect then
  656.             emptyCollectorChest(posCollector)
  657.         else
  658.             goToPos(startPos)
  659.             turnInDirection(getTurnDirection(direction, startDir))
  660.         end
  661.  
  662.         if not (getBoneMealCount() > 0) then
  663.             sleep(60)
  664.         else
  665.             sleep(30)
  666.         end
  667.     end
  668. end
  669.  
  670. initProgram()
  671. main()
Add Comment
Please, Sign In to add comment