diff --git a/kai/lib.lua b/kai/lib.lua index 42e3ca4..40ec963 100644 --- a/kai/lib.lua +++ b/kai/lib.lua @@ -1,5 +1,5 @@ -- Do not lose state when reset (chunkloading etc) -state = (function () +local state = (function () local path = ".state" local state_table = {} @@ -21,6 +21,8 @@ state = (function () }) end)() +-- Not sure whether this is a good idea +--[[ if state.x == nil or state.y == nil or state.z == nil or state.o == nil then printError("[lib] position is not set") printError("[lib] defaulting to 0, 0, 0, +z") @@ -29,6 +31,21 @@ if state.x == nil or state.y == nil or state.z == nil or state.o == nil then state.z = 0 state.o = 0 end +]] + +local function setPosition(x, y, z, direction) + local o = ({ + ["+z"] = 0, + ["-x"] = 1, + ["-z"] = 2, + ["+x"] = 3, + })[direction] + + state.x = x + state.y = y + state.z = z + state.o = o +end --[[ orientation: @@ -38,11 +55,7 @@ end 3 = East (+x) ]] -function getOrientation() - return ({"+z", "-x", "-z", "+x"})[1 + state.o] -end - -function move(raw_move, position_update) +local function move(raw_move, position_update) return function() success, err = raw_move() if not success then @@ -53,40 +66,80 @@ function move(raw_move, position_update) end end +local fwd = move(turtle.forward, function() + if state.o == 1 or state.o == 3 then + state.x = state.x + ((state.o == 1) and -1 or 1) + elseif state.o == 0 or state.o == 2 then + state.z = state.z + ((state.o == 2) and -1 or 1) + end +end) + +local back = move(turtle.back, function() + if state.o == 1 or state.o == 3 then + state.x = state.x + ((state.o == 1 and 1 or -1)) + elseif state.o == 0 or state.o == 2 then + state.z = state.z + ((state.o == 2) and 1 or -1) + end +end) + +local up = move(turtle.up, function() + state.y = state.y + 1 +end) + +local down = move(turtle.down, function() + state.y = state.y - 1 +end) + +local left = move(turtle.turnLeft, function() + state.o = (state.o - 1) % 4 +end) + +local right = move(turtle.turnRight, function() + state.o = (state.o + 1) % 4 +end) + +local function getOrientation() + return ({"+z", "-x", "-z", "+x"})[1 + state.o] +end + +local function rotateTowards(direction) + local target_o = ({ + ["+z"] = 0, + ["-x"] = 1, + ["-z"] = 2, + ["+x"] = 3, + })[direction] + + if not target_o then + error(tostring(direction) .. " is not a valid direction.") + end + + local delta_o = (target_o - state.o) % 4 + + if delta_o == 0 then + -- orientation already correct + elseif delta_o == 1 then + right() + elseif delta_o == 2 then + right() + right() + elseif delta_o == 3 then + left() + end +end + + return { state = state, + fwd = fwd, + back = back, + up = up, + down = down, + left = left, + right = right, + + setPosition = setPosition, getOrientation = getOrientation, - - fwd = move(turtle.forward, function() - if state.o == 1 or state.o == 3 then - state.x = state.x + ((state.o == 1) and -1 or 1) - elseif state.o == 0 or state.o == 2 then - state.z = state.z + ((state.o == 2) and -1 or 1) - end - end), - - back = move(turtle.back, function() - if state.o == 1 or state.o == 3 then - state.x = state.x + ((state.o == 1 and 1 or -1)) - elseif state.o == 0 or state.o == 2 then - state.z = state.z + ((state.o == 2) and 1 or -1) - end - end), - - up = move(turtle.up, function() - state.y = state.y + 1 - end), - - down = move(turtle.down, function() - state.y = state.y - 1 - end), - - left = move(turtle.turnLeft, function() - state.o = (state.o - 1) % 4 - end), - - right = move(turtle.turnRight, function() - state.o = (state.o + 1) % 4 - end), + rotateTowards = rotateTowards, } \ No newline at end of file diff --git a/kai/woodfarm.lua b/kai/woodfarm.lua new file mode 100644 index 0000000..36176c6 --- /dev/null +++ b/kai/woodfarm.lua @@ -0,0 +1,239 @@ +local T = require("lib.lua") + +--[[ + +States: +- MOVE_TO_TREE +- CHECK_TREE +- ASCEND_TREE +- FINALIZE_TREE +- OUTPUT +- RESUPPLY_SAPS +- RESUPPLY_FUEL + +Persistent Variables: +- current_state +- current_tree [1,14] +- current_row [1,6] + +Inventory: +64 + 20 Saplings in Slots 1 and 2 + +]] + +local MAX_TREE = 14 +local MAX_ROW = 6 + +local OUTPUT_TARGET = { x = 2, z = 0, o = "+z" } +local SAPS_TARGET = { x = 1, z = 0, o = "+z" } +local FUEL_TARGET = { x = 0, z = 0, o = "+z" } + +local TREE_MAX_HEIGHT = 7 +-- Saplings are at y = -1 (and coordinates are zero-based) +local TREE_MAX_Y = TREE_MAX_HEIGHT - 2 +local LOG_NAME = "minecraft:birch_log" + +local SAP_SLOT = 1 +local BACKUP_SAP_SLOT = 2 +local REFUEL_SLOT = 3 + +local SAP_SLOT_TARGET_COUNT = 64 +local BACKUP_SAP_SLOT_TARGET_COUNT = 20 + +-- Should be enough even if every tree is fully grown +local FUEL_LEVEL_TARGET = 1000 + +local function calculate_tree_position(row_index, tree_index) + + local x = (row_index - 1) * 5 + local z + + if row_index % 2 == 0 then + z = -2 * tree_index + else + z = -30 + 2 * tree_index + end + + return x, z +end + +local function greedy_move(x, z, o) + if z ~= T.state.z then + if z > T.state.z then + T.rotateTowards("+z") + else: + T.rotateTowards("-z") + end + for _ = 1, math.abs(z - T.state.z) do + T.fwd() + end + end + + if x ~= T.state.x then + if x > T.state.x then + T.rotateTowards("+x") + else: + T.rotateTowards("-x") + end + for _ = 1, math.abs(x - T.state.x) do + T.fwd() + end + end + + T.rotateTowards(o) +end + +local function ensure_item_count(count) + if turtle.getItemCount() >= count then + return + end + turtle.suck(count - turtle.getItemCount()) + -- turtle.suck might not suck enough items if the target container does not have them + -- but it won't tell us if this happened... + while turtle.getItemCount() ~= count do + sleep(1) + turtle.suck(count - turtle.getItemCount()) + end +end + +local state_machine = { + + ["MOVE_TO_TREE"] = function() + -- get target tree coordinates + -- and move such that we are facing it + local x, z = calculate_tree_position(T.state.current_row, T.state.current_tree) + local o + + if T.state.current_row ~= 1 and T.state.current_tree == 1 then + -- we just turned towards a new row and are approaching in positive x direction + x = x - 1 + o = "+x" + else + -- we are approaching in negative z direction + z = z + 1 + o = "-z" + end + + greedy_move(x, z, o) + return "CHECK_TREE" + end + + ["CHECK_TREE"] = function() + if not turtle.detect() then + -- no tree, so skip ascending + T.fwd() + return "FINALIZE_TREE" + else + -- tree present, chop it down + turtle.dig() + T.fwd() + turtle.digDown() + return "ASCEND_TREE" + end + end + + ["ASCEND_TREE"] = function() + local success, data = turtle.inspectUp() + if not s and T.state.y < TREE_MAX_Y - 1 then + -- probably reset after chopping a block + T.up() + end + + while true do + local success, data = turtle.inspectUp() + if not s or data.name ~= LOG_NAME then + -- chopped all the wood + break + end + turtle.digUp() + if T.state.y == TREE_MAX_Y - 1 then + -- we are below the last log, no need to move up + break + end + T.up() + end + return "FINALIZE_TREE" + end + + ["FINALIZE_TREE"] = function() + -- make sure we are directly above the sapling target + while T.state.y > 0 do + T.down() + end + + turtle.select(SAP_SLOT) + -- make sure we have a sapling + if turtle.getItemCount() == 0 then + turtle.select(BACKUP_SAP_SLOT) + turtle.transferTo(SAP_SLOT) + turtle.select(SAP_SLOT) + end + + + if T.state.current_tree < MAX_TREE then + -- go to the next tree in this row + T.state.current_tree = T.state.current_tree + 1 + return "MOVE_TO_TREE" + elseif T.state.current_row < MAX_ROW then + -- go to the first tree in the next row + T.state.current_tree = 0 + T.state.current_row = T.state.current_row + 1 + return "MOVE_TO_TREE" + else + -- went through the entire farm once + return "OUTPUT" + end + + ["OUTPUT"] = function() + greedy_move(OUTPUT_TARGET.x, OUTPUT_TARGET.z, OUTPUT_TARGET.o) + + -- Drop all logs + for slot = 1, 16 do + turtle.select(slot) + data = turtle.getItemDetail() + if data and data.name == LOG_NAME then + turtle.drop() + end + end + + return "RESUPPLY_SAPS" + end + + ["RESUPPLY_SAPS"] = function() + greedy_move(SAPS_TARGET.x, SAPS_TARGET.z, SAPS_TARGET.o) + + turtle.select(SAP_SLOT) + ensure_item_count(SAP_SLOT_TARGET_COUNT) + + turtle.select(BACKUP_SAP_SLOT) + ensure_item_count(BACKUP_SAP_SLOT_TARGET_COUNT) + + return "RESUPPLY_FUEL" + end + + ["RESUPPLY_FUEL"] = function() + greedy_move(FUEL_TARGET.x, FUEL_TARGET.z, FUEL_TARGET.o) + + local fuel_count = math.ceil(FUEL_LEVEL_TARGET - turtle.getFuelLevel() / 80) + turtle.select(REFUEL_SLOT) + ensure_item_count(fuel_count) + turtle.refuel() + + T.state.current_row = 0 + T.state.current_tree = 0 + return "MOVE_TO_TREE" + end + +} + +-- initial setup + +if not T.state.current_state then + T.setPosition(0, 0, 0, "-z") + T.state.current_state = "RESUPPLY_FUEL" +end + +-- main loop +while true do + T.state.current_state = state_machine[T.state.current_state]() +end \ No newline at end of file