entrance_carver.lua
240 lines · 12.9 KB
Entrance Carver by Silvamord Carves a rectangular entrance corridor with configurable width, height (above/below floor), and length. Start modes: - center: turtle stands at center column of doorway - bottom-left: turtle stands at doorway's bottom-left corner Features: torches (left/right/both, spacing), chest dump, throw items by sample, favorite config, auto-return for fuel along corridor. ]]-- local version = "1.0" -- UI helpers local function ask_number_default(prompt, default_value, min_val, max_val) while true do term.clear(); term.setCursorPos(1,1) write("Entrance Carver v"..version.."\n\n") write(string.format("%s [default: %s] ", prompt, tostring(default_value))) local s = read(); if s == nil or s == "" then return default_value end local n = tonumber(s) if n and (not min_val or n >= min_val) and (not max_val or n <= max_val) then return n end write("\nInvalid input. Press Enter to try again."); read() end end local function ask_yes_no(prompt, default_yes) while true do term.clear(); term.setCursorPos(1,1) write("Entrance Carver v"..version.."\n\n") local def = default_yes and "Y" or "N"
Copy & run
wget https://perlytiara.github.io/turtles.tips/raw/programs/perlytiara/entrance_carver.lua
| 1 | --[[ |
| 2 | Entrance Carver by Silvamord |
| 3 | Carves a rectangular entrance corridor with configurable width, height (above/below floor), and length. |
| 4 | Start modes: |
| 5 | - center: turtle stands at center column of doorway |
| 6 | - bottom-left: turtle stands at doorway's bottom-left corner |
| 7 | |
| 8 | Features: torches (left/right/both, spacing), chest dump, throw items by sample, favorite config, auto-return for fuel along corridor. |
| 9 | ]]-- |
| 10 | |
| 11 | local version = "1.0" |
| 12 | |
| 13 | -- UI helpers |
| 14 | local function ask_number_default(prompt, default_value, min_val, max_val) |
| 15 | while true do |
| 16 | term.clear(); term.setCursorPos(1,1) |
| 17 | write("Entrance Carver v"..version.."\n\n") |
| 18 | write(string.format("%s [default: %s] ", prompt, tostring(default_value))) |
| 19 | local s = read(); if s == nil or s == "" then return default_value end |
| 20 | local n = tonumber(s) |
| 21 | if n and (not min_val or n >= min_val) and (not max_val or n <= max_val) then return n end |
| 22 | write("\nInvalid input. Press Enter to try again."); read() |
| 23 | end |
| 24 | end |
| 25 | |
| 26 | local function ask_yes_no(prompt, default_yes) |
| 27 | while true do |
| 28 | term.clear(); term.setCursorPos(1,1) |
| 29 | write("Entrance Carver v"..version.."\n\n") |
| 30 | local def = default_yes and "Y" or "N" |
| 31 | write(string.format("%s (y/n) [default: %s] ", prompt, def)) |
| 32 | local s = read(); s = s and string.lower(s) or "" |
| 33 | if s == "" then return default_yes end |
| 34 | if s == "y" or s == "yes" then return true end |
| 35 | if s == "n" or s == "no" then return false end |
| 36 | write("\nInvalid input. Press Enter to try again."); read() |
| 37 | end |
| 38 | end |
| 39 | |
| 40 | local function ask_choice(prompt, default_value, choices) |
| 41 | while true do |
| 42 | term.clear(); term.setCursorPos(1,1) |
| 43 | write("Entrance Carver v"..version.."\n\n") |
| 44 | write(string.format("%s %s [default: %s] ", prompt, table.concat(choices, "/"), tostring(default_value))) |
| 45 | local s = read(); if s == nil or s == "" then return default_value end |
| 46 | s = string.lower(s) |
| 47 | for _, c in ipairs(choices) do if s == c then return s end end |
| 48 | write("\nInvalid input. Press Enter to try again."); read() |
| 49 | end |
| 50 | end |
| 51 | |
| 52 | -- Movement helpers |
| 53 | local function ensure_fuel(threshold) |
| 54 | if turtle.getFuelLevel() == "unlimited" then return end |
| 55 | if turtle.getFuelLevel() >= threshold then return end |
| 56 | for i=1,16 do |
| 57 | turtle.select(i) |
| 58 | if turtle.refuel(0) then while turtle.getFuelLevel()<threshold and turtle.refuel(1) do end; if turtle.getFuelLevel()>=threshold then return end end |
| 59 | end |
| 60 | term.clear(); term.setCursorPos(1,1); print("Out of fuel. Add fuel and press Enter."); read(); |
| 61 | return ensure_fuel(threshold) |
| 62 | end |
| 63 | |
| 64 | local function dig_forward() while turtle.detect() do if not turtle.dig() then turtle.attack(); sleep(0.1) end end end |
| 65 | local function dig_upwards() while turtle.detectUp() do if not turtle.digUp() then turtle.attackUp(); sleep(0.1) end end end |
| 66 | local function dig_downwards() while turtle.detectDown() do if not turtle.digDown() then turtle.attackDown(); sleep(0.1) end end end |
| 67 | |
| 68 | local function safe_forward() ensure_fuel(100); while not turtle.forward() do dig_forward(); sleep(0.05) end end |
| 69 | local function safe_up() ensure_fuel(100); while not turtle.up() do dig_upwards(); sleep(0.05) end end |
| 70 | local function safe_down() ensure_fuel(100); while not turtle.down() do dig_downwards(); sleep(0.05) end end |
| 71 | |
| 72 | local function turn_left() turtle.turnLeft() end |
| 73 | local function turn_right() turtle.turnRight() end |
| 74 | local function step_right() turn_right(); dig_forward(); safe_forward(); turn_left() end |
| 75 | local function step_left() turn_left(); dig_forward(); safe_forward(); turn_right() end |
| 76 | |
| 77 | -- Favorites |
| 78 | local function favorite_path() |
| 79 | local dir = ".entrance_carver"; if not fs.exists(dir) then fs.makeDir(dir) end |
| 80 | return fs.combine(dir, "favorite") |
| 81 | end |
| 82 | local function load_favorite() |
| 83 | local p = favorite_path(); if not fs.exists(p) then return nil end |
| 84 | local h = fs.open(p, "r"); if not h then return nil end; local d=h.readAll(); h.close() |
| 85 | local ok,t = pcall(textutils.unserialize,d); if ok and type(t)=="table" then return t end; return nil |
| 86 | end |
| 87 | local function save_favorite(tbl) |
| 88 | local p = favorite_path(); local h = fs.open(p,"w"); if not h then return end; h.write(textutils.serialize(tbl)); h.close() |
| 89 | end |
| 90 | |
| 91 | -- Inventory & IO |
| 92 | local function count_empty_slots() local e=0; for i=1,16 do if turtle.getItemCount(i)==0 then e=e+1 end end; return e end |
| 93 | local function any_item_matches_slot(slot_idx) |
| 94 | if turtle.getItemCount(slot_idx)==0 then return false end |
| 95 | for i=1,16 do if i~=slot_idx and turtle.getItemCount(i)>0 then turtle.select(i); if turtle.compareTo(slot_idx) then return true end end end |
| 96 | return false |
| 97 | end |
| 98 | local function drop_matching_front(slot_idx) |
| 99 | for i=1,16 do if i~=slot_idx and turtle.getItemCount(i)>0 then turtle.select(i); if turtle.compareTo(slot_idx) then turtle.drop() end end end |
| 100 | end |
| 101 | local function turn_to_side(side) if side=="left" then turn_left() else turn_right() end end |
| 102 | local function place_chest_in_wall(chest_slot, side) |
| 103 | if turtle.getItemCount(chest_slot)==0 then return false end |
| 104 | turn_to_side(side); dig_forward(); turtle.select(chest_slot) |
| 105 | local ok = turtle.place(); if not ok then dig_forward(); ok = turtle.place() end; if not ok then ok = turtle.placeDown() end |
| 106 | turn_to_side(side=="left" and "right" or "left"); return ok |
| 107 | end |
| 108 | local function deposit_into_front(chest_slot, torch_slot, throw_slot) |
| 109 | for i=1,16 do if i~=chest_slot and i~=torch_slot and i~=throw_slot and turtle.getItemCount(i)>0 then turtle.select(i); if not turtle.refuel(0) then turtle.drop() end end end |
| 110 | if throw_slot then local cnt=turtle.getItemCount(throw_slot); if cnt>1 then turtle.select(throw_slot); turtle.drop(cnt-1) end end |
| 111 | end |
| 112 | local function ensure_inventory_capacity(cfg, at_left) |
| 113 | if count_empty_slots()>0 then return end |
| 114 | if cfg.use_throw and turtle.getItemCount(cfg.throw_slot)>0 and any_item_matches_slot(cfg.throw_slot) then |
| 115 | drop_matching_front(cfg.throw_slot); if count_empty_slots()>0 then return end |
| 116 | end |
| 117 | if cfg.use_chests then local side = at_left and "left" or "right"; if place_chest_in_wall(cfg.chest_slot, side) then deposit_into_front(cfg.chest_slot, cfg.torch_slot, cfg.throw_slot) end end |
| 118 | end |
| 119 | |
| 120 | -- Torches |
| 121 | local function place_torch_if_needed(step_idx, cfg, width, at_left) |
| 122 | if not cfg.use_torches or cfg.torch_spacing<=0 then return end |
| 123 | if step_idx % cfg.torch_spacing ~= 0 then return end |
| 124 | if turtle.getItemCount(cfg.torch_slot)==0 then return end |
| 125 | local function place_on(side) |
| 126 | local climbed = 0; if width>=3 then safe_up(); climbed=1 end |
| 127 | turn_to_side(side); turtle.select(cfg.torch_slot) |
| 128 | local ok = turtle.place(); if not ok then turn_to_side(side=="left" and "right" or "left"); for i=1,climbed do safe_down() end; turtle.select(cfg.torch_slot); turtle.placeDown(); return end |
| 129 | turn_to_side(side=="left" and "right" or "left"); for i=1,climbed do safe_down() end |
| 130 | end |
| 131 | if cfg.torch_side=="both" then if at_left then place_on("left"); place_on("right") else place_on("right"); place_on("left") end else place_on(cfg.torch_side) end |
| 132 | end |
| 133 | |
| 134 | -- Carve one corridor slice at current face, optimized using level-2 reach |
| 135 | local function carve_slice(width, above, below, start_at_left, current_level) |
| 136 | local work_level = (above>=2) and 2 or 1 |
| 137 | if (current_level or 1) < work_level then while (current_level or 1) < work_level do safe_up(); current_level=(current_level or 1)+1 end end |
| 138 | if (current_level or 1) > work_level then while (current_level or 1) > work_level do safe_down(); current_level=(current_level or 1)-1 end end |
| 139 | local end_at_left |
| 140 | if start_at_left then |
| 141 | for x=1,width do |
| 142 | if work_level==1 then dig_forward() else turtle.digDown(); if above>=2 then dig_upwards() end end |
| 143 | if x<width then step_right() end |
| 144 | end |
| 145 | end_at_left=false |
| 146 | else |
| 147 | for x=width,1,-1 do |
| 148 | if work_level==1 then dig_forward() else turtle.digDown(); if above>=2 then dig_upwards() end end |
| 149 | if x>1 then step_left() end |
| 150 | end |
| 151 | end_at_left=true |
| 152 | end |
| 153 | if above>=3 then safe_up(); if start_at_left then for x=1,width do dig_upwards(); if x<width then step_right() end end else for x=width,1,-1 do dig_upwards(); if x>1 then step_left() end end end; safe_down() end |
| 154 | if below>=1 then if work_level==2 then safe_down() end; if start_at_left then for x=1,width do turtle.digDown(); if x<width then step_right() end end else for x=width,1,-1 do turtle.digDown(); if x>1 then step_left() end end end; if work_level==2 then safe_up() end end |
| 155 | return end_at_left, work_level |
| 156 | end |
| 157 | |
| 158 | local function estimate_moves_per_slice(width, above, below) |
| 159 | local vertical = (above>=2) and 1 or 0; if above>=3 then vertical=vertical+2 end; if below>=1 and above>=2 then vertical=vertical+2 end |
| 160 | local lateral = (width-1) |
| 161 | local advance = 1 |
| 162 | return vertical + lateral + advance |
| 163 | end |
| 164 | |
| 165 | local function attempt_return_for_refuel(depth, reserve_needed) |
| 166 | local minimal = depth*2 + 4 |
| 167 | if turtle.getFuelLevel() ~= "unlimited" and turtle.getFuelLevel() < minimal then ensure_fuel(minimal) end |
| 168 | turn_right(); turn_right(); for i=1,depth do safe_forward() end |
| 169 | term.clear(); term.setCursorPos(1,1); print("At start. Add fuel/torches, press Enter."); read() |
| 170 | ensure_fuel(reserve_needed) |
| 171 | turn_right(); turn_right(); for i=1,depth do safe_forward() end |
| 172 | end |
| 173 | |
| 174 | local function main() |
| 175 | term.clear(); term.setCursorPos(1,1); print("Entrance Carver v"..version); print("Place turtle at doorway, facing wall.") |
| 176 | local fav = load_favorite(); local use_fav = fav and ask_yes_no("Use favorite saved config?", true) or false |
| 177 | |
| 178 | local start_mode, width, length, above, below |
| 179 | local use_torches, torch_spacing, torch_side, torch_slot |
| 180 | local use_chests, chest_slot |
| 181 | local use_throw, throw_slot |
| 182 | local auto_return |
| 183 | |
| 184 | if use_fav then |
| 185 | start_mode = fav.start_mode or "center" |
| 186 | width = fav.width or 5; length = fav.length or 16; above = fav.above or 3; below = fav.below or 0 |
| 187 | auto_return = fav.auto_return or true |
| 188 | use_torches = fav.use_torches or true; torch_spacing = fav.torch_spacing or 9; torch_side = fav.torch_side or "both"; torch_slot = fav.torch_slot or 1 |
| 189 | use_chests = fav.use_chests or true; chest_slot = fav.chest_slot or 2 |
| 190 | use_throw = fav.use_throw or false; throw_slot = fav.throw_slot or 4 |
| 191 | else |
| 192 | start_mode = ask_choice("Start mode:", "center", {"center","bottom-left"}) |
| 193 | width = ask_number_default("Doorway width:", 5, 1, 64) |
| 194 | length = ask_number_default("Entrance length:", 16, 1, 100000) |
| 195 | above = ask_number_default("Height above floor:", 3, 1, 16) |
| 196 | below = ask_number_default("Height below floor:", 0, 0, 8) |
| 197 | auto_return = ask_yes_no("Auto-return for fuel/torches?", true) |
| 198 | use_torches = ask_yes_no("Place torches?", true) |
| 199 | if use_torches then torch_spacing = ask_number_default("Torch spacing (blocks):", 9, 1, 64); torch_side = ask_choice("Torch side:", "both", {"left","right","both"}); torch_slot = ask_number_default("Torch slot (1-16):", 1, 1, 16) else torch_spacing=0; torch_side="right"; torch_slot=1 end |
| 200 | use_chests = ask_yes_no("Place chests to dump when full?", true); if use_chests then chest_slot = ask_number_default("Chest slot (1-16):", 2, 1, 16) else chest_slot = 2 end |
| 201 | use_throw = ask_yes_no("Throw items matching a sample when full?", false); if use_throw then throw_slot = ask_number_default("Sample slot (1-16):", 4, 1, 16) else throw_slot = 4 end |
| 202 | if ask_yes_no("Save as favorite?", true) then save_favorite({start_mode=start_mode,width=width,length=length,above=above,below=below,auto_return=auto_return,use_torches=use_torches,torch_spacing=torch_spacing,torch_side=torch_side,torch_slot=torch_slot,use_chests=use_chests,chest_slot=chest_slot,use_throw=use_throw,throw_slot=throw_slot}) end |
| 203 | end |
| 204 | |
| 205 | local cfg = {use_torches=use_torches, torch_spacing=torch_spacing, torch_side=torch_side, torch_slot=torch_slot, use_chests=use_chests, chest_slot=chest_slot, use_throw=use_throw, throw_slot=throw_slot, auto_return=auto_return} |
| 206 | |
| 207 | term.clear(); term.setCursorPos(1,1) |
| 208 | print("Entrance Carver v"..version) |
| 209 | print("Width:", width, " Length:", length, " Above:", above, " Below:", below) |
| 210 | print("Torches:", use_torches and ("every "..torch_spacing.." blocks on "..torch_side) or "No") |
| 211 | print("Chests:", use_chests and ("slot "..chest_slot) or "No", " Throw:", use_throw and ("slot "..throw_slot) or "No") |
| 212 | print("") |
| 213 | print("Place torches in slot "..torch_slot..", chests in slot "..chest_slot..", sample in slot "..throw_slot.." if throwing.") |
| 214 | print("") |
| 215 | print("Press Enter to start..."); read() |
| 216 | |
| 217 | -- Align to left edge if starting at center |
| 218 | if start_mode=="center" then local left_offset = math.floor((width-1)/2); turn_left(); for i=1,left_offset do dig_forward(); safe_forward() end; turn_right() end |
| 219 | |
| 220 | local per_slice = estimate_moves_per_slice(width, above, below) |
| 221 | local depth=0; local at_left=true; local current_level=1 |
| 222 | |
| 223 | for step=1,length do |
| 224 | ensure_inventory_capacity(cfg, at_left) |
| 225 | local reserve = per_slice + depth + 10 |
| 226 | if turtle.getFuelLevel()~="unlimited" and turtle.getFuelLevel()<reserve then ensure_fuel(reserve); if turtle.getFuelLevel()<reserve and cfg.auto_return then attempt_return_for_refuel(depth, reserve) end end |
| 227 | at_left, current_level = carve_slice(width, above, below, at_left, current_level) |
| 228 | place_torch_if_needed(step, cfg, width, at_left) |
| 229 | ensure_inventory_capacity(cfg, at_left) |
| 230 | -- advance to next face |
| 231 | dig_forward(); safe_forward(); depth = depth + 1 |
| 232 | end |
| 233 | |
| 234 | term.clear(); term.setCursorPos(1,1); print("Done. Entrance completed.") |
| 235 | end |
| 236 | |
| 237 | main() |
| 238 | |
| 239 | |
| 240 |