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