
Ever wanted a Minecraft farm that plants, harvests, and manages itself? 🌾
With the ComputerCraft mod and a bit of Lua scripting, it’s absolutely possible!
In this guide, we’ll explore how to turn your Turtle into a smart automated farmer that plants, harvests, and refuels — all by itself.
đź§ The Concept: Automated Farming with Turtles
ComputerCraft’s Turtle is a programmable robot that can dig, build, farm, and more.
Using Lua, we can control every action it performs — making it a perfect candidate for creating a self-managing crop farm.
This script allows the Turtle to:
- Detect the farm’s boundaries automatically
- Harvest and replant crops when ready
- Refuel and restock seeds from a connected chest
- Return to its origin point after completing a farming cycle
⚙️ Step 1: Setting Up the Farm
Here’s what you’ll need:
- Farmland: hydrated dirt blocks for your crops
- Turtle: place it on one edge of your farm
- Chest + Wired Modem: put a chest next to a modem and connect it to the Turtle
- Seeds and Fuel: store them inside the chest
💡 Tip: Right-click the modem until it turns red — this means it’s active and connected!
đź’» Step 2: The Lua Code
Save the following script as farm.lua inside your Turtle.
Then, simply run it in ComputerCraft by typing:
local x, y, z = 0, 0, 0
local direction = 0
local invertDirection = false
-- Ground blocks that are part of the farm
local groundBlocks = {
["minecraft:dirt"] = true,
["minecraft:grass_block"] = true,
["minecraft:farmland"] = true,
["minecraft:water"] = true,
["minecraft:flowing_water"] = true,
-- add your own here:
--["<yourmod>:<block>"] = true,
}
-- Blocks that are crops, with their maximum ages
local cropBlocks = {
["minecraft:wheat"] = 7,
["minecraft:carrots"] = 7,
["minecraft:potatoes"] = 7,
["minecraft:beetroots"] = 3,
-- add your own here:
--["<yourmod>:<block>"] = <maximum age>,
}
-- Mappings of crop blocks to seed items
local seeds = {
["minecraft:wheat"] = "minecraft:wheat_seeds",
["minecraft:carrots"] = "minecraft:carrot",
["minecraft:potatoes"] = "minecraft:potato",
["minecraft:beetroots"] = "minecraft:beetroot_seeds",
-- add your own here:
--["<yourmod>:<block>"] = "<yourmod>:<seed>",
}
-- Fuel types to pull from a chest if no fuel is in the inventory
local fuels = {
["minecraft:coal"] = true,
["minecraft:charcoal"] = true,
["minecraft:lava_bucket"] = true,
-- add your own here:
--["<yourmod>:<item>"] = true,
}
local seedItems = {}
for k, v in pairs(seeds) do seedItems[v] = k end
local function writePos()
local file = fs.open("jackmacwindows.farm-state.txt", "w")
file.writeLine(x)
file.writeLine(y)
file.writeLine(z)
file.writeLine(direction)
file.writeLine(invertDirection and "true" or "false")
file.close()
end
local function refuel()
if turtle.getFuelLevel() == "unlimited" or turtle.getFuelLevel() ==
turtle.getFuelLimit() then return end
for i = 1, 16 do
if turtle.getItemCount(i) > 0 then
turtle.select(i)
turtle.refuel(turtle.getItemCount() - 1)
if turtle.getFuelLevel() == turtle.getFuelLimit() then return true end
end
end
if turtle.getFuelLevel() > 0 then return true
else return false, "Out of fuel" end
end
local function forward()
local ok, err = turtle.forward()
if ok then
if direction == 0 then x = x + 1
elseif direction == 1 then z = z + 1
elseif direction == 2 then x = x - 1
else z = z - 1 end
writePos()
return true
elseif err:match "[Ff]uel" then
ok, err = refuel()
if ok then return forward()
else return ok, err end
else return false, err end
end
local function back()
local ok, err = turtle.back()
if ok then
if direction == 0 then x = x - 1
elseif direction == 1 then z = z - 1
elseif direction == 2 then x = x + 1
else z = z + 1 end
writePos()
return true
elseif err:match "[Ff]uel" then
ok, err = refuel()
if ok then return forward()
else return ok, err end
else return false, err end
end
local function up()
local ok, err = turtle.up()
if ok then
y = y + 1
writePos()
return true
elseif err:match "[Ff]uel" then
ok, err = refuel()
if ok then return forward()
else return ok, err end
else return false, err end
end
local function down()
local ok, err = turtle.down()
if ok then
y = y - 1
writePos()
return true
elseif err:match "[Ff]uel" then
ok, err = refuel()
if ok then return forward()
else return ok, err end
else return false, err end
end
local function left()
local ok, err = turtle.turnLeft()
if ok then
direction = (direction - 1) % 4
writePos()
return true
else return false, err end
end
local function right()
local ok, err = turtle.turnRight()
if ok then
direction = (direction + 1) % 4
writePos()
return true
else return false, err end
end
local function panic(msg)
term.clear()
term.setCursorPos(1, 1)
term.setTextColor(colors.red)
print("An unrecoverable error occured while farming:", msg,
"\nPlease hold Ctrl+T to stop the program, then solve the issue described above, run
'rm jackmacwindows.farm-state.txt', and return the turtle to the start position.
Don't forget to label the turtle before breaking it.")
if peripheral.find("modem") then
peripheral.find("modem", rednet.open)
rednet.broadcast(msg, "jackmacwindows.farming-error")
end
local speaker = peripheral.find("speaker")
if speaker then
while true do
speaker.playNote("bit", 3, 12)
sleep(1)
end
else while true do os.pullEvent() end end
end
local function check(ok, msg) if not ok then panic(msg) end end
local function tryForward()
local ok, err, found, block
repeat
found, block = turtle.inspect()
if found then
if groundBlocks[block.name] or cropBlocks[block.name] then
ok, err = up()
if not ok then return ok, err end
else return false, "Out of bounds" end
end
until not found
ok, err = forward()
if not ok then return ok, err end
local lastY = y
repeat
found, block = turtle.inspectDown()
if not found then
ok, err = down()
if not ok then return ok, err end
end
until found
if groundBlocks[block.name] then
ok, err = up()
if not ok then return ok, err end
turtle.digDown()
elseif not cropBlocks[block.name] then
while y < lastY do
ok, err = up()
if not ok then return ok, err end
end
ok, err = back()
if not ok then return ok, err end
return false, "Out of bounds"
end
return true
end
local function selectItem(item)
local lut = {}
if type(item) == "table" then
if item[1] then for _, v in ipairs(item) do lut[v] = true end
else lut = item end
else lut[item] = true end
local lastEmpty
for i = 1, 16 do
local info = turtle.getItemDetail(i)
if info and lut[info.name] then
turtle.select(i)
return true, i
elseif not info and not lastEmpty then lastEmpty = i end
end
return false, lastEmpty
end
local function handleCrop()
local found, block = turtle.inspectDown()
if not found then
if selectItem(seedItems) then turtle.placeDown() end
elseif block.state.age == cropBlocks[block.name] then
local seed = seeds[block.name]
turtle.select(1)
turtle.digDown()
turtle.suckDown()
if turtle.getItemDetail().name ~= seed and not selectItem(seed)
then return end
turtle.placeDown()
end
end
local function exchangeItems()
local inventory, fuel, seed = {}, nil, nil
for i = 1, 16 do
turtle.select(i)
local item = turtle.getItemDetail(i)
if item then
if not seed and seedItems[item.name] then
seed = {slot = i, name = item.name, count = item.count, limit =
turtle.getItemSpace(i)}
elseif not turtle.refuel(0) then
inventory[item.name] = inventory[item.name] or {}
inventory[item.name][i] = item.count
elseif not fuel then
fuel = {slot = i, name = item.name, count = item.count, limit =
turtle.getItemSpace(i)}
end
end
end
local modem = peripheral.find("modem", function(_, v) return not v.isWireless() end)
local tries = 0
while not modem and tries < 4 do
tries = tries + 1
check(left())
modem = peripheral.find("modem", function(_, v) return not v.isWireless() end)
end
if not modem then panic("Could not find modem!") end
local name = modem.getNameLocal()
for _, chest in ipairs{peripheral.find("minecraft:chest")} do
local items = chest.list()
for i = 1, chest.size() do
if items[i] then
local item = items[i].name
if inventory[item] then
for slot, count in pairs(inventory[item]) do
local d = chest.pullItems(name, slot, count, i)
if d == 0 then break end
if count - d <= 0 then inventory[item][slot] = nil
else inventory[item][slot] = count - d end
end
if not next(inventory[item])
then inventory[item] = nil end
elseif fuel and fuel.count < fuel.limit and item == fuel.name
then
local d =
chest.pushItems(name, i, fuel.limit - fuel.count, fuel.slot)
fuel.count = fuel.count + d
elseif seed and seed.count < seed.limit and item == seed.name
then
local d =
chest.pushItems(name, i, seed.limit - seed.count, seed.slot)
seed.count = seed.count + d
end
end
if not next(inventory) then break end
end
if not next(inventory) then break end
end
if next(inventory) then
for _, chest in ipairs{peripheral.find("minecraft:chest")} do
local items = chest.list()
for i = 1, chest.size() do
if not items[i] then
local item, list = next(inventory)
for slot, count in pairs(list) do
local d = chest.pullItems(name, slot, count, i)
if d == 0 then break end
if count - d <= 0 then list[slot] = nil
else list[slot] = count - d end
end
if not next(list) then inventory[item] = nil end
end
if not next(inventory) then break end
end
if not next(inventory) then break end
end
end
if not fuel or not seed then
for _, chest in ipairs{peripheral.find("minecraft:chest")} do
local items = chest.list()
for i = 1, chest.size() do
if items[i]
and ((fuel and items[i].name == fuel.name and fuel.count < fuel.limit) or
(not fuel and fuels[items[i].name])) then
local d =
chest.pushItems(name, i, fuel and fuel.count - fuel.limit, 16)
if fuel then fuel.count = fuel.count + d
else fuel
= {name = items[i].name, count =
d, limit = turtle.getItemSpace(16)} end
end
if items[i]
and ((seed and items[i].name == seed.name
and seed.count < seed.limit) or
(not seed and seedItems[items[i].name])) then
local d = chest.pushItems(name, i, seed
and seed.count - seed.limit, 1)
if seed then seed.count = seed.count + d
else seed = {name = items[i].name, count = d,
limit = turtle.getItemSpace(1)} end
end
if (fuel and fuel.count >= fuel.limit)
and (seed and seed.count >= seed.limit) then break end
end
if (fuel and fuel.count >= fuel.limit)
and (seed and seed.count >= seed.limit) then break end
end
end
end
if fs.exists("jackmacwindows.farm-state.txt") then
local file = fs.open("jackmacwindows.farm-state.txt", "r")
x, y, z, direction =
tonumber(file.readLine()), tonumber(file.readLine()),
tonumber(file.readLine()),
tonumber(file.readLine())
invertDirection = file.readLine() == "true"
file.close()
-- check if we were on a boundary block first
local found, block, ok, err, boundary
local lastY = y
repeat
found, block = turtle.inspectDown()
if not found then check(down()) end
until found
if groundBlocks[block.name] then
check(up())
turtle.digDown()
elseif not cropBlocks[block.name] then
if y == lastY then lastY = lastY + 1 end
while y < lastY do check(up()) end
while not back() do check(up()) end
boundary = true
end
if direction == 1 or direction == 3 then
-- we were in the middle of a rotation, finish that before continuing
local mv = (direction == 0) == invertDirection and left or right
if boundary then
check(mv())
check(mv())
check(tryForward())
invertDirection = not invertDirection
mv = mv == left and right or left
writePos()
end
check(mv())
handleCrop()
if x == 0 and z == 0 then
while y > 0 do check(down()) end
while y < 0 do check(up()) end
exchangeItems()
end
end
elseif not peripheral.find("minecraft:chest")
or not peripheral.find("modem", function(_, m)
return not m.isWireless() end) then
print[[
Please move the turtle to the starting position next to a modem with a chest.
The expected setup is the turtle next to
a wired modem block, with a chest next to that modem block.
This program cannot run until placed correctly.
]]
return
else exchangeItems() end
local ok, err
while true do
ok, err = tryForward()
if not ok then
if err == "Out of bounds" then
local mv = (direction == 0) == invertDirection and left or right
check(mv())
ok, err = tryForward()
if not ok then
if err == "Out of bounds" then
check(mv())
check(mv())
check(tryForward())
invertDirection = not invertDirection
mv = mv == left and right or left
writePos()
else panic(err) end
end
check(mv())
else panic(err) end
end
handleCrop()
if x == 0 and z == 0 then
while y > 0 do check(down()) end
while y < 0 do check(up()) end
exchangeItems()
elseif peripheral.find("modem") then
x, y, z = 0, 0, 0
exchangeItems()
end
if turtle.getFuelLevel() < 100 then refuel() end
end
farm