local turtlePos, direction
local startPos, startDir
local function getGPSPosition()
local x, y, z = gps.locate()
if not x then print("Error: GPS not available") return nil end
return {x = math.floor(x), y = math.floor(y), z = math.floor(z)}
end
local function negative(vec)
return {x = -vec.x, y = -vec.y, z = -vec.z}
end
local function add(vec1, vec2)
return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z}
end
local function sub(vec1, vec2)
return add(vec1, negative(vec2))
end
local function multiply(s, vec)
return {x = s * vec.x, y = s * vec.y, z = s * vec.z}
end
local function getSign(number)
return number / math.abs(number)
end
local function isEqual(vec1, vec2)
return vec1.x == vec2.x and vec1.y == vec2.y and vec1.z == vec2.z
end
-- === Basic Movement ===
local function moveForward()
if turtle.forward() then
turtlePos = add(turtlePos, direction)
return true
end
return false
end
local function moveUp()
if turtle.up() then
turtlePos.y = turtlePos.y + 1
return true
end
return false
end
local function moveDown()
if turtle.down() then
turtlePos.y = turtlePos.y - 1
return true
end
return false
end
local function forceMoveForward()
while not moveForward() do turtle.dig() end
end
local function forceMoveUp()
while not moveUp() do turtle.digUp() end
end
local function forceMoveDown()
while not moveDown() do turtle.digDown() end
end
-- === Basic Turning ===
local function turnInDirection(dir)
if not direction then return end
if dir == "left" then
turtle.turnLeft()
direction = {x = direction.z, y = 0, z = -direction.x}
elseif dir == "right" then
turtle.turnRight()
direction = {x = -direction.z, y = 0, z = direction.x}
elseif dir == "around" then
turtle.turnLeft()
turtle.turnLeft()
direction = {x = -direction.x, y = 0, z = -direction.z}
end
end
local function getTurnDirection(from, to)
local function vectorsEqual(a, b)
return a.x == b.x and a.z == b.z
end
if vectorsEqual(from, to) then return nil end
local left = {x = from.z, y = 0, z = -from.x}
local right = {x = -from.z, y = 0, z = from.x}
local back = {x = -from.x, y = 0, z = -from.z}
if vectorsEqual(to, left) then
return "left"
elseif vectorsEqual(to, right) then
return "right"
elseif vectorsEqual(to, back) then
return "around"
else
error("Invalid target direction")
end
end
local function getDirection()
local pos1 = getGPSPosition()
if not pos1 then error("GPS failure, cannot get direction") end
for i = 1, 4 do
if turtle.forward() then
local pos2 = getGPSPosition()
if not pos2 then error("GPS failure after move") end
-- turtlePos = pos2
-- Richtung berechnen
direction = sub(pos2, pos1)
-- zurück zur ursprünglichen Position
turnInDirection("around")
forceMoveForward()
-- Sicherheitsprüfung
local posBack = getGPSPosition()
if not isEqual(pos1, posBack) then
error("Warnung: Position hat sich beim Rückweg verschoben!")
turtlePos = posBack -- explizit wieder auf GPS setzen
else
turtlePos = pos1
end
turnInDirection("around")
return
else
turtle.turnLeft()
end
end
error("Unable to move forward in any direction for direction detection")
end
-- === Positioning ===
local function goToPos(coord)
local deltaX = coord.x - turtlePos.x
local deltaY = coord.y - turtlePos.y
local deltaZ = coord.z - turtlePos.z
local dirX = startDir.x
local dirZ = startDir.z
local function moveToEqualX()
while not (deltaX == 0) do
turnInDirection(getTurnDirection(direction, {x = getSign(deltaX), y = 0, z = 0}))
forceMoveForward()
deltaX = coord.x - turtlePos.x
end
end
local function moveToEqualZ()
while not (deltaZ == 0) do
turnInDirection(getTurnDirection(direction, {x = 0, y = 0, z = getSign(deltaZ)}))
forceMoveForward()
deltaZ = coord.z - turtlePos.z
end
end
local function moveToEqualY()
while not (deltaY == 0) do
if deltaY > 0 then
forceMoveUp()
elseif deltaY < 0 then
forceMoveDown()
end
deltaY = coord.y - turtlePos.y
end
end
if dirX == 0 then
moveToEqualZ()
moveToEqualX()
elseif dirZ == 0 then
moveToEqualX()
moveToEqualZ()
end
if not (deltaY == 0) then
moveToEqualY()
deltaX = coord.x - turtlePos.x
deltaZ = coord.z - turtlePos.z
end
end
------------------------- Startpos adapten
local function goToStartOfTree()
startTreePos = add(startPos, multiply(7, startDir))
goToPos(startTreePos)
turnInDirection(getTurnDirection(direction, startDir))
end
-- === Tasks ===
local function isSapling(item)
if not item or not item.name then return false end
local saplingSuffix = "pale_oak_sapling"
return item.name:match(":.*" .. saplingSuffix .. "$") ~= nil
end
local function isBoneMeal(item)
if not item or not item.name then return false end
return item.name == "minecraft:bone_meal"
end
local function isLog(item)
if not item or not item.name then return false end
local logSuffix = "_log"
return item.name:match(":.*" .. logSuffix .. "$") ~= nil
end
local function getBoneMealCount()
local num = 0
for i = 1, 16 do
local item = turtle.getItemDetail(i)
if isBoneMeal(item) then
num = num + turtle.getItemCount(i)
end
end
return num
end
local function getSaplingCount()
local num = 0
for i = 1, 16 do
local item = turtle.getItemDetail(i)
if isSapling(item) then
num = num + turtle.getItemCount(i)
end
end
return num
end
local function selectSaplingSlot()
for i = 1, 16 do
local item = turtle.getItemDetail(i)
if isSapling(item) then
turtle.select(i)
return true
end
end
return false
end
local function selectBoneMealSlot()
for i = 1, 16 do
local item = turtle.getItemDetail(i)
if isBoneMeal(item) then
turtle.select(i)
return true
end
end
return false
end
local function plantSapling()
if not selectSaplingSlot() then return false end
if not turtle.place() then return false end
return true
end
local function plantAllSaplings()
local function plantSaplingDown()
if not selectSaplingSlot() then return false end
if not turtle.placeDown() then
suc, item = turtle.inspectDown()
if suc and isSapling(item) then return true end
return false
end
return true
end
goToStartOfTree()
forceMoveUp()
suc, item = turtle.inspect()
if suc and isLog(item) then
goToStartOfTree()
return false
end
local returnValue = true
forceMoveForward()
returnValue = returnValue and plantSaplingDown()
forceMoveForward()
returnValue = returnValue and plantSaplingDown()
turnInDirection("right")
forceMoveForward()
turnInDirection("right")
returnValue = returnValue and plantSaplingDown()
forceMoveForward()
returnValue = returnValue and plantSaplingDown()
goToStartOfTree()
return returnValue
end
local function fellTillLeaves_GetTurnDir(cnt)
local sucUp, itemUp = turtle.inspectUp()
local sucFor, itemFor = turtle.inspect()
local topTurnDir = "left"
while (not (sucUp or itemUp.name == "minecraft:pale_oak_leaves")) and cnt < 20 do
if sucFor and isLog(itemFor.name) then
turtle.dig()
end
forceMoveUp()
sucUp, itemUp = turtle.inspectUp()
sucFor, itemFor = turtle.inspect()
cnt = cnt + 1
end
if (not (sucFor or itemFor.name == "minecraft:pale_oak_leaves") and cnt < 20) then
topTurnDir = "right"
forceMoveForward()
sucUp, itemUp = turtle.inspectUp()
while (not (sucUp or itemUp.name == "minecraft:pale_oak_leaves")) and cnt < 20 do
forceMoveUp()
sucUp, itemUp = turtle.inspectUp()
cnt = cnt + 1
end
end
return topTurnDir
end
local function fellAtPos(startFellPos)
goToPos(startFellPos)
turnInDirection(getTurnDirection(direction, startDir))
local cnt = turtlePos.y - startFellPos.y
local topTurnDir = fellTillLeaves_GetTurnDir(cnt)
cnt = turtlePos.y - startFellPos.y
turnInDirection("right")
forceMoveForward()
turnInDirection(topTurnDir)
topTurnDir = fellTillLeaves_GetTurnDir(cnt)
if topTurnDir == "right" then
turnInDirection("around")
end
while turtlePos.y > startFellPos.y do
local sucFor, itemFor = turtle.inspect()
if sucFor and isLog(itemFor.name) then
turtle.dig()
end
forceMoveDown()
end
end
local function fellTree()
local forwDir = startDir
turnInDirection("left")
local leftDir = direction
local diffRight = negative(multiply(2, leftDir))
local diffFor = multiply(2, forwDir)
local startFellPos1 = add(negative(forwDir),add(turtlePos, multiply(2, leftDir)))
local startFellPos2 = add(startFellPos1, diffRight)
local startFellPos3 = add(startFellPos2, diffRight)
local startFellPos4 = add(startFellPos3, diffFor)
local startFellPos5 = add(startFellPos2, diffFor)
local startFellPos6 = add(startFellPos1, diffFor)
local startFellPos7 = add(startFellPos6, diffFor)
local startFellPos8 = add(startFellPos5, diffFor)
local startFellPos9 = add(startFellPos4, diffFor)
fellAtPos(startFellPos1)
fellAtPos(startFellPos2)
fellAtPos(startFellPos3)
fellAtPos(startFellPos4)
fellAtPos(startFellPos5)
fellAtPos(startFellPos6)
fellAtPos(startFellPos7)
fellAtPos(startFellPos8)
fellAtPos(startFellPos9)
goToStartOfTree()
end
-- === Inventory Management ===
local function compactInventory()
for i = 1, 16 do
local itemI = turtle.getItemDetail(i)
if itemI and itemI.count < 64 then
for j = i + 1, 16 do
local itemJ = turtle.getItemDetail(j)
if itemJ and itemJ.name == itemI.name then
turtle.select(j)
turtle.transferTo(i)
-- Aktuellen Slot updaten
itemI = turtle.getItemDetail(i)
if not itemI or itemI.count == 64 then
break
end
end
end
end
end
turtle.select(1) -- zum Standard zurückkehren
end
-- Keep saplings
local function dumpInventoryKeepSaplings(n)
local success, chest = turtle.inspect()
while not (success and chest.name == "minecraft:chest") do
print("No chest found. Pls place one in front.")
print("Press enter to continue.")
read()
success, chest = turtle.inspect()
end
local saplingsKept = 0
for slot = 1, 16 do
local item = turtle.getItemDetail(slot)
if item then
turtle.select(slot)
if isSapling(item) and saplingsKept < n then
local keep = math.min(item.count, n - saplingsKept)
local drop = item.count - keep
if drop > 0 then
turtle.drop(drop)
end
saplingsKept = saplingsKept + keep
elseif not isBoneMeal(item) then
turtle.drop()
end
end
end
turtle.select(1)
end
local function turtleNeedsFuel()
local fuelLevel = turtle.getFuelLevel()
if fuelLevel < 1000 then
if not isEqual(startPos, turtlePos) then
diffX = math.abs(startPos.x - turtlePos.x)
diffY = math.abs(startPos.y - turtlePos.y)
diffZ = math.abs(startPos.z - turtlePos.z)
if not (fuelLevel < diffX + diffY + diffZ +5) then
goToPos(startPos)
end
end
print("Turtle needs more fuel")
return true
end
return false
end
-- gets specified item in given amount (min(available, requested)) from chest
local function suckSpecificItem(itemName, count, strDir)
local chest = peripheral.wrap(strDir)
if not chest or not chest.list then
return false
end
local chestItems = chest.list()
local foundSlots = {}
local totalAvailable = 0
-- 1. Scanne Kiste nach dem gewünschten Item
for slot, item in pairs(chestItems) do
if item.name == itemName then
table.insert(foundSlots, {slot = slot, count = item.count})
totalAvailable = totalAvailable + item.count
end
end
count = math.min(totalAvailable, count)
if count == 0 then
return false
end
-- 2. Finde freien Slot in der Turtle für gecachtes Item
local cachedItemSlot = nil
for i = 1, 16 do
if turtle.getItemCount(i) == 0 then
cachedItemSlot = i
break
end
end
if not cachedItemSlot then
print("Kein freier Slot zum Zwischenspeichern des Kisten-Items.")
return false
end
turtle.select(cachedItemSlot)
local cachedItem = nil
local list = chest.list()
if list[1] and list[1].name ~= itemName then
if strDir == "front" then
if turtle.suck(64) then
cachedItem = turtle.getItemDetail(cachedItemSlot)
end
elseif strDir == "top" then
if turtle.suckUp(64) then
cachedItem = turtle.getItemDetail(cachedItemSlot)
end
elseif strDir == "bottom" then
if turtle.suckDown(64) then
cachedItem = turtle.getItemDetail(cachedItemSlot)
end
end
end
-- 3. Erstes gewünschtes Item in Slot 1 schieben
local nextSlotIndex = 1
if not chest.getItemDetail(1) then
local nextSource = foundSlots[nextSlotIndex]
chest.pushItems(strDir, nextSource.slot, nextSource.count, 1)
nextSlotIndex = nextSlotIndex + 1
elseif (chest.getItemDetail(1) and chest.getItemDetail(1).name == itemName) then
nextSlotIndex = nextSlotIndex + 1
end
-- 4. Gecachtes Item zurückgeben
if cachedItem then
turtle.select(cachedItemSlot)
if strDir == "front" then
turtle.drop() -- Kiste verteilt es automatisch auf einen freien Slot ≠ 1
elseif strDir == "top" then
turtle.dropUp() -- Kiste verteilt es automatisch auf einen freien Slot ≠ 1
elseif strDir == "bottom" then
turtle.dropDown() -- Kiste verteilt es automatisch auf einen freien Slot ≠ 1
end
end
-- 5. Zielitems absaugen
local collected = 0
turtle.select(cachedItemSlot) -- Wiederverwenden für Einsammeln
while collected < count do
local remaining = count - collected
local slot1Item = chest.getItemDetail(1)
local pullCount = math.min(slot1Item.count, remaining)
if strDir == "front" then
if turtle.suck(pullCount) then
collected = collected + pullCount
end
elseif strDir == "top" then
if turtle.suckUp(pullCount) then
collected = collected + pullCount
end
elseif strDir == "bottom" then
if turtle.suckDown(pullCount) then
collected = collected + pullCount
end
end
if collected < count and nextSlotIndex <= #foundSlots then
local nextSource = foundSlots[nextSlotIndex]
chest.pushItems(strDir, nextSource.slot, nextSource.count, 1)
nextSlotIndex = nextSlotIndex + 1
end
end
return true
end
local function processAndStockInventory()
goToPos(startPos)
turnInDirection(getTurnDirection(direction, negative(startDir)))
dumpInventoryKeepSaplings(64)
if getSaplingCount() < 8 then
suckSpecificItem("minecraft:pale_oak_sapling", 64 - getSaplingCount(), "bottom")
bFirst = true
while getSaplingCount() < 8 do
if bFirst then print("Waiting for new Saplings!") bFirst = false end
if not suckSpecificItem("minecraft:pale_oak_sapling", 64 - getSaplingCount(), "bottom") then sleep(5) end
end
end
if getBoneMealCount() < 64 then
suckSpecificItem("minecraft:bone_meal", 64 - getBoneMealCount(), "top")
bFirst = true
while getBoneMealCount() < 10 do
if bFirst then print("Waiting for new Bone Meal!") bFirst = false end
if not suckSpecificItem("minecraft:bone_meal", 64 - getBoneMealCount(), "top") then sleep(5) end
end
end
compactInventory()
goToStartOfTree()
end
-- === Initialization ===
local function destroyTreeBlocks()
local suc, item = turtle.inspect()
if not suc then
return
end
local treeBlocks = {"minecraft:pale_oak_leaves", "minecraft:pale_oak_log"}
cnt = 1
while cnt <= 2 do
if item.name == treeBlocks[cnt] then
turtle.dig()
return
end
cnt = cnt + 1
end
end
local function saveStartingParameters()
local parameterList = {pos = turtlePos, dir = direction}
local file = fs.open("paleOakStartingParameters.txt", "w")
file.write(textutils.serialize(parameterList))
file.close()
end
local function loadStartingParameters()
if not fs.exists("paleOakStartingParameters.txt") then return false end
local file = fs.open("paleOakStartingParameters.txt", "r")
local data = textutils.unserialize(file.readAll())
file.close()
startPos = data.pos
startDir = data.dir
return true
end
local function initProgram()
turtlePos = getGPSPosition()
if not turtlePos then error("GPS failure, cannot run the program") end
destroyTreeBlocks()
getDirection()
if not loadStartingParameters() then
startPos = turtlePos
startDir = direction
saveStartingParameters()
end
end
-- === Main ===
local function main()
bSaplingPlanted = true
while turtle.getFuelLevel() < 5 do
print("Turtle needs more fuel")
return
end
initProgram()
if turtleNeedsFuel() then return end
if (getBoneMealCount() == 0) or (getSaplingCount() < 4) then
processAndStockInventory()
end
goToStartOfTree()
plantAllSaplings()
while true do
if turtleNeedsFuel() then return end
if bSaplingPlanted then
suc, item = turtle.inspect()
if suc and isSapling(item) then
if selectBoneMealSlot() then
turtle.place()
else
processAndStockInventory()
end
else
if suc and isLog(item) then
fellTree()
bSaplingPlanted = false
end
slotCount = turtle.getItemCount(16)
if slotCount > 0 then
processAndStockInventory()
end
end
else
if not plantAllSaplings() then
processAndStockInventory()
if not plantAllSaplings() then
goToPos(startPos)
print("Error: Saplings cannot be planted!")
return
end
end
bSaplingPlanted = true
end
end
end
main()