local kp= getfenv() local x local y local z local facing local was_swimming local record_mode local facing_strings= {"-z", "+x", "+z", "-x"} local inventory_size= 16 local inventory_slots_table= {} for s=1, inventory_size do inventory_slots_table[s]= s end kp.ist= inventory_slots_table -- Index coord_limits with kp.nz and friends local coord_limits= {} kp.nz= 1 kp.px= 2 kp.pz= 3 kp.nx= 4 kp.py= 5 kp.ny= 6 kp.rev_dirs= {kp.pz, kp.nx, kp.nz, kp.px, kp.ny, kp.py} kp.reason_turtle_func_failed= 1 kp.reason_hit_limit= 2 kp.reason_repulsed= 3 kp.reason_bad_input= 4 kp.reason_file_open_failed= 5 kp.reason_block_not_recognized= 6 local reason_strings= {"reason_turtle_func_failed", "reason_hit_limit", "reason_repulsed"} local function less(a, b) return a < b end local function more(a, b) return a > b end local limit_compare_functions= {less, more, more, less, more, less} local function plus(a, b) return a + b end local function minus(a, b) return a - b end local coord_size_combine_functions= {minus, plus, plus, minus, plus, minus} function kp.init() x= 0 y= 0 z= 0 facing= 1 rev_facing= rev_dirs[facing] was_swimming= false record_mode= false end local function check_limit_helper(coord, limit, less_or_more) if limit then return less_or_more(coord, limit) end return false end function kp.check_limits(nx, ny, nz) return check_limit_helper(nx, coord_limits[kp.nx], less) or check_limit_helper(nx, coord_limits[kp.px], more) or check_limit_helper(ny, coord_limits[kp.ny], less) or check_limit_helper(ny, coord_limits[kp.py], more) or check_limit_helper(nz, coord_limits[kp.nz], less) or check_limit_helper(nz, coord_limits[kp.pz], more) end function kp.set_limits(xs, xe, ys, ye, zs, ze) coord_limits[kp.nx], coord_limits[kp.px]= xs, xe coord_limits[kp.ny], coord_limits[kp.py]= ys, ye coord_limits[kp.nz], coord_limits[kp.pz]= zs, ze end local deltas= {} deltas[kp.nz]= function() return x, y, z-1 end deltas[kp.px]= function() return x+1, y, z end deltas[kp.pz]= function() return x, y, z+1 end deltas[kp.nx]= function() return x-1, y, z end deltas[kp.py]= function() return x, y+1, z end deltas[kp.ny]= function() return x, y-1, z end function kp.facing_as_string() return facing_strings[facing] end function kp.show_pos() io.write("{ " .. tostring(x) .. ", " .. tostring(y) .. ", " .. tostring(z) .. "} f: " .. facing_strings[facing] .. " rf: " .. facing_strings[rev_facing] .. " s: " .. tostring(was_swimming) .. "\n") end function kp.get_pos() return x,y,z end local boolean_translation_warkaround_table_bullshit= {["true"]= false, ["false"]= true} function kp.record_pos(rx, ry, rz) if not rx or not ry or not rz then rx, ry, rz= x, y, z end local pos_file= fs.open("pos~", "w") if pos_file then pos_file.writeLine(tostring(x)) pos_file.writeLine(tostring(y)) pos_file.writeLine(tostring(z)) pos_file.writeLine(tostring(facing)) pos_file.writeLine(tostring(not was_swimming)) pos_file.close() else io.write("Could not open file to record pos.\n") end end function kp.move_recorded_pos() if fs.exists("pos") then fs.delete("pos") end fs.move("pos~", "pos") end function kp.load_pos() local pos_file= fs.open("pos", "r") if pos_file then local tx= tonumber(pos_file.readLine()) local ty= tonumber(pos_file.readLine()) local tz= tonumber(pos_file.readLine()) local tf= tonumber(pos_file.readLine()) local ts= boolean_translation_warkaround_table_bullshit[pos_file.readLine()] if tx and ty and tz and tf then x, y, z, facing, was_swimming= tx, ty, tz, tf, ts rev_facing= kp.rev_dirs[facing] kp.show_pos() else io.write("Pos file contained bad data:\n") io.write(tostring(tx) .. "\n") io.write(tostring(ty) .. "\n") io.write(tostring(tz) .. "\n") io.write(tostring(tf) .. "\n") io.write(tostring(ts) .. "\n") end pos_file.close() else io.write("Could not open file to load pos.\n") end end function kp.log(message) if not message then return false, kp.reason_bad_input end local log_file= fs.open("log", "a") if log_file then log_file.write(tostring(message)) else io.write("Could not open log file for message.\n") return false end return true end function kp.set_record_mode(m) record_mode= m end function kp.maybe_record_pos(rx, ry, rz) if record_mode then kp.record_pos(rx, ry, rz) end end function kp.maybe_move_recorded_pos() if record_mode then kp.move_recorded_pos() end end function kp.move_worker(turtle_func, dir) local nx, ny, nz= deltas[dir]() if check_limits(nx, ny, nz) then return false, kp.reason_hit_limit end if not turtle_func() then kp.maybe_record_pos() kp.maybe_move_recorded_pos() return false, kp.reason_turtle_func_failed end kp.end_swim(kp.rev_dirs[dir]) x, y, z= nx, ny, nz kp.maybe_record_pos() kp.maybe_move_recorded_pos() return true end function kp.forward() return kp.move_worker(turtle.forward, facing) end function kp.back() return kp.move_worker(turtle.back, rev_facing) end function kp.up() return kp.move_worker(turtle.up, kp.py) end function kp.down() return kp.move_worker(turtle.down, kp.ny) end function kp.turn_left() if not turtle.turnLeft() then return false end facing= facing - 1 if facing <= 0 then facing= 4 end rev_facing= rev_dirs[facing] kp.maybe_record_pos() kp.maybe_move_recorded_pos() return true end function kp.turn_right() if not turtle.turnRight() then return false end facing= facing + 1 if facing >= 5 then facing= 1 end rev_facing= rev_dirs[facing] kp.maybe_record_pos() kp.maybe_move_recorded_pos() return true end function kp.get_left_dir(dir) if not dir then dir= facing end local ret= dir - 1 if ret <= 0 then ret= 4 end return ret end function kp.get_right_dir(dir) if not dir then dir= facing end local ret= dir + 1 if ret >= 5 then ret= 1 end return ret end function kp.dir_to_side(dir) if not dir then return nil end if dir == kp.ny then return "top" end if dir == kp.py then return "bottom" end if dir == facing then return "front" end if dir == rev_facing then return "back" end if dir == kp.get_left_dir(facing) then return "left" end if dir == kp.get_right_dir(facing) then --io.write("The ComputerCraft people are fucking morons.\nRewrite your function to special case the right side if you need it to work.\n") return "right" end return nil end function kp.rs_get_input(dir) if not dir then return nil end local s= kp.dir_to_side(dir) if dir == kp.get_right_dir(facing) then local orig_facing= facing kp.face(dir) local ret= rs.getInput("front") kp.face(orig_facing) return ret else return rs.getInput(s) end end kp.face_helpers= {} kp.face_helpers[-3]= kp.turn_left kp.face_helpers[-2]= function() return kp.turn_right() and kp.turn_right() end kp.face_helpers[-1]= kp.turn_right kp.face_helpers[0]= function() return true end kp.face_helpers[1]= kp.turn_left kp.face_helpers[2]= kp.face_helpers[-2] kp.face_helpers[3]= kp.turn_right function kp.face(dir) if dir == kp.py or dir == kp.ny then return true end local fdif= facing - dir return kp.face_helpers[fdif]() end -- write_turtle_dir_getters is intended to write functions of the following -- form for the turtle functions that have directionally named variants: -- local function kp.something(direction) -- if direction == kp.py then return turtle.somethingUp -- elseif direction == kp.ny then return turtle.somethingDown -- end -- return turtle.something -- end -- These getters are a convenience for the directional variants of the related -- kp functions. local function write_turtle_dir_getters() local names_to_wrap= {"attack", "compare", "detect", "dig", "drop", "place", "suck"} for k, v in ipairs(names_to_wrap) do local get_name= "get_dir_"..v.."_func" kp[get_name]= function(dir) if dir == kp.py then return turtle[v.."Up"] elseif dir == kp.ny then return turtle[v.."Down"] end return turtle[v] end end end write_turtle_dir_getters() -- write_turtle_dir_wrappers is intended to write functions of the following -- form for the turtle functions that perform an action in a direction and -- thus need a kp function that takes on the kp direction to perform in. -- local function kp.something(direction) -- kp.face(direction) -- return kp.get_dir_something(direction)() -- end -- These wrappers provide a way to be able to pass the kp direction for an -- action as an argument, instead of it being part of the name the way it is -- in the turtle API. -- Functions that are not wrapped by write_turtle_dir_wrappers should have -- spectial case wrappers for the extra arguments they require. local function write_turtle_dir_wrappers() local names_to_wrap= {"attack", "detect", "dig", "drop", "suck"} for k, v in ipairs(names_to_wrap) do local get_name= "get_dir_"..v.."_func" kp[v]= function(dir) kp.face(dir) return kp[get_name](dir)() end end end function kp.write_generic_kp_wrapper_tbfblr(func) return function(dir, ...) if not dir then return nil end if dir == kp.ny then return func("top", ...) end if dir == kp.py then return func("bottom", ...) end if dir == facing then return func("front", ...) end if dir == rev_facing then return func("back", ...) end -- Many modules that go on the right side block access to peripheral -- devices, which are what this function is meant for. -- So the turtle has to turn to face the direction. kp.face(dir) return func("front", ...) end end function kp.write_generic_kp_wrapper_fud(func, func_up, func_down) -- Intended to wrap functions that take the form "foo()" "fooUp()" "fooDown()". return function(dir, ...) kp.face(dir) if dir == kp.py then return func_up(...) end if dir == kp.ny then return func_down(...) end return func(...) end end write_turtle_dir_wrappers() function kp.compare(slot, dir) turtle.select(slot) kp.face(dir) return kp.get_dir_compare_func(dir)() end function kp.get_dir_move_func(dir) if dir == kp.py then return kp.up elseif dir == kp.ny then return kp.down end return kp.forward end function kp.get_rev_dir(dir) return kp.rev_dirs[dir] end function kp.get_dir_coord(dir) if dir == kp.nx or dir == kp.px then return x elseif dir == kp.ny or dir == kp.py then return y elseif dir == kp.nz or dir == kp.pz then return z end end function kp.detect_turtle(dir) if not dir then dir= facing end local side= kp.dir_to_side(dir) return peripheral.getType(side) == "turtle" end function kp.move(dir, count) if not count then count= 1 end if count < 0 then return kp.move(kp.rev_dirs(dir), -count) end local move_func= kp.get_dir_move_func(dir) if dir == kp.rev_dirs[facing] then move_func= kp.back else kp.face(dir) end for n= 1, count, 1 do local succeeded, reason= move_func() if not succeeded then return false, reason, n-1 end end return true end function kp.retry_move(dir, count) if not count then count= 1 end if not dir then dir= facing end if count < 0 then return kp.retry_move(kp.rev_dirs(dir), -count) end local move_func= kp.get_dir_move_func(dir) if dir == kp.rev_dirs[facing] then move_func= kp.back else kp.face(dir) end for n= 1, count, 1 do local succeeded, reason= false, 0 while not succeeded do succeeded, reason= move_func() if not succeeded then os.sleep(0.25) end end end return true end function kp.sidestep_move(dir, count, sidestep_dir) if not count then count= 1 end if not dir then dir= facing end if not sidestep_dir then sidestep_dir= kp.px end for n= 1, count, 1 do local succeeded, reason= false, 0 local sidestep_attempts= 0 while not succeeded and sidestep_attempts < 10 do succeeded, reason= kp.move(dir) if not succeeded then kp.retry_move(sidestep_dir) os.sleep(math.random()+.5) -- .5 is added to make the turtle wait additional time after the move animation ends, to prevent sync dance blocking of turtles. kp.retry_move(kp.rev_dirs[sidestep_dir]) sidestep_attempts= sidestep_attempts + 1 end end if sidestep_attempts >= 10 then print("Ran into object that could not be sidestepped.") return false, kp.reason_turtle_func_failed, n-1 end end return true end function kp.sidestep_px_move(dir, count) kp.sidestep_move(dir, count, kp.px) end function kp.sidestep_nx_move(dir, count) kp.sidestep_move(dir, count, kp.nx) end function kp.sidestep_pz_move(dir, count) kp.sidestep_move(dir, count, kp.pz) end function kp.sidestep_nz_move(dir, count) kp.sidestep_move(dir, count, kp.nz) end function kp.sidestep_py_move(dir, count) kp.sidestep_move(dir, count, kp.py) end function kp.sidestep_ny_move(dir, count) kp.sidestep_move(dir, count, kp.ny) end function kp.dig(dir) local dx, dy, dz= deltas[dir]() if check_limits(dx, dy, dz) then return false, kp.reason_hit_limit end if not dir then dir= facing end kp.face(dir) return kp.get_dir_dig_func(dir)(), kp.reason_turtle_func_failed end -- kp.dig_hidden exists so that kp.dig can be "hidden" by another function -- that needs to replace it, and unhidden as needed. -- Naturally, any function that replaces it should be as similar as possible. kp.dig_hidden= kp.dig function kp.dig_only_recognized(dir) local dx, dy, dz= deltas[dir]() if check_limits(dx, dy, dz) then return false, kp.reason_hit_limit end local recognized= false if not dir then dir= facing end if kp.detect(dir) then for s= 1, inventory_size do if kp.compare(s, dir) then recognized= true --print("recogonized") break end end else -- The intent is that air and liquid blocks always be considered recognized. --print("is air") recognized= true end if recognized then kp.dig_hidden(dir) else --print("not recognized") return false, kp.reason_block_not_recognized end return true end function kp.dortest() kp.hide_dig_with_dig_recognized() kp.clear_layer(kp.nz, 4, kp.px, 4, false, true) kp.unhide_dig() kp.turn_right() end function kp.hide_dig_with_dig_recognized() kp.dig= kp.dig_only_recognized return true end function kp.unhide_dig() kp.dig= kp.dig_hidden return true end function kp.retry_dig_move(dir, move_func) local blocked= true local succeeded, reason -- Attempt to move first, to save time in water/lava. succeeded, reason= move_func() if not succeeded and reason ~= kp.reason_turtle_func_failed then return false, reason end blocked= not succeeded while blocked do succeeded, reason= kp.dig(dir) if not succeeded and reason ~= kp.reason_turtle_func_failed then return false, reason end succeeded, reason= move_func() if not succeeded and reason ~= kp.reason_turtle_func_failed then return false, reason end blocked= not succeeded if blocked then os.sleep(0.25) end end return true end function kp.dig_move(dir, count) if not dir then dir= facing end if not count then count= 1 end if count < 0 then return kp.dig_move(kp.rev_dirs(dir), -count) end kp.face(dir) local move_func= kp.get_dir_move_func(dir) local succeeded, reason for n= 1, count, 1 do succeeded, reason= kp.retry_dig_move(dir, move_func) if not succeeded and reason ~= kp.reason_turtle_func_failed then return succeeded, reason, n-1 end end return true end function kp.end_swim(place_dir) if was_swimming then local should_face= facing kp.place(1, place_dir) kp.face(should_face) was_swimming= false end end function kp.swim(dir, count) kp.face(dir) local move_func= kp.get_dir_move_func(dir) local dig_func= kp.get_dir_dig_func(dir) local place_dir= kp.get_rev_dir(dir) local succeeded, reason for n= 1, count, 1 do succeeded, reason= kp.retry_dig_move(dig_func, move_func) if not succeeded and reason ~= kp.reason_turtle_func_failed then return succeeded, reason, n-1 end -- Do not abort if placing fails, because swimming may be used in cases -- where some things should be collected. if n > 1 then kp.place(1, place_dir) end kp.face(dir) end was_swimming= true return true end function kp.mds_to_pos_helper(mds_func, coord_diff, pos_dir, neg_dir) if coord_diff > 0 then return mds_func(pos_dir, coord_diff) elseif coord_diff < 0 then return mds_func(neg_dir, -coord_diff) end return true end function kp.mds_to_pos(mds_func, tx, ty, tz) local xdiff= tx - x local ydiff= ty - y local zdiff= tz - z if not kp.mds_to_pos_helper(mds_func, xdiff, kp.px, kp.nx) then return false end if not kp.mds_to_pos_helper(mds_func, ydiff, kp.py, kp.ny) then return false end if not kp.mds_to_pos_helper(mds_func, zdiff, kp.pz, kp.nz) then return false end return true end function kp.mds_to_x(mds_func, tx) local xdiff= tx - x return kp.mds_to_pos_helper(mds_func, xdiff, kp.px, kp.nx) end function kp.mds_to_y(mds_func, ty) local ydiff= ty - y return kp.mds_to_pos_helper(mds_func, ydiff, kp.py, kp.ny) end function kp.mds_to_z(mds_func, tz) local zdiff= tz - z return kp.mds_to_pos_helper(mds_func, zdiff, kp.pz, kp.nz) end local place_shouldnt_empty= false function kp.set_place_shouldnt_empty(pse) place_shouldnt_empty= pse end local function place_low_level(slots, dir) local placed= false local place_func= kp.get_dir_place_func(dir) for k, v in ipairs(slots) do turtle.select(v) if place_func() then placed= true break end end return placed end function kp.place(slot, dir, dont_empty) if not slot then return false, kp.reason_bad_input end if not dir then dir= facing end kp.face(dir) local good_slots= {} if type(slot) == "number" then turtle.select(slot) good_slots= {slot} elseif type(slot) == "table" then local found= false for k, v in ipairs(slot) do -- Keep going through the table after finding a slot with something -- This allows the user to supply some slots with an unplaceable -- item in them. local min= 1 if dont_empty or place_shouldnt_empty then min= 2 end if turtle.getItemCount(v) >= min then good_slots[#good_slots+1]= v turtle.select(v) found= true end end if not found then return false, kp.reason_turtle_func_failed end end return place_low_level(good_slots, dir) end function kp.collect_type(slot) io.write("kp.collect_type was supposed to be a function to selectively collect a given type of block. It behaves badly. Convince ky to rewrite it if you want to use it.\n") do return false end if turtle.getItemCount(slot) == 0 then io.write("Stubbornly refusing to collect air.\n") return end local made_progress= true local drop_x= x local drop_y= y local drop_z= z while made_progress do made_progress= false if kp.is_inventory_full() then local pause_x= x local pause_y= y local pause_z= z made_progress= kp.mds_to_y(kp.swim, drop_y) made_progress= kp.mds_to_x(kp.swim, drop_x) made_progress= kp.mds_to_z(kp.swim, drop_z) kp.drop_inventory_full() made_progress= false else if kp.compare(slot, kp.nz) then made_progress= kp.dig_move(kp.nz, 1) elseif kp.compare(slot, kp.px) then made_progress= kp.dig_move(kp.px, 1) elseif kp.compare(slot, kp.pz) then made_progress= kp.dig_move(kp.pz, 1) elseif kp.compare(slot, kp.nx) then made_progress= kp.dig_move(kp.nx, 1) elseif kp.compare(slot, kp.py) then made_progress= kp.dig_move(kp.py, 1) elseif kp.compare(slot, kp.ny) then made_progress= kp.dig_move(kp.ny, 1) end end end kp.mds_to_y(kp.swim, drop_y) kp.mds_to_x(kp.swim, drop_x) kp.mds_to_z(kp.swim, drop_z) end function kp.drop(dir, slot, amount) if not dir then io.write("kp.drop requires a direction and a slot.\n") return false end kp.face(dir) if type(slot) == "number" then slot= {slot} end local ret= false if type(slot) == "table" then for k, v in ipairs(slot) do turtle.select(v) -- turtle.drop does not work if you pass it nil. if type(amount) == "number" then ret= kp.get_dir_drop_func(dir)(amount) else ret= kp.get_dir_drop_func(dir)() end end end return ret end function kp.drop_sam(dir, slots, amount) -- Intended to drop exactly the desired amount, pulling from multiple slots if not dir or not slots or not amount or type(slots) ~= "table" then io.write("kp.drop_sam(dir, slots, amount)") return false end local drop_func= kp.get_dir_drop_func(dir) kp.face(dir) local dropped_amount= 0 for k, v in ipairs(slots) do if dropped_amount < amount then turtle.select(v) local can_drop= turtle.getItemCount(v) if can_drop ~= 0 then if can_drop >= amount - dropped_amount then drop_func(amount - dropped_amount) dropped_amount= amount else dropped_amount= dropped_amount + can_drop drop_func(can_drop) end end end end return (dropped_amount == amount), (amount - dropped_amount) end function kp.drop_except(dir, slot, except) local drop_amount= turtle.getItemCount(slot) - except return kp.drop(dir, slot, drop_amount) end function kp.drop_inventory_full(dir) if not dir then dir= facing end return kp.drop(dir, inventory_slots_table) end function kp.drop_inventory_except(except) if type(except) == "number" then except= {except} end for s= 1, inventory_size, 1 do local okay_to_dump= true for k, v in ipairs(except) do if s == v then okay_to_dump= false break end end if okay_to_dump then kp.drop(facing, s) end end end function kp.is_slot_full(s) return turtle.getItemSpace(s) < 1 end function kp.is_inventory_full() for s= 1, inventory_size, 1 do if turtle.getItemSpace(s) > 0 then return false end end return true end function kp.something_in_inventory() for s= 1, inventory_size, 1 do if turtle.getItemSpace(s) < 64 then return true end end return false end function kp.are_inventory_slots_full() local used_slots= 0 for s= 1, inventory_size, 1 do if turtle.getItemCount(s) > 0 then used_slots= used_slots + 1 end end return used_slots >= inventory_size end function kp.space_remaining_in_slots(slots) local total= 0 if type(slots) == "number" then total= turtle.getItemCount(slots) elseif type(slots) == "table" then for k, v in ipairs(slots) do total= total + turtle.getItemSpace(v) end end return total end function kp.count_slots_total(slots) local total= 0 if type(slots) == "number" then total= turtle.getItemCount(slots) elseif type(slots) == "table" then for k, v in ipairs(slots) do total= total + turtle.getItemCount(v) end end return total end function kp.tableify_inventory_state() local ret= {} for s= 1, inventory_size do ret[s]= turtle.getItemCount(s) end return ret end function kp.diff_inventory_states(old, new) local ret= {} for s= 1, inventory_size do if old[s] ~= new[s] then ret[s]= new[s] - old[s] end end return ret end function kp.grab_into_allowed_slots(dir, allowed_slots) if not allowed_slots then io.write("kp.grab_into_allowed_slots usage:\n" .."kp.grab_into_allowed_slots(dir, allowed_slots)\n" .."allowed_slots can be a number or a table of numbers.\n" .."If a slot not listed by allowed_slots is changed by the grab, it will be dropped back into the chest, leaving it empty even if it had something before.\n") return true end if type(allowed_slots) == "number" then allowed_slots= {allowed_slots} end if type(allowed_slots) ~= "table" then io.write("Bad argument to kp.grab_into_allowed_slots.\n") return false, kp.reason_bad_input end local old_inv= kp.tableify_inventory_state() turtle.select(allowed_slots[1]) local got_some= kp.suck(dir) local new_inv= kp.tableify_inventory_state() local inv_diff= kp.diff_inventory_states(old_inv, new_inv) for k, v in pairs(inv_diff) do local allowed= false for ak, av in ipairs(allowed_slots) do if av == k then allowed= true break end end if not allowed then kp.drop(dir, k) end end return got_some end function kp.safe_nav(dest_x, dest_y, dest_z, nav_y, face_at_end, move_func) if not move_func then move_func= kp.retry_move end if dest_x == x and dest_z == z then kp.mds_to_y(move_func, dest_y) else kp.mds_to_y(move_func, nav_y) if dest_x then kp.mds_to_x(move_func, dest_x) end if dest_z then kp.mds_to_z(move_func, dest_z) end if dest_y then kp.mds_to_y(move_func, dest_y) end end if face_at_end then kp.face(face_at_end) end end function kp.restock_no_move(slots, chest_dir, need_some) local got_some= false while need_some do got_some= kp.grab_into_allowed_slots(chest_dir, slots) need_some= kp.space_remaining_in_slots(slots) > 0 if not got_some then need_some= false io.write("Please refill chest at "..tostring(x)..", ".. tostring(y)..", "..tostring(z)..".\n") end end return got_some end function kp.restock(slots, nav_y, chest_dir, turtle_x, turtle_y, turtle_z) if not slots then io.write("kp.restock usage:\n" .."kp.restock(slots, chest_dir, turtle_x, turtle_y, turtle_z, nav_y)\n" .."turtle_xyz is the position the turtle should move to for loading.\n" .."chest_dir is the direction of the chest at that position.\n" .."nav_y is the y level the turtle should ascend to before moving in x and z.\n" .."Unspecified chest_dir is set to facing.\n" .."Unspecified position is set to the current position.\n" .."If nav_y is not specified, only turtle_y is used.\n" .."The turtle will not move if the slots are already full.\n") return true end local need_some= (kp.space_remaining_in_slots(slots) > 0) if not need_some then io.write("Already full.\n") return true end kp.safe_nav(turtle_x, turtle_y, turtle_z, nav_y, chest_dir) return kp.restock_no_move(slots,chest_dir, need_some) end function kp.unstock(slots, nav_y, chest_dir, turtle_x, turtle_y, turtle_z) if not slots then io.write("kp.unstock: See kp.restock for usage.\n") return true end local need_to_unstock= (kp.count_slots_total(slots) > 0) if not need_to_unstock then return true end kp.safe_nav(turtle_x, turtle_y, turtle_z, nav_y, chest_dir) kp.drop(facing, slots) return true end function kp.is_fuel_too_low(too_low) local fuel_level= turtle.getFuelLevel() if fuel_level == "unlimited" then return false end if type(fuel_level) == "number" then return fuel_level < too_low end io.write("Unrecognized fuel level '"..tostring(fuel_level).."', assuming unlimited.\n") return false end local function size_limit_helper(size, coord, dir) local ret= size if coord_limits[dir] then io.write("coord_limit: " .. tostring(coord_limits[dir]) .. "\n") end if coord_limits[dir] then local coord_plus_size= coord_size_combine_functions[dir](coord, size) io.write("coord + size: " .. tostring(coord_plus_size) .. "\n") if limit_compare_functions[dir](coord_plus_size, coord_limits[dir]) then ret= coord_limits[dir] - coord if coord_limits[dir] < 0 then ret= -ret end io.write("coord + size exceeds limit, setting size to " .. tostring(ret) .. "\n") if ret <= 0 then io.write("Volume is entirely outside of limit.\n") return nil end end end return ret end local function traverse_layer_unchecked(forward_dir, forward_dist, right_dir, right_dist, step_function) local sx= x local sy= y local sz= z for r= 1, right_dist, 1 do for f= 1, forward_dist, 1 do if step_function then step_function() end if f < forward_dist then if r % 2 == 0 then kp.dig_move(kp.rev_dirs[forward_dir], 1) else kp.dig_move(forward_dir, 1) end end end if r < right_dist then kp.dig_move(right_dir, 1) end end kp.mds_to_pos(kp.dig_move, sx, sy, sz) return true end function kp.traverse_layer(forward_dir, forward_dist, right_dir, right_dist, step_function) if forward_dist < 0 then return kp.traverse_layer(kp.rev_dirs[forward_dir], -forward_dist, right_dir, right_dist, step_function) end if right_dist < 0 then return kp.traverse_layer(forward_dir, forward_dist, kp.rev_dirs[right_dir], -right_dist, step_function) end local forward_end= size_limit_helper(forward_dist, kp.get_dir_coord(forward_dir), forward_dir) local right_end= size_limit_helper(right_dist, kp.get_dir_coord(right_dir), right_dir) if not forward_end or not right_end then return false, kp.reason_hit_limit end --io.write("forward_end: " .. tostring(forward_end) .. " right_end: " .. tostring(right_end) .. "\n") return traverse_layer_unchecked(forward_dir, forward_end, right_dir, right_end, step_function) end local function make_clear_tb(clear_above, clear_below) return function() if clear_above then kp.dig(kp.py) end if clear_below then kp.dig(kp.ny) end end end function kp.clear_layer(forward_dir, forward_dist, right_dir, right_dist, clear_above, clear_below) local clear_tb= make_clear_tb(clear_above, clear_below) return kp.traverse_layer(forward_dir, forward_dist, right_dir, right_dist, clear_tb) end function kp.cl_simple() return kp.clear_layer(kp.nz, 6, kp.px, 6, false, false) end function kp.clear_cube(forward_dir, forward_dist, right_dir, right_dist, up_dir, up_dist, dont_return) local sx= x local sy= y local sz= z if forward_dist < 0 then return kp.clear_cube(kp.rev_dirs[forward_dir], -forward_dist, right_dir, right_dist, up_dir, up_dist) end if right_dist < 0 then return kp.clear_cube(forward_dir, forward_dist, kp.rev_dirs[right_dir], -right_dist, up_dir, up_dist) end if up_dist < 0 then return kp.clear_cube(forward_dir, forward_dist, right_dir, right_dist, kp.rev_dirs[up_dir], -up_dist) end local forward_end= size_limit_helper(forward_dist, kp.get_dir_coord(forward_dir), forward_dir) local right_end= size_limit_helper(right_dist, kp.get_dir_coord(right_dir), right_dir) local up_end= size_limit_helper(up_dist, kp.get_dir_coord(up_dir), up_dir) if not forward_end or not right_end or not up_end then return false, kp.reason_hit_limit end if up_dir ~= kp.ny and up_dir ~= kp.py then io.write("clear_cube: up_dir must be kp.ny or kp.py.\n") return false, kp.reason_bad_input end local layers= {} local layer_count= math.floor(up_end / 3) for n= 1, layer_count do -- n= 1, 2, 3, ... -> y= 1, 4, 7, ... layers[n]= {y= (n * 3) - 2, ca= true, cb= true} end local special_layer= #layers+1 if up_end % 3 == 1 then layers[special_layer]= {y= (special_layer - 1)*3, ca= false, cb= false} elseif up_end % 3 == 2 then layers[special_layer]= {y= (special_layer - 1)*3, ca= true, cb= false} end for l= 1, #layers do local ly= coord_size_combine_functions[up_dir](sy, layers[l].y) kp.mds_to_y(kp.dig_move, ly) if up_dir == kp.ny then -- Intentional swap of clear_above and clear_below, because up is down. traverse_layer_unchecked(forward_dir, forward_end, right_dir, right_end, make_clear_tb(layers[l].cb, layers[l].ca)) else traverse_layer_unchecked(forward_dir, forward_end, right_dir, right_end, make_clear_tb(layers[l].ca, layers[l].cb)) end end if not dont_return then kp.mds_to_pos(kp.dig_move, sx, sy, sz) end return true end function kp.cc_simple() return kp.clear_cube(kp.nz, 8, kp.px, 8, kp.py, 9) end function kp.cc_simple_down() return kp.clear_cube(kp.nz, 8, kp.px, 8, kp.ny, 9) end function kp.simple_cc_collection(rise_amount) local start_y= y local drop_y= start_y + 6 local collect_y= start_y + rise_amount local cube_count= 18 local drop_z= z local next_cube_z= z kp.mds_to_y(kp.dig_move, collect_y) for c= 1, cube_count, 1 do kp.mds_to_z(kp.dig_move, next_cube_z) kp.cc_simple() next_cube_z= next_cube_z - 6 kp.mds_to_z(kp.dig_move, drop_z) kp.mds_to_y(kp.dig_move, drop_y) kp.drop_inventory_full() kp.mds_to_y(kp.dig_move, collect_y) end kp.mds_to_y(kp.dig_move, start_y) return true end function kp.write_cobbler_startup() local startup_file= fs.open("startup", "w") if startup_file then startup_file.writeLine("kp.init()") startup_file.writeLine("kp.cobble_generator()") startup_file.close() else io.write("Could not open startup file to write.\n") end end function kp.write_cobbler_controller() local cobble_file= fs.open("cobble", "w") if cobble_file then cobble_file.writeLine("rednet.open(\"right\")") cobble_file.writeLine("rednet.broadcast(\"start_cobble\")") cobble_file.close(); else io.write("Could not open cobble file to write.\n") end cobble_file= fs.open("uncobble", "w") if cobble_file then cobble_file.writeLine("rednet.open(\"right\")") cobble_file.writeLine("rednet.broadcast(\"stop_cobble\")") cobble_file.close(); else io.write("Could not open uncobble file to write.\n") end end function kp.cobble_generator() rednet.open("right") -- Gee, I sure wish I could tell if there actually was a modem. local cobble_should_stop= false local dig_cobble= function() kp.dig(kp.ny) kp.dig(facing) kp.dig(kp.py) end local dump_load= function() if not kp.drop_inventory_full(rev_facing) then cobble_should_stop= true end kp.face(rev_facing) end local wait_for_stop_message= function() local sender,message,dist while not cobble_should_stop do sender,message,dist= rednet.receive() if message == "stop_cobble" then rednet.broadcast("stop_cobble") cobble_should_stop= true end end io.write("Message received.\n") end local dig_and_dump= function() while not cobble_should_stop do while not kp.is_inventory_full() and not cobble_should_stop do dig_cobble() -- Due to lava flowing being a complete fucking retarded piece of shit, detecting the new cobble block and immediately digging it leads to lava getting into a state where it will not flow. Sometimes. It doesn't always happen, but it definitely happens, and it stays in that state until something unknown happens, which often takes several minutes. -- Make a certain number of attempts to pass the time, then continue anyway. local n= 1 for n= 1, 13, 1 do if kp.detect(kp.ny) then n= 13 end end end dump_load() end io.write("Cobbling stopped.\n") end while true do local sender,message,dist= rednet.receive() if message == "start_cobble" then rednet.broadcast("start_cobble") cobble_should_stop= false parallel.waitForAll(wait_for_stop_message, dig_and_dump) elseif message == "restart" then os.reboot() elseif message == "full_stop_cobble" then return true end end return true end function kp.mad_bomber() kp.set_limits(-64, 64, -64, 64, -64, 64) rs.setOutput("bottom", true) local move_dir, move_dist while kp.place(1, kp.ny) do move_dir= math.random(kp.nz, kp.nx) move_dist= math.random(0, 32) kp.move(move_dir, move_dist) end end function kp.build_wall(forward_dir, forward_dist, height, slots) if not slots then slots= inventory_slots_table end if height <= 0 then return false, kp.reason_bad_input end if forward_dist < 0 then return kp.build_wall(kp.rev_dirs[forward_dir], -forward_dist, height) end if forward_dist == 0 then return false, kp.reason_bad_input end for h= 1, height, 1 do for f= 1, forward_dist, 1 do kp.place(slots, kp.ny) if f < forward_dist then kp.dig_move(forward_dir, 1) end end kp.dig_move(kp.py, 1) forward_dir= kp.rev_dirs[forward_dir] end return true end local function show_time_diffs(ta, tb, tc, td, te) if ta and tb then io.write(tostring(tb - ta)) if tc then io.write(", " .. tostring(tc - tb)) if td then io.write(", " .. tostring(td - tc)) if te then io.write(", " .. tostring(te - td)) end end end end io.write("\n") end function kp.dig_time_measure() local st= os.clock() turtle.dig() local pta= os.clock() turtle.digUp() local ptb= os.clock() turtle.digDown() local ptc= os.clock() io.write("Dig times: ") show_time_diffs(st, pta, ptb, ptc) end function kp.turn_time_measure() local st= os.clock() turtle.turnLeft() local pta= os.clock() turtle.turnLeft() local ptb= os.clock() turtle.turnLeft() local ptc= os.clock() turtle.turnLeft() local ptd= os.clock() io.write("Turn times: ") show_time_diffs(st, pta, ptb, ptc, ptd) end function kp.move_time_measure() local st= os.clock() turtle.forward() local pta= os.clock() turtle.forward() local ptb= os.clock() turtle.forward() local ptc= os.clock() turtle.forward() local ptd= os.clock() io.write("Forward times: ") show_time_diffs(st, pta, ptb, ptc, ptd) end function kp.lava_time_measure() local st local start_set= false local et local end_set= false local sgt local egt while not start_set do if turtle.dig() then start_set= true st= os.clock() sgt= os.time() end end while not end_set do if turtle.dig() then end_set= true et= os.clock() egt= os.time() end end io.write("Lava time: " .. tostring(st) .. " to " .. tostring(et) .. " : " .. tostring(et - st) .. "\n") io.write("sgt: " .. tostring(sgt) .. " egt: " .. tostring(egt) .. "\n") return true end function kp.test_chunk_loaded() local keep_going= true while keep_going do if not kp.place(1, kp.nz) then keep_going= false else kp.place(1, kp.px) kp.dig_move(kp.nz) end end kp.mds_to_z(kp.move, 0) end function kp.build_platform(forward_dir, forward_dist, right_dir, right_dist, slots) if not forward_dir then forward_dir= facing end if not forward_dist then forward_dist= 8 end if not right_dir then right_dir= kp.get_right_dir(forward_dir) end if not right_dist then right_dist= 8 end if not slots then slots= inventory_slots_table end local function place_block() kp.place(slots, kp.ny) end kp.traverse_layer(forward_dir, forward_dist, right_dir, right_dist, place_block) end function kp.build_platform_simple(forward_dir, right_dir, width) if not forward_dir then forward_dir= kp.nz end if not right_dir then right_dir= kp.px end if not width then width= 9 end local sx= x local sy= y local sz= z local place_succeeded= true local move_succeeded= true local column_adv_dir= right_dir local rev_column_adv_dir= kp.rev_dirs[right_dir] local slots= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} while kp.something_in_inventory() do for r= 1, width, 1 do place_succeeded= kp.place(slots, kp.ny) if r < width then move_succeeded= kp.retry_move(column_adv_dir, 1) end end move_succeeded= kp.retry_move(forward_dir, 1) column_adv_dir, rev_column_adv_dir= rev_column_adv_dir, column_adv_dir end kp.mds_to_pos(kp.dig_move, sx, sy, sz) return true end function kp.bps() return kp.build_platform_simple(kp.nz, kp.px, 9) end local dock_entry_x= 0 local dock_entry_y= 7 local dock_entry_z= 0 local dock_y= 0 local dock_exit_lane_dir= kp.px local dump_dir= kp.ny local dock_fuel_dir= kp.nz function kp.exit_dock() if x == dock_entry_x and z == dock_entry_z then kp.dig_move(dock_exit_lane_dir) kp.mds_to_y(kp.dig_move, dock_entry_y) end end function kp.nav_to_shaft_start(shaft_x, shaft_y, shaft_z) kp.safe_nav(shaft_x, shaft_y, shaft_z, dock_entry_y, kp.nz, kp.dig_move) end function kp.nav_to_dock_entry() kp.safe_nav(dock_entry_x, dock_entry_y, dock_entry_z, dock_entry_y, kp.nz, kp.dig_move) end function kp.enter_dock_and_dump() kp.mds_to_y(kp.dig_move, dock_y) kp.face(dump_dir) kp.drop_inventory_full(dump_dir) end function kp.wait_for_signal(signal_dir) rep_til_true(kp.rs_get_input, 1, signal_dir) end function kp.maybe_refuel_at_dock() if kp.is_fuel_too_low(6144) then kp.face(dock_fuel_dir) turtle.select(1) for n= 1, 27, 1 do turtle.suck() kp.refuel(1) end end end local koo_state= {x_spacing= 8, y_spacing= 9, z_spacing= -8, x_min= -80, y_min= -3, z_min= -3, x_max= 80, y_max= 48, z_max= -85, next_x= -80, next_y= -2, next_z= -3, } function kp.calc_next_koo_cube() koo_state.next_x= koo_state.next_x + koo_state.x_spacing if koo_state.next_x > koo_state.x_max then koo_state.next_x= koo_state.x_min koo_state.next_y= koo_state.next_y + koo_state.y_spacing if koo_state.next_y > koo_state.y_max then koo_state.next_y= koo_state.y_min koo_state.next_z= koo_state.next_z + koo_state.z_spacing -- The signal line is in the +z direction, so -z is forward. if koo_state.next_z < koo_state.z_max then koo_state.next_z= koo_state.z_min koo_state.finished= true end end end end function kp.record_next_koo_pos(tx, ty, tz) local pos_file= fs.open("koo", "w") if pos_file then pos_file.writeLine(tostring(tx)) pos_file.writeLine(tostring(ty)) pos_file.writeLine(tostring(tz)) pos_file.close() else io.write("Could not open file to record koo.\n") return false, kp.reason_file_open_failed end return true end function kp.load_koo_pos() local pos_file= fs.open("koo", "r") if pos_file then local tx= tonumber(pos_file.readLine()) local ty= tonumber(pos_file.readLine()) local tz= tonumber(pos_file.readLine()) if tx and ty and tz then koo_state.next_x= tx koo_state.next_y= ty koo_state.next_z= tz else io.write("koo file contained bad data:\n") io.write(tostring(tx).."\n") io.write(tostring(ty).."\n") io.write(tostring(tz).."\n") pos_file.close() return false, kp.reason_bad_input end pos_file.close() else io.write("Could not open file to load koo.\n") return false, kp.reason_file_open_failed end return true end function kp.koo_loop_body() local this_x, this_y, this_z= koo_state.next_x, koo_state.next_y, koo_state.next_z kp.wait_for_signal(kp.nx) kp.calc_next_koo_cube() print("Heading to " .. this_x .. ", " .. this_y .. ", " .. this_z) kp.record_next_koo_pos(this_x, this_y, this_z) kp.exit_dock() kp.nav_to_shaft_start(this_x, this_y, this_z) kp.cc_simple() kp.nav_to_dock_entry() kp.enter_dock_and_dump() kp.maybe_refuel_at_dock() return koo_state.finished end function kp.resume_koo() kp.load_pos() local succeeded, reason= kp.load_koo_pos() if not succeeded then return false end kp.koo() end function kp.koo() kp.set_record_mode(true) kp.rep_til_true(kp.koo_loop_body, 1) end function kp.write_koo_startup() local startup_file= fs.open("startup", "w") if startup_file then startup_file.writeLine("kp.init()") startup_file.writeLine("kp.resume_koo()") startup_file.close() else io.write("Could not open startup file to write.\n") end end function kp.pulverize(sleep_time) if not tonumber(sleep_time) then sleep_time= .05 end while true do rs.setOutput("top", true) rs.setOutput("left", true) rs.setOutput("right", true) rs.setOutput("front", true) rs.setOutput("back", true) os.sleep(sleep_time) rs.setOutput("top", false) rs.setOutput("left", false) rs.setOutput("right", false) rs.setOutput("front", false) rs.setOutput("back", false) os.sleep(sleep_time) end end function kp.rep_action_forever(action_func, sleep_time) if not action_func then return false, kp.reason_bad_input end if not tonumber(sleep_time) then sleep_time= .05 end while true do action_func() os.sleep(sleep_time) end return true end function kp.rep_til_true(action_func, sleep_time, param) if not action_func then return false, kp.reason_bad_input end if not tonumber(sleep_time) then sleep_time= .05 end while not action_func(param) do sleep(sleep_time) end end function kp.rep_til_false(action_func, sleep_time, param) if not action_func then return false, kp.reason_bad_input end if not tonumber(sleep_time) then sleep_time= .05 end while action_func(param) do sleep(sleep_time) end end function kp.greedy_refuel() for n= 1, inventory_size, 1 do turtle.select(n) turtle.refuel() end end function kp.refuel(slot) if slot then turtle.select(slot) end turtle.refuel() end function kp.refuel_from_chest(chest_dir) local empty_slot= 1 while turtle.getItemCount(empty_slot) > 0 do empty_slot= empty_slot+1 end if empty_slot > inventory_size then io.write("kp.refuel_from_chest could not refuel, no space in inventory.\n") return false end turtle.select(empty_slot) while kp.suck(chest_dir) do kp.refuel(empty_slot) end return true end function kp.suck_til_false(dir) kp.face(dir) local suck_func= kp.get_dir_suck_func(dir) while suck_func() do end end function kp.harvest_tree(trunk_only, sapling_slots) local sy= y kp.dig(kp.ny) -- replant if sapling_slots then local placed= false kp.place(sapling_slots, kp.ny) end if trunk_only then while kp.detect(kp.py) do for d= 1, 4, 1 do kp.dig(d) end kp.dig_move(kp.py) end else local lowest_leaf_y= y + 1 local leaf_dirs= {} local best_leaf_count= 0 while kp.detect(kp.py) do local cur_leaf_dirs= {} local cur_leaf_count= 0 if best_leaf_count < 4 then for d= 1, 4, 1 do if kp.detect(d) then cur_leaf_dirs[d]= true cur_leaf_count= cur_leaf_count + 1 else cur_leaf_dirs[d]= false end end end if cur_leaf_count > best_leaf_count then leaf_dirs= cur_leaf_dirs best_leaf_count= cur_leaf_count end if cur_leaf_count > 0 and not lowest_leaf_y then lowest_leaf_y= y end kp.dig_move(kp.py) end if not lowest_leaf_y then for d= 1, 4, 1 do leaf_dirs[d]= false end end local sx= x local sz= z local switcheroo= {[true]= {[true]= {cdist= 5, cdir= "n", mdist= 2, mdir= "p"}, [false]= {cdist= 3, cdir= "n", mdist= 0, mdir= "p"}}, [false]= {[true]= {cdist= 3, cdir= "p", mdist= 0, mdir= "p"}, [false]= {cdist= 1, cdir= "n", mdist= 0, mdir= "p"}}} local x_info= switcheroo[leaf_dirs[kp.nx]][leaf_dirs[kp.px]] local z_info= switcheroo[leaf_dirs[kp.nz]][leaf_dirs[kp.pz]] local clear_x_size= x_info.cdist local clear_x_dir= kp[x_info.cdir .. "x"] local move_x_dist= x_info.mdist local move_x_dir= kp[x_info.mdir .. "x"] local clear_z_size= z_info.cdist local clear_z_dir= kp[z_info.cdir .. "z"] local move_z_dist= z_info.mdist local move_z_dir= kp[z_info.mdir .. "z"] kp.dig_move(move_x_dir, move_x_dist) kp.dig_move(move_z_dir, move_z_dist) kp.clear_cube(clear_z_dir, clear_z_size, clear_x_dir, clear_x_size, kp.ny, (y - lowest_leaf_y) + 1, true) kp.mds_to_y(kp.dig_move, sy) -- Sucking up fallen saplings is probably unnecessary and interferes with supply chests. -- local suck_func= function() kp.suck_til_false(kp.ny) return true end -- kp.traverse_layer(clear_z_dir, clear_z_size, clear_x_dir, clear_x_size, -- suck_func) kp.mds_to_x(kp.dig_move, sx) kp.mds_to_z(kp.dig_move, sz) end kp.mds_to_y(kp.dig_move, sy) end function kp.bone_tree(trunk_only) local bonemeal_supply_dir= kp.px local tree_dir= kp.nz local loot_dir= kp.ny local fuel_dir= kp.pz local sapling_slot= 1 local bonemeal_slot= 16 local get_bonemeal= function() io.write("Getting bonemeal.\n") kp.face(bonemeal_supply_dir) turtle.select(bonemeal_slot) turtle.suck() turtle.select(2) turtle.drop() return turtle.getItemCount(bonemeal_slot) > 0 end local grow_tree= function() io.write("Growing tree.\n") return kp.place(bonemeal_slot, tree_dir) end local cut_tree= function() io.write("Cutting tree.\n") turtle.select(1) kp.dig_move(kp.py) kp.dig_move(tree_dir) kp.harvest_tree(trunk_only, sapling_slot) kp.face(tree_dir) kp.dig_move(kp.rev_dirs[tree_dir]) kp.dig_move(kp.ny) end local drop_loot= function() io.write("Dropping loot.\n") kp.drop(kp.ny, 2) kp.drop(kp.ny, 3) end local get_fuel= function() io.write("Getting fuel.\n") kp.face(fuel_dir) turtle.select(2) for n= 1, 27, 1 do turtle.suck() turtle.refuel() end end while true do kp.rep_til_true(get_bonemeal, 1) kp.rep_til_false(grow_tree, .05) cut_tree() drop_loot() if kp.is_fuel_too_low(6144) then get_fuel() end end end function kp.bone_farm_tile() local bonemeal_supply_dir= kp.nz local loot_dir= kp.py local seed_slots= {1} local bone_meal_slots= {11, 12, 13, 14, 15, 16} local dump_slots= {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} local non_bone_non_seed_slots= {2, 3, 4, 5, 6, 7, 8, 9, 10} local space_for_loot= true local bonemeal_remains= true while space_for_loot and bonemeal_remains do kp.place(seed_slots, kp.ny) if not kp.place(bone_meal_slots, kp.ny) then kp.drop(loot_dir, dump_slots) bonemeal_remains= kp.restock_no_move(bone_meal_slots, bonemeal_supply_dir, true) kp.place(bone_meal_slots, kp.ny) end turtle.select(seed_slots[1]) kp.dig(kp.ny) if kp.count_slots_total(non_bone_non_seed_slots) >= #non_bone_non_seed_slots * 64 then kp.drop(loot_dir, non_bone_non_seed_slots) end while not space_for_loot do os.sleep(1) kp.drop(loot_dir, non_bone_non_seed_slots) end while not bonemeal_remains do bonemeal_remains= kp.restock_no_move(bone_meal_slots, bonemeal_supply_dir, true) end end end function kp.harvest_tree_row(row_dir, row_dist, row_stride, trunk_only, sapling_slots) if not row_dir then row_dir= kp.nz end if not row_dist then row_dist= 16 end if not row_stride then row_stride= 2 end -- Assume that the turtle is 3m from the first tree on the ground and the row starts at the first tree. local start_x= x local start_z= z kp.dig_move(kp.py) kp.dig_move(row_dir, 3) for n= 1, row_dist+1, 1 do if (n-1) % row_stride == 0 then kp.harvest_tree(trunk_only, sapling_slots) end if n < row_dist then kp.dig_move(row_dir) end end kp.mds_to_x(kp.dig_move, start_x) kp.mds_to_z(kp.dig_move, start_z) kp.dig_move(kp.ny) end function kp.check_and_harvest_tree_rows(check_dir, check_dist, check_stride, row_dir, row_dist, row_stride, trunk_only, sapling_slots, no_signal) if not check_dir then check_dir= kp.nz end if not check_dist then check_dist= 16 end if not check_stride then check_stride= 2 end if not row_dir then row_dir= kp.nz end if not row_dist then row_dist= 16 end if not row_stride then row_stride= 2 end local sx= x local sz= z for n= 1, check_dist+1, 1 do if (n-1) % check_stride == 0 then if kp.rs_get_input(row_dir) or no_signal then kp.harvest_tree_row(row_dir, row_dist, row_stride, trunk_only, sapling_slots) end end if n < check_dist then kp.dig_move(check_dir) end end kp.mds_to_x(kp.dig_move, sx) kp.mds_to_z(kp.dig_move, sz) end function kp.harvest_tree_farm(rows, columns, no_signal, sapling_slots) local stride= 3 local check_dist= (rows * 3) - 2 local row_dist= (columns * 3) - 2 local old_pse= place_shouldnt_empty if not sapling_slots then sapling_slots= {2} end kp.set_place_shouldnt_empty(true) kp.retry_move(kp.py) kp.retry_move(kp.px, 2) turtle.select(2) kp.check_and_harvest_tree_rows(kp.px, check_dist, stride, kp.nz, row_dist, stride, true, sapling_slots, no_signal) kp.mds_to_x(kp.retry_move, 0) kp.mds_to_z(kp.retry_move, 0) kp.mds_to_y(kp.retry_move, 0) kp.face(kp.nx) kp.drop_inventory_except(sapling_slots) if kp.is_fuel_too_low(6144) then kp.refuel_from_chest(kp.pz) end kp.set_place_shouldnt_empty(old_pse) end function kp.harvest_ky_tree_farm(no_signal) kp.harvest_tree_farm(16, 4, no_signal) end function kp.auto_harvest_ky_tree_farm(no_signal) -- Ky's tree farm is 4 trees in 16 rows. -- The monitoring station is at the turtle's 0, 0, 0. local input_check= function() return kp.rs_get_input(kp.px) end while true do if not no_signal then kp.rep_til_true(input_check, 1) else os.sleep(600) end kp.harvest_ky_tree_farm(no_signal) end end function kp.build_ky_tree_farm(no_signal) local fluff_slots= {1, 2, 3, 4, 5, 6, 7, 8} local dirt_slots= 9 local pumpkin_slot= 10 local sapling_slot= 11 local redstone_slots= {12, 13} local repeater_slots= {14, 15} local torch_slot= 16 local rows= 16 local trees_per_row= 4 local stride= 3 -- Lay down the dirt for the row signal combining area first. local place_dirt_below= function() kp.place(fluff_slots, kp.ny) end kp.traverse_layer(kp.px, rows * stride, kp.pz, 2, place_dirt_below) -- Lay the traces for the signal lines. local dx, dy, dz= x, y, z local trace_place_func= function() local xdiff= x - dx local zdiff= z - dz if no_signal then return end if zdiff == 0 then if xdiff == 1 then kp.place(redstone_slots, kp.ny) elseif ((xdiff + 1) % stride) == 0 then kp.face(kp.pz) kp.place(repeater_slots, kp.ny) end else if ((xdiff + 1) % 16) == 0 then kp.face(kp.nx) kp.place(repeater_slots, kp.ny) else kp.place(redstone_slots, kp.ny) end end end kp.up() kp.traverse_layer(kp.px, rows * stride, kp.pz, 2, trace_place_func) -- Lay the dirt and pumpkins. kp.dig_move(kp.nz) kp.dig_move(kp.px, 2) dx, dy, dz= x, y, z local dirt_layer_place= function() local xdiff= x - dx local zdiff= z - dz if (xdiff % stride) == 0 then if ((zdiff - 2) % stride) == 0 then kp.dig_move(kp.ny) kp.place(fluff_slots, kp.ny) kp.dig_move(kp.py) kp.place(pumpkin_slot, kp.ny) elseif ((zdiff - 1) % stride) == 0 then kp.place(dirt_slots, kp.ny) else kp.place(fluff_slots, kp.ny) end else --kp.place(fluff_slots, kp.ny) end end kp.traverse_layer(kp.nz, (trees_per_row * stride) + 2, kp.px, (rows * stride) - 2, dirt_layer_place) -- Place the redstone lines and the saplings. kp.up() kp.dig_move(kp.pz) dx, dy, dz= x, y, z local rs_or_sapling_place= function() local zdiff= dz - z if zdiff > 0 then if zdiff < (trees_per_row * stride) + 2 then local zms= zdiff % stride if zms == 0 then kp.place(sapling_slot, kp.ny) elseif zms == 1 then if not no_signal then kp.place(redstone_slots, kp.ny) end elseif zms == 2 then if not no_signal then kp.face(kp.pz) kp.place(repeater_slots, kp.ny) end end else kp.place(torch_slot, kp.ny) end end end local traverse_row= function() local xdiff= x - dx if xdiff % stride == 0 then kp.traverse_layer(kp.nz, (trees_per_row * stride) + 3, kp.px, 1, rs_or_sapling_place) end end kp.traverse_layer(kp.px, (rows * stride) - (stride - 1), kp.nz, 1, traverse_row) end function kp.build_ladder_pillar(dir, height, fluff, ladder) kp.place(fluff, facing) for n= 1, height, 1 do kp.dig_move(dir) kp.place(fluff, facing) kp.place(ladder, kp.rev_dirs[dir]) end end function kp.enchant_books() local attack_dir= kp.nz local book_drop_dir= kp.ny local book_stack_slot= 2 local enchant_level= 30 local xp= peripheral.wrap("right") xp.setAutoCollect(true) while true do kp.face(attack_dir) while xp.getLevels() < enchant_level do turtle.attack() end turtle.select(1) turtle.drop() turtle.select(book_stack_slot) turtle.transferTo(1, 1) turtle.select(1) xp.enchant(enchant_level) kp.drop(book_drop_dir, 1) end end function kp.buffer_turtle(buffer_chest_dir, machine_dir, sleep_time) buffer_chest_dir= buffer_chest_dir or kp.nz machine_dir= machine_dir or kp.ny sleep_time= sleep_time or 1 turtle.select(1) while true do if turtle.getItemCount(1) == 0 then kp.suck(buffer_chest_dir) end kp.drop(machine_dir, 1) os.sleep(sleep_time) end end function kp.macerate_gravel() local cur_gravel_slot= 1 while true do kp.place(cur_gravel_slot, kp.nz) if turtle.getItemCount(cur_gravel_slot) <= 0 then cur_gravel_slot= cur_gravel_slot + 1 end kp.dig(kp.nz) os.sleep(.05) end end function kp.condense_stacks() local start_time= os.clock() local drop_total= 0 local prev_drop_total= 0 local curr_slot= 1 local curr_time= start_time local td while true do curr_slot= 1 prev_drop_total= drop_total while curr_slot <= inventory_size and kp.is_slot_full(curr_slot) do drop_total= drop_total + turtle.getItemCount(curr_slot) kp.drop(kp.ny, curr_slot) curr_slot= curr_slot + 1 end if drop_total > prev_drop_total then curr_time= os.clock() td= curr_time - start_time io.write(tostring(drop_total) .. "i / " .. tostring(td) .. "s = " .. tostring(drop_total / td) .. "ips\n") end sleep(.05) end end function kp.write_condenser_startup() local startup_file= fs.open("startup", "w") if startup_file then startup_file.writeLine("kp.init()") startup_file.writeLine("kp.condense_stacks()") startup_file.close() else io.write("Could not open startup file to write.\n") end end function kp.refeed() local curr_slot= 1 local dropped_something= false while true do if not rs.getInput("left") then dropped_something= false curr_slot= 1 while curr_slot <= inventory_size and not dropped_something do if turtle.getItemCount(curr_slot) > 0 then dropped_something= true kp.drop(kp.ny, curr_slot) end curr_slot= curr_slot + 1 end end sleep(.05) end end function kp.harvest_crop_farm(forward_dir, forward_dist, right_dir, right_dist) local seed_slots= inventory_slots_table local sx, sz= x, z local function pos_has_pumpkin(x, z) local function has(x) return (x - 4) % 9 == 0 end return has(x) and has(z) end local function harvest_and_place() local dx, dz= x - sx, z - sz if not pos_has_pumpkin(dx, dz) then kp.dig(kp.ny) kp.place(seed_slots, kp.ny) end end kp.traverse_layer(forward_dir, forward_dist, right_dir, right_dist, harvest_and_place) end function kp.auto_harvest_ky_crop_farm() local fuel_dir= kp.nz local farm_x, farm_y, farm_z= 0, 0, 0 local dock_x, dock_y, dock_z= 0, 0, 0 while true do kp.harvest_crop_farm(kp.px, 18, kp.pz, 18) kp.face(kp.nz) os.sleep(2400) if kp.is_fuel_too_low(6144) then kp.refuel_from_chest(fuel_dir) end end end function kp.add_stuff_to_machines(m_dir) -- This function is intended to add cells to two rows of centrifuges. -- Centrifuge layout: -- CCCCCC -- -- CCCCCC -- -- The turtle will travel down row one, turn, and come back along row two. -- m_dir should be kp.py for cells, kp.ny for stuff to work on. -- All cells provided in the chest will be placed into the centrifuges. -- Cells will be evenly distributed. local cell_chest_dir= kp.pz local dock_x= x local dock_z= z local row_x= x local row_z= z-1 local row_dir= kp.nz local column_dir= kp.px local signal_dir= kp.nx local row_len= 6 local num_rows= 2 local cell_count= 0 local per_centrifuge= 0 function do_row(rd) for n= 1, row_len do kp.drop_sam(m_dir, inventory_slots_table, per_centrifuge) kp.move(rd) end end while true do kp.wait_for_signal(signal_dir) kp.suck_til_false(cell_chest_dir) cell_count= kp.count_slots_total(inventory_slots_table) per_centrifuge= cell_count / (row_len * num_rows) io.write(tostring(cell_count) .. " cells gathered, " .. tostring(per_centrifuge) .. " for each centrifuge.\n") if cell_count ~= 0 or per_centrifuge ~= 0 then kp.mds_to_x(kp.move, row_x) kp.mds_to_z(kp.move, row_z) do_row(row_dir) kp.move(column_dir, 2) kp.move(kp.rev_dirs[row_dir]) do_row(kp.rev_dirs[row_dir]) kp.mds_to_z(kp.move, dock_z) kp.mds_to_x(kp.move, dock_x) kp.drop_inventory_full(cell_chest_dir) end end end function kp.gather_from_machines() local loot_chest_dir= kp.pz local dock_x= x local dock_z= z local row_x= x local row_z= z-1 local row_dir= kp.nz local signal_dir= kp.nx local row_len= 6 while true do kp.wait_for_signal(signal_dir) kp.mds_to_x(kp.move, row_x) kp.mds_to_z(kp.move, row_z) for n= 1, row_len do kp.suck_til_false(kp.px) kp.suck_til_false(kp.nx) kp.move(kp.nz) end kp.mds_to_z(kp.move, dock_z) kp.mds_to_x(kp.move, dock_x) kp.drop_inventory_full(loot_chest_dir) end end function kp.build_monster_harvest_house() -- Place the turtle on top of the monster spawner and run this function to -- build 4 walls, a floor, and a ceiling around the spawner. -- Place sufficient fuel in slot 1, it will be consumed. -- This function is intended for blaze spawners, which are often on -- balconies, with long drops into lava around, where you don't want a -- player doing the building. -- Sufficient fuel: (525 for radius 5, wall_height 6) -- Best to add some extra to be sure of not running out. local cobble_slots= inventory_slots_table local radius= 5 -- Radius is the number of empty blocks desired around the -- spawner, plus 1 for the wall. local minx= x - radius local minz= z - radius local maxx= x + radius local maxz= z + radius local above_spawner_y= y local wall_height= 6 -- Arbitrary. Should give sufficient height for -- spawning monsters. kp.refuel(1) kp.mds_to_x(kp.dig_move, minx) kp.mds_to_z(kp.dig_move, minz) kp.mds_to_y(kp.dig_move, above_spawner_y - 2) -- This should put the turtle -- moving on the layer below the spawner, so the spawner will be safe. local platform_size= (radius * 2) + 1 -- 1 for the spawner kp.build_platform(kp.px, platform_size, kp.pz, platform_size, cobble_slots) kp.build_wall(kp.px, platform_size - 1, wall_height, cobble_slots) kp.mds_to_x(kp.dig_move, maxx) kp.mds_to_z(kp.dig_move, minz) kp.mds_to_y(kp.dig_move, above_spawner_y - 2) kp.build_wall(kp.pz, platform_size - 1, wall_height, cobble_slots) kp.mds_to_x(kp.dig_move, maxx) kp.mds_to_z(kp.dig_move, maxz) kp.mds_to_y(kp.dig_move, above_spawner_y - 2) kp.build_wall(kp.nx, platform_size - 1, wall_height, cobble_slots) kp.mds_to_x(kp.dig_move, minx) kp.mds_to_z(kp.dig_move, maxz) kp.mds_to_y(kp.dig_move, above_spawner_y - 2) kp.build_wall(kp.nz, platform_size - 1, wall_height, cobble_slots) kp.mds_to_x(kp.dig_move, minx) kp.mds_to_z(kp.dig_move, minz) kp.mds_to_y(kp.dig_move, above_spawner_y - 2 + wall_height) kp.build_platform(kp.px, platform_size, kp.pz, platform_size, cobble_slots) -- After finishing the house, move the turtle inside it, so the player can -- grab it easily. kp.mds_to_x(kp.dig_move, minx+1) kp.mds_to_z(kp.dig_move, minz+1) kp.dig_move(kp.ny, 2) kp.place(cobble_slots, kp.py) kp.mds_to_y(kp.dig_move, above_spawner_y) end function kp.bmhh() -- Quick type shortcut. kp.build_monster_harvest_house() end function kp.build_sand_pillar_below(sand_slots) local block_below= false while not block_below do kp.place(sand_slots, kp.ny) block_below= kp.detect(kp.ny) end end function kp.build_arm(dir, len, sand_slots, torch_slots) if not dir or not len or not sand_slots or not torch_slots then io.write("kp.build_arm args: dir, len, sand_slots, torch_slots\n" .. "sand_slots and torch_slots can be tables of slots.\n") return end for n= 1, len do kp.dig_move(dir) kp.place(torch_slots, kp.ny) kp.dig_move(kp.py) kp.place(sand_slots, kp.ny) end end function kp.crawl_down_arm(arm_dir, arm_len) -- arm_dir is the direction the arm was built in, to make build_arm_nest -- easier to understand.. for n= 1, arm_len do kp.dig_move(kp.rev_dirs[arm_dir]) kp.dig_move(kp.ny) end end function kp.build_arm_nest(len, sand_slots, torch_slots, peak_locations) local arm_dirs= {kp.nz, kp.pz, kp.nx, kp.px} for d= 1, #arm_dirs do kp.build_arm(arm_dirs[d], len, sand_slots, torch_slots) if peak_locations then peak_locations[#peak_locations+1]= {kp.get_pos()} end kp.crawl_down_arm(arm_dirs[d], len) end end function kp.kpyr(pyramid_size) kp.build_sand_torch_inverted_pyramid(pyramid_size) end function kp.build_sand_torch_inverted_pyramid(pyramid_size) if not pyramid_size then io.write("kp.build_sand_torch_inverted_pyramid requires pyramid_size.\n") return end local dock_x= 0 local dock_y= 0 local dock_z= 0 local base_x= pyramid_size local base_y= 0 local base_z= pyramid_size local sand_chest_dir= kp.nz local sand_slots= {1, 2, 3, 4, 5, 6, 7, 8} local torch_chest_dir= kp.pz local torch_slots= {9, 10, 11, 12, 13, 14, 15, 16} local peak_locations= {{base_x, base_y, base_z}} local old_peak_locations= {} local cur_height= 0 -- Plan: -- 1. Build half of each of the four main arms. -- 2. Go halfway up each arm and build arms half of the remaining distance -- 3. Repeat if height not reached. -- 4. Suck at explaining. local nav_y= base_y + pyramid_size local function nav_to_armpit(pit_x, pit_y, pit_z) kp.mds_to_y(kp.dig_move, nav_y) kp.mds_to_x(kp.dig_move, pit_x) kp.mds_to_z(kp.dig_move, pit_z) kp.mds_to_y(kp.dig_move, pit_y) end local function restock_and_return(slots, dir) local sx, sy, sz, f= x, y, z, facing kp.restock(slots, nav_y, dir, dock_x, dock_y, dock_z) nav_to_armpit(sx, sy, sz) kp.face(f) end local function maybe_restock_then_place(slots, restock_dir) if kp.count_slots_total(slots) < 1 then restock_and_return(slots, restock_dir) end kp.place(slots, kp.ny) end kp.restock(sand_slots, nav_y, sand_chest_dir, dock_x, dock_y, dock_z) kp.restock(torch_slots, nav_y, torch_chest_dir, dock_x, dock_y, dock_z) nav_to_armpit(base_x, base_y, base_z) kp.build_sand_pillar_below(sand_slots) while cur_height < pyramid_size - 1 do old_peak_locations= peak_locations peak_locations= {} local arm_len= math.floor((pyramid_size - cur_height) / 2) local will_need= arm_len * 4 for n= 1, #old_peak_locations do local have= kp.count_slots_total(sand_slots) if have < will_need then io.write(tostring(have).." < "..tostring(will_need).."\n") kp.restock(sand_slots, nav_y, sand_chest_dir, dock_x, dock_y, dock_z) kp.restock(torch_slots, nav_y, torch_chest_dir, dock_x, dock_y, dock_z) end nav_to_armpit(unpack(old_peak_locations[n])) kp.build_arm_nest(arm_len, sand_slots, torch_slots, peak_locations) end cur_height = cur_height + arm_len end nav_to_armpit(base_x + pyramid_size, base_y + pyramid_size - 1, base_z) local function traverse_pyramid_top(right_dir, thing_to_place, restock_dir) local dir= kp.nz for n= 1, pyramid_size * 2, 2 do for o= 1, n do maybe_restock_then_place(thing_to_place, restock_dir) kp.dig_move(dir) end kp.dig_move(right_dir) dir= kp.rev_dirs[dir] end for n= pyramid_size * 2, 1, -2 do for o= 1, n do maybe_restock_then_place(thing_to_place, restock_dir) kp.dig_move(dir) end maybe_restock_then_place(thing_to_place, restock_dir) kp.dig_move(right_dir) dir= kp.rev_dirs[dir] kp.dig_move(dir) end maybe_restock_then_place(thing_to_place, restock_dir) end traverse_pyramid_top(kp.nx, torch_slots, torch_chest_dir) kp.dig_move(kp.py) traverse_pyramid_top(kp.px, sand_slots, sand_chest_dir) nav_to_armpit(dock_x, dock_y, dock_z) end function kp.threed_print(build_x, build_y, build_z, blocks, oreos, color_map, color_nav_y, build_nav_y) local function show_usage() io.write("kp.threed_print usage:\n" .."kp.threed_print(blocks, oreos, color_map, color_nav_y, build_nav_y)\n" .."blocks:\n" .."3d array of numbers indicating what block type goes at each position.\n" .."oreos:\n" .."3d array of numbers indicating the direction the turtle should face when placing each block.\n" .."Coordinate index order for blocks and oreos is y, x, z.\n" .."color_map:\n" .."Table of positions of stock chests for each color. Each entry should be of the form \"{dir, x, y, z}\". kp.restock is passed each entry when grabbing colors.\n" .."color_nav_y: y to use when navigating color area.\n" .."build_nav_y: y to use when navigating build area.\n" .."build_nav_y must also be safe to use in the color area.\n") end if not blocks or not oreos or not color_map then show_usage() return false end if type(blocks) ~= "table" or type(oreos) ~= "table" or type(color_map) ~= "table" then show_usage() return false end local chunk_size= 16 local function determine_colors_needed_for_layer(blocks, lx, ly, lz) local color_counts= {} local num_unique_colors= 0 local layer= blocks[ly] local column local element for cx= lx, lx+chunk_size do column= layer[cx] for cz= lz, lz+chunk_size do element= column[cz] if not color_counts[element] then num_unique_colors= num_unique_colors + 1 color_counts[element]= 0 end color_counts[element]= color_counts[element] + 1 end end return color_counts, num_unique_colors end -- palette layout: -- Each entry is a table of the slots the color is in. local function unload_palette(current_palette) for k, v in pairs(current_palette) do kp.unstock(v, color_nav_y, table.unpack(color_map[k])) end end local function load_palette(palette) for k, v in pairs(palette) do kp.restock(v, color_nav_y, table.unpack(color_map[k])) end end local function build_palette_array_from_needed_colors(color_counts, num_unique_colors) local palettes= {} local cur_palette= {} local next_slot_in_palette= 1 for k, v in pairs(color_counts) do local slots_needed= math.ceil(v / 64) local slot_table= {} for n= 1, slots_needed do slot_table[#slot_table+1]= next_slot_in_palette next_slot_in_palette= next_slot_in_palette + 1 if next_slot_in_palette > inventory_size then cur_palette[k]= slot_table palettes[#palettes+1]= cur_palette next_slot_in_palette= 1 cur_palette= {} slot_table= {} end end if #slot_table >= 1 then cur_palette[k]= slot_table end end if next_slot_in_palette > 1 then palettes[#palettes+1]= cur_palette end return palettes end local function print_layer(block_layer, oreo_layer, lx, ly, lz, palette) local function print_tile_callback() local dx, dz= x - build_x, z - build_z local block= block_layer[dx][dz] local oreo= oreo_layer[dx][dz] if palette[block] then kp.place(palette[block], kp.ny) end end kp.safe_nav(lx + build_x, ly + build_y, lz + build_z, build_nav_y) kp.traverse_layer(kp.pz, chunk_size, kp.px, chunk_size, print_tile_callback) end end function kp.set_up_search_koo(drive_pos) -- This mining function will not return materials. -- Dock shape: -- d= disk drive -- t= turtle -- c= charger -- -- ^ py -- d -- tc --> nz if not drive_pos or not drive_pos.x or not drive_pos.y or not drive_pos.z then print("drive_pos must be a table with x, y, and z values for the position of the disk drive.") return nil end local ret= {drive_pos= drive_pos, next_shaft= {x= drive_pos.x - 64, y= 6, z= drive_pos.z + 1}} local mp= disk.getMountPath("top") if not mp then print("Search koo cannot be set up without a disk drive above the turtle for storage.") return nil end local f= fs.open(mp.."/data", "w") if not f then print("Coult not open data file on disk.") return nil end f.write(tostring(drive_pos.x).."\n") f.write(tostring(drive_pos.y).."\n") f.write(tostring(drive_pos.z).."\n") f.write(tostring(ret.next_shaft.x).."\n") f.write(tostring(ret.next_shaft.y).."\n") f.write(tostring(ret.next_shaft.z).."\n") f.close() kp.write_search_koo_startup() end function kp.write_search_koo_startup() local startup_file= fs.open("startup", "w") if startup_file then startup_file.write("local drive, to_do= kp.search_koo_read_and_update_next_shaft()\n") startup_file.write("kp.search_koo_dig_shaft(drive, to_do)\n") startup_file.close() else io.write("Could not open startup file to write.\n") end end function kp.search_koo_read_and_update_next_shaft() local mp= disk.getMountPath("top") if not mp then return nil else local f= fs.open(mp.."/data", "r") local dx= tonumber(f.readLine()) local dy= tonumber(f.readLine()) local dz= tonumber(f.readLine()) local sx= tonumber(f.readLine()) local sy= tonumber(f.readLine()) local sz= tonumber(f.readLine()) f.close() x, y, z= dx, dy-1, dz local to_do_shaft= {x= sx, y= sy, z= sz} local next_shaft= {x= to_do_shaft.x + 1, y= to_do_shaft.y, z= to_do_shaft.z} if next_shaft.x > dx + 64 then next_shaft.x= dx - 64 next_shaft.y= next_shaft.y + 3 end local wf= fs.open(mp.."/data", "w") wf.write(tostring(dx).."\n") wf.write(tostring(dy).."\n") wf.write(tostring(dz).."\n") wf.write(tostring(next_shaft.x).."\n") wf.write(tostring(next_shaft.y).."\n") wf.write(tostring(next_shaft.z).."\n") wf.close() if to_do_shaft.y >= 64 then to_do_shaft= nil end return {x= dx, y= dy, z= dz}, to_do_shaft end return nil end function kp.search_koo_dig_shaft(drive, shaft) if not drive or not drive.x or not drive.y or not drive.z then print("No drive pos information given.") return end if not shaft then print("No shaft to dig.") return end kp.retry_move(kp.nx) kp.retry_move(kp.pz) kp.mds_to_x(kp.retry_move, shaft.x) kp.mds_to_y(kp.sidestep_px_move, shaft.y) kp.mds_to_z(kp.retry_move, shaft.z) local dist_gone= 0 local front_clear= true while dist_gone < 64 and front_clear do kp.dig_only_recognized(kp.py) kp.dig_only_recognized(kp.ny) front_clear= not kp.detect(kp.pz) local front_dug= true while front_dug and not front_clear do front_dug, reason= kp.dig_only_recognized(kp.pz) front_clear= not kp.detect(kp.pz) end if front_clear then kp.retry_move(kp.pz) end dist_gone= dist_gone + 1 end kp.mds_to_z(kp.dig_move, shaft.z) kp.mds_to_x(kp.retry_move, drive.x) kp.mds_to_y(kp.sidestep_nx_move, drive.y-1) kp.mds_to_z(kp.retry_move, drive.z) kp.face(kp.nz) os.reboot() end function kp.kill_and_drop() turtle.attack() for s= 1, inventory_size, 1 do if turtle.getItemCount(s) > 0 then kp.drop(facing, s) end end end function kp.set_pos(sx, sy, sz) x= sx y= sy z= sz return true end function kp.set_facing(f) facing= f rev_facing= kp.rev_dirs[f] return true end function kp.interact() local keep_interacting= true local succeeded, reason while keep_interacting do succeeded= false reason= false io.write("kp > ") local s= io.read() if s == "quit" then keep_interacting= false elseif s == "pos" then local px= tonumber(io.read()) local py= tonumber(io.read()) local pz= tonumber(io.read()) if px and py and pz then succeeded, reason= kp.set_pos(px, py, pz) end elseif s == "facing" then local f= tonumber(io.read()) if f and f > 0 and f < 5 then succeeded, reason= kp.set_facing(f) end elseif s == "face_to" then local f= tonumber(io.read()) if f and f > 0 and f < 5 then succeeded, reason= kp.face(f) end elseif s == "f" then succeeded, reason= kp.forward() elseif s == "b" then succeeded, reason= kp.back() elseif s == "u" then succeeded, reason= kp.up() elseif s == "d" then succeeded, reason= kp.down() elseif s == "l" then succeeded, reason= kp.turn_left() elseif s == "r" then succeeded, reason= kp.turn_right() elseif s == "mds_to_x" then local func_name= io.read() local px= tonumber(io.read()) if kp[func_name] and px then succeeded, reason= kp.mds_to_x(kp[func_name], px) end elseif s == "mds_to_y" then local func_name= io.read() local py= tonumber(io.read()) if kp[func_name] and py then succeeded, reason= kp.mds_to_y(kp[func_name], py) end elseif s == "mds_to_z" then local func_name= io.read() local pz= tonumber(io.read()) if kp[func_name] and pz then succeeded, reason= kp.mds_to_z(kp[func_name], pz) end elseif s == "mds_to_pos" then local func_name= io.read() local px= tonumber(io.read()) local py= tonumber(io.read()) local pz= tonumber(io.read()) if kp[func_name] and px and py and pz then succeeded, reason= kp.mds_to_pos(kp[func_name], px, py, pz) end elseif s == "cl" then succeeded, reason= kp.cl_simple() elseif s == "cc" then succeeded, reason= kp.cc_simple() elseif s == "ccd" then succeeded, reason= kp.cc_simple_down() elseif s == "acc" then succeeded, reason= kp.mds_to_z(kp.dig_move, z - 8) if succeeded then succeeded, reason= kp.cc_simple() end elseif s == "mdsrx" then local func_name= io.read() local px= tonumber(io.read()) if kp[func_name] and px then succeeded, reason= kp.mds_to_x(kp[func_name], x + px) end elseif s == "mdsry" then local func_name= io.read() local py= tonumber(io.read()) if kp[func_name] and py then succeeded, reason= kp.mds_to_y(kp[func_name], y + py) end elseif s == "mdsrz" then local func_name= io.read() local pz= tonumber(io.read()) if kp[func_name] and pz then succeeded, reason= kp.mds_to_z(kp[func_name], z + pz) end elseif s == "mdsrp" then local func_name= io.read() local px= tonumber(io.read()) local py= tonumber(io.read()) local pz= tonumber(io.read()) if kp[func_name] and px and py and pz then succeeded, reason= kp.mds_to_pos(kp[func_name], x + px, y + py, z + pz) end elseif s == "collect" then local rise_amount= tonumber(io.read()) if rise_amount then succeeded, reason= kp.simple_cc_collection(rise_amount) end elseif s == "cobble" then succeeded, reason= kp.cobble_generator() elseif s == "platform" then succeeded, reason= kp.build_platform_simple() elseif s == "fl" then io.write("Fuel Level: " .. tostring(turtle.getFuelLevel()) .. "\n") succeeded= true elseif s == "dm" then kp.dig_time_measure() succeeded= true elseif s == "tm" then kp.turn_time_measure() succeeded= true elseif s == "mm" then kp.move_time_measure() succeeded= true elseif s == "record_pos" then succeeded, reason= kp.record_pos() elseif s == "load_pos" then succeeded, reason= kp.load_pos() elseif s == "sp" then succeeded, reason= kp.show_pos() elseif s == "rec_on" then succeeded, reason= kp.set_record_mode(true) elseif s == "rec_off" then succeeded, reason= kp.set_record_mode(true) end if not succeeded then if reason then io.write("Failed: " .. reason_strings[reason] .. "\n") else io.write("Failed.\n") end end end end function kp.stair() if not kp.forward() then return false end if not kp.down() then return false end end function kp.multi_stair(num_stairs) for n= 1, num_stairs, 1 do stair() end end function kp.start() kp.init() kp.interact() kp.face(1) end kp.init()