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