diff --git a/inferium-harvester.lua b/inferium-harvester.lua index 3c80780..cdb0c0f 100644 --- a/inferium-harvester.lua +++ b/inferium-harvester.lua @@ -1,13 +1,88 @@ local turtleUtils = require('libs/turtle-utils') local config = require('config/harvesting') -local VERSION = "0.4.2" +local VERSION = "0.5.0 local IDLE_TIME = 2 -local MIN_FUEL_NEEDED = (10 + config.firstCropZ + config.length) * 2 +local WAIT_ITEM_IDLE_TIME = 5 +local MIN_FUEL_NEEDED = (100 + config.firstCropZ + config.length) * 2 local MIN_FREE_SLOTS_BEFORE_COMPACT = 4 +local function mystical(essenceName) + return 'mysticalagriculture:' .. essenceName .. '_seeds' +end + +local fakeRemotePlan = { + mystical('coal'), + mystical('diamond'), + mystical('quartz'), + mystical('dye'), + mystical('inferium'), + mystical('inferium'), + mystical('inferium'), + mystical('inferium') +} + +-- a table with the list of current crops +local localPlan = nil + -- UTILS +local function difference(a, b) + local aa = {} + + for k,v in pairs(a) do aa[v]=true end + for k,v in pairs(b) do aa[v]=nil end + + local ret = {} + local n = 0 + + for k,v in pairs(a) do + if aa[v] then n=n+1 ret[n]=v end + end + + return ret +end + +local function shallowClone(t) + local res = {} + for k,v in pairs(t) do + res[k] = v + end + return res +end + +local function removeFirst(t, x) + local res = {} + local removed = false + + for k,v in pairs(t) do + if not removed and x == v then + removed = true + else + table.insert(res, v) + end + end +end + +local function find(t, predicate) do + for k,v in pairs(t) do + if predicate(v, k) then + return v, k + end + end +end + +local function getIndexedCount(t) + local res = {} + + for k,v in pairs(t) do + local count = res[v] or 0 + res[v] = count + 1 + end + + return res +end + local function assertEnoughFuel() if turtle.getFuelLevel() < MIN_FUEL_NEEDED then error('Not enough fuel') @@ -32,9 +107,35 @@ local function retrieveChestFuelOrientation() end local function getSeedNameFromCropName(cropName) + + if not cropName then + return cropName + end + return string.gsub(cropName, 'crop', 'seeds') end +function isSeed(item) + local tags = item.tags or {} + return tags['forge:seeds'] or tags['mysticalagriculture:seeds'] or false +end + +-- Inventory utils +-- the slot of the item is returned +local function waitForItem(inventory, itemName, count, sleepTime) + sleepTime = sleepTime or 5 -- TODO: DEFAULT_IDLE_TIME + + while true do + for slot, item in pairs(inventory.list()) do + if item.name == itemName and item.count >= count then + return slot + end + end + + os.sleep(IDLE_TIME) + end +end + -- Implementations local function retrieveHomePositionProcedure() if turtleUtils.getInventory('bottom') then @@ -65,7 +166,7 @@ local function retrieveHomePositionProcedure() while true do if turtleUtils.getInventory('bottom') then - return + break end if turtle.inspect() then @@ -74,6 +175,8 @@ local function retrieveHomePositionProcedure() turtle.forward() end end + + turtle.turnLeft() end local function dropAllProcedure() @@ -91,18 +194,16 @@ local function dropAllProcedure() end local function refuelProcedure() - turtle.turnLeft() - local refuelOk, refuelErr = turtleUtils.refuelWithBuffer('bottom', 'front', MIN_FUEL_NEEDED, IDLE_TIME) if not refuelOk then error('Cannot refuel the turtle: "' .. refuelErr .. '"') end - - turtle.turnLeft() end local function goToHarvestPoint() + turtle.turnLeft() + for i=1, config.firstCropZ, 1 do turtle.forward() end @@ -112,6 +213,14 @@ local function goBackToHome() for i=1, config.firstCropZ, 1 do turtle.forward() end + + turtle.turnLeft() +end + +local function compactIfNeeded() + if turtleUtils.countFreeSlots() < MIN_FREE_SLOTS_BEFORE_COMPACT then + turtleUtils.compactInventory() + end end local function harvestDown() @@ -130,11 +239,10 @@ local function harvestDown() error('turtle cannot place crop') end - if turtleUtils.countFreeSlots() < MIN_FREE_SLOTS_BEFORE_COMPACT then - turtleUtils.compactInventory() - end + compactIfNeeded() end + local function forward() if not turtle.forward() then error('turtle is blocked') @@ -164,6 +272,151 @@ local function harvestProcedure() end end +local function retrieveLocalPlan() + goToHarvestPoint() + + localPlan = {} + + for i=1, config.length, 1 do + local ok, block = turtle.inspectDown() + local blockName = block and block.name + localPlan[i] = getSeedNameFromCropName(blockName) + + if i ~= config.length then + forward() + end + end + + turtle.turnLeft() + turtle.turnLeft() + + for i=1, config.length, 1 do + if i ~= config.length then + forward() + end + end + + goBackToHome() +end + +local function fetchRemotePlan() + -- TODO + return fakeRemotePlan +end + +local function removeSeeds(seeds) + goToHarvestPoint() + + local stateSeeds = seeds + + for i=1, config.length, 1 do + local ok, block = turtle.inspectDown() + local blockName = block and block.name + + local found = find(stateSeeds, function(seedName) return seedName == getSeedNameFromCropName(blockName) end) + + if found then + local digOk = turtle.digDown() + compactIfNeeded() + + if not digOk then + error('cannot remove seed') + end + + stateSeeds = removeFirst(stateSeeds, found) + end + + if i ~= config.length then + forward() + end + end + + turtle.turnLeft() + turtle.turnLeft() + + for i=1, config.length, 1 do + if i ~= config.length then + forward() + end + end + + goBackToHome() + dropAllProcedure() +end + +local function retrieveSeeds(seeds) + local seedsCount = getIndexedCount(seeds) + + local storageInventory = turtleUtils.waitForInventory('bottom', WAIT_ITEM_IDLE_TIME) + local bufferInventory = turtleUtils.waitForInventory('front', WAIT_ITEM_IDLE_TIME) + + for seedName, count in pairs(seedsCount) do + local slot = waitForItem(storageInventory, seedName, count, WAIT_ITEM_IDLE_TIME) + local pushOk = storageInventory.pushItems(peripheral.getName(bufferInventory), slot, count) + + if not pushOk then + error('retrieveSeeds error: cannot pushItems from storage to buffer') + end + + local suckOk = turtle.suck() + + if not suckOk then + error('retrieveSeeds error: cannot suck items from buffer') + end + end +end + +local function replantSeeds() + goToHarvestPoint() + + for i=1, config.length, 1 do + local ok, block = turtle.inspectDown() + + if not ok then + turtleUtils.selectItemBy(function(slot) + local item = turtle.getItemDetail(slot, true) + return isSeed(item) + end) + + turtle.placeDown() + end + + if i ~= config.length then + forward() + end + end + + turtle.turnLeft() + turtle.turnLeft() + + for i=1, config.length, 1 do + if i ~= config.length then + forward() + end + end + + goBackToHome() +end + +local function replantProcedure() + if localPlan == nil then + retrieveLocalPlan() + end + + local remotePlan = fetchRemotePlan() + + if remotePlan == nil then + error('cannot fetch the remote plan') + end + + local seedsToRemove = difference(localPlan, remotePlan) + local seedsToPlant = difference(remotePlan, localPlan) + + removeSeeds(seedsToRemove) + retrieveSeeds(seedsToPlant) + replantSeeds() +end + -- Main procedure local function main() print("Starting Trap's inferium harvester v" .. VERSION) @@ -180,6 +433,8 @@ local function main() dropAllProcedure() refuelProcedure() + replantProcedure() + goToHarvestPoint() harvestProcedure() goBackToHome() diff --git a/libs/turtle-utils.lua b/libs/turtle-utils.lua index 2fab15f..75065b1 100644 --- a/libs/turtle-utils.lua +++ b/libs/turtle-utils.lua @@ -181,6 +181,17 @@ turtleUtils.selectItemByName = function(itemName) return false end +turtleUtils.selectItemBy = function(predicate) + for i = 1, 16, 1 do + if predicate(i) then + turtle.select(i) + return true + end + end + + return false +end + turtleUtils.refuel = function(minFuel, suckFn, sleepTime) suckFn = suckFn or turtle.suck sleepTime = sleepTime or DEFAULT_IDLE_TIME