-- ============================================================ -- Six-tree automatic tree farm -- For a ComputerCraft / CC:Tweaked Felling Turtle -- -- Starting position: -- X = 286 -- Z = 108 -- Y = 63 -- -- IMPORTANT: -- Turtle must start facing +Z (toward Z=109,110,...) -- -- Chest must be directly UNDER the turtle's starting position. -- ============================================================ -- ------------------------------------------------------------ -- CONFIGURATION -- ------------------------------------------------------------ local HOME_X = 286 local HOME_Z = 108 -- Tree coordinates, expressed as X/Z because all are at Y=63. local trees = { { x = 284, z = 110 }, { x = 288, z = 110 }, { x = 284, z = 114 }, { x = 288, z = 114 }, { x = 284, z = 118 }, { x = 288, z = 118 } } -- How long to wait between complete farm checks. local CHECK_INTERVAL = 30 -- Maximum bone-meal attempts before giving up on a sapling. local MAX_BONEMEAL_ATTEMPTS = 32 -- Refuel if below this amount. local MIN_FUEL = 250 -- ------------------------------------------------------------ -- POSITION TRACKING -- ------------------------------------------------------------ -- -- Direction: -- 0 = +Z -- 1 = -X -- 2 = -Z -- 3 = +X -- -- We start facing +Z. -- ------------------------------------------------------------ local posX = HOME_X local posZ = HOME_Z local direction = 0 local function turnLeft() turtle.turnLeft() direction = (direction + 1) % 4 end local function turnRight() turtle.turnRight() direction = (direction + 3) % 4 end local function face(target) while direction ~= target do turnRight() end end local function forward() local attempts = 0 while not turtle.forward() do attempts = attempts + 1 if attempts > 20 then error("Unable to move forward.") end -- Don't automatically destroy random obstacles. sleep(0.25) end if direction == 0 then posZ = posZ + 1 elseif direction == 1 then posX = posX - 1 elseif direction == 2 then posZ = posZ - 1 elseif direction == 3 then posX = posX + 1 end end local function back() local attempts = 0 while not turtle.back() do attempts = attempts + 1 if attempts > 20 then error("Unable to move backward.") end sleep(0.25) end if direction == 0 then posZ = posZ - 1 elseif direction == 1 then posX = posX + 1 elseif direction == 2 then posZ = posZ + 1 elseif direction == 3 then posX = posX - 1 end end -- ------------------------------------------------------------ -- NAVIGATION -- ------------------------------------------------------------ local function moveTo(x, z) -- X movement if posX < x then face(3) -- +X while posX < x do forward() end elseif posX > x then face(1) -- -X while posX > x do forward() end end -- Z movement if posZ < z then face(0) -- +Z while posZ < z do forward() end elseif posZ > z then face(2) -- -Z while posZ > z do forward() end end end local function goHome() moveTo(HOME_X, HOME_Z) face(0) end -- ------------------------------------------------------------ -- ITEM IDENTIFICATION -- ------------------------------------------------------------ local function endsWith(text, suffix) return text:sub(-#suffix) == suffix end local function isSaplingName(name) if not name then return false end return endsWith(name, "_sapling") or name == "minecraft:mangrove_propagule" end local function isLogName(name) if not name then return false end return endsWith(name, "_log") or endsWith(name, "_wood") or endsWith(name, "_stem") or endsWith(name, "_hyphae") end local function isFuelName(name) return name == "minecraft:coal" or name == "minecraft:charcoal" or name == "minecraft:coal_block" end local function isBoneMealName(name) return name == "minecraft:bone_meal" end -- ------------------------------------------------------------ -- INVENTORY SEARCH -- ------------------------------------------------------------ local function findItem(testFunction) for slot = 1, 16 do local item = turtle.getItemDetail(slot) if item and testFunction(item.name) then return slot, item end end return nil end local function findBoneMeal() return findItem(isBoneMealName) end local function findAnySapling() return findItem(isSaplingName) end local function findExactItem(name) for slot = 1, 16 do local item = turtle.getItemDetail(slot) if item and item.name == name then return slot end end return nil end -- ------------------------------------------------------------ -- FUEL -- ------------------------------------------------------------ local function refuel() local fuel = turtle.getFuelLevel() if fuel == "unlimited" then return true end if fuel >= MIN_FUEL then return true end print("Fuel low: " .. fuel) for slot = 1, 16 do local item = turtle.getItemDetail(slot) if item and isFuelName(item.name) then turtle.select(slot) while turtle.getItemCount(slot) > 0 and turtle.getFuelLevel() < MIN_FUEL do turtle.refuel(1) end if turtle.getFuelLevel() >= MIN_FUEL then print("Refueled: " .. turtle.getFuelLevel()) return true end end end print("WARNING: No fuel available!") return false end -- ------------------------------------------------------------ -- BLOCK INSPECTION -- ------------------------------------------------------------ local function blockInFront() local success, block = turtle.inspect() if success then return block end return nil end local function logInFront() local block = blockInFront() return block and isLogName(block.name) end local function logAbove() local success, block = turtle.inspectUp() return success and isLogName(block.name) end local function logBelow() local success, block = turtle.inspectDown() return success and isLogName(block.name) end -- ------------------------------------------------------------ -- RECURSIVE TREE HARVEST -- -- Turtle should be standing INSIDE the location of a log which -- it has just removed. -- -- Every recursive call returns the turtle to exactly the same -- location and orientation from which that call started. -- ------------------------------------------------------------ local function harvestConnectedLogs() -- ----- LOG ABOVE ----- if logAbove() then turtle.digUp() if turtle.up() then harvestConnectedLogs() turtle.down() end end -- ----- LOG BELOW ----- -- -- Normally the parent log has already been removed, so this -- will not cause us to walk back through the tree. -- -- It also catches unusual downward-connected branches. -- if logBelow() then turtle.digDown() if turtle.down() then harvestConnectedLogs() turtle.up() end end -- ----- FOUR HORIZONTAL DIRECTIONS ----- for side = 1, 4 do if logInFront() then turtle.dig() if turtle.forward() then harvestConnectedLogs() turtle.back() end end turtle.turnRight() end end -- ------------------------------------------------------------ -- DETERMINE REPLACEMENT SAPLING FROM LOG TYPE -- ------------------------------------------------------------ local function saplingFromLog(logName) if not logName then return nil end -- minecraft:oak_log -> minecraft:oak_sapling if endsWith(logName, "_log") then local prefix = logName:sub(1, #logName - 4) return prefix .. "_sapling" end -- Wood variants if endsWith(logName, "_wood") then local prefix = logName:sub(1, #logName - 5) return prefix .. "_sapling" end return nil end -- ------------------------------------------------------------ -- GROW SAPLING -- ------------------------------------------------------------ local function growSapling() for attempt = 1, MAX_BONEMEAL_ATTEMPTS do local block = blockInFront() -- It grew. if block and isLogName(block.name) then print("Tree grew after " .. (attempt - 1) .. " bone meal attempts.") return true, block.name end -- No longer a sapling and not a log. if not block or not isSaplingName(block.name) then return false, nil end local slot = findBoneMeal() if not slot then print("Out of bone meal.") return false, nil end turtle.select(slot) turtle.place() sleep(0.15) end local block = blockInFront() if block and isLogName(block.name) then return true, block.name end print("Sapling did not grow.") return false, nil end -- ------------------------------------------------------------ -- PLANT SAPLING -- ------------------------------------------------------------ local function plantSapling(preferredName) local slot = nil -- Try to use the same species. if preferredName then slot = findExactItem(preferredName) end -- Otherwise use whatever sapling is available. if not slot then slot = findAnySapling() end if not slot then print("WARNING: No saplings available!") return false end turtle.select(slot) if turtle.place() then print("Sapling planted.") return true end print("WARNING: Unable to plant sapling.") return false end -- ------------------------------------------------------------ -- HARVEST A SINGLE TREE -- -- Turtle is standing one block NORTH (-Z) of the planting spot -- and facing SOUTH (+Z). -- ------------------------------------------------------------ local function processTree() local block = blockInFront() if not block then print("Planting spot is empty.") plantSapling(nil) return end local preferredSapling = nil -- -------------------------------------------------------- -- SAPLING -- -------------------------------------------------------- if isSaplingName(block.name) then print("Sapling detected: " .. block.name) preferredSapling = block.name local grown, logName = growSapling() if not grown then print("Leaving sapling for next pass.") return end block = blockInFront() if not block then return end end -- -------------------------------------------------------- -- TREE -- -------------------------------------------------------- if isLogName(block.name) then print("Tree detected: " .. block.name) if not preferredSapling then preferredSapling = saplingFromLog(block.name) end -- Remove base trunk. turtle.dig() -- Move into the space previously occupied by the trunk. if not turtle.forward() then print("ERROR: Could not enter trunk location.") return end -- Cut every connected log. harvestConnectedLogs() -- Return to our approach square. turtle.back() -- Plant replacement. plantSapling(preferredSapling) print("Tree harvested.") return end print("Not a tree or sapling: " .. block.name) end -- ------------------------------------------------------------ -- DROP HARVEST INTO CHEST -- ------------------------------------------------------------ local function dumpHarvest() goHome() print("Dropping harvest into chest...") for slot = 1, 16 do local item = turtle.getItemDetail(slot) if item then local keep = isBoneMealName(item.name) or isSaplingName(item.name) or isFuelName(item.name) if not keep then turtle.select(slot) if not turtle.dropDown() then print("Chest may be full!") end end end end end -- ------------------------------------------------------------ -- VISIT ONE TREE -- ------------------------------------------------------------ local function visitTree(tree) refuel() -- Stand one block north of the tree. local approachX = tree.x local approachZ = tree.z - 1 moveTo(approachX, approachZ) -- Face south, toward the tree. face(0) print("") print("---------------------------") print("Tree " .. tree.x .. ", " .. tree.z) print("---------------------------") processTree() -- Deposit after EACH tree. dumpHarvest() end -- ------------------------------------------------------------ -- MAIN PROGRAM -- ------------------------------------------------------------ print("") print("================================") print(" Six Tree Automatic Tree Farm") print("================================") print("") print("Home: " .. HOME_X .. ", " .. HOME_Z) print("Trees: " .. #trees) print("") refuel() while true do print("") print("Beginning farm sweep...") for i, tree in ipairs(trees) do print("") print("Tree " .. i .. " of " .. #trees) visitTree(tree) end goHome() dumpHarvest() print("") print("Sweep complete.") print("Sleeping " .. CHECK_INTERVAL .. " seconds.") sleep(CHECK_INTERVAL) end