Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local version = 20240209.0800
- --[[
- https://pastebin.com/GLbfWEZr
- This script runs a turtle powered self-sustaining kelp farm.
- The farm is 3 blocks deep and can be any size as long as the width/length dimensions are divisible by 2
- Any corner can be used as a base with 2 smokers/hoppers and 2 separate chests/barrels for temporary and long-term storage
- The turtle MUST be equipped with a diamond hoe NOT a pickaxe, otherwise it will break the farm!
- ]]
- local border = ""
- local function clear()
- term.clear()
- term.setCursorPos(1,1)
- end
- local function getBlockType(direction)
- --[[ turtle.inspect() returns two values
- 1) boolean (true/false) success
- 2) table with two or more values:
- .name
- .state {axis = "y"}
- .tags (["minecraft:logs"] = true, ["minecraft:logs_that_burn"] = true, ["minecraft:oak_logs"] = true}
- ]]
- local blockType = "" -- initialise blockType
- local success = false
- local data = nil -- initialise empty table variable
- local inspect = turtle.inspect -- assign inspect() function to a variable
- if direction == "up" then
- inspect = turtle.inspectUp -- inspect re-assigned to inspectUp
- elseif direction == "down" then
- inspect = turtle.inspectDown -- inspect re-assigned to inspectDown
- end
- success, data = inspect() -- store information about the block below in a table
- if success then -- block found
- blockType = data.name -- eg "minecraft:log"
- end
- return blockType, data -- eg "minecraft:oak_log" , data table OR "", nil if empty
- end
- local function values(t) -- general diy iterator
- local i = 0
- return function()
- i = i + 1
- return t[i]
- end
- end
- local function move(direction, steps, hoe)
- local move = turtle.forward
- local dig = turtle.dig
- if direction == "up" then
- move = turtle.up
- dig = turtle.digUp
- elseif direction == "down" then
- move = turtle.down
- dig = turtle.digDown
- elseif direction == "back" then
- move = turtle.back
- end
- local moves = 0
- for i = 1, steps do
- if hoe then -- hoe = true so dig kelp
- turtle.digDown() -- dig down anyway
- dig()
- end
- if move() then -- if move successful
- moves = moves + 1
- else
- return false, moves -- unable to move further
- end
- end
- return true, moves
- end
- local function down(steps, hoe)
- steps = steps or 1 -- if called without parameters, set default 1
- hoe = hoe or false -- if called without parameters, set default false
- return move("down", steps, hoe)
- end
- local function up(steps, hoe)
- steps = steps or 1
- hoe = hoe or false
- return move("up", steps, hoe)
- end
- local function forward(steps, hoe)
- steps = steps or 1
- hoe = hoe or false
- return move("forward", steps, hoe)
- end
- local function back(steps)
- steps = steps or 1
- hoe = hoe or false -- if called without parameters, set default false
- return move("back", steps, hoe)
- end
- local function turnLeft(steps)
- steps = steps or 1
- for i = 1, steps do
- turtle.turnLeft()
- end
- return true
- end
- local function turnRight(steps)
- steps = steps or 1
- for i = 1, steps do
- turtle.turnRight()
- end
- return true
- end
- local function isItemOfType(direction, itemTable)
- -- check if item in front of turtle is a specific type
- local success, data = false, nil
- if direction == "up" then
- success, data= turtle.inspectUp()
- elseif direction == "down" then
- success, data= turtle.inspectDown()
- else
- success, data= turtle.inspect() -- check if anything in front
- end
- if success then -- a block is in front
- if type(itemTable) == "table" then -- Are we checking for multiple items eg {"chest", "barrel"} ?
- for _, item in ipairs(itemTable) do -- iterate items
- if data.name:find(item) ~= nil then -- match found
- return true
- end
- end
- elseif type(itemTable) == "string" then -- checking for single item
- if data.name:find(itemTable) ~= nil then -- match found
- return true
- end
- end
- end
- return false -- no match
- end
- local function turnToStorage()
- local turns = 0
- while not isItemOfType("forward", {"chest", "barrel"}) do -- look for chest or barrel
- turnRight(1)
- turns = turns + 1
- if turns == 4 then -- prevent infinite loop if no barrel/chest
- return false
- end
- end
- return true
- end
- local function turnToSmelter(number)
- number = number or 1
- local turns = 0
- if isItemOfType("forward", {"smoker", "furnace"}) then -- smoker/furnace in front
- turnLeft(1)
- if isItemOfType("forward", {"smoker", "furnace"}) then -- smoker/furnace in front
- if number == 1 then
- return true
- else
- turnRight(1)
- return true
- end
- else
- turnRight(1)
- if number == 1 then
- return true
- else
- turnRight(1)
- return true
- end
- end
- else
- repeat
- turnLeft(1)
- turns = turns + 1
- if turns == 4 then
- return false
- end
- until isItemOfType("forward", {"smoker", "furnace"}) -- look for smoker or furnace
- if number == 2 then
- return true
- else
- turnLeft(1)
- if isItemOfType("forward", {"smoker", "furnace"}) then
- return true
- end
- end
- return true
- end
- end
- local function go(path)
- local commandList = {}
- local command = ""
- local direction = {"up", "forward", "down"}
- path = path:gsub(" ", "") -- remove spaces from path
- -- make a list of commands from path string eg "x0F12U1" = x0, F12, U1
- for i = 1, #path do
- local character = path:sub(i, i) -- examine each character in the string
- if tonumber(character) == nil then -- char is NOT a number
- if command ~= "" then -- command is NOT empty eg "x0"
- table.insert(commandList, command) -- add to table eg "x0"
- end
- command = character:upper() -- replace command with new character eg "F"
- else -- char IS a number
- command = command..character:upper() -- add eg 1 to F = F1, 2 to F1 = F12
- if i == #path then -- last character in the string
- table.insert(commandList, command)
- end
- end
- end
- -- R# L# F# B# U# D# +0 -0 d0 = Right, Left, Forward, Back, Up, Down, up while detect and return, down while not detect, down and place while not detect
- -- dig: x0,x1,x2 (up/fwd/down)
- for cmd in values(commandList) do -- eg F12 or x1
- local move = (cmd:sub(1, 1)):upper()
- local modifier = tonumber(cmd:sub(2))
- if move == "B" then
- back(modifier)
- elseif move == "D" then
- down(modifier)
- elseif move == "F" then
- forward(modifier)
- elseif move == "U" then
- up(modifier)
- elseif move == "R" then
- turnRight(modifier)
- elseif move == "L" then
- turnLeft(modifier)
- elseif move == "X" then
- if modifier == 0 then
- turtle.digUp()
- elseif modifier == 1 then
- turtle.dig()
- elseif modifier == 2 then
- turtle.digDown()
- end
- end
- end
- end
- local function addToStorage()
- -- assume starting facing storage
- for i = 1, 16 do
- if turtle.getItemCount(i) > 0 then -- slot is not empty
- turtle.select(i) -- select slot
- local data = turtle.getItemDetail(i) -- get data of slot contents
- if data.name:find("block") ~= nil then -- block of dried kelp found
- turtle.drop() -- drop into general storage
- elseif data.name == "minecraft:dried_kelp" then -- loose dried kelp somehow left behind
- turtle.dropDown() -- back into smelter output store
- else
- turtle.dropUp() -- random block found during harvesting so dump it
- end
- end
- end
- turnToSmelter(1)
- end
- local function craftKelp()
- local sourceSlots = {4,8,12,13,14,15,16}
- local craftSlots = {1,2,3,5,6,7,9,10,11}
- local quantity = 0
- local lib = {}
- function lib.getSourceCount()
- for _, sourceSlot in ipairs(sourceSlots) do
- local count = turtle.getItemCount(sourceSlot)
- if count > 0 then
- turtle.select(sourceSlot)
- return sourceSlot, count
- end
- end
- end
- for _, slot in ipairs(sourceSlots) do
- turtle.select(slot) -- select a slot out of crafting area
- turtle.suckDown() -- remove items from chest below (dried kelp)
- quantity = quantity + turtle.getItemCount(slot)
- end
- print(quantity.. " dried kelp found")
- if quantity >= 9 then -- need min 9 to craft
- local amount = math.floor(quantity / 9) -- calculate how many to put in each of 9 slots
- for _, craftSlot in ipairs(craftSlots) do
- local sourceSlot, available = lib.getSourceCount()
- if available >= amount then
- turtle.select(sourceSlot)
- turtle.transferTo(craftSlot, amount) -- fill slots with same amount each
- else
- turtle.select(sourceSlot)
- turtle.transferTo(craftSlot, available) -- fill slots with same amount each
- local moved = available
- sourceSlot, available = lib.getSourceCount()
- turtle.select(sourceSlot)
- turtle.transferTo(craftSlot, amount - moved)
- end
- end
- for _, slot in ipairs(sourceSlots) do
- if turtle.getItemCount(slot) > 0 then
- turtle.select(slot)
- turtle.dropDown() -- drop remaining back into chest
- end
- end
- turtle.select(1) -- put crafted items in slot 1
- amount = 0
- if turtle.craft() then
- for i = 1, 16 do
- amount = amount + turtle.getItemCount(i)
- end
- return amount
- end
- else
- for i = 1, 16 do
- if turtle.getItemCount(slot) > 0 then
- turtle.select(slot)
- turtle.dropDown()
- end
- end -- drop remaining back into chest
- end
- turtle.select(1)
- return 0
- end
- local function dropKelp()
- local lib = {}
- function lib.drop(half)
- for i = 1, 16 do
- if turtle.getItemCount(i) > 0 then
- turtle.select(i)
- local data = turtle.getItemDetail(i) -- get data of slot contents
- if data.name == "minecraft:kelp" then -- kelp found
- if half == -1 then
- turtle.drop()
- else
- if half > 64 then
- turtle.drop()
- half = half - 64
- else
- turtle.drop(half)
- half = 0
- end
- end
- end
- if half == 0 then break end
- end
- end
- end
- -- assume starting in front of hopper
- local kelp = 0
- if getBlockType("forward") == "minecraft:hopper" then -- make sure in front of hopper
- local count = 0
- for i = 1, 16 do -- iterate all slots
- count = count + turtle.getItemCount(i)
- kelp = count
- end
- if count > 0 then -- slot has contents
- local half = math.ceil(count / 2)
- lib.drop(half)
- turnLeft(1)
- lib.drop(-1)
- end
- go("U1F1R1F1R1D2")
- else
- go("U1F1R1B1D2")
- end
- return kelp -- return total kelp found
- end
- local function refuel(kelp)
- -- assume facing 1st smelter. kelp is amount of kelp dropped into the hoppers
- -- 1 kelp block smelts 20 kelp could be >200 kelp dropped
- -- kelp will be nil if called at start of home run from emptyTurtle()
- turnToSmelter(1)
- turtle.select(1)
- turtle.suck() -- remove any fuel from fuel store into slot 1
- turnToSmelter(2)
- turtle.suck()
- if turtle.getFuelLevel() < 200 then -- check turtle fuel level
- turtle.refuel(2) -- refuel with 2 kelp blocks (400 moves)
- end
- if kelp ~= nil then
- kelp = math.ceil(kelp / 40) -- calculate amount of fuel required per smelter. re-purpose kelp variable
- turnToSmelter(1)
- turtle.drop(kelp) -- drop fuel into smelter. estimated max 9 blocks
- turnToSmelter(2) -- face 2nd smelter
- turtle.drop(kelp)
- end
- turnToSmelter(1)
- return true
- end
- local function returnToHopper()
- -- should be alongside a retaining wall
- local lib = {} -- local library
- function lib.isHopper() -- check if block in front is a hopper
- local blockType = getBlockType("forward")
- if blockType:find("hopper") ~= nil then
- return true -- hopper found
- end
- return false -- hopper not found
- end
- while not lib.isHopper() do -- move forward while hopper not found
- forward(1, true)
- end
- local kelp = dropKelp() -- drop kelp into hoppers, returns to face smelter 1
- if craftKelp() > 0 then -- craft kelp blocks
- if turtle.getFuelLevel() < 200 then -- check turtle fuel level
- turtle.refuel(2) -- refuel with 2 kelp blocks (400 moves)
- end
- if kelp ~= nil then
- refuel(kelp)
- kelp = nil
- turnToStorage()
- end
- turtle.drop() -- rotate to general storage and drop excess kelp blocks
- end
- addToStorage() -- clear inventory and face farm
- turnToSmelter(1) -- facing smelter 1
- end
- local function wait()
- -- wait for furnace to go out before continuing
- local time = 0
- local waitTime = 5
- local success, data = turtle.inspect()
- if success then
- while data.state.lit do
- clear()
- -- following code for display purposes only
- local mins = math.floor(time / 60)
- local secs = time % 60
- local output = "Waiting for smelter to finish:\n"..time.." seconds ("..mins.." minute"
- if mins ~= 1 then output = output.."s" end
- output = output..", "..secs.." second"
- if secs ~= 1 then output = output.."s" end
- output = output..")"
- print(output)
- sleep(waitTime)
- time = time + waitTime
- success, data = turtle.inspect()
- end
- clear()
- end
- end
- local function farm()
- local lib = {} -- local library
- function lib.harvest() -- dig kelp below, right and left
- turtle.dig()
- turtle.digDown()
- local success, moves = forward(1, true) -- true if moved forward
- return success, moves -- false if against wall
- end
- function lib.changeRow(direction) -- move left/right, attempt 3 blocks forward
- if direction == "left" then
- turnLeft(1)
- else
- turnRight(1)
- end
- local success, moves = forward(1, true) -- try to move 1 block against the wall
- if success then -- moved 3 blocks
- if direction == "left" then -- turn to face farm for next harvest run
- turnLeft(1)
- else
- turnRight(1)
- end
- return true
- else -- hit side wall, so turn 180 and prepare to head for home
- if direction == "left" then
- turnLeft(2)
- else
- turnRight(2)
- end
- return false
- end
- end
- wait()
- turnToSmelter(2) -- facing smelter 2
- wait()
- turnToSmelter(1) -- facing smelter 1
- go("U2F1 R1F1 L1X2D1") -- exit home and enter water
- local outward = true -- set direction flag
- while true do
- while lib.harvest() do end -- harvests a whole row
- if outward then
- local success = lib.changeRow("right")
- if not success then
- break
- end
- else
- local success = lib.changeRow("left")
- if not success then
- break
- end
- end
- outward = not outward -- reverse direction
- end
- returnToHopper() -- returned to hopper
- end
- local function emptyTurtle()
- -- only used on startup
- wait() -- wait for smelter to finish
- turtle.select(1)
- turtle.suck() -- remove existing furnace fuel
- turnRight(1) -- face 2nd smelter
- wait() -- wait for smelter to finish
- turtle.suck() -- remove existing furnace fuel
- if turtle.getItemCount(1) > 0 and turtle.getFuelLevel() < 200 then -- check turtle fuel level
- turtle.refuel(2) -- refuel with 2 kelp blocks (400 moves)
- end
- if turtle.getItemCount(1) > 0 then
- turnToStorage()
- turtle.drop() -- drop any furnace fuel into storage
- turnToSmelter(1)
- else
- turnToSmelter(1)
- end
- end
- local function isHome()
- -- chest or barrel below confirms home position
- if isItemOfType("down", {"chest", "barrel"}) then
- return "c"
- else
- local blockType = getBlockType("down")
- if blockType == "" then -- air. must be above home
- return "a"
- elseif isItemOfType("down", {"water", "kelp"}) then -- in water farming
- return "w"
- elseif blockType == "minecraft:hopper" then -- on top of hopper
- return "h"
- else
- return "" -- somehow got lost
- end
- end
- end
- local function goHome(startOn)
- -- a, w, h, b (air, water, hopper, border)
- local lib = {}
- function lib.addToMap(map)
- blockType = getBlockType("down")
- if blockType == "" then
- map = map.."a"
- elseif isItemOfType("down", {"water", "kelp"}) then
- map = map.."w"
- elseif blockType == "minecraft:hopper" then
- map = map.."h"
- else
- map = map.."b"
- end
- return map
- end
- function lib.makeMap()
- local map = ""
- forward(1) -- to north
- map = lib.addToMap(map)
- go("B1R1F1") -- to east
- map = lib.addToMap(map)
- go("B1L1B1") -- to south
- map = lib.addToMap(map)
- go("F1L1F1") -- to west
- map = lib.addToMap(map)
- go("B1R1") -- to start position
- return map
- end
- function lib.aboveHopper() -- already on a hopper
- -- go into water then call returnToHopper()
- local map = lib.makeMap()
- if map == "aabw" then
- go("L1F1R1")
- elseif map == "abwa" then
- go("B1L1")
- elseif map == "bwaa" then
- go("R1F1R1")
- elseif map == "waab" then
- go("F1R1")
- elseif map == "aawb" then
- go("B1")
- elseif map == "awba" then
- go("L1B1")
- elseif map == "wbaa" then
- go("R2B1")
- elseif map == "baaw" then
- go("R1B1")
- end
- down(1, true)
- returnToHopper()
- return true
- end
- function lib.findBorder()
- repeat
- turtle.dig()
- turtle.digDown()
- local success, moves = forward(1, true) -- true if moved forward
- until not success
- if getBlockType("forward") == "minecraft:hopper" then
- return true
- else
- turnRight(1)
- return false
- end
- end
- function lib.findHopper()
- -- turtle is on top of wall looking for hopper below
- local blockType = getBlockType("down")
- border = blockType
- local blockCount = 0
- while blockType == border do -- border has already been confirmed
- forward(1)
- blockCount = blockCount + 1
- blockType = getBlockType("down")
- if blockType:find("hopper") ~= nil then -- hopper found
- return true, ""
- end
- if blockCount > 20 then -- prevents travel too far from farm
- return false, "max number of moves "..blockCount.." exceeded"
- end
- end
- return false, "" -- hopper not found
- end
- local blockType = ""
- -- already checked if above chest/barrel
- -- block in front can only be air, hopper, water/kelp, or border wall blocks
- -- block below already known
- if startOn == "h" then -- on hopper
- lib.aboveHopper() -- go into water then returnToHopper()
- elseif startOn == "w" then -- on or in water
- up(1)
- if isItemOfType("down", {"water", "kelp"}) then -- above water
- down(1) -- go back into water
- else -- up too far: no water below
- down(2)
- end
- while not lib.findBorder() do end -- find nearest border and turn right until hit hopper
- returnToHopper() -- same as end of harvest
- else -- starting on air can only be above home
- down(2) -- go down to chest
- end
- end
- local function checkStorageLevel()
- local chest = peripheral.wrap("front")
- local count = 0
- for slot, item in pairs(chest.list()) do
- count = count + item.count
- end
- return count, chest.size()
- end
- local function main()
- clear() -- clear terminal
- local startOn = isHome() -- c = home, else a, w, h, ""
- if startOn == "" then
- print("Unable to determine position\nMove to start and reboot")
- return
- elseif startOn ~= "c" then -- check if starting in correct position
- goHome(startOn) -- find starting position
- end
- if not turnToStorage() then -- ensure storage is available
- print("Unable to locate storage chest / barrel")
- return
- end
- local count, size = checkStorageLevel()
- if count / 64 > size * 0.8 then
- print("Storage at 80%. Farm disabled")
- return
- end
- turnToSmelter(1) -- facing smelter 1
- emptyTurtle() -- wait for furnaces to finish, refuel turtle if any spare fuel
- while true do
- farm() -- enter farming process (infinite loop)
- turnToSmelter(1) -- make sure facing smelter
- end
- end
- main()
Add Comment
Please, Sign In to add comment