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