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