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