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