· 5 years ago · Nov 09, 2020, 09:16 AM
1--[[
2
3===================================
4 DRONES v4.1
5===================================
6
7Originally created by Pooslice <pooslice_yt@gmail.com> (replace underscore with dot)
8
9Changes to previous version:
10* (v4.1) Fixed a wrong loop-condition that prevented initial startup
11* Fixed a bug where drones could get confused if two controller turtles were used directly next to each other.
12* Finished turtles now get picked up by the main turtle at the end and are placed in their respective chest.
13* Introduced the 'force' parameter to start immediately without further user interaction.
14* Improved some of the pathfinding for handling unexpected blocks in the turtle's way during maintenance.
15* Improved fuel management during vertical digging (after hitting bedrock).
16* Reduced some waiting times.
17
18For more information read the forum post: http://www.computercraft.info/forums2/index.php?/topic/5241-14-the-lazy-mans-way-to-clear-a-chunk/
19]]--
20
21STARTUPTIME = os.clock()
22
23-- get call parameters
24local tArgs = { ... }
25
26-- get the script name and path. a bit hacky, but it works ...
27SCRIPTPATH = shell.getRunningProgram()
28if string.find(SCRIPTPATH, "/") then
29 SCRIPTNAME = string.reverse(SCRIPTPATH)
30 SCRIPTNAME = string.reverse(string.sub(SCRIPTNAME, 0, string.find(SCRIPTNAME, "/")-1))
31else
32 SCRIPTNAME = SCRIPTPATH
33end
34
35-- constants
36-- values below 100 make no sense as the distance to the refuel station will be that high already
37-- 4 coal per turtle -> one stack of coal initially plus one coal for the floppy setup
38REFUELTHRESHOLD = 96 * 4
39DEBUGMODE = false
40LOGFILENAME = nil
41-- untested and therefore not yet accessible
42DILIGENTMODE = false
43
44function debug(message)
45 if DEBUGMODE then print(message) end
46 if LOGFILENAME then
47 local fp = fs.open(LOGFILENAME, "a")
48 if fp then
49 fp.writeLine(message)
50 fp.close()
51 end
52 end
53end
54
55debug("Startup-Time: " .. STARTUPTIME)
56
57-- compensate for fuelless mode
58FUELLESS = false
59if turtle.getFuelLevel() == "unlimited" then
60 FUELLESS = true
61 turtle.getFuelLevel = function()
62 return 2 + REFUELTHRESHOLD
63 end
64end
65
66-- don't care where
67rednet.open("right")
68rednet.open("left")
69rednet.open("top")
70rednet.open("bottom")
71rednet.open("front")
72rednet.open("back")
73
74-- try to clear the space above the turtle
75function clearUp()
76 debug("clearUp()")
77 if not turtle.detectUp() then
78 while turtle.attackUp() do end
79 end
80 turtle.digUp()
81 return not turtle.detectUp()
82end
83
84-- see clearUp()
85function clearDown()
86 debug("clearDown()")
87 if not turtle.detectDown() then
88 while turtle.attackDown() do end
89 end
90 turtle.digDown()
91 return not turtle.detectDown()
92end
93
94-- see clearUp()
95function clearForward()
96 debug("clearForward()")
97 if not turtle.detect() then
98 while turtle.attack() do end
99 end
100 turtle.dig()
101 return not turtle.detect()
102end
103
104-- as clearUp(), but also tries to move in that direction
105function tryUp()
106 debug("tryUp()")
107 if turtle.up() then
108 return true
109 end
110 clearUp()
111 return turtle.up()
112end
113
114-- see tryUp()
115function tryDown()
116 debug("tryDown()")
117 if turtle.down() then
118 return true
119 end
120 clearDown()
121 return turtle.down()
122end
123
124-- see tryUp()
125-- with our digging strategy a maximum of 2 blocks must be digged out (one normal and one if it is falling sand or gravel)
126-- we add one try in case something went wrong or there was some lag
127function tryForward()
128 debug("tryForward()")
129 for i = 1, 3 do
130 if turtle.forward() then
131 return true
132 end
133 if not turtle.detect() then
134 while turtle.attack() do end
135 end
136 turtle.dig()
137 end
138 return turtle.forward()
139end
140
141-- try to use one item from the inventory as fuel
142function refuelOneFromInventory()
143 for i = 1, 16 do
144 local fl = turtle.getFuelLevel()
145 turtle.select(i)
146 turtle.refuel(1)
147 if turtle.getFuelLevel() > fl then
148 return true
149 end
150 end
151 return false
152end
153
154-- check if turtle's inventory is completely full
155function isInventoryFull()
156 debug("isInventoryFull()")
157 for i = 1, 16 do
158 if turtle.getItemCount(i) == 0 then
159 return false
160 end
161 end
162 return true
163end
164
165-- return to our chest, drop everything there and refuel if necessary
166-- returns new x, y, z and face which should be the same as when calling the function
167function maintenance(refuelId, x, y, z, face)
168 debug("maintenance(" .. refuelId .. ", " .. x .. ", " .. y .. ", " .. z .. ", ??)")
169 local dx, dy, dz, dface = x, y, z, face
170 -- move below our chest
171 while y > 4 do
172 if tryUp() then
173 y = y - 1
174 end
175 end
176 -- could come across other turtles -> no digging
177 while y > 0 do
178 while not turtle.up() do sleep(1) end
179 y = y - 1
180 end
181 if face then
182 turtle.turnRight()
183 turtle.turnRight()
184 end
185 while x > 0 do
186 while not turtle.forward() do sleep(1) end
187 x = x - 1
188 end
189 turtle.turnRight()
190 turtle.turnRight()
191 -- Try to refuel from inventory if necessary
192 while turtle.getFuelLevel() < REFUELTHRESHOLD and refuelOneFromInventory() do
193 debug("Refuelled one from inventory. New level: " .. turtle.getFuelLevel())
194 end
195 -- drop all into the chest - overflow is ignored
196 for i = 1, 16 do
197 turtle.select(i)
198 turtle.dropUp()
199 end
200 -- refuel from station if still necessary
201 if turtle.getFuelLevel() < REFUELTHRESHOLD then
202 debug("Refuel needed: " .. turtle.getFuelLevel() .. " < " .. REFUELTHRESHOLD)
203 debug("Going to refuel at the station")
204 -- go to fuel station
205 -- from now on we could cross other turtles' lanes and will therefore only move, not dig
206 while not turtle.forward() do sleep(1) end
207 turtle.turnRight()
208 while z > 0 do
209 while not turtle.forward() do sleep(1) end
210 z = z - 1
211 end
212 turtle.turnRight()
213 turtle.turnRight()
214 while not turtle.up() do sleep(1) end
215 while not turtle.up() do sleep(1) end
216 -- refuel
217 while true do
218 -- refuel all and send fuel level until > REFUELTHRESHOLD
219 for i = 1, 16 do
220 turtle.select(i)
221 turtle.refuel()
222 end
223 local message = {}
224 message["message"] = "fuelLevel"
225 message["fuelLevel"] = turtle.getFuelLevel()
226 rednet.send(refuelId, textutils.serialize(message))
227 if message["fuelLevel"] > REFUELTHRESHOLD then
228 break
229 end
230 end
231 debug("Refuelling at station successful. Returning to lane.")
232 -- go back to lane
233 if dz == 0 then
234 turtle.turnRight()
235 while not turtle.forward() do sleep(1) end
236 while not turtle.down() do sleep(1) end
237 while not turtle.down() do sleep(1) end
238 while not turtle.down() do sleep(1) end
239 while not turtle.back() do sleep(1) end
240 while not turtle.back() do sleep(1) end
241 x = 0
242 y = 1
243 z = 0
244 else
245 while z < dz do
246 while not turtle.forward() do sleep(1) end
247 z = z + 1
248 end
249 turtle.turnRight()
250 while not turtle.down() do sleep(1) end
251 while not turtle.down() do sleep(1) end
252 y = 0
253 while not turtle.back() do sleep(1) end
254 x = 0
255 end
256 end
257 face = true
258 -- go back to old position
259 debug("Returning to old position before maintenance-call.")
260 while x < dx do
261 while not turtle.forward() do sleep(1) end
262 x = x + 1
263 end
264 if face ~= dface then
265 turtle.turnRight()
266 turtle.turnRight()
267 face = dface
268 end
269 while y < dy and y < 4 do
270 while not turtle.down() do sleep(1) end
271 y = y + 1
272 end
273 while y < dy do
274 while not tryDown() do sleep(1) end
275 y = y + 1
276 end
277 return x, y, z, face
278end
279
280function diglane(lane, refuelId)
281 debug("digLane(" .. lane .. ", " .. refuelId .. ")")
282 -- x is forward (lane direction), y is depth below start (positive int), z is lane, true means facing in positive x direction
283 local x, y, z, face = 1, 0, lane, true
284 -- near bedrock we switch the digging strategy to vertical shafts instead of horizontal tunnels
285 local digVertical = false
286 -- big loop with many if-statements that magically does the right next step
287 while true do
288 -- check if we have enough fuel for returning to the station
289 local distToRefuelStation = y + x + 1 + z + 2
290 -- check if we have enough fuel for clearing the next row and returning to the refuel station (if not mining vertically)
291 -- 15 forward, 3 down, 15 backward, 3 up
292 local distToRefuelStationAfterNextLane = 15 + 3 + 15 + 3 + distToRefuelStation
293 debug("Digging ... distToRefuelStation: " .. distToRefuelStation .. ", Fuel: " .. turtle.getFuelLevel())
294 if turtle.getFuelLevel() - distToRefuelStation < 5 then -- some safety margin that was chosen arbitrarily
295 debug("Trying to refuel from inventory.")
296 -- try to refuel from inventory
297 if not refuelOneFromInventory() then
298 debug("Inventory was not enough. Starting maintenance routine.")
299 -- did not work or was not enough -> start maintenance routine
300 x, y, z, face = maintenance(refuelId, x, y, z, face)
301 end
302 elseif (x == 0) and (not digVertical) and (turtle.getFuelLevel() - distToRefuelStationAfterNextLane < 3) then
303 debug("Trying to refuel from inventory.")
304 -- try to refuel from inventory
305 if not refuelOneFromInventory() then
306 debug("Inventory was not enough. Starting maintenance routine.")
307 -- did not work or was not enough -> start maintenance routine
308 x, y, z, face = maintenance(refuelId, x, y, z, face)
309 end
310 elseif isInventoryFull() then
311 debug("Inventory is full. Starting maintenance routine.")
312 x, y, z, face = maintenance(refuelId, x, y, z, face)
313 else
314 -- dig
315 if not digVertical then
316 if y % 3 > 0 then
317 if tryDown() then
318 y = y + 1
319 else
320 debug("Hit the ground. Will go to vertical mode.")
321 digVertical = "start"
322 end
323 else
324 if not clearUp() then
325 debug("Seems, we moved one too far. Going back.")
326 -- can only be the case if we accidently moved into a bedrock "gap"
327 turtle.turnRight()
328 turtle.turnRight()
329 tryForward()
330 turtle.turnRight()
331 turtle.turnRight()
332 if face then
333 x = x - 1
334 else
335 x = x + 1
336 end
337 if tryUp() then
338 y = y - 1
339 end
340 debug("And starting vertical mode.")
341 digVertical = "start"
342 else
343 if (not clearDown()) or (not tryForward()) then
344 debug("Could not clear down or go forward. Starting vertical mode.")
345 digVertical = "start"
346 else
347 if face then
348 x = x + 1
349 else
350 x = x - 1
351 end
352 clearUp()
353 if x == 0 or x == 15 then
354 turtle.turnRight()
355 turtle.turnRight()
356 face = not face
357 if not tryDown() then
358 debug("At a border and could not go down. Starting vertical mode.")
359 digVertical = "start"
360 else
361 y = y + 1
362 end
363 end
364 end
365 end
366 end
367 else
368 if digVertical == "start" then
369 -- make next step towards x15 with face towards x0
370 if not face then
371 turtle.turnRight()
372 turtle.turnRight()
373 face = not face
374 end
375 if x < 15 then
376 debug("Moving towards the end of the lane.")
377 if not tryForward() then
378 if tryUp() then
379 y = y - 1
380 else
381 -- need to backtrack
382 debug("Moved into a gap. Backtracking ...")
383 turtle.turnRight()
384 turtle.turnRight()
385 if tryForward() then
386 x = x - 1
387 end
388 if tryUp() then
389 y = y - 1
390 end
391 turtle.turnRight()
392 turtle.turnRight()
393 end
394 else
395 x = x + 1
396 end
397 else
398 debug("In position for vertical digging")
399 digVertical = "digVertical"
400 turtle.turnRight()
401 turtle.turnRight()
402 face = not face
403 end
404 elseif digVertical == "digVertical" then
405 -- because we are in the last stages and hit bedrock, we have a clearer estimate (upper bound) of how much fuel we need for the rest
406 if (z == 0) then
407 -- as below, but the cost for returning to the lane is 6 (see maintenance routine)
408 REFUELTHRESHOLD = 10 * x + 2 * y + 14
409 else
410 -- back to lane, 2 down, 1 back, x forward, y down, at most 4 down and 4 up, 4 up plus 4 down plus 1 forward per unfinished field, then y to our chest
411 -- REFUELTHRESHOLD = z + 2 + 1 + x + y + 4 + 4 + x * (1 + 4 + 4) + y
412 REFUELTHRESHOLD = 10 * x + 2 * y + z + 11
413 end
414 if tryDown() then
415 y = y + 1
416 else
417 if x > 0 then
418 debug("At the bottom. Going to next field.")
419 -- bedrock has 5 layers at most
420 -- also clear any 'gaps' we might encounter when diligent mode is enabled
421 if DILIGENTMODE then
422 for i = 1, 3 do
423 clearForward()
424 turtle.turnRight()
425 turtle.turnRight()
426 clearForward()
427 while not tryUp() do sleep(1) end
428 end
429 turtle.turnRight()
430 turtle.turnRight()
431 while not tryUp() do sleep(1) end
432 else
433 while not tryUp() do sleep(1) end
434 while not tryUp() do sleep(1) end
435 while not tryUp() do sleep(1) end
436 while not tryUp() do sleep(1) end
437 end
438 y = y - 4
439 while not tryForward() do sleep(1) end
440 x = x - 1
441 else
442 debug("Cleared the last block. Returning now.")
443 break
444 end
445 end
446 end
447 end
448 end
449 end
450 -- TODO: DILIGENTMODE ...
451
452 -- we should be at x0. so we go up and drop everything to our chest
453 while y > 4 do
454 while not tryUp() do sleep(1) end
455 y = y - 1
456 end
457 -- could cross other turtles' paths -> no digging
458 while y > 0 do
459 while not turtle.up() do sleep(1) end
460 y = y - 1
461 end
462 debug("Dropping everything into the chest.")
463 for i = 1, 16 do
464 if turtle.getItemCount(i) > 0 then
465 turtle.select(i)
466 turtle.dropUp()
467 end
468 end
469 turtle.turnRight()
470 turtle.turnRight()
471 face = not face
472 -- send finished to control
473 local m = {}
474 m["message"] = "laneFinished"
475 rednet.send(refuelId, textutils.serialize(m))
476end
477
478function setuplane()
479 -- copy program from disk to local dir
480 fs.copy(SCRIPTPATH, SCRIPTNAME)
481
482 turtle.turnLeft()
483
484 local lane, refuelId
485 while true do
486 -- say hello
487 local broadcast = {}
488 broadcast["message"] = "hello"
489 rednet.broadcast(textutils.serialize(broadcast))
490 -- get welcome with our lane number
491 local id, message, distance = rednet.receive(5)
492 if message and distance < 1.4 then
493 local m = {}
494 m = textutils.unserialize(message)
495 if m["message"] == "welcome" and m["lane"] > -1 then
496 lane = m["lane"] + 0
497 refuelId = id
498 break
499 end
500 end
501 end
502
503 -- send fuel level, then get and use one fuel
504 for i = 1, 16 do
505 if turtle.getItemCount(i) == 0 then
506 turtle.select(i)
507 break
508 end
509 end
510 local message = {}
511 message["message"] = "fuelLevel"
512 message["fuelLevel"] = turtle.getFuelLevel()
513 message["lane"] = lane
514 rednet.send(refuelId, textutils.serialize(message))
515 local chestSlot
516 while true do
517 -- refuel all and send fuel level until > REFUELTHRESHOLD
518 while (not FUELLESS) and (not refuelOneFromInventory()) do sleep(1) end
519 -- select lowest empty slot (to detect chest placement later on)
520 for i = 1, 16 do
521 if turtle.getItemCount(i) == 0 then
522 chestSlot = i
523 break
524 end
525 end
526 message["fuelLevel"] = turtle.getFuelLevel()
527 rednet.send(refuelId, textutils.serialize(message))
528 if message["fuelLevel"] > REFUELTHRESHOLD then
529 break
530 end
531 end
532 -- get chest
533 while turtle.getItemCount(chestSlot) < 1 do sleep(1) end
534 -- only the first turtle needs to clear a path
535 if lane == 15 then
536 while not tryDown() do sleep(1) end
537 else
538 while not turtle.down() do sleep(1) end
539 end
540 -- dig/go to our lane
541 for i = 1, lane do
542 if lane == 15 then
543 while not tryForward() do sleep(1) end
544 else
545 while not turtle.forward() do sleep(1) end
546 end
547 end
548 turtle.turnRight()
549 -- place chest
550 turtle.select(chestSlot)
551 clearUp()
552 clearDown()
553 turtle.placeUp()
554 while not tryForward() do sleep(1) end
555 if lane == 0 then
556 turtle.suckUp()
557 end
558
559 -- dig lane
560 shell.run(SCRIPTNAME, "diglane", lane, refuelId)
561end
562
563function refuelStation(workers)
564 debug("refuelStation: " .. textutils.serialize(workers))
565 if not workers then
566 workers = {}
567 for i = 0, 15 do
568 workers[i] = -1
569 end
570 end
571 print("Waiting for refuel requests. Will automatically exit if all lanes are complete. Press SPACE to exit early.")
572 print()
573 while true do
574 local allDone = true
575 for i = 0, 15 do
576 if workers[i] > -1 then
577 allDone = false
578 break
579 end
580 end
581 if allDone then
582 print("All lanes are done.")
583 break
584 end
585 local event, id, message, distance = os.pullEvent()
586 if event == "rednet_message" and distance < 1.4 and message then
587 local m = {}
588 m = textutils.unserialize(message)
589 if m["message"] == "fuelLevel" and m["fuelLevel"] <= REFUELTHRESHOLD then
590 for i = 5, 16 do
591 if turtle.getItemCount(i) > 0 then
592 turtle.select(i)
593 turtle.drop(1)
594 break
595 end
596 end
597 end
598 elseif event == "rednet_message" and message then
599 local m = {}
600 m = textutils.unserialize(message)
601 if m["message"] == "laneFinished" then
602 for i = 0, 15 do
603 if workers[i] == id then
604 debug("Received laneFinished from " .. id .. " and cleared workers[" .. i .. "]")
605 workers[i] = -1
606 break
607 end
608 end
609 end
610 elseif event == "key" and id == 57 then
611 break
612 end
613 end
614
615 -- clearup the drones (place them into their chest)
616 tryForward()
617 turtle.turnRight()
618 turtle.turnRight()
619 while not tryDown() do sleep(1) end
620 while not tryDown() do sleep(1) end
621 while not tryForward() do sleep(1) end
622 turtle.turnRight()
623 for i = 1, 16 do
624 if turtle.getItemCount(i) > 0 then
625 turtle.select(i)
626 turtle.dropUp()
627 end
628 end
629 turtle.select(1)
630 for i = 1, 15 do
631 while not tryForward() do sleep(1) end
632 turtle.dropUp()
633 end
634end
635
636function setup(force)
637 local force = force or false
638
639 print("Please put ...")
640 print("... 16 wireless mining turtles in slot 1.")
641 print("... 16 iron or better chests in slot 2.")
642 print("... a floppy disk in slot 3.")
643 print("... a disk drive in slot 4.")
644 print("... fuel of any kind in slots 5-16.")
645 print()
646 print("Press RETURN to start or SPACE to exit.")
647 print()
648
649 while not force do
650 local event, id, message = os.pullEvent()
651 if event == "key" and id == 28 then
652 break
653 elseif event == "key" and id == 57 then
654 return
655 end
656 end
657
658 -- need fuel to place the floppy and cleanup after finish
659 while turtle.getFuelLevel() < 23 do
660 for i = 1, 16 do
661 turtle.select(i)
662 if turtle.refuel(1) then
663 break
664 end
665 end
666 sleep(1)
667 end
668
669 -- need some space below me
670 if not tryDown() then
671 print("No space below me")
672 return
673 end
674
675 -- clear the floppy block
676 turtle.turnRight()
677 if not tryForward() then
678 print("Could not clear block in front for the floppy.")
679 return
680 end
681 while not turtle.back() do sleep(1) end
682
683 -- place the disk drive and insert floppy
684 turtle.select(4)
685 while not turtle.place() do sleep(1) end
686 turtle.select(3)
687 while not turtle.drop() do sleep(1) end
688
689 -- write startup file to floppy
690 fs.copy(SCRIPTPATH, "disk/" .. SCRIPTNAME)
691 local fp = fs.open("disk/startup", "w")
692 fp.writeLine("shell.run(\"disk/".. SCRIPTNAME .. "\", \"setuplane\")")
693 fp.close()
694
695 -- move back to our position
696 while not tryUp() do sleep(1) end
697
698 -- list of worker computerIDs
699 local workers = {}
700
701 -- place, refuel and send off 16 drones
702 for i = 0, 15 do
703 print("Placing drone " .. i + 1 .. " of 16.")
704 print()
705 -- place the drone
706 turtle.select(1)
707 while turtle.detectDown() do sleep(1) end
708 turtle.placeDown()
709 -- boot the drone, thanks to dustpuppy for reminding me about the peripheral API
710 peripheral.call("bottom", "turnOn")
711 -- wait for announce, send lane number, wait for confirmation
712 -- the drone id is stored in the workers table
713 while true do
714 local id, message, distance = rednet.receive(5)
715 local m = {}
716 if message then
717 m = textutils.unserialize(message)
718 end
719 if m["message"] == "hello" and distance < 1.4 then
720 local answer = {}
721 answer["message"] = "welcome"
722 answer["lane"] = 15 - i
723 rednet.send(id, textutils.serialize(answer))
724 elseif m and m["message"] == "fuelLevel" and m["lane"] == 15 - i then
725 workers[15 - i] = id
726 break
727 end
728 end
729 -- refuel drone to at least the threshold
730 if not FUELLESS then
731 while true do
732 for j = 5, 16 do
733 if turtle.getItemCount(j) > 0 then
734 turtle.select(j)
735 turtle.dropDown(1)
736 break
737 end
738 end
739 local id, message, distance = rednet.receive(5)
740 local m = {}
741 if message then
742 m = textutils.unserialize(message)
743 end
744 if m["message"] == "fuelLevel" and m["fuelLevel"] > REFUELTHRESHOLD then
745 break
746 end
747 end
748 end
749 -- place chest in drone inventory
750 turtle.select(2)
751 turtle.dropDown(1)
752 end
753
754 -- All drones placed. We switch to refuelling mode
755 shell.run(SCRIPTNAME, "refuelstation", textutils.serialize(workers))
756end
757
758function printUsage()
759 print("Usage of " .. SCRIPTNAME .. " command:")
760 print()
761 print(SCRIPTNAME .. " setup|force|setuplane|diglane <laneNo> <refuelId>|refuelstation <serializedIdList>")
762 print("If you are not sure, you probably want the setup.")
763 print()
764end
765
766-- =========================================
767-- MAIN
768-- =========================================
769
770if tArgs[1] == "setup" then
771 setup()
772elseif tArgs[1] == "force" then
773 setup(true)
774elseif tArgs[1] == "setuplane" then
775 setuplane()
776elseif tArgs[1] == "diglane" and tArgs[2] and tArgs[3] and tArgs[2] + 0 > -1 and tArgs[3] + 0 > -1 then
777 diglane(tArgs[2] + 0, tArgs[3] + 0)
778elseif tArgs[1] == "refuelstation" and tArgs[2] then
779 refuelStation(textutils.unserialize(tArgs[2]))
780else
781 printUsage()
782end
783
784rednet.close("right")
785rednet.close("left")
786rednet.close("top")
787rednet.close("bottom")
788rednet.close("front")
789rednet.close("back")
790
791ENDTIME = os.clock()
792print("Starttime:" .. STARTUPTIME)
793print("Endtime: " .. ENDTIME)