CCCPaste

deforestation

-- ============================================================
-- 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