quarry_multi.lua

533 lines · 17.6 KB

Open raw

quarry_multi.lua

Copy & run

wget https://perlytiara.github.io/turtles.tips/raw/programs/perlytiara/quarry/quarry_multi.lua
1-- quarry_multi.lua
2-- Master script to launch multiple turtles with divided params for quarry
3
4-- Auto-detect a modem and open rednet
5local function findModem()
6 for _, p in pairs(rs.getSides()) do
7 if peripheral.isPresent(p) and peripheral.getType(p) == "modem" then
8 return p
9 end
10 end
11 error("No modem attached to this computer.")
12end
13
14local modemSide = findModem()
15rednet.open(modemSide)
16
17local wrapped = peripheral.wrap(modemSide)
18if wrapped and wrapped.isWireless and not wrapped.isWireless() then
19 print("Note: Wired modem detected. Ensure turtles are cabled to the same network.")
20end
21
22-- Corner definitions (normalized x=0 left, 1 right; z=0 bottom, 1 top)
23-- Turtles face EACH OTHER across the quarry for proper coordination
24local corner_info = {
25 [1] = {name = "bottom-left (SW)", x = 0, z = 0, default_facing = 1}, -- +Z (facing north toward turtle 4)
26 [2] = {name = "bottom-right (SE)", x = 1, z = 0, default_facing = 1}, -- +Z (facing north toward turtle 3)
27 [3] = {name = "top-right (NE)", x = 1, z = 1, default_facing = -1}, -- -Z (facing south toward turtle 2)
28 [4] = {name = "top-left (NW)", x = 0, z = 1, default_facing = -1} -- -Z (facing south toward turtle 1)
29}
30
31-- Function to divide dimension into n parts
32local function divide_dim(dim, n)
33 local parts = {}
34 local base = math.floor(dim / n)
35 local rem = dim % n
36 for i = 1, n do
37 parts[i] = base + (i <= rem and 1 or 0)
38 end
39 return parts
40end
41
42-- Wizard - Get dimensions
43print("=== Quarry Dimensions ===")
44local total_length
45while true do
46 print("Enter total length (sizeZ, positive):")
47 total_length = tonumber(read())
48 if total_length and total_length > 0 then
49 break
50 else
51 print("Invalid input. Please enter a positive number.")
52 end
53end
54
55local total_width
56while true do
57 print("Enter total width (sizeX, positive):")
58 total_width = tonumber(read())
59 if total_width and total_width > 0 then
60 break
61 else
62 print("Invalid input. Please enter a positive number.")
63 end
64end
65
66local total_depth
67while true do
68 print("Enter total depth (sizeY, positive, default 256):")
69 local input = read()
70 if input == "" then
71 total_depth = 256
72 break
73 else
74 total_depth = tonumber(input)
75 if total_depth and total_depth > 0 then
76 break
77 else
78 print("Invalid input. Please enter a positive number or press Enter for default (256).")
79 end
80 end
81end
82
83-- Get number of turtles
84local num
85while true do
86 print("Enter number of turtles (1-4):")
87 num = tonumber(read())
88 if num and num >= 1 and num <= 4 then
89 break
90 else
91 print("Invalid input. Please enter a number between 1 and 4.")
92 end
93end
94
95-- Setup each turtle one by one
96local turtles = {}
97for i = 1, num do
98 print("\n=== Setting up Turtle " .. i .. " ===")
99
100 -- Get corner
101 local corner
102 while true do
103 print("Turtle placement diagram (viewed from above):")
104 print(" 4(NW) <-----> 3(NE)")
105 print(" ^ ^")
106 print(" | QUARRY |")
107 print(" | AREA |")
108 print(" v v")
109 print(" 1(SW) <-----> 2(SE)")
110 print("")
111 print("Turtles face EACH OTHER across quarry:")
112 print(" 1 = bottom-left (SW) - face +Z (north toward turtle 4)")
113 print(" 2 = bottom-right (SE) - face +Z (north toward turtle 3)")
114 print(" 3 = top-right (NE) - face -Z (south toward turtle 2)")
115 print(" 4 = top-left (NW) - face -Z (south toward turtle 1)")
116 print("Enter corner number for turtle " .. i .. ":")
117 corner = tonumber(read())
118 if corner and corner_info[corner] then
119 break
120 else
121 print("Invalid corner. Please enter 1, 2, 3, or 4.")
122 end
123 end
124
125 -- Get turtle ID
126 local id
127 while true do
128 print("Enter turtle ID for " .. corner_info[corner].name .. ":")
129 print("(Use 'id' command on the turtle to get its ID)")
130 id = tonumber(read())
131 if id and id > 0 then
132 break
133 else
134 print("Invalid ID. Please enter a positive number.")
135 end
136 end
137
138 -- Get facing direction
139 local facing
140 while true do
141 print("Enter facing direction for " .. corner_info[corner].name .. ":")
142 print(" 1 = +Z (forward/north)")
143 print(" 2 = -Z (backward/south)")
144 print(" (default: " .. corner_info[corner].default_facing .. ")")
145 local input = read()
146 if input == "" then
147 facing = corner_info[corner].default_facing
148 break
149 else
150 local facing_input = tonumber(input)
151 if facing_input == 1 then
152 facing = 1
153 break
154 elseif facing_input == 2 then
155 facing = -1
156 break
157 else
158 print("Invalid facing. Please enter 1 or 2, or press Enter for default.")
159 end
160 end
161 end
162
163 turtles[i] = {corner = corner, id = id, facing = facing}
164 print("Turtle " .. i .. " configured: " .. corner_info[corner].name .. " (ID: " .. id .. ") facing " .. (facing == 1 and "+Z" or "-Z"))
165end
166
167-- Get options
168print("\n=== Quarry Options ===")
169local start_below
170while true do
171 print("Preserve top layer (start digging below)? (y/n, default n):")
172 local input = read():lower()
173 if input == "" or input == "n" or input == "no" then
174 start_below = 0
175 break
176 elseif input == "y" or input == "yes" then
177 start_below = 1
178 break
179 else
180 print("Invalid input. Please enter 'y' for yes, 'n' for no, or press Enter for default (no).")
181 end
182end
183
184local debug
185while true do
186 print("Debug mode? (y/n, default n):")
187 local input = read():lower()
188 if input == "" or input == "n" or input == "no" then
189 debug = 0
190 break
191 elseif input == "y" or input == "yes" then
192 debug = 1
193 break
194 else
195 print("Invalid input. Please enter 'y' for yes, 'n' for no, or press Enter for default (no).")
196 end
197end
198
199local auto_start
200while true do
201 print("Auto-start turtles (skip fuel prompt)? (y/n, default y):")
202 local input = read():lower()
203 if input == "" or input == "y" or input == "yes" then
204 auto_start = 1
205 break
206 elseif input == "n" or input == "no" then
207 auto_start = 0
208 break
209 else
210 print("Invalid input. Please enter 'y' for yes, 'n' for no, or press Enter for default (yes).")
211 end
212end
213
214local is_horizontal_pref = true
215if num == 3 then
216 while true do
217 print("For 3 turtles, preferred split direction:")
218 print(" 1 = horizontal/width")
219 print(" 2 = vertical/length")
220 print(" (default: horizontal)")
221 local input = read()
222 if input == "" or input == "1" then
223 is_horizontal_pref = true
224 break
225 elseif input == "2" then
226 is_horizontal_pref = false
227 break
228 else
229 print("Invalid input. Please enter 1, 2, or press Enter for default (horizontal).")
230 end
231 end
232end
233
234-- Compute counts
235local count_bottom = 0
236local count_top = 0
237local count_left = 0
238local count_right = 0
239local bottom_turtles = {}
240local top_turtles = {}
241local left_turtles = {}
242local right_turtles = {}
243for _, t in ipairs(turtles) do
244 local info = corner_info[t.corner]
245 if info.z == 0 then
246 count_bottom = count_bottom + 1
247 table.insert(bottom_turtles, t)
248 else
249 count_top = count_top + 1
250 table.insert(top_turtles, t)
251 end
252 if info.x == 0 then
253 count_left = count_left + 1
254 table.insert(left_turtles, t)
255 else
256 count_right = count_right + 1
257 table.insert(right_turtles, t)
258 end
259end
260
261-- Function to sort turtles by position (x or z)
262local function sort_by_pos(t_list, key)
263 table.sort(t_list, function(a, b)
264 return corner_info[a.corner][key] < corner_info[b.corner][key]
265 end)
266end
267
268-- Compute parameters for each turtle
269local params_list = {}
270local roles = {}
271
272for i, t in ipairs(turtles) do
273 local info = corner_info[t.corner]
274 local is_left = (info.x == 0)
275 local desired_x_dir = is_left and 1 or -1
276 local facing_sign = t.facing
277 local sizeX_sign = desired_x_dir * facing_sign
278 local sizeZ_sign = facing_sign
279
280 -- Placeholder, will compute abs later
281 params_list[i] = ""
282 roles[i] = info.name
283end
284
285-- Now compute based on num
286if num == 1 then
287 local t = turtles[1]
288 local info = corner_info[t.corner]
289 local is_left = (info.x == 0)
290 local desired_x_dir = is_left and 1 or -1
291 local facing_sign = t.facing
292 local sizeX_sign = desired_x_dir * facing_sign
293 local sizeZ_sign = facing_sign
294
295 local abs_sizeZ = total_length
296 local abs_sizeX = total_width
297 local sizeZ = t.facing * abs_sizeZ
298 local sizeX = (corner_info[t.corner].x == 0) and abs_sizeX or (-abs_sizeX)
299 params_list[1] = tostring(sizeZ) .. " " .. tostring(sizeX) .. " " .. tostring(total_depth) .. " " .. debug .. " " .. start_below .. " " .. auto_start
300
301elseif num == 2 then
302 -- Determine type
303 local all_bottom = count_bottom == 2
304 local all_top = count_top == 2
305 local all_left = count_left == 2
306 local all_right = count_right == 2
307
308 if all_bottom or all_top then
309 -- Horizontal aligned, split width, full length
310 local group = all_bottom and bottom_turtles or top_turtles
311 sort_by_pos(group, "x")
312 local parts = divide_dim(total_width, 2)
313 local abs_sizeZ = total_length
314 for j = 1, 2 do
315 local idx = 0 -- find index in turtles
316 for k, tt in ipairs(turtles) do
317 if tt.corner == group[j].corner then idx = k break end
318 end
319 local abs_sizeX = parts[j]
320 local t = turtles[idx]
321 local info = corner_info[t.corner]
322 local is_left = (info.x == 0)
323 local desired_x_dir = is_left and 1 or -1
324 local facing_sign = t.facing
325 local sizeX_sign = desired_x_dir * facing_sign
326 local sizeZ_sign = facing_sign
327 local sizeZ = t.facing * abs_sizeZ
328 local sizeX = (corner_info[t.corner].x == 0) and abs_sizeX or (-abs_sizeX)
329 params_list[idx] = tostring(sizeZ) .. " " .. tostring(sizeX) .. " " .. tostring(total_depth) .. " " .. debug .. " " .. start_below .. " " .. auto_start
330 end
331 elseif all_left or all_right then
332 -- Vertical aligned, split length, full width
333 local group = all_left and left_turtles or right_turtles
334 sort_by_pos(group, "z")
335 local parts = divide_dim(total_length, 2)
336 local abs_sizeX = total_width
337 for j = 1, 2 do
338 local idx = 0
339 for k, tt in ipairs(turtles) do
340 if tt.corner == group[j].corner then idx = k break end
341 end
342 local abs_sizeZ = parts[j]
343 local t = turtles[idx]
344 local info = corner_info[t.corner]
345 local is_left = (info.x == 0)
346 local desired_x_dir = is_left and 1 or -1
347 local facing_sign = t.facing
348 local sizeX_sign = desired_x_dir * facing_sign
349 local sizeZ_sign = facing_sign
350 local sizeZ = t.facing * abs_sizeZ
351 local sizeX = (corner_info[t.corner].x == 0) and abs_sizeX or (-abs_sizeX)
352 params_list[idx] = tostring(sizeZ) .. " " .. tostring(sizeX) .. " " .. tostring(total_depth) .. " " .. debug .. " " .. start_below .. " " .. auto_start
353 end
354 else
355 -- Diagonal
356 local h_parts_l = divide_dim(total_length, 2)
357 local h_parts_w = divide_dim(total_width, 2)
358 for j = 1, 2 do
359 local t = turtles[j]
360 local c = t.corner
361 local info = corner_info[c]
362 local is_left = (info.x == 0)
363 local desired_x_dir = is_left and 1 or -1
364 local facing_sign = t.facing
365 local sizeX_sign = desired_x_dir * facing_sign
366 local sizeZ_sign = facing_sign
367 local abs_sizeZ = (corner_info[c].z == 0) and h_parts_l[1] or h_parts_l[2]
368 local abs_sizeX = (corner_info[c].x == 0) and h_parts_w[1] or h_parts_w[2]
369 -- Use facing direction for Z, position for X
370 local sizeZ = t.facing * abs_sizeZ -- facing determines Z direction
371 local sizeX = (corner_info[c].x == 0) and abs_sizeX or (-abs_sizeX) -- position determines X direction
372 params_list[j] = tostring(sizeZ) .. " " .. tostring(sizeX) .. " " .. tostring(total_depth) .. " " .. debug .. " " .. start_below .. " " .. auto_start
373 end
374 end
375
376elseif num == 3 then
377 -- Balanced split
378 local multiple_side, single_side, is_horizontal
379 if math.max(count_bottom, count_top) == 2 then
380 is_horizontal = true
381 elseif math.max(count_left, count_right) == 2 then
382 is_horizontal = false
383 else
384 is_horizontal = is_horizontal_pref
385 end
386
387 if is_horizontal then
388 -- Split horizontal, balanced
389 local multiple_group, single_group
390 if count_bottom == 2 then
391 multiple_group = bottom_turtles
392 single_group = top_turtles
393 multiple_is_bottom = true
394 else
395 multiple_group = top_turtles
396 single_group = bottom_turtles
397 multiple_is_bottom = false
398 end
399 sort_by_pos(multiple_group, "x")
400
401 local frac = 2 / 3
402 local z_multiple = math.floor(total_length * frac + 0.5)
403 local z_single = total_length - z_multiple
404 local w_multiple1 = math.floor(total_width / 2)
405 local w_multiple2 = total_width - w_multiple1
406 local w_single = total_width
407
408 -- Multiple side
409 for j = 1, 2 do
410 local tt = multiple_group[j]
411 local idx = 0
412 for k, u in ipairs(turtles) do
413 if u.corner == tt.corner then idx = k break end
414 end
415 local abs_sizeZ = z_multiple
416 local abs_sizeX = (j == 1) and w_multiple1 or w_multiple2
417 local sizeZ = turtles[idx].facing * abs_sizeZ
418 local sizeX = ( (corner_info[tt.corner].x == 0 and 1 or -1) * turtles[idx].facing ) * abs_sizeX
419 params_list[idx] = tostring(sizeZ) .. " " .. tostring(sizeX) .. " " .. tostring(total_depth) .. " " .. debug .. " " .. start_below .. " " .. auto_start
420 end
421
422 -- Single side
423 local tt = single_group[1]
424 local idx = 0
425 for k, u in ipairs(turtles) do
426 if u.corner == tt.corner then idx = k break end
427 end
428 local abs_sizeZ = z_single
429 local abs_sizeX = w_single
430 local sizeZ = turtles[idx].facing * abs_sizeZ
431 local sizeX = ( (corner_info[tt.corner].x == 0 and 1 or -1) * turtles[idx].facing ) * abs_sizeX
432 params_list[idx] = tostring(sizeZ) .. " " .. tostring(sizeX) .. " " .. tostring(total_depth) .. " " .. debug .. " " .. start_below .. " " .. auto_start
433 else
434 -- Vertical balanced
435 local multiple_group, single_group
436 if count_left == 2 then
437 multiple_group = left_turtles
438 single_group = right_turtles
439 multiple_is_left = true
440 else
441 multiple_group = right_turtles
442 single_group = left_turtles
443 multiple_is_left = false
444 end
445 sort_by_pos(multiple_group, "z")
446
447 local frac = 2 / 3
448 local w_multiple = math.floor(total_width * frac + 0.5)
449 local w_single = total_width - w_multiple
450 local l_multiple1 = math.floor(total_length / 2)
451 local l_multiple2 = total_length - l_multiple1
452
453 -- Multiple side
454 for j = 1, 2 do
455 local tt = multiple_group[j]
456 local idx = 0
457 for k, u in ipairs(turtles) do
458 if u.corner == tt.corner then idx = k break end
459 end
460 local abs_sizeX = w_multiple
461 local abs_sizeZ = (j == 1) and l_multiple1 or l_multiple2
462 local sizeZ = turtles[idx].facing * abs_sizeZ
463 local sizeX = ( ( (multiple_is_left and 1 or -1) ) * turtles[idx].facing ) * abs_sizeX
464 params_list[idx] = tostring(sizeZ) .. " " .. tostring(sizeX) .. " " .. tostring(total_depth) .. " " .. debug .. " " .. start_below .. " " .. auto_start
465 end
466
467 -- Single side
468 local tt = single_group[1]
469 local idx = 0
470 for k, u in ipairs(turtles) do
471 if u.corner == tt.corner then idx = k break end
472 end
473 local abs_sizeX = w_single
474 local abs_sizeZ = total_length
475 local sizeZ = turtles[idx].facing * abs_sizeZ
476 local sizeX = ( ( (corner_info[tt.corner].x == 0 and 1 or -1) ) * turtles[idx].facing ) * abs_sizeX
477 params_list[idx] = tostring(sizeZ) .. " " .. tostring(sizeX) .. " " .. tostring(total_depth) .. " " .. debug .. " " .. start_below .. " " .. auto_start
478 end
479
480elseif num == 4 then
481 -- Split both dimensions
482 local h_parts_l = divide_dim(total_length, 2)
483 local h_parts_w = divide_dim(total_width, 2)
484
485 for _, t in ipairs(turtles) do
486 local c = t.corner
487 local idx = 0
488 for k, u in ipairs(turtles) do
489 if u.corner == c then idx = k break end
490 end
491 local abs_sizeZ = (corner_info[c].z == 0) and h_parts_l[1] or h_parts_l[2]
492 local abs_sizeX = (corner_info[c].x == 0) and h_parts_w[1] or h_parts_w[2]
493 -- Calculate based on position and facing direction
494 -- All turtles dig toward quarry center using their facing direction
495 local sizeZ = t.facing * abs_sizeZ -- facing determines Z direction
496 local sizeX = (corner_info[c].x == 0) and abs_sizeX or (-abs_sizeX) -- position determines X direction
497 params_list[idx] = tostring(sizeZ) .. " " .. tostring(sizeX) .. " " .. tostring(total_depth) .. " " .. debug .. " " .. start_below .. " " .. auto_start
498 end
499end
500
501-- Summary
502print("\n=== Quarry Configuration Summary ===")
503print("Dimensions: " .. total_length .. " x " .. total_width .. " x " .. total_depth)
504print("Turtles: " .. num)
505for i = 1, num do
506 local t = turtles[i]
507 print(" Turtle " .. i .. ": " .. corner_info[t.corner].name .. " (ID: " .. t.id .. ") facing " .. (t.facing == 1 and "+Z" or "-Z"))
508end
509print("Start below: " .. (start_below == 1 and "Yes" or "No"))
510print("Debug mode: " .. (debug == 1 and "Yes" or "No"))
511print("Auto-start: " .. (auto_start == 1 and "Yes" or "No"))
512
513print("\nPress Enter to send commands to turtles, or Ctrl+T to cancel...")
514read()
515
516-- Send to each
517print("\n=== Sending Commands ===")
518for i = 1, num do
519 local id = turtles[i].id
520 local param = params_list[i]
521 local role = corner_info[turtles[i].corner].name .. " facing " .. (turtles[i].facing == 1 and "+Z" or "-Z")
522 local payload = { command = "RUN", program = "quarry", args = param, masterId = os.getComputerID(), role = role }
523
524 print("Sending to turtle ID " .. id .. " (" .. role .. "): quarry " .. param)
525 rednet.send(id, payload, "quarry-run")
526 rednet.send(id, params_list[i], "quarry-run") -- fallback with plain string
527end
528
529print("\nCommands sent! Turtles should start digging now.")
530print("Make sure each turtle is running 'quarry_listener' and has fuel in slot 1.")
531
532-- Note: For num=3, areas are approximately equal, but may differ slightly due to integer dimensions. Adjust total sizes for better balance if needed.
533