Blackhome

MangroveFarmer

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