View difference between Paste ID: dQxJJAas and Byq5cPNe
SHOW: | | - or go back to the newest paste.
1
-- ********************************************************************************** --
2
-- **                                                                              ** --
3
-- **   Minecraft Mining Turtle Ore Quarry v0.71 by AustinKK (Modified by WillisS) ** --
4
-- **   (Add fuel control and broadcast location function)                         ** --
5
-- **   ----------------------------------------------------                       ** --
6
-- **                                                                              ** --
7
-- **   For instructions on how to use:                                            ** --
8
-- **                                                                              ** --
9
-- **     http://www.youtube.com/watch?v=PIugLVzUz3g                               ** --
10
-- **                                                                              ** --
11
-- **  Change Log:                                                                 ** --
12
-- **    27th Dec 2012: [v0.2] Initial Draft Release                               ** --
13
-- **    29th Dec 2012: [v0.3] Minor Performance Improvements                      ** --
14
-- **    30th Dec 2012: [v0.4] Further Performance Improvements                    ** --
15
-- **    9th  Jan 2013: [v0.5] Debug Version (dropping off chest)                  ** --
16
-- **    10th Jan 2013: [v0.51] Further Debug (dropping off chest)                 ** --
17
-- **    10th Jan 2013: [v0.52] Fix for dropping off chest bug                     ** --
18
-- **    11th Jan 2013: [v0.53] Fix for dropping off chest bug (release)           ** --
19
-- **    12th Jan 2013: [v0.6] Added support for resume                            ** --
20
-- **    31st Mar 2013: [v0.7] Fixes for ComputerCraft 1.52                        ** --
21
-- **    25th Aug 2013: [v0.71] Support ComputerCraft 1.56 and Chunk Loader Module ** --
22
-- **                                                                              ** --
23
-- ********************************************************************************** --
24
25
-- ********************************************************************************** --
26
-- Note: If you are in a world with flat bedrock, change the value below from 5 to 2.
27
--       You don't need to change this, but the turtle is slightly faster if you do.
28
-- ********************************************************************************** --
29
local bottomLayer = 2 -- The y co-ords of the layer immediately above bedrock
30
local useBlackList = true
31
32
local blacklist = {}
33
if useBlackList then
34
	local blacklistArray = {
35
		"minecraft:air",
36
		"minecraft:bedrock",
37
		"minecraft:cobblestone",
38
		"minecraft:dirt",
39
		"minecraft:ice",
40
		"minecraft:ladder",
41
		"minecraft:snow",
42
		"minecraft:snow_layer",
43
		"minecraft:stone",
44
		"minecraft:grass",
45
		"minecraft:torch"
46
	}
47
	for a, b in pairs(blacklistArray) do
48
		blacklist[b] = true
49
	end
50
end
51
52
-- Enumeration to store the the different types of message that can be written
53
messageLevel = {DEBUG = 0, INFO = 1, WARNING = 2, ERROR = 3, FATAL = 4}
54
55
-- Enumeration to store names for the 6 directions
56
direction = {FORWARD = 0, RIGHT = 1, BACK = 2, LEFT = 3, UP = 4, DOWN = 5}
57
58
-- Enumeration of mining states
59
miningState = {START = 0, LAYER = 1, EMPTYCHESTDOWN = 2, EMPTYINVENTORY = 3}
60
61
local messageOutputLevel = messageLevel.INFO
62
local messageOutputFileName
63
local fuelLevelToRefuelAt = 100
64
local refuelItemsToUseWhenRefuelling = 20
65
local emergencyFuelToRetain = 3
66
local maximumGravelStackSupported = 25 -- The number of stacked gravel or sand blocks supported
67
local noiseBlocksCount
68
local returningToStart = false
69
local lookForChests = false -- Determines if chests should be located as part of the quarrying
70
local miningOffset  -- The offset to the mining layer. This is set depending on whether chests are being looked for or not
71
local lastEmptySlot  -- The last inventory slot that was empty when the program started (is either 15 if not looking for chests or 14 if we are)
72
local turtleId
73
local isWirelessTurtle
74
local currentlySelectedSlot = 0 -- The slot that the last noise block was found in
75
local lastMoveNeededDig = true -- Determines whether the last move needed a dig first
76
local haveBeenAtZeroZeroOnLayer  -- Determines whether the turtle has been at (0, 0) in this mining layer
77
local orientationAtZeroZero  -- The turtle's orientation when it was at (0, 0)
78
local levelToReturnTo  -- The level that the turtle should return to in order to head back to the start to unload
79
local isGPSlost  -- whether GPS is lost
80
81
-- Variables used to support a resume
82
local startupParamsFile = "OreQuarryParams.txt"
83
local oreQuarryLocation = "OreQuarryLocation.txt"
84
local returnToStartFile = "OreQuarryReturn.txt"
85
local startupBackup = "startup_bak"
86
local supportResume = true -- Determines whether the turtle is being run in the mode that supports resume
87
local resuming = false -- Determines whether the turtle is currently in the process of resuming
88
local resumeX
89
local resumeY
90
local resumeZ
91
local resumeOrient
92
local resumeMiningState
93
94
-- Variables to store the current location and orientation of the turtle. x is right, left, y is up, down and
95
-- z is forward, back with relation to the starting orientation. Y is the actual turtle level, x and z are
96
-- in relation to the starting point (i.e. the starting point is (0, 0))
97
local currX
98
local currY
99
local currZ
100
local currOrient
101
local currMiningState = miningState.START
102
103
-- Command line parameters
104
local startHeight  -- Represents the height (y co-ord) that the turtle started at
105
local quarryWidth  -- Represents the length of the mines that the turtle will dig
106
107
-- ********************************************************************************** --
108
-- Writes an output message
109
-- ********************************************************************************** --
110
function writeMessage(message, msgLevel)
111
	if (msgLevel >= messageOutputLevel) then
112
		print(message)
113
114
		-- If this turtle has a modem, then write the message to red net
115
		if (isWirelessTurtle == true) then
116
			if (turtleId == nil) then
117
				rednet.broadcast(message)
118
			else
