superTreefarm

This commit is contained in:
kepler155c@gmail.com
2018-12-21 19:48:40 -05:00
parent 12aebb0b88
commit fdf9c70338
4 changed files with 701 additions and 14 deletions

View File

@@ -53,17 +53,6 @@ local sensor = device['plethora:sensor']
turtle.setMovementStrategy('goto')
turtle.setPolicy(turtle.policies.attack)
function Point.iterateClosest(spt, ipts)
local pts = Util.shallowCopy(ipts)
return function()
local pt = Point.closest(spt, pts)
if pt then
Util.removeByValue(pts, pt)
return pt
end
end
end
local function findChests()
if chest then
return { chest }
@@ -109,6 +98,7 @@ local function dropOff()
end
end
end
turtle.select(1)
end
local function normalize(b)

697
farms/superTreefarm.lua Normal file
View File

@@ -0,0 +1,697 @@
--[[
Requirements:
Place turtle against an oak tree or oak sapling
Area around turtle must be flat and can only be dirt or grass
(10 blocks in each direction from turtle)
Turtle must have: crafting table, chest
Turtle must have a pick equipped on the LEFT side
Optional:
Add additional sapling types that can grow with a single sapling
Notes:
If the turtle does not get any saplings from the initial tree, place
down another sapling in front of the turtle.
The program will be able to survive server restarts as long as it has
created the cobblestone line. If the program is stopped before that time,
place the turtle in the original position before restarting the program.
]]--
local GPS = require('gps')
local Peripheral = require('peripheral')
local Point = require('point')
local Util = require('util')
local fs = _G.fs
local os = _G.os
local peripheral = _G.peripheral
local turtle = _G.turtle
local STARTUP_FILE = 'usr/autorun/superTreefarm.lua'
local FUEL_BASE = 0
local FUEL_DIRE = FUEL_BASE + 10
local FUEL_GOOD = FUEL_BASE + 2000
local MIN_CHARCOAL = 24
local MAX_SAPLINGS = 32
local GRID = {
TL = { x = 8, y = 0, z = -7 },
TR = { x = 8, y = 0, z = 8 },
BL = { x = -7, y = 0, z = -7 },
BR = { x = -7, y = 0, z = 8 },
}
local HOME_PT = { x = 0, y = 0, z = 0, heading = 0 }
local HIGH_PT = { x = 0, y = 8, z = 0, heading = 0 }
local DIG_BLACKLIST = {
[ 'minecraft:furnace' ] = true,
[ 'minecraft:lit_furnace' ] = true,
[ 'minecraft:chest' ] = true,
}
local APPLE = 'minecraft:apple:0'
local CHARCOAL = 'minecraft:coal:1'
local CHEST = 'minecraft:chest:0'
local COBBLESTONE = 'minecraft:cobblestone:0'
local CRAFTING_TABLE = 'minecraft:crafting_table:0'
local PICKAXE = 'minecraft:diamond_pickaxe'
local DIRT = 'minecraft:dirt:0'
local FURNACE = 'minecraft:furnace:0'
local MODEM = 'computercraft:peripheral:1'
local LEAVES = 'minecraft:leaves'
local LOG = 'minecraft:log'
local LOG2 = 'minecraft:log2'
local OAK_LOG = 'minecraft:log:0'
local OAK_PLANK = 'minecraft:planks:0'
local SAPLING = 'minecraft:sapling:0'
local SCANNER = 'plethora:module:2'
local STICK = 'minecraft:stick:0'
local STONE = 'minecraft:stone:0'
local TORCH = 'minecraft:torch:0'
local ALL_SAPLINGS = {
SAPLING
}
local state = Util.readTable('usr/config/superTreefarm') or {
trees = {
{ x = 1, y = 0, z = 0 }
}
}
local clock = os.clock()
local function equip(side, item, rawName)
local equipped = Peripheral.lookup('side/' .. side)
if equipped and equipped.type == item then
return true
end
if not turtle.equip(side, rawName or item) then
if not turtle.selectSlotWithQuantity(0) then
error('No slots available')
end
turtle.equip(side)
if not turtle.equip(side, item) then
error('Unable to equip ' .. (rawName or item))
end
end
turtle.select(1)
end
local function inspect(fn)
local s, item = fn()
if s and item then
return item.name .. ':' .. item.metadata
end
return 'minecraft:air:0'
end
local function setState(key, value)
state[key] = value
Util.writeTable('usr/config/superTreefarm', state)
end
local function refuel()
if turtle.getFuelLevel() < FUEL_GOOD then
local charcoal = turtle.getItemCount(CHARCOAL)
if charcoal > 1 then
turtle.refuel(CHARCOAL, math.min(charcoal - 1, MIN_CHARCOAL / 2))
print('fuel: ' .. turtle.getFuelLevel())
end
end
return true
end
local function safePlaceBlock(item)
if turtle.placeUp(item) then
return true
end
local s, m = turtle.inspectUp()
if s and not DIG_BLACKLIST[m.name] then
turtle.digUp()
return turtle.placeUp(item)
end
turtle.forward()
return turtle.placeUp(item)
end
local function craftItem(item, qty)
local success, msg
if safePlaceBlock(CHEST) then
os.sleep(.2) -- needed for minecraft 1.12
Util.print('Crafting %d %s', (qty or 1), item)
success, msg = turtle.craftItem(item, qty or 1, {
side = 'top',
direction = 'down',
})
repeat until not turtle.suckUp()
if not success then
print(msg)
end
turtle.digUp()
end
return success
end
local function emptyFurnace()
if state.cooking then
print('Emptying furnace')
turtle.suckDownAt(state.furnace)
turtle.suckForwardAt(state.furnace)
turtle.suckUpAt(state.furnace)
setState('cooking')
end
end
local function cook(item, count, result, fuel, fuelCount)
emptyFurnace()
setState('cooking', true)
fuel = fuel or CHARCOAL
fuelCount = fuelCount or math.ceil(count / 8)
Util.print('Making %d %s', count, result)
turtle.dropForwardAt(state.furnace, fuel, fuelCount)
turtle.dropDownAt(state.furnace, item, count)
count = count + turtle.getItemCount(result)
turtle.select(1)
turtle.pathfind(Point.below(state.furnace))
local lastSuck = os.clock()
repeat
os.sleep(1)
if turtle.suckUp() then
lastSuck = os.clock()
end
if os.clock() - lastSuck > 10 then
-- sponge bug
Util.print('Timed out waiting for furnace')
return
end
until turtle.getItemCount(result) >= count
setState('cooking')
end
local function makeSingleCharcoal()
local slots = turtle.getSummedInventory()
if not state.furnace or
slots[CHARCOAL] or
not slots[OAK_LOG] or
slots[OAK_LOG].count < 2 then
return true
end
turtle.faceAgainst(state.furnace)
if craftItem(OAK_PLANK) then
cook(OAK_LOG, 1, CHARCOAL, OAK_PLANK, 1)
turtle.refuel(OAK_PLANK)
end
return true
end
local function makeCharcoal()
local slots = turtle.getSummedInventory()
if not state.furnace or
not slots[CHARCOAL] or
slots[CHARCOAL].count >= MIN_CHARCOAL then
return true
end
local function getLogSlot()
local maxslot = { count = 0 }
for k,slot in pairs(slots) do
if string.match(k, 'minecraft:log') then
if slot.count > maxslot.count then
maxslot = slot
end
end
end
return maxslot
end
repeat
slots = turtle.getSummedInventory()
local charcoal = slots[CHARCOAL].count
local slot = getLogSlot(slots)
if slot.count < 8 then
break
end
local toCook = math.min(charcoal, math.floor(slot.count / 8))
toCook = math.min(toCook, math.floor((MIN_CHARCOAL + 8 - charcoal) / 8))
toCook = toCook * 8
cook(slot.key, toCook, CHARCOAL)
until charcoal + toCook >= MIN_CHARCOAL
return true
end
local function getCobblestone(count)
local slots = turtle.getSummedInventory()
if not slots[COBBLESTONE] or slots[COBBLESTONE].count < count then
print('Collecting cobblestone')
slots[COBBLESTONE] = true
slots[DIRT] = true
local pt = Point.copy(GRID.BR)
pt.x = GRID.BR.x + 2
pt.z = GRID.BR.z - 2
turtle.pathfind(pt)
repeat
turtle.select(1)
turtle.digDown()
turtle.down()
for _ = 1, 4 do
if inspect(turtle.inspect) == STONE then
turtle.dig()
end
turtle.turnRight()
end
for item in pairs(turtle.getSummedInventory()) do
if not slots[item] then
turtle.drop(item)
end
end
until turtle.getItemCount(COBBLESTONE) >= count
turtle._goto(pt)
turtle.placeDown(DIRT)
turtle.drop(DIRT)
end
end
local function createFurnace()
if not state.furnace then
if turtle.getFuelLevel() < FUEL_BASE + 100 then
return true -- try again later
end
print('Adding a furnace')
getCobblestone(8)
if turtle.has(FURNACE) or craftItem(FURNACE) then
-- turtle.drop(COBBLESTONE)
local furnacePt = { x = GRID.BL.x + 2, y = 1, z = GRID.BL.z + 2 }
turtle.placeAt(furnacePt, FURNACE)
setState('furnace', furnacePt)
end
end
end
local function createChests()
if state.chest then
return
end
if turtle.getFuelLevel() > FUEL_GOOD and
turtle.canCraft(CHEST, 4, turtle.getSummedInventory()) then
print('Adding storage')
if turtle.has(CHEST, 2) or craftItem(CHEST, 2) then
local pt = Point.copy(GRID.BL)
pt.x = pt.x + 1
pt.y = pt.y - 1
pt.z = pt.z + 1
turtle.digDownAt(pt)
turtle.placeDown(CHEST)
pt.z = pt.z + 1
turtle.digDownAt(pt)
turtle.placeDown(CHEST)
setState('chest', Util.shallowCopy(pt))
turtle.drop(DIRT)
turtle.refuel(OAK_PLANK)
end
end
return true
end
local function dropOffItems()
if state.chest then
local slots = turtle.getSummedInventory()
if state.chest and
slots[CHARCOAL] and
slots[CHARCOAL].count >= MIN_CHARCOAL and
(turtle.getItemCount(LOG) > 0 or
turtle.getItemCount(LOG2) > 0) then
print('Storing logs')
turtle.pathfind(Point.above(state.chest))
turtle.dropDown(LOG)
turtle.dropDown(LOG2)
for _, sapling in pairs(ALL_SAPLINGS) do
if slots[sapling] and slots[sapling].count > MAX_SAPLINGS then
turtle.dropDown(sapling, slots[sapling].count - MAX_SAPLINGS)
end
end
turtle.dropDown(APPLE)
end
end
return true
end
local function eatSaplings()
local slots = turtle.getSummedInventory()
for _, sapling in pairs(ALL_SAPLINGS) do
if slots[sapling] and slots[sapling].count > MAX_SAPLINGS then
turtle.refuel(sapling, slots[sapling].count - MAX_SAPLINGS)
end
end
return true
end
local function placeTorches()
if state.torches then
return
end
if turtle.getFuelLevel() > 100 and
turtle.canCraft(TORCH, 4, turtle.getSummedInventory()) then
print('Placing torches')
if turtle.has(TORCH, 4) or craftItem(TORCH, 4) then
local pts = { }
for x = -4, 4, 8 do
for z = -4, 4, 8 do
table.insert(pts, { x = x, y = 0, z = z })
end
end
Point.eachClosest(turtle.point, pts, function(pt)
turtle.placeAt(pt, TORCH)
end)
turtle.refuel(STICK)
turtle.refuel(OAK_PLANK)
setState('torches', true)
end
end
return true
end
local function randomSapling()
local sapling = SAPLING
if #state.trees > 1 then
ALL_SAPLINGS = { }
local slots = turtle.getFilledSlots()
for _, slot in pairs(slots) do
if slot.name == 'minecraft:sapling' then
table.insert(ALL_SAPLINGS, slot.key)
end
end
if #ALL_SAPLINGS > 0 then
sapling = ALL_SAPLINGS[math.random(1, #ALL_SAPLINGS)]
end
end
return sapling
end
local function scan(pt, filter)
turtle.pathfind(pt)
equip('left', 'plethora:scanner', SCANNER)
local raw = peripheral.call('left', 'scan')
equip('left', PICKAXE)
local blocks = Util.reduce(raw, function(acc, b)
Point.rotate(b, state.home.heading)
b.x = b.x + turtle.point.x
b.y = b.y + turtle.point.y
b.z = b.z + turtle.point.z
if filter(b) then
table.insert(acc, b)
end
end, { })
return blocks
end
local function plantTrees()
local blocks = scan(HOME_PT, function(b)
return b.name == 'minecraft:sapling'
end)
local plant = Util.reduce(state.trees, function(acc, b)
for _, sapling in pairs(blocks) do
if sapling.x == b.x and sapling.z == b.z then
return
end
end
table.insert(acc, b)
end, { })
Point.eachClosest(turtle.point, plant, function(pt)
turtle.digAt(pt)
turtle.placeAt(pt, randomSapling())
turtle.select(1)
end)
end
local function fellTrees(blocks)
local function desperateRefuel(min)
if turtle.getFuelLevel() < min then
local logs = turtle.getItemCount(OAK_LOG)
if logs > 0 then
if craftItem(OAK_PLANK, math.min(8, logs * 4)) then
turtle.refuel(OAK_PLANK)
print('fuel: ' .. turtle.getFuelLevel())
end
end
end
end
turtle.setMoveCallback(function() desperateRefuel(FUEL_DIRE) end)
desperateRefuel(FUEL_DIRE)
turtle.setPolicy("digAttack")
for pt in Point.iterateClosest(turtle.point, blocks) do
turtle.digAt(pt)
end
desperateRefuel(FUEL_BASE + 100)
turtle.clearMoveCallback()
turtle.setPolicy("attackOnly")
return true
end
local function fell()
local blocks = scan(HOME_PT, function(b)
return b.y > 0 and (b.name == LEAVES or b.name == LOG or b.name == LOG2)
end)
if #blocks > 0 then
print('Chopping')
local fuel = turtle.getFuelLevel()
fellTrees(blocks)
blocks = scan(HIGH_PT, function(b)
return b.y > 0 and (b.name == LEAVES or b.name == LOG or b.name == LOG2)
end)
fellTrees(blocks)
print('Used ' .. (fuel - turtle.getFuelLevel()) .. ' fuel')
plantTrees()
end
return true
end
local function moreTrees()
if #state.trees > 1 then
return
end
if not state.chest or turtle.getItemCount('minecraft:sapling') < 15 then
return true
end
print('Adding more trees')
local singleTree = state.trees[1]
state.trees = { }
for x = -2, 2, 1 do
for z = -2, 2, 2 do
if x ~= 0 or z ~= 0 then
table.insert(state.trees, { x = x, y = 0, z = z })
end
end
end
turtle.digAt(singleTree)
setState('trees', state.trees)
Point.eachClosest(turtle.point, state.trees, function(pt)
turtle.placeDownAt(pt, randomSapling())
end)
end
local function findHome()
local pt = GPS.getPoint(2) or error('GPS not found')
equip('left', SCANNER)
local facing = peripheral.call('left', 'getBlockMeta', 0, 0, 0).state.facing
pt.heading = Point.facings[facing].heading
equip('left', PICKAXE)
if not state.home then
setState('home', pt)
end
-- convert to relative coordinates
turtle.setPoint({
x = pt.x - state.home.x,
y = pt.y - state.home.y,
z = pt.z - state.home.z,
heading = pt.heading,
})
Point.rotate(turtle.point, state.home.heading)
turtle.setHeading(state.home.heading)
turtle.point.heading = 0
turtle.setPathingBox({
x = GRID.TL.x,
y = GRID.TL.y,
z = GRID.TL.z,
ex = GRID.BR.x,
ey = 16,
ez = GRID.BR.z,
})
end
local function updateClock()
local ONE_HOUR = 50
if os.clock() - clock > ONE_HOUR then
clock = os.clock()
else
print('sleeping for ' .. math.floor(ONE_HOUR - (os.clock() - clock)))
os.sleep(ONE_HOUR - (os.clock() - clock))
clock = os.clock()
end
return true
end
local function startupCheck()
equip('right', MODEM, 'modem')
equip('left', PICKAXE)
local slots = turtle.getSummedInventory()
if not slots[CHEST] or not slots[CRAFTING_TABLE] or not slots[SCANNER] then
error('A chest and crafting table must be in inventory')
end
if not fs.exists(STARTUP_FILE) then
Util.writeFile(STARTUP_FILE,
[[os.sleep(1)
shell.openForegroundTab('superTreefarm.lua')]])
print('Autorun program created: ' .. STARTUP_FILE)
end
end
local tasks = {
{ desc = 'Startup check', fn = startupCheck },
{ desc = 'Finding home', fn = findHome },
{ desc = 'Emptying furnace', fn = emptyFurnace },
{ desc = 'Adding trees', fn = moreTrees },
{ desc = 'Chopping', fn = fell },
{ desc = 'Snacking', fn = eatSaplings },
{ desc = 'Creating chest', fn = createChests },
{ desc = 'Creating furnace', fn = createFurnace },
{ desc = 'Making charcoal', fn = makeSingleCharcoal },
{ desc = 'Making charcoal', fn = makeCharcoal },
{ desc = 'Placing torches', fn = placeTorches },
{ desc = 'Refueling', fn = refuel },
{ desc = 'Dropping off items', fn = dropOffItems },
{ desc = 'Condensing', fn = turtle.condense },
{ desc = 'Sleeping', fn = updateClock },
}
--local s, m = turtle.run(function()
turtle.reset()
turtle.addFeatures('level', 'crafting')
turtle.setPolicy("attack")
while not turtle.isAborted() do
print('fuel: ' .. turtle.getFuelLevel())
for _,task in ipairs(Util.shallowCopy(tasks)) do
--print(task.desc)
turtle.setStatus(task.desc)
turtle.select(1)
if not task.fn() then
Util.filterInplace(tasks, function(v) return v.fn ~= task.fn end)
end
end
end
--end)
--if not s then
-- error(m or 'Failed')
--end

View File

@@ -206,7 +206,7 @@ function Craft.craftRecipeInternal(recipe, count, storage, origItem)
count = canCraft
end
_G._debug({'eval', recipe.result, count })
--_G._debug({'eval', recipe.result, count })
--local maxCount = math.floor((recipe.maxCount or 64) / recipe.count)
local maxCount = recipe.maxCount or math.floor(64 / recipe.count)
@@ -239,7 +239,7 @@ _G._debug({'eval', recipe.result, count })
while canCraft > 0 do
local batch = math.min(canCraft, maxCount)
local machine = Craft.machineLookup[recipe.result]
_G._debug({ 'crafting', recipe.result, batch })
--_G._debug({ 'crafting', recipe.result, batch })
if machine then
if not machineCraft(recipe, storage, machine, request, batch, origItem) then
break

View File

@@ -8,7 +8,7 @@ local device = _G.device
local peripheral = _G.peripheral
local scanner = device.neuralInterface or device['plethora:scanner'] or peripheral.find('manipulator')
if not scanner or not scanner.sense then
if not scanner or not scanner.scan then
error('Plethora scanner must be equipped')
end