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