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