119
				-- Broadcast the message (prefixed with the turtle's id)
120
				rednet.broadcast("[" .. turtleId .. "] " .. message)
121
			end
122
		end
123
124
		if (messageOutputFileName ~= nil) then
125
			-- Open file, write message and close file (flush doesn't seem to work!)
126
			local outputFile
127
			if (fs.exists(messageOutputFileName) == true) then
128
				outputFile = io.open(messageOutputFileName, "a")
129
			else
130
				outputFile = io.open(messageOutputFileName, "w")
131
			end
132
133
			outputFile:write(message)
134
			outputFile:write("\n")
135
			outputFile:close()
136
		end
137
	end
138
end
139
140
-- ********************************************************************************** --
141
-- Ensures that the turtle has fuel
142
-- ********************************************************************************** --
143
function ensureFuel()
144
	-- Determine whether a refuel is required
145
	local fuelLevel = turtle.getFuelLevel()
146
	if (fuelLevel ~= "unlimited") then
147
		if (fuelLevel < fuelLevelToRefuelAt) then
148
			-- Need to refuel
149
			turtle.select(16)
150
			currentlySelectedSlot = 16
151
			local fuelItems = turtle.getItemCount(16)
152
153
			-- Do we need to impact the emergency fuel to continue? (always
154
			-- keep one fuel item in slot 16)
155
			if (fuelItems == 0) then
156
				writeMessage("Completely out of fuel!", messageLevel.FATAL)
157
			elseif (fuelItems == 1) then
158
				writeMessage("Out of Fuel!", messageLevel.ERROR)
159
				turtle.refuel()
160
			elseif (fuelItems <= (emergencyFuelToRetain + 1)) then
161
				writeMessage(
162
					"Consuming emergency fuel supply. " .. (fuelItems - 2) .. " emergency fuel items remain",
163
					messageLevel.WARNING
164
				)
165
				turtle.refuel(1)
166
				if returningToStart ~= true then
167
					returnToStartAndUnload(true)
168
				end
169
			else
170
				-- Refuel the lesser of the refuelItemsToUseWhenRefuelling and the number of items more than
171
				-- the emergency fuel level
172
				if (fuelItems - (emergencyFuelToRetain + 1) < refuelItemsToUseWhenRefuelling) then
173
					turtle.refuel(fuelItems - (emergencyFuelToRetain + 1))
174
				else
175
					turtle.refuel(refuelItemsToUseWhenRefuelling)
176
				end
177
			end
178
		end
179
	end
180
end
181
182
-- ********************************************************************************** --
183
-- Checks that the turtle has inventory space by checking for spare slots and returning
184
-- to the starting point to empty out if it doesn't.
185
--
186
-- Takes the position required to move to in order to empty the turtle's inventory
187
-- should it be full as arguments
188
-- ********************************************************************************** --
189
function ensureInventorySpace()
190
	-- If already returning to start, then don't need to do anything
191
	if (returningToStart == false) then
192
		-- If the last inventory slot is full, then need to return to the start and empty
193
		if (turtle.getItemCount(lastEmptySlot) > 0) then
194
			-- Return to the starting point and empty the inventory, then go back to mining
195
			returnToStartAndUnload(true)
196
		end
197
	end
198
end
199
200
-- ********************************************************************************** --
201
-- Function to move to the starting point, call a function that is passed in
202
-- and return to the same location (if required)
203
-- ********************************************************************************** --
204
function returnToStartAndUnload(returnBackToMiningPoint)
205
	writeMessage("returnToStartAndUnload called", messageLevel.DEBUG)
206
	returningToStart = true
207
	local storedX, storedY, storedZ, storedOrient
208
	local prevMiningState = currMiningState
209
210
	if (resuming == true) then
211
		-- Get the stored parameters from the necessary file
212
		local resumeFile = fs.open(returnToStartFile, "r")
213
		if (resumeFile ~= nil) then
214
			-- Restore the parameters from the file
215
			local beenAtZero = resumeFile.readLine()
216
			if (beenAtZero == "y") then
217
				haveBeenAtZeroZeroOnLayer = true
218
			else
219
				haveBeenAtZeroZeroOnLayer = false
220
			end
221
222
			local miningPointFlag = resumeFile.readLine()
223
			if (miningPointFlag == "y") then
224
				returnBackToMiningPoint = true
225
			else
226
				returnBackToMiningPoint = false
227
			end
228
229
			currX = readNumber(resumeFile)
230
			currY = readNumber(resumeFile)
231
			currZ = readNumber(resumeFile)
232
			currOrient = readNumber(resumeFile)
233
			levelToReturnTo = readNumber(resumeFile)
234
			prevMiningState = readNumber(resumeFile)
235
			orientationAtZeroZero = readNumber(resumeFile)
236
			resumeFile.close()
237
		else
238
			writeMessage("Failed to read return to start file", messageLevel.ERROR)
239
		end
240
	elseif (supportResume == true) then
241
		local outputFile = io.open(returnToStartFile, "w")
242
243
		if (haveBeenAtZeroZeroOnLayer == true) then
244
			outputFile:write("y\n")
245
		else
246
			outputFile:write("n\n")
247
		end
248
		if (returnBackToMiningPoint == true) then
249
			outputFile:write("y\n")
250
		else
251
			outputFile:write("n\n")
252
		end
253
254
		outputFile:write(currX)
255
		outputFile:write("\n")
256
		outputFile:write(currY)
257
		outputFile:write("\n")
258
		outputFile:write(currZ)
259
		outputFile:write("\n")
260
		outputFile:write(currOrient)
261
		outputFile:write("\n")
262
		outputFile:write(levelToReturnTo)
263
		outputFile:write("\n")
264
		outputFile:write(prevMiningState)
265
		outputFile:write("\n")
266
		outputFile:write(orientationAtZeroZero)
267
		outputFile:write("\n")
268
269
		outputFile:close()
270
	end
271
272
	storedX = currX
273
	storedY = currY
274
	storedZ = currZ
275
	storedOrient = currOrient
276
277
	-- Store the current location and orientation so that it can be returned to
278
	currMiningState = miningState.EMPTYINVENTORY
279
	writeMessage("last item count = " .. turtle.getItemCount(lastEmptySlot), messageLevel.DEBUG)
280
281
	if ((turtle.getItemCount(lastEmptySlot) > 0) or (returnBackToMiningPoint == false)) then
282
		writeMessage("Heading back to surface", messageLevel.DEBUG)
283
284
		-- Move down to the correct layer to return via
285
		if (currY > levelToReturnTo) then
286
			while (currY > levelToReturnTo) do
287
				turtleDown()
288
			end
289
		elseif (currY < levelToReturnTo) then
290
			while (currY < levelToReturnTo) do
291
				turtleUp()
292
			end
293
		end
294
295
		if ((haveBeenAtZeroZeroOnLayer == false) or (orientationAtZeroZero == direction.FORWARD)) then
296
			-- Move back to the correct X position first
297
			if (currX > 0) then
298
				turtleSetOrientation(direction.LEFT)
299
				while (currX > 0) do
300
					turtleForward()
301
				end
302
			elseif (currX < 0) then
303
				-- This should never happen
304
				writeMessage("Current x is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
305
			end
306
307
			-- Then move back to the correct Z position
308
			if (currZ > 0) then
309
				turtleSetOrientation(direction.BACK)
310
				while (currZ > 0) do
311
					turtleForward()
312
				end
313
			elseif (currZ < 0) then
314
				-- This should never happen
315
				writeMessage("Current z is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
316
			end
317
		else
318
			-- Move back to the correct Z position first
319
			if (currZ > 0) then
320
				turtleSetOrientation(direction.BACK)
321
				while (currZ > 0) do
322
					turtleForward()
323
				end
324
			elseif (currZ < 0) then
325
				-- This should never happen
326
				writeMessage("Current z is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
327
			end
328
329
			-- Then move back to the correct X position
330
			if (currX > 0) then
331
				turtleSetOrientation(direction.LEFT)
332
				while (currX > 0) do
333
					turtleForward()
334
				end
335
			elseif (currX < 0) then
336
				-- This should never happen
337
				writeMessage("Current x is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
338
			end
339
		end
340
341
		-- Return to the starting layer
342
		if (currY < startHeight) then
343
			while (currY < startHeight) do
344
				turtleUp()
345
			end
346
		elseif (currY > startHeight) then
347
			-- This should never happen
348
			writeMessage("Current height is greater than start height in returnToStartAndUnload", messageLevel.ERROR)
349
		end
350
351
		-- Empty the inventory
352
		local slotLoop = 1
353
354
		-- Face the chest
355
		turtleSetOrientation(direction.BACK)
356
357
		-- Loop over each of the slots (except the 16th one which stores fuel)
358
		while (slotLoop < 16) do
359
			-- If this is one of the slots that contains a noise block, empty all blocks except
360
			-- one
361
			turtle.select(slotLoop) -- Don't bother updating selected slot variable as it will set later in this function
362
			if ((slotLoop <= noiseBlocksCount) or ((slotLoop == 15) and (lastEmptySlot == 14))) then
363
				writeMessage(
364
					"Dropping (n-1) from slot " .. slotLoop .. " [" .. turtle.getItemCount(slotLoop) .. "]",
365
					messageLevel.DEBUG
366
				)
367
				if (turtle.getItemCount(slotLoop) > 0) then
368
					turtle.drop(turtle.getItemCount(slotLoop) - 1)
369
				end
370
			else
371
				-- Not a noise block, drop all of the items in this slot
372
				writeMessage(
373
					"Dropping (all) from slot " .. slotLoop .. " [" .. turtle.getItemCount(slotLoop) .. "]",
374
					messageLevel.DEBUG
375
				)
376
				if (turtle.getItemCount(slotLoop) > 0) then
377
					turtle.drop()
378
				end
379
			end
380
381
			slotLoop = slotLoop + 1
382
		end
383
384
		-- While we are here, refill the fuel items if there is capacity
385
		if (turtle.getItemCount(16) < 64) then
386
			turtleSetOrientation(direction.LEFT)
387
			turtle.select(16) -- Don't bother updating selected slot variable as it will set later in this function
388
			local currFuelItems = turtle.getItemCount(16)
389
			turtle.suck()
390
			while ((currFuelItems ~= turtle.getItemCount(16)) and (turtle.getItemCount(16) < 64)) do
391
				currFuelItems = turtle.getItemCount(16)
392
				turtle.suck()
393
			end
394
395
			slotLoop = noiseBlocksCount + 1
396
			-- Have now picked up all the items that we can. If we have also picked up some
397
			-- additional fuel in some of the other slots, then drop it again
398
			while (slotLoop <= lastEmptySlot) do
399
				-- Drop any items found in this slot
400
				if (turtle.getItemCount(slotLoop) > 0) then
401
					turtle.select(slotLoop) -- Don't bother updating selected slot variable as it will set later in this function
402
					turtle.drop()
403
				end
404
				slotLoop = slotLoop + 1
405
			end
406
		end
407
408
		-- Select the 1st slot because sometimes when leaving the 15th or 16th slots selected it can result
409
		-- in that slot being immediately filled (resulting in the turtle returning to base again too soon)
410
		turtle.select(1)
411
		currentlySelectedSlot = 1
412
	end
413
414
	-- If required, move back to the point that we were mining at before returning to the start
415
	if (returnBackToMiningPoint == true) then
416
		-- If resuming, refresh the starting point to be the top of the return shaft
417
		if (resuming == true) then
418
			currX = 0
419
			currY = startHeight
420
			currZ = 0
421
			currOrient = resumeOrient
422
		end
423
424
		-- Return back to the required layer
425
		while (currY > levelToReturnTo) do
426
			turtleDown()
427
		end
428
429
		if ((haveBeenAtZeroZeroOnLayer == false) or (orientationAtZeroZero == direction.FORWARD)) then
430
			-- Move back to the correct Z position first
431
			writeMessage("Stored Z: " .. storedZ .. ", currZ: " .. currZ, messageLevel.DEBUG)
432
			if (storedZ > currZ) then
433
				writeMessage("Orienting forward", messageLevel.DEBUG)
434
				writeMessage("Moving in z direction", messageLevel.DEBUG)
435
				turtleSetOrientation(direction.FORWARD)
436
				while (storedZ > currZ) do
437
					turtleForward()
438
				end
439
			elseif (storedZ < currZ) then
440
				-- This should never happen
441
				writeMessage("Stored z is less than current z in returnToStartAndUnload", messageLevel.ERROR)
442
			end
443
444
			-- Then move back to the correct X position
445
			if (storedX > currX) then
446
				writeMessage("Stored X: " .. storedX .. ", currX: " .. currX, messageLevel.DEBUG)
447
				writeMessage("Orienting right", messageLevel.DEBUG)
448
				writeMessage("Moving in x direction", messageLevel.DEBUG)
449
				turtleSetOrientation(direction.RIGHT)
450
				while (storedX > currX) do
451
					turtleForward()
452
				end
453
			elseif (storedX < currX) then
454
				-- This should never happen
455
				writeMessage("Stored x is less than current x in returnToStartAndUnload", messageLevel.ERROR)
456
			end
457
		else
458
			-- Move back to the correct X position first
459
			if (storedX > currX) then
460
				writeMessage("Stored X: " .. storedX .. ", currX: " .. currX, messageLevel.DEBUG)
461
				writeMessage("Orienting right", messageLevel.DEBUG)
462
				writeMessage("Moving in x direction", messageLevel.DEBUG)
463
				turtleSetOrientation(direction.RIGHT)
464
				while (storedX > currX) do
465
					turtleForward()
466
				end
467
			elseif (storedX < currX) then
468
				-- This should never happen
469
				writeMessage("Stored x is less than current x in returnToStartAndUnload", messageLevel.ERROR)
470
			end
471
472
			-- Then move back to the correct Z position
473
			writeMessage("Stored Z: " .. storedZ .. ", currZ: " .. currZ, messageLevel.DEBUG)
474
			if (storedZ > currZ) then
475
				writeMessage("Orienting forward", messageLevel.DEBUG)
476
				writeMessage("Moving in z direction", messageLevel.DEBUG)
477
				turtleSetOrientation(direction.FORWARD)
478
				while (storedZ > currZ) do
479
					turtleForward()
480
				end
481
			elseif (storedZ < currZ) then
482
				-- This should never happen
483
				writeMessage("Stored z is less than current z in returnToStartAndUnload", messageLevel.ERROR)
484
			end
485
		end
486
487
		-- Move back to the correct layer
488
		if (storedY < currY) then
489
			while (storedY < currY) do
490
				turtleDown()
491
			end
492
		elseif (storedY > currY) then
493
			while (storedY > currY) do
494
				turtleUp()
495
			end
496
		end
497
498
		-- Finally, set the correct orientation
499
		turtleSetOrientation(storedOrient)
500
501
		writeMessage("Have returned to the mining point", messageLevel.DEBUG)
502
	end
503
504
	-- Store the current location and orientation so that it can be returned to
505
	currMiningState = prevMiningState
506
507
	returningToStart = false
508
end
509
510
-- ********************************************************************************** --
511
-- Empties a chest's contents
512
-- ********************************************************************************** --
513
function emptyChest(suckFn)
514
	local prevInventoryCount = {}
515
	local inventoryLoop
516
	local chestEmptied = false
517
518
	-- Record the number of items in each of the inventory slots
519
	for inventoryLoop = 1, 16 do
520
		prevInventoryCount[inventoryLoop] = turtle.getItemCount(inventoryLoop)
521
	end
522
523
	while (chestEmptied == false) do
524
		-- Pick up the next item
525
		suckFn()
526
527
		-- Determine the number of items in each of the inventory slots now
528
		local newInventoryCount = {}
529
		for inventoryLoop = 1, 16 do
530
			newInventoryCount[inventoryLoop] = turtle.getItemCount(inventoryLoop)
531
		end
532
533
		-- Now, determine whether there have been any items taken from the chest
534
		local foundDifferentItemCount = false
535
		inventoryLoop = 1
536
		while ((foundDifferentItemCount == false) and (inventoryLoop <= 16)) do
537
			if (prevInventoryCount[inventoryLoop] ~= newInventoryCount[inventoryLoop]) then
538
				foundDifferentItemCount = true
539
			else
540
				inventoryLoop = inventoryLoop + 1
541
			end
542
		end
543
544
		-- If no items have been found with a different item count, then the chest has been emptied
545
		chestEmptied = not foundDifferentItemCount
546
547
		if (chestEmptied == false) then
548
			prevInventoryCount = newInventoryCount
549
			-- Check that there is sufficient inventory space as may have picked up a block
550
			ensureInventorySpace()
551
		end
552
	end
553
554
	writeMessage("Finished emptying chest", messageLevel.DEBUG)
555
end
556
557
-- ********************************************************************************** --
558
-- Write the current location to a file
559
-- ********************************************************************************** --
560
function saveLocation()
561
	-- Write the x, y, z and orientation to the file
562
	if ((supportResume == true) and (resuming == false)) then
563
		local outputFile = io.open(oreQuarryLocation, "w")
564
		outputFile:write(currMiningState)
565
		outputFile:write("\n")
566
		outputFile:write(currX)
567
		outputFile:write("\n")
568
		outputFile:write(currY)
569
		outputFile:write("\n")
570
		outputFile:write(currZ)
571
		outputFile:write("\n")
572
		outputFile:write(currOrient)
573
		outputFile:write("\n")
574
		outputFile:close()
575
	end
576
end
577
578
-- ********************************************************************************** --
579
-- If the turtle is resuming and the current co-ordinates, orientation and
580
-- mining state have been matched, then no longer resuming
581
-- ********************************************************************************** --
582
function updateResumingFlag()
583
	if (resuming == true) then
584
		if
585
			((resumeMiningState == currMiningState) and (resumeX == currX) and (resumeY == currY) and (resumeZ == currZ) and
586
				(resumeOrient == currOrient))
587
		 then
588
			resuming = false
589
		end
590
	end
591
end
592
593
-- ********************************************************************************** --
594
-- Generic function to move the Turtle (pushing through any gravel or other
595
-- things such as mobs that might get in the way).
596
--
597
-- The only thing that should stop the turtle moving is bedrock. Where this is
598
-- found, the function will return after 15 seconds returning false
599
-- ********************************************************************************** --
600
function moveTurtle(moveFn, detectFn, digFn, attackFn, inspectFunct, suckFn, maxDigCount, newX, newY, newZ)
601
	local moveSuccess = false
602
603
	-- If we are resuming, then don't do anything in this function other than updating the
604
	-- co-ordinates as if the turtle had moved
605
	if (resuming == true) then
606
		-- Set the move success to true (but don't move) - unless this is below bedrock level
607
		-- in which case return false
608
		if (currY <= 0) then
609
			moveSuccess = false
610
		else
611
			moveSuccess = true
612
		end
613
614
		-- Update the co-ordinates to reflect the movement
615
		currX = newX
616
		currY = newY
617
		currZ = newZ
618
	else
619
		local prevX, prevY, prevZ
620
		prevX = currX
621
		prevY = currY
622
		prevZ = currZ
623
624
		ensureFuel()
625
626
		-- Flag to determine whether digging has been tried yet. If it has
627
		-- then pause briefly before digging again to allow sand or gravel to
628
		-- drop
629
		local digCount = 0
630
631
		if (lastMoveNeededDig == false) then
632
			-- Don't need to set the last move needed dig. It is already false, if
633
			-- move success is now true, then it won't be changed
634
			-- Didn't need to dig last time the turtle moved, so try moving first
635
636
			currX = newX
637
			currY = newY
638
			currZ = newZ
639
			saveLocation()
640
641
			moveSuccess = moveFn()
642
643
			-- If move failed, update the co-ords back to the previous co-ords
644
			if (moveSuccess == false) then
645
				currX = prevX
646
				currY = prevY
647
				currZ = prevZ
648
				saveLocation()
649
			end
650
		else
651
			-- If we are looking for chests, then check that this isn't a chest before trying to dig it
652
			if (lookForChests == true) then
653
				if (isNoiseBlock(inspectFunct) == false) then
654
					if (detectFn() == true) then
655
						-- Determine if it is a chest before digging it
656
						if (isChestBlock(inspectFunct) == true) then
657
							-- Have found a chest, empty it before continuing
658
							emptyChest(suckFn)
659
						end
660
					end
661
				end
662
			end
663
664
			-- Try to dig (without doing a detect as it is quicker)
665
			local digSuccess = digFn()
666
			if (digSuccess == true) then
667
				digCount = 1
668
			end
669
670
			currX = newX
671
			currY = newY
672
			currZ = newZ
673
			saveLocation()
674
675
			moveSuccess = moveFn()
676
677
			if (moveSuccess == true) then
678
				lastMoveNeededDig = digSuccess
679
			else
680
				currX = prevX
681
				currY = prevY
682
				currZ = prevZ
683
				saveLocation()
684
			end
685
		end
686
687
		-- Loop until we've successfully moved
688
		if (moveSuccess == false) then
689
			while ((moveSuccess == false) and (digCount < maxDigCount)) do
690
				-- If there is a block in front, dig it
691
				if (detectFn() == true) then
692
					-- If we've already tried digging, then pause before digging again to let
693
					-- any sand or gravel drop, otherwise check for a chest before digging
694
					if (digCount == 0) then
695
						-- Am about to dig a block - check that it is not a chest if necessary
696
						-- If we are looking for chests, then check that this isn't a chest before moving
697
						if (lookForChests == true) then
698
							if (isNoiseBlock(inspectFunct) == false) then
699
								if (detectFn() == true) then
700
									-- Determine if it is a chest before digging it
701
									if (isChestBlock(inspectFunct) == true) then
702
										-- Have found a chest, empty it before continuing
703
										emptyChest(suckFn)
704
									end
705
								end
706
							end
707
						end
708
					else
709
						sleep(0.1)
710
					end
711
712
					digFn()
713
					digCount = digCount + 1
714
				else
715
					-- Am being stopped from moving by a mob, attack it
716
					attackFn()
717
				end
718
719
				currX = newX
720
				currY = newY
721
				currZ = newZ
722
				saveLocation()
723
724
				-- Try the move again
725
				moveSuccess = moveFn()
726
727
				if (moveSuccess == false) then
728
					currX = prevX
729
					currY = prevY
730
					currZ = prevZ
731
					saveLocation()
732
				end
733
			end
734
735
			if (digCount == 0) then
736
				lastMoveNeededDig = false
737
			else
738
				lastMoveNeededDig = true
739
			end
740
		end
741
	end
742
743
	-- If we are resuming and the current co-ordinates and orientation are the resume point
744
	-- then are no longer resuming
745
	if (moveSuccess == true) then
746
		updateResumingFlag()
747
		-- broadcast gps location"
748
		if (isWirelessTurtle == true) then
749
			local x, y, z = gps.locate()
750
			if (x ~= nil and y ~= nil and z ~= nil) then
751
				isGPSlost = nil
752
				rednet.broadcast(turtleId .. " is now at (" .. x .. ", " .. y .. ", " .. z .. ")")
753
			else
754
				-- only broadcast lost signal once
755
				if (isGPSlost == nil) then
756
					rednet.broadcast(turtleId .. ": GPS lost")
757
				end
758
				isGPSlost = true
759
			end
760
		end
761
	end
762
763
	-- Return the move success
764
	return moveSuccess
765
end
766
767
-- ********************************************************************************** --
768
-- Move the turtle forward one block (updating the turtle's position)
769
-- ********************************************************************************** --
770
function turtleForward()
771
	-- Determine the new co-ordinate that the turtle will be moving to
772
	local newX, newZ
773
774
	-- Update the current co-ordinates
775
	if (currOrient == direction.FORWARD) then
776
		newZ = currZ + 1
777
		newX = currX
778
	elseif (currOrient == direction.LEFT) then
779
		newX = currX - 1
780
		newZ = currZ
781
	elseif (currOrient == direction.BACK) then
782
		newZ = currZ - 1
783
		newX = currX
784
	elseif (currOrient == direction.RIGHT) then
785
		newX = currX + 1
786
		newZ = currZ
787
	else
788
		writeMessage("Invalid currOrient in turtleForward function", messageLevel.ERROR)
789
	end
790
791
	local returnVal =
792
		moveTurtle(
793
		turtle.forward,
794
		turtle.detect,
795
		turtle.dig,
796
		turtle.attack,
797
		turtle.inspect,
798
		turtle.suck,
799
		maximumGravelStackSupported,
800
		newX,
801
		currY,
802
		newZ
803
	)
804
805
	if (returnVal == true) then
806
		-- Check that there is sufficient inventory space as may have picked up a block
807
		ensureInventorySpace()
808
	end
809
810
	return returnVal
811
end
812
813
-- ********************************************************************************** --
814
-- Move the turtle up one block (updating the turtle's position)
815
-- ********************************************************************************** --
816
function turtleUp()
817
	local returnVal =
818
		moveTurtle(
819
		turtle.up,
820
		turtle.detectUp,
821
		turtle.digUp,
822
		turtle.attackUp,
823
		turtle.inspectUp,
824
		turtle.suckUp,
825
		maximumGravelStackSupported,
826
		currX,
827
		currY + 1,
828
		currZ
829
	)
830
831
	if (returnVal == true) then
832
		-- Check that there is sufficient inventory space as may have picked up a block
833
		ensureInventorySpace()
834
	end
835
836
	return returnVal
837
end
838
839
-- ********************************************************************************** --
840
-- Move the turtle down one block (updating the turtle's position)
841
-- ********************************************************************************** --
842
function turtleDown()
843
	local returnVal =
844
		moveTurtle(
845
		turtle.down,
846
		turtle.detectDown,
847
		turtle.digDown,
848
		turtle.attackDown,
849
		turtle.inspectDown,
850
		turtle.suckDown,
851
		1,
852
		currX,
853
		currY - 1,
854
		currZ
855
	)
856
857
	if (returnVal == true) then
858
		-- Check that there is sufficient inventory space as may have picked up a block
859
		ensureInventorySpace()
860
	end
861
862
	return returnVal
863
end
864
865
-- ********************************************************************************** --
866
-- Move the turtle back one block (updating the turtle's position)
867
-- ********************************************************************************** --
868
function turtleBack()
869
	-- Assume that the turtle will move, and switch the co-ords back if it doesn't
870
	-- (do this so that we can write the co-ords to a file before moving)
871
	local newX, newZ
872
	local prevX, prevZ
873
	prevX = currX
874
	prevZ = currZ
875
876
	-- Update the current co-ordinates
877
	if (currOrient == direction.FORWARD) then
878
		newZ = currZ - 1
879
		newX = currX
880
	elseif (currOrient == direction.LEFT) then
881
		newX = currX + 1
882
		newZ = currZ
883
	elseif (currOrient == direction.BACK) then
884
		newZ = currZ + 1
885
		newX = currX
886
	elseif (currOrient == direction.RIGHT) then
887
		newX = currX - 1
888
		newZ = currZ
889
	else
890
		writeMessage("Invalid currOrient in turtleBack function", messageLevel.ERROR)
891
	end
892
893
	-- First try to move back using the standard function
894
895
	currX = newX
896
	currZ = newZ
897
	saveLocation()
898
	local returnVal = turtle.back()
899
900
	if (returnVal == false) then
901
		-- Didn't move. Reset the co-ordinates to the previous value
902
		currX = prevX
903
		currZ = prevZ
904
905
		-- Reset the location back to the previous location (because the turn takes 0.8 of a second
906
		-- so could be stopped before getting to the forward function)
907
		saveLocation()
908
909
		turtle.turnRight()
910
		turtle.turnRight()
911
912
		-- Try to move by using the forward function (note, the orientation will be set as
913
		-- the same way as this function started because if the function stops, that is the
914
		-- direction that we want to consider the turtle to be pointing)
915
916
		returnVal =
917
			moveTurtle(
918
			turtle.forward,
919
			turtle.detect,
920
			turtle.dig,
921
			turtle.attack,
922
			turtle.inspect,
923
			turtle.suck,
924
			maximumGravelStackSupported,
925
			newX,
926
			currY,
927
			newZ
928
		)
929
930
		turtle.turnRight()
931
		turtle.turnRight()
932
	end
933
934
	if (returnVal == true) then
935
		-- Check that there is sufficient inventory space as may have picked up a block
936
		ensureInventorySpace()
937
	end
938
939
	return returnVal
940
end
941
942
-- ********************************************************************************** --
943
-- Turns the turtle (updating the current orientation at the same time)
944
-- ********************************************************************************** --
945
function turtleTurn(turnDir)
946
	if (turnDir == direction.LEFT) then
947
		if (currOrient == direction.FORWARD) then
948
			currOrient = direction.LEFT
949
		elseif (currOrient == direction.LEFT) then
950
			currOrient = direction.BACK
951
		elseif (currOrient == direction.BACK) then
952
			currOrient = direction.RIGHT
953
		elseif (currOrient == direction.RIGHT) then
954
			currOrient = direction.FORWARD
955
		else
956
			writeMessage("Invalid currOrient in turtleTurn function", messageLevel.ERROR)
957
		end
958
959
		-- If we are resuming, just check to see whether have reached the resume point, otherwise
960
		-- turn
961
		if (resuming == true) then
962
			updateResumingFlag()
963
		else
964
			-- Write the new orientation and turn
965
			saveLocation()
966
			turtle.turnLeft()
967
		end
968
	elseif (turnDir == direction.RIGHT) then
969
		if (currOrient == direction.FORWARD) then
970
			currOrient = direction.RIGHT
971
		elseif (currOrient == direction.LEFT) then
972
			currOrient = direction.FORWARD
973
		elseif (currOrient == direction.BACK) then
974
			currOrient = direction.LEFT
975
		elseif (currOrient == direction.RIGHT) then
976
			currOrient = direction.BACK
977
		else
978
			writeMessage("Invalid currOrient in turtleTurn function", messageLevel.ERROR)
979
		end
980
981
		-- If we are resuming, just check to see whether have reached the resume point, otherwise
982
		-- turn
983
		if (resuming == true) then
984
			updateResumingFlag()
985
986
			writeMessage(
987
				"[" .. currMiningState .. ", " .. currX .. ", " .. currY .. ", " .. currZ .. ", " .. currOrient .. "]",
988
				messageLevel.DEBUG
989
			)
990
		else
991
			-- Write the new orientation and turn
992
			saveLocation()
993
			turtle.turnRight()
994
		end
995
	else
996
		writeMessage("Invalid turnDir in turtleTurn function", messageLevel.ERROR)
997
	end
998
end
999
1000
-- ********************************************************************************** --
1001
-- Sets the turtle to a specific orientation, irrespective of its current orientation
1002
-- ********************************************************************************** --
1003
function turtleSetOrientation(newOrient)
1004
	if (currOrient ~= newOrient) then
1005
		if (currOrient == direction.FORWARD) then
1006
			if (newOrient == direction.RIGHT) then
1007
				currOrient = newOrient
1008
1009
				-- If resuming, check whether the resume point has been reached, otherwise turn
1010
				if (resuming == true) then
1011
					updateResumingFlag()
1012
				else
1013
					-- Write the new orientation and turn
1014
					saveLocation()
1015
					turtle.turnRight()
1016
				end
1017
			elseif (newOrient == direction.BACK) then
1018
				currOrient = newOrient
1019
1020
				-- If resuming, check whether the resume point has been reached, otherwise turn
1021
				if (resuming == true) then
1022
					updateResumingFlag()
1023
				else
1024
					-- Write the new orientation and turn
1025
					saveLocation()
1026
					turtle.turnRight()
1027
					turtle.turnRight()
1028
				end
1029
			elseif (newOrient == direction.LEFT) then
1030
				currOrient = newOrient
1031
1032
				-- If resuming, check whether the resume point has been reached, otherwise turn
1033
				if (resuming == true) then
1034
					updateResumingFlag()
1035
				else
1036
					-- Write the new orientation and turn
1037
					saveLocation()
1038
					turtle.turnLeft()
1039
				end
1040
			else
1041
				writeMessage("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
1042
			end
1043
		elseif (currOrient == direction.RIGHT) then
1044
			if (newOrient == direction.BACK) then
1045
				currOrient = newOrient
1046
1047
				-- If resuming, check whether the resume point has been reached, otherwise turn
1048
				if (resuming == true) then
1049
					updateResumingFlag()
1050
				else
1051
					-- Write the new orientation and turn
1052
					saveLocation()
1053
					turtle.turnRight()
1054
				end
1055
			elseif (newOrient == direction.LEFT) then
1056
				currOrient = newOrient
1057
1058
				-- If resuming, check whether the resume point has been reached, otherwise turn
1059
				if (resuming == true) then
1060
					updateResumingFlag()
1061
				else
1062
					-- Write the new orientation and turn
1063
					saveLocation()
1064
					turtle.turnRight()
1065
					turtle.turnRight()
1066
				end
1067
			elseif (newOrient == direction.FORWARD) then
1068
				currOrient = newOrient
1069
1070
				-- If resuming, check whether the resume point has been reached, otherwise turn
1071
				if (resuming == true) then
1072
					updateResumingFlag()
1073
				else
1074
					-- Write the new orientation and turn
1075
					saveLocation()
1076
					turtle.turnLeft()
1077
				end
1078
			else
1079
				writeMessage("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
1080
			end
1081
		elseif (currOrient == direction.BACK) then
1082
			if (newOrient == direction.LEFT) then
1083
				currOrient = newOrient
1084
1085
				-- If resuming, check whether the resume point has been reached, otherwise turn
1086
				if (resuming == true) then
1087
					updateResumingFlag()
1088
				else
1089
					-- Write the new orientation and turn
1090
					saveLocation()
1091
					turtle.turnRight()
1092
				end
1093
			elseif (newOrient == direction.FORWARD) then
1094
				currOrient = newOrient
1095
1096
				-- If resuming, check whether the resume point has been reached, otherwise turn
1097
				if (resuming == true) then
1098
					updateResumingFlag()
1099
				else
1100
					-- Write the new orientation and turn
1101
					saveLocation()
1102
					turtle.turnRight()
1103
					turtle.turnRight()
1104
				end
1105
			elseif (newOrient == direction.RIGHT) then
1106
				currOrient = newOrient
1107
1108
				-- If resuming, check whether the resume point has been reached, otherwise turn
1109
				if (resuming == true) then
1110
					updateResumingFlag()
1111
				else
1112
					-- Write the new orientation and turn
1113
					saveLocation()
1114
					turtle.turnLeft()
1115
				end
1116
			else
1117
				writeMessage("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
1118
			end
1119
		elseif (currOrient == direction.LEFT) then
1120
			if (newOrient == direction.FORWARD) then
1121
				currOrient = newOrient
1122
1123
				-- If resuming, check whether the resume point has been reached, otherwise turn
1124
				if (resuming == true) then
1125
					updateResumingFlag()
1126
				else
1127
					-- Write the new orientation and turn
1128
					saveLocation()
1129
					turtle.turnRight()
1130
				end
1131
			elseif (newOrient == direction.RIGHT) then
1132
				currOrient = newOrient
1133
1134
				-- If resuming, check whether the resume point has been reached, otherwise turn
1135
				if (resuming == true) then
1136
					updateResumingFlag()
1137
				else
1138
					-- Write the new orientation and turn
1139
					saveLocation()
1140
					turtle.turnRight()
1141
					turtle.turnRight()
1142
				end
1143
			elseif (newOrient == direction.BACK) then
1144
				currOrient = newOrient
1145
1146
				-- If resuming, check whether the resume point has been reached, otherwise turn
1147
				if (resuming == true) then
1148
					updateResumingFlag()
1149
				else
1150
					-- Write the new orientation and turn
1151
					saveLocation()
1152
					turtle.turnLeft()
1153
				end
1154
			else
1155
				writeMessage("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
1156
			end
1157
		else
1158
			writeMessage("Invalid currOrient in turtleTurn function", messageLevel.ERROR)
1159
		end
1160
	end
1161
end
1162
1163
-- ********************************************************************************** --
1164
-- Determines if a particular block is considered a noise block or not. A noise
1165
-- block is one that is a standard block in the game (stone, dirt, gravel etc.) and
1166
-- is one to ignore as not being an ore. Function works by comparing the block
1167
-- in question against a set of blocks in the turtle's inventory which are known not to
1168
-- be noise blocks. Param is the function to use to compare the block for a noise block
1169
-- ********************************************************************************** --
1170
function isNoiseBlock(inspectFunct)
1171
	local returnVal = false
1172
1173
	if (resuming == true) then
1174
		returnVal = true
1175
	else
1176
		local success, blockData = inspectFunct()
1177
		returnVal = blacklist[blockData.name] or false
1178
	end
1179
1180
	-- Return the calculated value
1181
	return returnVal
1182
end
1183
1184
-- ********************************************************************************** --
1185
-- Determines if a particular block is a chest. Returns false if it is not a chest
1186
-- or chests are not being detected
1187
-- ********************************************************************************** --
1188
function isChestBlock(inspectFunct)
1189
	-- Check the block in the appropriate direction to see whether it is a chest. Only
1190
	-- do this if we are looking for chests
1191
	local returnVal = false
1192
	if (lookForChests == true) then
1193
		turtle.select(15)
1194
		currentlySelectedSlot = 15
1195
		returnVal = inspectFunct()
1196
	end
1197
1198
	-- Return the calculated value
1199
	return returnVal
1200
end
1201
1202
-- ********************************************************************************** --
1203
-- Function to calculate the number of non seam blocks in the turtle's inventory. This
1204
-- is all of the blocks at the start of the inventory (before the first empty slot is
1205
-- found
1206
-- ********************************************************************************** --
1207
function determineNoiseBlocksCountCount()
1208
	-- Determine the location of the first empty inventory slot. All items before this represent
1209
	-- noise items.
1210
	local foundFirstBlankInventorySlot = false
1211
	noiseBlocksCount = 1
1212
	while ((noiseBlocksCount < 16) and (foundFirstBlankInventorySlot == false)) do
1213
		if (turtle.getItemCount(noiseBlocksCount) > 0) then
1214
			noiseBlocksCount = noiseBlocksCount + 1
1215
		else
1216
			foundFirstBlankInventorySlot = true
1217
		end
1218
	end
1219
	noiseBlocksCount = noiseBlocksCount - 1
1220
1221
	-- Determine whether a chest was provided, and hence whether we should support
1222
	-- looking for chests
1223
	if (turtle.getItemCount(15) > 0) then
1224
		lookForChests = true
1225
		lastEmptySlot = 14
1226
		miningOffset = 0
1227
		writeMessage("Looking for chests...", messageLevel.DEBUG)
1228
	else
1229
		lastEmptySlot = 15
1230
		miningOffset = 1
1231
		writeMessage("Ignoring chests...", messageLevel.DEBUG)
1232
	end
1233
end
1234
1235
-- ********************************************************************************** --
1236
-- Creates a quarry mining out only ores and leaving behind any noise blocks
1237
-- ********************************************************************************** --
1238
function createQuarry()
1239
	-- Determine the top mining layer layer. The turtle mines in layers of 3, and the bottom layer
1240
	-- is the layer directly above bedrock.
1241
	--
1242
	-- The actual layer that the turtle operates in is the middle of these three layers,
1243
	-- so determine the top layer
1244
	local topMiningLayer = startHeight + ((bottomLayer - startHeight - 2) % 3) - 1 + miningOffset
1245
1246
	-- If the top layer is up, then ignore it and move to the next layer
1247
	if (topMiningLayer > currY) then
1248
		topMiningLayer = topMiningLayer - 3
1249
	end
1250
1251
	local startedLayerToRight = true -- Only used where the quarry is of an odd width
1252
1253
	-- Loop over each mining row
1254
	local miningLevel
1255
	for miningLevel = (bottomLayer + miningOffset), topMiningLayer, 3 do
1256
		writeMessage("Mining Layer: " .. miningLevel, messageLevel.INFO)
1257
		haveBeenAtZeroZeroOnLayer = false
1258
1259
		-- While the initial shaft is being dug out, set the level to return to in order to unload
1260
		-- to the just take the turtle straight back up
1261
		if (miningLevel == (bottomLayer + miningOffset)) then
1262
			levelToReturnTo = startHeight
1263
		end
1264
1265
		-- Move to the correct level to start mining
1266
		if (currY > miningLevel) then
1267
			while (currY > miningLevel) do
1268
				turtleDown()
1269
			end
1270
		elseif (currY < miningLevel) then
1271
			while (currY < miningLevel) do
1272
				turtleUp()
1273
			end
1274
		end
1275
1276
		-- Am now mining the levels (update the mining state to reflect that fact)
1277
		currMiningState = miningState.LAYER
1278
1279
		-- Set the layer to return via when returning to the surface as the one below the currently
1280
		-- mined one
1281
		if (miningLevel == (bottomLayer + miningOffset)) then
1282
			levelToReturnTo = (bottomLayer + miningOffset)
1283
		else
1284
			levelToReturnTo = miningLevel - 3
1285
		end
1286
1287
		-- Move turtle into the correct orientation to start mining (if this is the
1288
		-- first row to be mined, then don't need to turn, otherwise turn towards the next
1289
		-- mining section)
1290
1291
		writeMessage(
1292
			"Mining Level: " .. miningLevel .. ", Bottom Layer: " .. bottomLayer .. ", Mining Offset: " .. miningOffset,
1293
			messageLevel.DEBUG
1294
		)
1295
1296
		if (miningLevel > (bottomLayer + miningOffset)) then
1297
			-- Turn towards the next mining layer
1298
			if (quarryWidth % 2 == 0) then
1299
				-- An even width quarry, always turn right
1300
				turtleTurn(direction.RIGHT)
1301
			else
1302
				-- Turn the opposite direction to that which we turned before
1303
				if (startedLayerToRight == true) then
1304
					turtleTurn(direction.LEFT)
1305
					startedLayerToRight = false
1306
				else
1307
					turtleTurn(direction.RIGHT)
1308
					startedLayerToRight = true
1309
				end
1310
			end
1311
		end
1312
1313
		local mineRows
1314
		local onNearSideOfQuarry = true
1315
		local diggingAway = true
1316
		for mineRows = 1, quarryWidth do
1317
			-- If this is not the first row, then get into position to mine the next row
1318
			if ((mineRows == 1) and (lookForChests == false)) then
1319
				-- Not looking for chests, check the block below for being an ore. Only do this
1320
				-- if we're not looking for chests since the program doesn't support chests in
1321
				-- bedrock
1322
				if (isNoiseBlock(turtle.inspectDown) == false) then
1323
					turtle.digDown()
1324
					ensureInventorySpace()
1325
				end
1326
			elseif (mineRows > 1) then
1327
				-- Move into position for mining the next row
1328
				if (onNearSideOfQuarry == diggingAway) then
1329
					if (startedLayerToRight == true) then
1330
						turtleTurn(direction.LEFT)
1331
					else
1332
						turtleTurn(direction.RIGHT)
1333
					end
1334
				else
1335
					if (startedLayerToRight == true) then
1336
						turtleTurn(direction.RIGHT)
1337
					else
1338
						turtleTurn(direction.LEFT)
1339
					end
1340
				end
1341
1342
				turtleForward()
1343
1344
				-- Before making the final turn, check the block below. Do this
1345
				-- now because if it is a chest, then we want to back up and
1346
				-- approach it from the side (so that we don't lose items if we
1347
				-- have to return to the start through it).
1348
				--
1349
				-- This is the point at which it is safe to back up without moving
1350
				-- out of the quarry area (unless at bedrock in which case don't bother
1351
				-- as we'll be digging down anyway)
1352
				if (miningLevel ~= bottomLayer) then
1353
					if (isNoiseBlock(turtle.inspectDown) == false) then
1354
						-- If we are not looking for chests, then just dig it (it takes
1355
						-- less time to try to dig and fail as it does to do detect and
1356
						-- only dig if there is a block there)
1357
						if (lookForChests == false) then
1358
							turtle.digDown()
1359
							ensureInventorySpace()
1360
						elseif (turtle.detectDown() == true) then
1361
							if (isChestBlock(turtle.inspectDown) == true) then
1362
								-- There is a chest block below. Move back and approach
1363
								-- from the side to ensure that we don't need to return to
1364
								-- start through the chest itself (potentially losing items)
1365
								turtleBack()
1366
								turtleDown()
1367
								currMiningState = miningState.EMPTYCHESTDOWN
1368
								emptyChest(turtle.suck)
1369
								currMiningState = miningState.LAYER
1370
								turtleUp()
1371
								turtleForward()
1372
								turtle.digDown()
1373
								ensureInventorySpace()
1374
							else
1375
								turtle.digDown()
1376
								ensureInventorySpace()
1377
							end
1378
						end
1379
					end
1380
				end
1381
1382
				-- Move into final position for mining the next row
1383
				if (onNearSideOfQuarry == diggingAway) then
1384
					if (startedLayerToRight == true) then
1385
						turtleTurn(direction.LEFT)
1386
					else
1387
						turtleTurn(direction.RIGHT)
1388
					end
1389
				else
1390
					if (startedLayerToRight == true) then
1391
						turtleTurn(direction.RIGHT)
1392
					else
1393
						turtleTurn(direction.LEFT)
1394
					end
1395
				end
1396
			end
1397
1398
			-- Dig to the other side of the quarry
1399
			local blocksMined
1400
			for blocksMined = 0, (quarryWidth - 1) do
1401
				if (blocksMined > 0) then
1402
					-- Only move forward if this is not the first space
1403
					turtleForward()
1404
				end
1405
1406
				-- If the current block is (0,0), then record the fact that the
1407
				-- turtle has been through this block and what it's orientation was and update the layer
1408
				-- that it should return via to get back to the surface (it no longer needs to go down
1409
				-- a level to prevent losing ores).
1410
				if ((currX == 0) and (currZ == 0)) then
1411
					-- Am at (0, 0). Remember this, and what direction I was facing so that the quickest route
1412
					-- to the surface can be taken
1413
					levelToReturnTo = miningLevel
1414
					haveBeenAtZeroZeroOnLayer = true
1415
					orientationAtZeroZero = currOrient
1416
				end
1417
1418
				-- If currently at bedrock, just move down until the turtle can't go any
1419
				-- further. This allows the blocks within the bedrock to be mined
1420
				if (miningLevel == bottomLayer) then
1421
					-- Temporarily turn off looking for chests to increase bedrock mining speed (this
1422
					-- means that the program doesn't support chests below level 5 - but I think
1423
					-- they they don't exist anyway)
1424
					local lookForChestsPrev = lookForChests
1425
					lookForChests = false
1426
1427
					-- Manually set the flag to determine whether the turtle should try to move first or
1428
					-- dig first. At bedrock, is very rarely any space
1429
1430
					-- Just above bedrock layer, dig down until can't dig any lower, and then
1431
					-- come back up. This replicates how the quarry functions
1432
					lastMoveNeededDig = true
1433
					local moveDownSuccess = turtleDown()
1434
					while (moveDownSuccess == true) do
1435
						moveDownSuccess = turtleDown()
1436
					end
1437
1438
					-- Know that we are moving back up through air, therefore set the flag to force the
1439
					-- turtle to try moving first
1440
					lastMoveNeededDig = false
1441
1442
					-- Have now hit bedrock, move back to the mining layer
1443
					while (currY < bottomLayer) do
1444
						turtleUp()
1445
					end
1446
1447
					-- Now back at the level above bedrock, again reset the flag to tell the turtle to
1448
					-- try digging again (because it is rare to find air at bedrock level)
1449
					lastMoveNeededDig = false
1450
1451
					-- Reset the look for chests value
1452
					lookForChests = lookForChestsPrev
1453
				elseif ((blocksMined > 0) and ((currX ~= 0) or (currZ ~= 0))) then
1454
					-- This isn't the first block of the row, nor are we at (0, 0) so we need to check the
1455
					-- block below
1456
1457
					-- Check the block down for being a noise block (don't need to check the first
1458
					-- block as it has already been checked in the outer loop)
1459
					if (isNoiseBlock(turtle.inspectDown) == false) then
1460
						-- If we are not looking for chests, then just dig it (it takes
1461
						-- less time to try to dig and fail as it does to do detect and
1462
						-- only dig if there is a block there)
1463
						if (lookForChests == false) then
1464
							turtle.digDown()
1465
							ensureInventorySpace()
1466
						elseif (turtle.detectDown() == true) then
1467
							if (isChestBlock(turtle.inspectDown) == true) then
1468
								-- There is a chest block below. Move back and approach
1469
								-- from the side to ensure that we don't need to return to
1470
								-- start through the chest itself (potentially losing items)
1471
								turtleBack()
1472
								currMiningState = miningState.EMPTYCHESTDOWN
1473
								turtleDown()
1474
								emptyChest(turtle.suck)
1475
								currMiningState = miningState.LAYER
1476
								turtleUp()
1477
								turtleForward()
1478
								turtle.digDown()
1479
								ensureInventorySpace()
1480
							else
1481
								turtle.digDown()
1482
								ensureInventorySpace()
1483
							end
1484
						end
1485
					end
1486
				end
1487
1488
				-- Check the block above for ores (if we're not a (0, 0) in which case
1489
				-- we know it's air)
1490
				if ((currX ~= 0) or (currZ ~= 0)) then
1491
					if (isNoiseBlock(turtle.inspectUp) == false) then
1492
						-- If we are not looking for chests, then just dig it (it takes
1493
						-- less time to try to dig and fail as it does to do detect and
1494
						-- only dig if there is a block there)
1495
						if (lookForChests == false) then
1496
							turtle.digUp()
1497
							ensureInventorySpace()
1498
						elseif (turtle.detectUp() == true) then
1499
							-- Determine if it is a chest before digging it
1500
							if (isChestBlock(turtle.inspectUp) == true) then
1501
								-- There is a chest block above. Empty it before digging it
1502
								emptyChest(turtle.suckUp)
1503
								turtle.digUp()
1504
								ensureInventorySpace()
1505
							else
1506
								turtle.digUp()
1507
								ensureInventorySpace()
1508
							end
1509
						end
1510
					end
1511
				end
1512
			end
1513
1514
			-- Am now at the other side of the quarry
1515
			onNearSideOfQuarry = not onNearSideOfQuarry
1516
		end
1517
1518
		-- If we were digging away from the starting point, will be digging
1519
		-- back towards it on the next layer
1520
		diggingAway = not diggingAway
1521
	end
1522
1523
	-- Return to the start
1524
	returnToStartAndUnload(false)
1525
1526
	-- Face forward
1527
	turtleSetOrientation(direction.FORWARD)
1528
end
1529
1530
-- ********************************************************************************** --
1531
-- Reads the next number from a given file
1532
-- ********************************************************************************** --
1533
function readNumber(inputFile)
1534
	local returnVal
1535
	local nextLine = inputFile.readLine()
1536
	if (nextLine ~= nil) then
1537
		returnVal = tonumber(nextLine)
1538
	end
1539
1540
	return returnVal
1541
end
1542
1543
-- ********************************************************************************** --
1544
-- Startup function to support resuming mining turtle
1545
-- ********************************************************************************** --
1546
function isResume()
1547
	local returnVal = false
1548
1549
	-- Try to open the resume file
1550
	local resumeFile = fs.open(startupParamsFile, "r")
1551
	if (resumeFile == nil) then
1552
		-- No resume file (presume that we are not supporting it)
1553
		supportResume = false
1554
	else
1555
		writeMessage("Found startup params file", messageLevel.DEBUG)
1556
1557
		-- Read in the startup params
1558
		quarryWidth = readNumber(resumeFile)
1559
		startHeight = readNumber(resumeFile)
1560
		noiseBlocksCount = readNumber(resumeFile)
1561
		lastEmptySlot = readNumber(resumeFile)
1562
		resumeFile.close()
1563
1564
		-- If the parameters were successfully read, then set the resuming flag to true
1565
		if ((quarryWidth ~= nil) and (startHeight ~= nil) and (noiseBlocksCount ~= nil) and (lastEmptySlot ~= nil)) then
1566
			resuming = true
1567
			writeMessage("Read params", messageLevel.DEBUG)
1568
1569
			-- Determine the look for chest and mining offset
1570
			if (lastEmptySlot == 14) then
1571
				lookForChests = true
1572
				miningOffset = 0
1573
			else
1574
				lookForChests = false
1575
				miningOffset = 1
1576
			end
1577
1578
			-- Get the turtle resume location
1579
			resumeFile = fs.open(oreQuarryLocation, "r")
1580
			if (resumeFile ~= nil) then
1581
				resumeMiningState = readNumber(resumeFile)
1582
				resumeX = readNumber(resumeFile)
1583
				resumeY = readNumber(resumeFile)
1584
				resumeZ = readNumber(resumeFile)
1585
				resumeOrient = readNumber(resumeFile)
1586
				resumeFile.close()
1587
1588
				-- Ensure that the resume location has been found
1589
				if
1590
					((resumeMiningState ~= nil) and (resumeX ~= nil) and (resumeY ~= nil) and (resumeZ ~= nil) and
1591
						(resumeOrient ~= nil))
1592
				 then
1593
					returnVal = true
1594
					local emptiedInventory = false
1595
1596
					-- Perform any mining state specific startup
1597
					if (resumeMiningState == miningState.EMPTYINVENTORY) then
1598
						-- Am mid way through an empty inventory cycle. Complete it before
1599
						-- starting the main Quarry function
1600
						returnToStartAndUnload(true)
1601
						resuming = true
1602
1603
						-- Continue from the current position
1604
						resumeX = currX
1605
						resumeY = currY
1606
						levelToReturnTo = resumeY
1607
						resumeZ = currZ
1608
						resumeOrient = currOrient
1609
1610
						writeMessage("Resuming with state of " .. currMiningState, messageLevel.DEBUG)
1611
						resumeMiningState = currMiningState
1612
						emptiedInventory = true
1613
					end
1614
1615
					-- If was emptying a chest when the program stopped, then move back
1616
					-- to a point which the Quarry
1617
					if (resumeMiningState == miningState.EMPTYCHESTDOWN) then
1618
						-- Set the current X, Y, Z and orientation to the true position that
1619
						-- the turtle is at
1620
						if (emptiedInventory == false) then
1621
							currX = resumeX
1622
							currY = resumeY
1623
							currZ = resumeZ
1624
							currOrient = resumeOrient
1625
						end
1626
1627
						-- Set the mining state as layer, assume haven't been through zero
1628
						-- zero and set the level to return to as the one below the current one
1629
						currMiningState = miningState.LAYER
1630
						levelToReturnTo = currY - 2
1631
						haveBeenAtZeroZeroOnLayer = false
1632
1633
						-- Temporarily disable resuming (so that the new location is written to the file
1634
						-- in case the program stops again)
1635
						resuming = false
1636
						turtleUp()
1637
						resuming = true
1638
1639
						resumeY = currY
1640
						resumeMiningState = miningState.LAYER
1641
					end
1642
				end
1643
			end
1644
		end
1645
1646
		if (returnVal == false) then
1647
			writeMessage("Failed to resume", messageLevel.ERROR)
1648
		end
1649
	end
1650
1651
	return returnVal
1652
end
1653
1654
-- ********************************************************************************** --
1655
-- Main Function
1656
-- ********************************************************************************** --
1657
-- Process the input arguments - storing them to global variables
1658
local args = {...}
1659
local paramsOK = true
1660
1661
-- Detect whether this is a wireless turtle, and if so, open the modem
1662
local peripheralConnected = peripheral.getType("right")
1663
if (peripheralConnected == "modem") then
1664
	isWirelessTurtle = true
1665
end
1666
1667
-- If a wireless turtle, open the modem
1668
if (isWirelessTurtle == true) then
1669
	turtleId = os.getComputerLabel()
1670
	rednet.open("right")
1671
end
1672
1673
-- give an empty turtleID if it's nil
1674
if (turtleId == nil) then
1675
	turtleId = " "
1676
end
1677
1678
if (#args == 0) then
1679
	-- Is this a resume?
1680
	if (isResume() == false) then
1681
		paramsOK = false
1682
	end
1683
elseif (#args == 1) then
1684
	quarryWidth = tonumber(args[1])
1685
	local x, y, z = gps.locate(5)
1686
	startHeight = y
1687
	if (startHeight == nil) then
1688
		writeMessage("Can't locate GPS", messageLevel.FATAL)
1689
		paramsOK = false
1690
	end
1691
elseif (#args == 2) then
1692
	if (args[2] == "/r") then
1693
		quarryWidth = tonumber(args[1])
1694
		supportResume = false
1695
	else
1696
		quarryWidth = tonumber(args[1])
1697
		startHeight = tonumber(args[2])
1698
	end
1699
elseif (#args == 3) then
1700
	quarryWidth = tonumber(args[1])
1701
	startHeight = tonumber(args[2])
1702
	if (args[3] == "/r") then
1703
		supportResume = false
1704
	else
1705
		paramsOK = false
1706
	end
1707
end
1708
1709
if ((paramsOK == false) and (resuming == false)) then
1710
	writeMessage("Usage: " .. shell.getRunningProgram() .. " <diameter> [turtleY] [/r]", messageLevel.FATAL)
1711
	paramsOK = false
1712
end
1713
1714
if (paramsOK == true) then
1715
	if ((startHeight < bottomLayer + 1) or (startHeight > 128)) then
1716
		writeMessage("turtleY must be between 3 and 128", messageLevel.FATAL)
1717
		paramsOK = false
1718
	end
1719
1720
	if ((quarryWidth < 2) or (quarryWidth > 64)) then
1721
		writeMessage("diameter must be between 2 and 64", messageLevel.FATAL)
1722
		paramsOK = false
1723
	end
1724
end
1725
1726
if (paramsOK == true) then
1727
	if (resuming == true) then
1728
		writeMessage("Resuming Ore Quarry...", messageLevel.INFO)
1729
	else
1730
		writeMessage("--------------------------------------------------------", messageLevel.INFO)
1731
		writeMessage("** Ore Quarry v1 by by WillisS **", messageLevel.INFO)
1732
		writeMessage("--------------------------------------------------------", messageLevel.INFO)
1733
	end
1734
1735
	-- Set the turtle's starting position
1736
	currX = 0
1737
	currY = startHeight
1738
	currZ = 0
1739
	currOrient = direction.FORWARD
1740
1741
	-- Calculate which blocks in the inventory signify noise blocks
1742
	if (resuming == false) then
1743
		determineNoiseBlocksCountCount()
1744
	end
1745
1746
	-- if ((noiseBlocksCount == 0) or (noiseBlocksCount > 13)) then
1747
	-- 	writeMessage(
1748
	-- 		"No noise blocks have been been added. Please place blocks that the turtle should not mine (e.g. Stone, Dirt, Gravel etc.) in the first few slots of the turtle's inventory. The first empty slot signifies the end of the noise blocks.",
1749
	-- 		messageLevel.FATAL
1750
	-- 	)
1751
	-- else
1752
	-- If we are supporting resume (and are not currently in the process of resuming)
1753
	-- then store startup parameters in appropriate files
1754
	if ((supportResume == true) and (resuming == false)) then
1755
		-- Write the startup parameters to  file
1756
		local outputFile = io.open(startupParamsFile, "w")
1757
		outputFile:write(quarryWidth)
1758
		outputFile:write("\n")
1759
		outputFile:write(startHeight)
1760
		outputFile:write("\n")
1761
		outputFile:write(noiseBlocksCount)
1762
		outputFile:write("\n")
1763
		outputFile:write(lastEmptySlot)
1764
		outputFile:write("\n")
1765
		outputFile:close()
1766
1767
		-- Setup the startup file
1768
1769
		-- Take a backup of the current startup file
1770
		if (fs.exists("startup") == true) then
1771
			fs.copy("startup", startupBackup)
1772
			outputFile = io.open("startup", "a")
1773
		else
1774
			outputFile = io.open("startup", "w")
1775
		end
1776
1777
		-- Write an info message so that people know how to get out of auto-resume
1778
		outputFile:write('\nprint("Running auto-restart...")\n')
1779
		outputFile:write('print("If you want to stop auto-resume and restore original state:")\n')
1780
		outputFile:write('print("1) Hold Ctrl-T until the program terminates")\n')
1781
		outputFile:write('print("2) Type \\"rm startup\\" (without quotes) and hit Enter")\n')
1782
		outputFile:write('print("")\n\n')
1783
1784
		-- Write the code required to restart the turtle
1785
		outputFile:write('shell.run("')
1786
		outputFile:write(shell.getRunningProgram())
1787
		outputFile:write('")\n')
1788
		outputFile:close()
1789
	end
1790
1791
	-- Create a Quarry
1792
	turtle.select(1)
1793
	currentlySelectedSlot = 1
1794
	createQuarry()
1795
1796
	-- Restore the file system to its original configuration
1797
	if (supportResume == true) then
1798
		fs.delete("startup")
1799
		if (fs.exists(startupBackup) == true) then
1800
			fs.move(startupBackup, "startup")
1801
		end
1802
1803
		if (fs.exists(startupParamsFile) == true) then
1804
			fs.delete(startupParamsFile)
1805
		end
1806
1807
		if (fs.exists(oreQuarryLocation) == true) then
1808
			fs.delete(oreQuarryLocation)
1809
		end
1810
1811
		if (fs.exists(returnToStartFile) == true) then
1812
			fs.delete(returnToStartFile)
1813
		end
1814
	end
1815
-- end
1816
end