View difference between Paste ID: wXP3brFR and b9qtJxc1
SHOW: | | - or go back to the newest paste.
1
-- ********************************************************************************** --
2
-- **                                                                              ** --
3
-- **   Minecraft Mining Turtle Ore Quarry v0.4 by AustinKK                        ** --
4
-- **   ---------------------------------------------------                        ** -- 
5
-- **                                                                              ** --
6
-- **   For instructions on how to use:                                            ** --
7
-- **                                                                              ** --
8
-- **     http://www.youtube.com/watch?v=DS1H4OY0yyg                               ** --
9
-- **                                                                              ** --
10
-- **                                                                              ** --
11
-- **  Change Log:                                                                 ** --
12
-- **    27th Dec 2012: [v0.2] Initial Draft Release                               ** --
13
-- **    29th Dec 2012: [v0.3] Minor Performance Improvements                      ** --
14
-- **    30th Dec 2012: [v0.4] Further Performance Improvements                    ** --
15
-- **                                                                              ** --
16
-- ********************************************************************************** --
17
18
-- Enumeration to store the the different types of message that can be written
19
messageLevel = { DEBUG=0, INFO=1, WARNING=2, ERROR=3, FATAL=4 }
20
 
21
-- Enumeration to store names for the 6 directions
22
direction = { FORWARD=0, RIGHT=1, BACK=2, LEFT=3, UP=4, DOWN=5 }
23
 
24
local messageOutputLevel = messageLevel.INFO
25
local fuelLevelToRefuelAt = 5
26
local refuelItemsToUseWhenRefuelling = 63
27
local emergencyFuelToRetain = 0
28
local maximumGravelStackSupported = 25 -- The number of stacked gravel or sand blocks supported
29
local noiseBlocksCount
30
local bottomLayer = 5 -- The y co-ords of the layer immediately above bedrock
31
local returningToStart = false
32
local lookForChests = false -- Determines if chests should be located as part of the quarrying
33
local miningOffset -- The offset to the mining layer. This is set depending on whether chests are being looked for or not
34
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)
35
local turtleId
36
local currentlySelectedSlot = 0 -- The slot that the last noise block was found in
37
local lastMoveNeededDig = true -- Determines whether the last move needed a dig first
38
local haveBeenAtZeroZeroOnLayer -- Determines whether the turtle has been at (0, 0) in this mining layer
39
local orientationAtZeroZero -- The turtle's orientation when it was at (0, 0)
40
local levelToReturnTo -- The level that the turtle should return to in order to head back to the start to unload
41
42
-- Variables to store the current location and orientation of the turtle. x is right, left, y is up, down and
43
-- z is forward, back with relation to the starting orientation. Y is the actual turtle level, x and z are
44
-- in relation to the starting point (i.e. the starting point is (0, 0))
45
local currX
46
local currY
47
local currZ
48
local currOrient
49
 
50
-- Command line parameters
51
local startHeight -- Represents the height (y co-ord) that the turtle started at
52
local quarryWidth -- Represents the length of the mines that the turtle will dig
53
 
54
-- ********************************************************************************** --
55
-- Writes an output message
56
-- ********************************************************************************** --
57
function writeMessage(message, msgLevel)
58
  if (msgLevel >= messageOutputLevel) then
59
    print(message)
60
    if (turtleId == nil) then
61
      rednet.broadcast(message)
62
    else
63
      -- Broadcast the message (prefixed with the turtle's id)
64
      rednet.broadcast("[".. turtleId.."] "..message)
65
    end
66
  end
67
end
68
 
69
-- ********************************************************************************** --
70
-- Ensures that the turtle has fuel
71
-- ********************************************************************************** --
72
function ensureFuel()
73
 
74
  -- Determine whether a refuel is required
75
  local fuelLevel = turtle.getFuelLevel()
76
  if (fuelLevel ~= "unlimited") then
77
    if (fuelLevel < fuelLevelToRefuelAt) then
78
      -- Need to refuel
79
      turtle.select(16)
80
      currentlySelectedSlot = 16
81
      local fuelItems = turtle.getItemCount(16)
82
 
83
      -- Do we need to impact the emergency fuel to continue? (always  
84
      -- keep one fuel item in slot 16)
85
      if (fuelItems == 0) then
86
        writeMessage("Completely out of fuel!", messageLevel.FATAL)
87
      elseif (fuelItems == 1) then
88
        writeMessage("Out of Fuel!", messageLevel.ERROR)
89
        turtle.refuel()
90
      elseif (fuelItems <= (emergencyFuelToRetain + 1)) then
91
        writeMessage("Consuming emergency fuel supply. "..(fuelItems - 2).." emergency fuel items remain", messageLevel.WARNING)
92
        turtle.refuel(1)
93
      else
94
        -- Refuel the lesser of the refuelItemsToUseWhenRefuelling and the number of items more than
95
        -- the emergency fuel level
96
        if (fuelItems - (emergencyFuelToRetain + 1) < refuelItemsToUseWhenRefuelling) then
97
          turtle.refuel(fuelItems - (emergencyFuelToRetain + 1))
98
        else
99
          turtle.refuel(refuelItemsToUseWhenRefuelling)
100
        end
101
      end
102
    end
103
  end
104
end        
105
 
106
-- ********************************************************************************** --
107
-- Checks that the turtle has inventory space by checking for spare slots and returning
108
-- to the starting point to empty out if it doesn't.
109
--
110
-- Takes the position required to move to in order to empty the turtle's inventory
111
-- should it be full as arguments
112
-- ********************************************************************************** --
113
function ensureInventorySpace()
114
 
115
  -- If already returning to start, then don't need to do anything
116
  if (returningToStart == false) then
117
 
118
    -- If the last inventory slot is full, then need to return to the start and empty
119
    if (turtle.getItemCount(lastEmptySlot) > 0) then
120
 
121
      -- Return to the starting point and empty the inventory, then go back to mining
122
      returnToStartAndUnload(true)
123
    end
124
  end
125
end
126
 
127
-- ********************************************************************************** --
128
-- Function that is called when the turtle has returned to the start in order to
129
-- empty its inventory into the chest and also pick up any fuel that is
130
-- available
131
-- ********************************************************************************** --
132
function emptyInventory()
133
 
134
  local slotLoop = 1
135
 
136
  -- Face the chest
137
  turtleSetOrientation(direction.BACK)
138
 
139
  -- Loop over each of the slots (except the 16th one which stores fuel)
140
  while (slotLoop < 16) do
141
    -- If this is one of the slots that contains a noise block, empty all blocks except
142
    -- one
143
    turtle.select(slotLoop) -- Don't bother updating selected slot variable as it will set later in this function
144
    if ((slotLoop <= noiseBlocksCount) or ((slotLoop == 15) and (lookForChests == true))) then
145
      turtle.drop(turtle.getItemCount(slotLoop) - 1)
146
    else
147
      -- Not a noise block, drop all of the items in this slot
148
      turtle.drop()
149
    end
150
     
151
    slotLoop = slotLoop + 1
152
  end
153
 
154
  -- While we are here, refill the fuel items
155
  turtleSetOrientation(direction.LEFT)
156
  turtle.select(16) -- Don't bother updating selected slot variable as it will set later in this function
157
  local currFuelItems = turtle.getItemCount(16)
158
  turtle.suck()
159
  while ((currFuelItems ~= turtle.getItemCount(16)) and (turtle.getItemCount(16) < 64)) do
160
    currFuelItems = turtle.getItemCount(16)
161
    turtle.suck()
162
  end
163
 
164
  slotLoop = noiseBlocksCount + 1
165
  -- Have now picked up all the items that we can. If we have also picked up some
166
  -- additional fuel in some of the other slots, then drop it again
167
  while (slotLoop < lastEmptySlot) do
168
    -- Drop any items found in this slot
169
    turtle.select(slotLoop) -- Don't bother updating selected slot variable as it will set later in this function
170
    turtle.drop()
171
    slotLoop = slotLoop + 1
172
  end
173
 
174
  -- Select the 1st slot because sometimes when leaving the 15th or 16th slots selected it can result
175
  -- in that slot being immediately filled (resulting in the turtle returning to base again too soon)
176
  turtle.select(1)
177
  currentlySelectedSlot = 1
178
end
179
 
180
-- ********************************************************************************** --
181
-- Function to move to the starting point, call a function that is passed in
182
-- and return to the same location (if required)
183
-- ********************************************************************************** --
184
function returnToStartAndUnload(returnBackToMiningPoint)
185
 
186
  writeMessage("returnToStartAndUnload called", messageLevel.DEBUG)
187
  returningToStart = true
188
 
189
  -- Store the current location and orientation so that it can be returned to
190
  local storedX = currX
191
  local storedY = currY
192
  local storedZ = currZ
193
  local storedOrient = currOrient
194
 
195
  writeMessage("Return to start, return level: "..levelToReturnTo, messageLevel.DEBUG)
196
197
  -- Move down to the correct layer to return via
198
  if (currY > levelToReturnTo) then
199
    while (currY > levelToReturnTo) do
200
      turtleDown()
201
    end
202
  elseif (currY < levelToReturnTo) then 
203
    while (currY < levelToReturnTo) do
204
      turtleUp()
205
    end
206
  end
207
 
208
  if ((haveBeenAtZeroZeroOnLayer == false) or (orientationAtZeroZero == direction.FORWARD)) then
209
    -- Move back to the correct X position first
210
    if (currX > 0) then
211
      turtleSetOrientation(direction.LEFT)
212
      while (currX > 0) do
213
        turtleForward()
214
      end
215
    elseif (currX < 0) then
216
      -- This should never happen
217
      writeMessage("Current x is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
218
    end
219
 
220
    -- Then move back to the correct Z position
221
    if (currZ > 0) then
222
      turtleSetOrientation(direction.BACK)
223
      while (currZ > 0) do
224
        turtleForward()
225
      end
226
    elseif (currZ < 0) then
227
      -- This should never happen
228
      writeMessage("Current z is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
229
    end
230
  else
231
    -- Move back to the correct Z position first
232
    if (currZ > 0) then
233
      turtleSetOrientation(direction.BACK)
234
      while (currZ > 0) do
235
        turtleForward()
236
      end
237
    elseif (currZ < 0) then
238
      -- This should never happen
239
      writeMessage("Current z is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
240
    end
241
242
    -- Then move back to the correct X position
243
    if (currX > 0) then
244
      turtleSetOrientation(direction.LEFT)
245
      while (currX > 0) do
246
        turtleForward()
247
      end
248
    elseif (currX < 0) then
249
      -- This should never happen
250
      writeMessage("Current x is less than 0 in returnToStartAndUnload", messageLevel.ERROR)
251
    end
252
  end
253
 
254
  -- Return to the starting layer
255
  if (currY < startHeight) then
256
    while (currY < startHeight) do
257
      turtleUp()
258
    end
259
  elseif (currY > startHeight) then
260
    -- This should never happen
261
    writeMessage("Current height is greater than start height in returnToStartAndUnload", messageLevel.ERROR)
262
  end
263
 
264
  -- Empty the inventory
265
  local slotLoop = 1
266
 
267
  -- Face the chest
268
  turtleSetOrientation(direction.BACK)
269
 
270
  -- Loop over each of the slots (except the 16th one which stores fuel)
271
  while (slotLoop < 16) do
272
    -- If this is one of the slots that contains a noise block, empty all blocks except
273
    -- one
274
    turtle.select(slotLoop) -- Don't bother updating selected slot variable as it will set later in this function
275
    if ((slotLoop <= noiseBlocksCount) or ((slotLoop == 15) and (lookForChests == true))) then
276
      turtle.drop(turtle.getItemCount(slotLoop) - 1)
277
    else
278
      -- Not a noise block, drop all of the items in this slot
279
      turtle.drop()
280
    end
281
     
282
    slotLoop = slotLoop + 1
283
  end
284
 
285
  -- While we are here, refill the fuel items if there is capacity
286
  if (turtle.getItemCount(16) < 64) then
287
    turtleSetOrientation(direction.LEFT)
288
    turtle.select(16) -- Don't bother updating selected slot variable as it will set later in this function
289
    local currFuelItems = turtle.getItemCount(16)
290
    turtle.suck()
291
    while ((currFuelItems ~= turtle.getItemCount(16)) and (turtle.getItemCount(16) < 64)) do
292
      currFuelItems = turtle.getItemCount(16)
293
      turtle.suck()
294
    end
295
 
296
    slotLoop = noiseBlocksCount + 1
297
    -- Have now picked up all the items that we can. If we have also picked up some
298
    -- additional fuel in some of the other slots, then drop it again
299
    while (slotLoop <= lastEmptySlot) do
300
      -- Drop any items found in this slot
301
      turtle.select(slotLoop) -- Don't bother updating selected slot variable as it will set later in this function
302
      turtle.drop()
303
      slotLoop = slotLoop + 1
304
    end
305
  end
306
307
  -- Select the 1st slot because sometimes when leaving the 15th or 16th slots selected it can result
308
  -- in that slot being immediately filled (resulting in the turtle returning to base again too soon)
309
  turtle.select(1)
310
  currentlySelectedSlot = 1
311
 
312
  -- If required, move back to the point that we were mining at before returning to the start
313
  if (returnBackToMiningPoint == true) then
314
    -- Return back to the required layer
315
    while (currY > levelToReturnTo) do
316
      turtleDown()
317
    end
318
319
    if ((haveBeenAtZeroZeroOnLayer == false) or (orientationAtZeroZero == direction.FORWARD)) then
320
      -- Move back to the correct Z position first
321
      writeMessage("Stored Z: "..storedZ..", currZ: "..currZ, messageLevel.DEBUG)
322
      if (storedZ > currZ) then
323
        writeMessage("Orienting forward", messageLevel.DEBUG)
324
        writeMessage("Moving in z direction", messageLevel.DEBUG)
325
        turtleSetOrientation(direction.FORWARD)
326
        while (storedZ > currZ) do
327
          turtleForward()
328
        end
329
      elseif (storedZ < currZ) then
330
        -- This should never happen
331
        writeMessage("Stored z is less than current z in returnToStartAndUnload", messageLevel.ERROR)
332
      end
333
334
      -- Then move back to the correct X position
335
      if (storedX > currX) then
336
        writeMessage("Stored X: "..storedX..", currX: "..currX, messageLevel.DEBUG)
337
        writeMessage("Orienting right", messageLevel.DEBUG)
338
        writeMessage("Moving in x direction", messageLevel.DEBUG)
339
        turtleSetOrientation(direction.RIGHT)
340
        while (storedX > currX) do
341
          turtleForward()
342
        end
343
      elseif (storedX < currX) then
344
        -- This should never happen
345
        writeMessage("Stored x is less than current x in returnToStartAndUnload", messageLevel.ERROR)
346
      end
347
    else 
348
      -- Move back to the correct X position first
349
      if (storedX > currX) then
350
        writeMessage("Stored X: "..storedX..", currX: "..currX, messageLevel.DEBUG)
351
        writeMessage("Orienting right", messageLevel.DEBUG)
352
        writeMessage("Moving in x direction", messageLevel.DEBUG)
353
        turtleSetOrientation(direction.RIGHT)
354
        while (storedX > currX) do
355
          turtleForward()
356
        end
357
      elseif (storedX < currX) then
358
        -- This should never happen
359
        writeMessage("Stored x is less than current x in returnToStartAndUnload", messageLevel.ERROR)
360
      end
361
 
362
      -- Then move back to the correct Z position
363
      writeMessage("Stored Z: "..storedZ..", currZ: "..currZ, messageLevel.DEBUG)
364
      if (storedZ > currZ) then
365
        writeMessage("Orienting forward", messageLevel.DEBUG)
366
        writeMessage("Moving in z direction", messageLevel.DEBUG)
367
        turtleSetOrientation(direction.FORWARD)
368
        while (storedZ > currZ) do
369
          turtleForward()
370
        end
371
      elseif (storedZ < currZ) then
372
        -- This should never happen
373
        writeMessage("Stored z is less than current z in returnToStartAndUnload", messageLevel.ERROR)
374
      end
375
    end
376
 
377
    -- Move back to the correct layer
378
    if (storedY < currY) then
379
      while (storedY < currY) do
380
        turtleDown()
381
      end
382
    elseif (storedY > currY) then 
383
      while (storedY > currY) do
384
        turtleUp()
385
      end
386
    end
387
 
388
    -- Finally, set the correct orientation
389
    turtleSetOrientation(storedOrient)
390
 
391
    writeMessage("Have returned to the mining point", messageLevel.DEBUG)
392
  end
393
 
394
  returningToStart = false
395
 
396
end
397
 
398
-- ********************************************************************************** --
399
-- Empties a chest's contents
400
-- ********************************************************************************** --
401
function emptyChest(suckFn)
402
 
403
  local prevInventoryCount = {}
404
  local inventoryLoop
405
  local chestEmptied = false
406
 
407
  -- Record the number of items in each of the inventory slots
408
  for inventoryLoop = 1, 16 do
409
    prevInventoryCount[inventoryLoop] = turtle.getItemCount(inventoryLoop)
410
  end
411
 
412
  while (chestEmptied == false) do
413
    -- Pick up the next item
414
    suckFn()
415
 
416
    -- Determine the number of items in each of the inventory slots now
417
    local newInventoryCount = {}
418
    for inventoryLoop = 1, 16 do
419
      newInventoryCount[inventoryLoop] = turtle.getItemCount(inventoryLoop)
420
    end
421
 
422
    -- Now, determine whether there have been any items taken from the chest
423
    local foundDifferentItemCount = false
424
    inventoryLoop = 1
425
    while ((foundDifferentItemCount == false) and (inventoryLoop <= 16)) do
426
      if (prevInventoryCount[inventoryLoop] ~= newInventoryCount[inventoryLoop]) then
427
        foundDifferentItemCount = true
428
      else
429
        inventoryLoop = inventoryLoop + 1
430
      end
431
    end
432
   
433
    -- If no items have been found with a different item count, then the chest has been emptied
434
    chestEmptied = not foundDifferentItemCount
435
 
436
    if (chestEmptied == false) then
437
      prevInventoryCount = newInventoryCount
438
      -- Check that there is sufficient inventory space as may have picked up a block
439
      ensureInventorySpace()
440
    end
441
  end
442
 
443
  writeMessage("Finished emptying chest", messageLevel.DEBUG)
444
end
445
 
446
 
447
-- ********************************************************************************** --
448
-- Generic function to move the Turtle (pushing through any gravel or other
449
-- things such as mobs that might get in the way).
450
--
451
-- The only thing that should stop the turtle moving is bedrock. Where this is
452
-- found, the function will return after 15 seconds returning false
453
-- ********************************************************************************** --
454
function moveTurtle(moveFn, detectFn, digFn, attackFn, compareFn, suckFn, maxDigCount)
455
 
456
  ensureFuel()
457
 
458
  -- Flag to determine whether digging has been tried yet. If it has
459
  -- then pause briefly before digging again to allow sand or gravel to
460
  -- drop
461
  local digCount = 0
462
  local moveSuccess = false
463
464
  if (lastMoveNeededDig == false) then
465
    -- Didn't need to dig last time the turtle moved, so try moving first
466
    writeMessage("Trying to move before digging", messageLevel.DEBUG)
467
    moveSuccess = moveFn()
468
469
    -- Don't need to set the last move needed dig. It is already false, if 
470
    -- move success is now true, then it won't be changed
471
  else    
472
    writeMessage("Trying to dig before moving", messageLevel.DEBUG)
473
    -- If we are looking for chests, then check that this isn't a chest before trying to dig it
474
    if (lookForChests == true) then
475
      if (isNoiseBlock(compareFn) == false) then
476
        if (detectFn() == true) then
477
          -- Determine if it is a chest before digging it
478
          if (isChestBlock(compareFn) == true) then
479
            -- Have found a chest, empty it before continuing
480
            emptyChest (suckFn)
481
          end
482
        end
483
      end
484
    end
485
 
486
    -- Try to dig (without doing a detect as it is quicker)
487
    local digSuccess = digFn()
488
    if (digSuccess == true) then
489
      digCount = 1
490
    end
491
    moveSuccess = moveFn()
492
493
    if (moveSuccess == true) then
494
      lastMoveNeededDig = digSuccess
495
    end
496
  end
497
 
498
  -- Loop until we've successfully moved
499
  if (moveSuccess == false) then
500
    while ((moveSuccess == false) and (digCount < maxDigCount)) do
501
 
502
      -- If there is a block in front, dig it
503
      if (detectFn() == true) then
504
       
505
          -- If we've already tried digging, then pause before digging again to let
506
          -- any sand or gravel drop, otherwise check for a chest before digging
507
          if(digCount == 0) then
508
            -- Am about to dig a block - check that it is not a chest if necessary
509
            -- If we are looking for chests, then check that this isn't a chest before moving
510
            if (lookForChests == true) then
511
              if (isNoiseBlock(compareFn) == false) then
512
                if (detectFn() == true) then
513
                  -- Determine if it is a chest before digging it
514
                  if (isChestBlock(compareFn) == true) then
515
                    -- Have found a chest, empty it before continuing
516
                    emptyChest (suckFn)
517
                  end
518
                end
519
              end
520
            end
521
          else
522
            sleep(0.1)
523
          end
524
 
525
          digFn()
526
          digCount = digCount + 1
527
      else
528
         -- Am being stopped from moving by a mob, attack it
529
         attackFn()
530
      end
531
 
532
      -- Try the move again
533
      moveSuccess = moveFn()
534
    end
535
536
    if (digCount == 0) then
537
      lastMoveNeededDig = false
538
    else
539
      lastMoveNeededDig = true
540
    end
541
  end
542
 
543
  -- Return the move success
544
  return moveSuccess
545
 
546
end
547
 
548
-- ********************************************************************************** --
549
-- Move the turtle forward one block (updating the turtle's position)
550
-- ********************************************************************************** --
551
function turtleForward()
552
  local returnVal = moveTurtle(turtle.forward, turtle.detect, turtle.dig, turtle.attack, turtle.compare, turtle.suck, maximumGravelStackSupported)
553
  if (returnVal == true) then
554
    -- Update the current co-ordinates
555
    if (currOrient == direction.FORWARD) then
556
      currZ = currZ + 1
557
    elseif (currOrient == direction.LEFT) then
558
      currX = currX - 1
559
    elseif (currOrient == direction.BACK) then
560
      currZ = currZ - 1
561
    elseif (currOrient == direction.RIGHT) then
562
      currX = currX + 1
563
    else
564
      writeMessage ("Invalid currOrient in turtleForward function", messageLevel.ERROR)
565
    end
566
 
567
    -- Check that there is sufficient inventory space as may have picked up a block
568
    ensureInventorySpace()
569
  end
570
 
571
  return returnVal
572
end
573
 
574
-- ********************************************************************************** --
575
-- Move the turtle up one block (updating the turtle's position)
576
-- ********************************************************************************** --
577
function turtleUp()
578
  local returnVal = moveTurtle(turtle.up, turtle.detectUp, turtle.digUp, turtle.attackUp, turtle.compareUp, turtle.suckUp, maximumGravelStackSupported)
579
  if (returnVal == true) then
580
    currY = currY + 1
581
 
582
    -- Check that there is sufficient inventory space as may have picked up a block
583
    ensureInventorySpace()
584
  end
585
  return returnVal
586
end
587
 
588
-- ********************************************************************************** --
589
-- Move the turtle down one block (updating the turtle's position)
590
-- ********************************************************************************** --
591
function turtleDown()
592
  local returnVal
593
 
594
  -- Because the turtle is digging down, can fail fast (only allow 1 dig attempt).
595
  returnVal = moveTurtle(turtle.down, turtle.detectDown, turtle.digDown, turtle.attackDown, turtle.compareDown, turtle.suckDown, 1)
596
  if (returnVal == true) then
597
    currY = currY - 1
598
 
599
    -- Check that there is sufficient inventory space as may have picked up a block
600
    ensureInventorySpace()
601
  end
602
  return returnVal
603
end
604
 
605
-- ********************************************************************************** --
606
-- Move the turtle back one block (updating the turtle's position)
607
-- ********************************************************************************** --
608
function turtleBack()
609
  -- First try to move back using the standard function
610
  local returnVal = turtle.back()
611
 
612
  -- Moving back didn't work (might be a block or a mob in the way). Turn round and move
613
  -- forward instead (whereby anything in the way can be cleared)
614
  if(returnVal == false) then
615
    turtle.turnRight()
616
    turtle.turnRight()
617
    returnVal = turtleForward()
618
    turtle.turnRight()
619
    turtle.turnRight()
620
  end  
621
 
622
  if (returnVal == true) then
623
    -- Update the current co-ordinates
624
    if (currOrient == direction.FORWARD) then
625
      currZ = currZ - 1
626
    elseif (currOrient == direction.LEFT) then
627
      currX = currX + 1
628
    elseif (currOrient == direction.BACK) then
629
      currZ = currZ + 1
630
    elseif (currOrient == direction.RIGHT) then
631
      currX = currX - 1
632
    else
633
      writeMessage ("Invalid currOrient in turtleBack function", messageLevel.ERROR)
634
    end
635
 
636
    -- Check that there is sufficient inventory space as may have picked up a block
637
    ensureInventorySpace()
638
  end
639
   
640
  return returnVal
641
end
642
 
643
-- ********************************************************************************** --
644
-- Turns the turtle (updating the current orientation at the same time)
645
-- ********************************************************************************** --
646
function turtleTurn(turnDir)
647
 
648
  if (turnDir == direction.LEFT) then
649
    if (currOrient == direction.FORWARD) then
650
      currOrient = direction.LEFT
651
      turtle.turnLeft()
652
    elseif (currOrient == direction.LEFT) then
653
      currOrient = direction.BACK
654
      turtle.turnLeft()
655
    elseif (currOrient == direction.BACK) then
656
      currOrient = direction.RIGHT
657
      turtle.turnLeft()
658
    elseif (currOrient == direction.RIGHT) then
659
      currOrient = direction.FORWARD
660
      turtle.turnLeft()
661
    else
662
      writeMessage ("Invalid currOrient in turtleTurn function", messageLevel.ERROR)
663
    end
664
  elseif (turnDir == direction.RIGHT) then
665
    if (currOrient == direction.FORWARD) then
666
      currOrient = direction.RIGHT
667
      turtle.turnRight()
668
    elseif (currOrient == direction.LEFT) then
669
      currOrient = direction.FORWARD
670
      turtle.turnRight()
671
    elseif (currOrient == direction.BACK) then
672
      currOrient = direction.LEFT
673
      turtle.turnRight()
674
    elseif (currOrient == direction.RIGHT) then
675
      currOrient = direction.BACK
676
      turtle.turnRight()
677
    else
678
      writeMessage ("Invalid currOrient in turtleTurn function", messageLevel.ERROR)
679
    end
680
  else
681
    writeMessage ("Invalid turnDir in turtleTurn function", messageLevel.ERROR)
682
  end
683
end
684
 
685
-- ********************************************************************************** --
686
-- Sets the turtle to a specific orientation, irrespective of its current orientation
687
-- ********************************************************************************** --
688
function turtleSetOrientation(newOrient)
689
 
690
  if (currOrient ~= newOrient) then
691
    if (currOrient == direction.FORWARD) then
692
      if (newOrient == direction.RIGHT) then
693
        turtle.turnRight()
694
        currOrient = newOrient
695
      elseif (newOrient == direction.BACK) then
696
        turtle.turnRight()
697
        turtle.turnRight()
698
        currOrient = newOrient
699
      elseif (newOrient == direction.LEFT) then
700
        turtle.turnLeft()
701
        currOrient = newOrient
702
      else
703
        writeMessage ("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
704
      end
705
    elseif (currOrient == direction.RIGHT) then
706
      if (newOrient == direction.BACK) then
707
        turtle.turnRight()
708
        currOrient = newOrient
709
      elseif (newOrient == direction.LEFT) then
710
        turtle.turnRight()
711
        turtle.turnRight()
712
        currOrient = newOrient
713
      elseif (newOrient == direction.FORWARD) then
714
        turtle.turnLeft()
715
        currOrient = newOrient
716
      else
717
        writeMessage ("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
718
      end
719
    elseif (currOrient == direction.BACK) then
720
      if (newOrient == direction.LEFT) then
721
        turtle.turnRight()
722
        currOrient = newOrient
723
      elseif (newOrient == direction.FORWARD) then
724
        turtle.turnRight()
725
        turtle.turnRight()
726
        currOrient = newOrient
727
      elseif (newOrient == direction.RIGHT) then
728
        turtle.turnLeft()
729
        currOrient = newOrient
730
      else
731
        writeMessage ("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
732
      end
733
    elseif (currOrient == direction.LEFT) then
734
      if (newOrient == direction.FORWARD) then
735
        turtle.turnRight()
736
        currOrient = newOrient
737
      elseif (newOrient == direction.RIGHT) then
738
        turtle.turnRight()
739
        turtle.turnRight()
740
        currOrient = newOrient
741
      elseif (newOrient == direction.BACK) then
742
        turtle.turnLeft()
743
        currOrient = newOrient
744
      else
745
        writeMessage ("Invalid newOrient in turtleSetOrientation function", messageLevel.ERROR)
746
      end
747
    else
748
      writeMessage ("Invalid currOrient in turtleTurn function", messageLevel.ERROR)
749
    end
750
  end
751
end
752
 
753
-- ********************************************************************************** --
754
-- Determines if a particular block is considered a noise block or not. A noise
755
-- block is one that is a standard block in the game (stone, dirt, gravel etc.) and
756
-- is one to ignore as not being an ore. Function works by comparing the block
757
-- in question against a set of blocks in the turtle's inventory which are known not to
758
-- be noise blocks. Param is the function to use to compare the block for a noise block
759
-- ********************************************************************************** --
760
function isNoiseBlock(compareFn)
761
 
762
  -- Consider air to be a noise block
763
  local returnVal = false
764
  local seamLoop = 1
765
  local prevSelectedSlot  
766
767
  -- If the currently selected slot is a noise block, then compare against this first
768
  -- so that the slot doesn't need to be selected again (there is a 0.05s cost to do
769
  -- this even if it is the currently selected slot)
770
  if (currentlySelectedSlot <= noiseBlocksCount) then
771
    returnVal = compareFn()
772
  end
773
774
  if (returnVal == false) then
775
    prevSelectedSlot = currentlySelectedSlot
776
    while((returnVal == false) and (seamLoop <= noiseBlocksCount)) do
777
      if (seamLoop ~= prevSelectedSlot) then
778
        turtle.select(seamLoop) 
779
        currentlySelectedSlot = seamLoop
780
        returnVal = compareFn()
781
      end
782
      seamLoop = seamLoop + 1
783
    end
784
  end
785
786
  -- Return the calculated value
787
  return returnVal
788
 
789
end
790
 
791
-- ********************************************************************************** --
792
-- Determines if a particular block is a chest. Returns false if it is not a chest
793
-- or chests are not being detected
794
-- ********************************************************************************** --
795
function isChestBlock(compareFn)
796
 
797
  -- Check the block in the appropriate direction to see whether it is a chest. Only
798
  -- do this if we are looking for chests
799
  local returnVal = false
800
  if (lookForChests == true) then
801
    turtle.select(15)
802
    currentlySelectedSlot = 15
803
    returnVal = compareFn()
804
  end
805
 
806
  -- Return the calculated value
807
  return returnVal
808
 
809
end
810
 
811
-- ********************************************************************************** --
812
-- Function to calculate the number of non seam blocks in the turtle's inventory. This
813
-- is all of the blocks at the start of the inventory (before the first empty slot is
814
-- found
815
-- ********************************************************************************** --
816
function determineNoiseBlocksCountCount()
817
  -- Determine the location of the first empty inventory slot. All items before this represent
818
  -- noise items.
819
  local foundFirstBlankInventorySlot = false
820
  noiseBlocksCount = 1
821
  while ((noiseBlocksCount < 16) and (foundFirstBlankInventorySlot == false)) do
822
    if (turtle.getItemCount(noiseBlocksCount) > 0) then
823
      noiseBlocksCount = noiseBlocksCount + 1
824
    else
825
      foundFirstBlankInventorySlot = true
826
    end
827
  end
828
  noiseBlocksCount = noiseBlocksCount - 1
829
 
830
  -- Determine whether a chest was provided, and hence whether we should support
831
  -- looking for chests
832
  if (turtle.getItemCount(15) > 0) then
833
    lookForChests = true
834
    lastEmptySlot = 14
835
    miningOffset = 0
836
    writeMessage("Looking for chests...", messageLevel.DEBUG)
837
  else
838
    lastEmptySlot = 15
839
    miningOffset = 1
840
    writeMessage("Ignoring chests...", messageLevel.DEBUG)
841
  end
842
end
843
 
844
-- ********************************************************************************** --
845
-- Creates a quarry mining out only ores and leaving behind any noise blocks
846
-- ********************************************************************************** --
847
function createQuarry()
848
 
849
  -- Determine the top mining layer layer. The turtle mines in layers of 3, and the bottom layer
850
  -- is the layer directly above bedrock.
851
  --
852
  -- The actual layer that the turtle operates in is the middle of these three layers,
853
  -- so determine the top layer
854
  local topMiningLayer = startHeight + ((bottomLayer - startHeight - 2) % 3) - 1 + miningOffset
855
 
856
  -- If the top layer is up, then ignore it and move to the next layer
857
  if (topMiningLayer > currY) then
858
    topMiningLayer = topMiningLayer - 3
859
  end
860
 
861
  local startedLayerToRight = true -- Only used where the quarry is of an odd width
862
 
863
  -- Loop over each mining row
864
  local miningLevel
865
  for miningLevel = (bottomLayer + miningOffset), topMiningLayer, 3 do
866
    writeMessage("Mining Layer: "..miningLevel, messageLevel.INFO)
867
    haveBeenAtZeroZeroOnLayer = false
868
 
869
    -- While the initial shaft is being dug out, set the level to return to in order to unload
870
    -- to the just take the turtle straight back up
871
    if (miningLevel == (bottomLayer + miningOffset)) then
872
      levelToReturnTo = startHeight
873
    end
874
875
    -- Move to the correct level to start mining
876
    if (currY > miningLevel) then
877
      while (currY > miningLevel) do
878
        turtleDown()
879
      end
880
    elseif (currY < miningLevel) then
881
      while (currY < miningLevel) do
882
        turtleUp()
883
      end
884
    end
885
886
    -- Set the layer to return via when returning to the surface as the one below the currently
887
    -- mined one
888
    if (miningLevel == (bottomLayer + miningOffset)) then
889
      levelToReturnTo = (bottomLayer + miningOffset)
890
    else
891
      levelToReturnTo = miningLevel - 3
892
    end
893
 
894
    -- Move turtle into the correct orientation to start mining (if this is the
895
    -- first row to be mined, then don't need to turn, otherwise turn towards the next
896
    -- mining section)
897
898
    writeMessage("Mining Level: "..miningLevel..", Bottom Layer: "..bottomLayer..", Mining Offset: "..miningOffset, messageLevel.DEBUG)
899
900
    if (miningLevel > (bottomLayer + miningOffset)) then
901
      writeMessage ("Turning at start of layer", messageLevel.DEBUG)
902
      -- Turn towards the next mining layer
903
      if (quarryWidth % 2 == 0) then
904
        -- An even width quarry, always turn right
905
        turtleTurn(direction.RIGHT)
906
      else
907
        -- Turn the opposite direction to that which we turned before
908
        if (startedLayerToRight == true) then
909
          turtleTurn(direction.LEFT)
910
          startedLayerToRight = false
911
        else
912
          turtleTurn(direction.RIGHT)
913
          startedLayerToRight = true
914
        end
915
      end
916
    end
917
 
918
    local mineRows
919
    local onNearSideOfQuarry = true
920
    local diggingAway = true
921
    for mineRows = 1, quarryWidth do
922
923
      -- If this is not the first row, then get into position to mine the next row
924
      if ((mineRows == 1) and (lookForChests == false)) then
925
        -- Not looking for chests, check the block below for being an ore. Only do this
926
        -- if we're not looking for chests since the program doesn't support chests in
927
        -- bedrock
928
        if (isNoiseBlock(turtle.compareDown) == false) then
929
          turtle.digDown()
930
          ensureInventorySpace()
931
        end
932
      elseif (mineRows > 1) then
933
        -- Move into position for mining the next row
934
        if (onNearSideOfQuarry == diggingAway) then
935
          if (startedLayerToRight == true) then
936
            turtleTurn(direction.LEFT)
937
          else
938
            turtleTurn(direction.RIGHT)
939
          end
940
        else
941
          if (startedLayerToRight == true) then
942
            turtleTurn(direction.RIGHT)
943
          else
944
            turtleTurn(direction.LEFT)
945
          end
946
        end
947
 
948
        turtleForward()
949
 
950
        -- Before making the final turn, check the block below. Do this
951
        -- now because if it is a chest, then we want to back up and 
952
        -- approach it from the side (so that we don't lose items if we 
953
        -- have to return to the start through it). 
954
        --
955
        -- This is the point at which it is safe to back up without moving
956
        -- out of the quarry area (unless at bedrock in which case don't bother
957
        -- as we'll be digging down anyway)
958
        if (miningLevel ~= bottomLayer) then
959
          if (isNoiseBlock(turtle.compareDown) == false) then
960
            -- If we are not looking for chests, then just dig it (it takes 
961
            -- less time to try to dig and fail as it does to do detect and
962
            -- only dig if there is a block there)
963
            if (lookForChests == false) then
964
              turtle.digDown()
965
              ensureInventorySpace()
966
            elseif (turtle.detectDown() == true) then
967
              if (isChestBlock(turtle.compareDown) == true) then
968
                -- There is a chest block below. Move back and approach
969
                -- from the side to ensure that we don't need to return to
970
                -- start through the chest itself (potentially losing items) 
971
                turtleBack()
972
                turtleDown()
973
                emptyChest(turtle.suck)
974
                turtleUp()
975
                turtleForward()
976
                turtle.digDown()
977
                ensureInventorySpace()
978
              else
979
                turtle.digDown()
980
                ensureInventorySpace()
981
              end
982
            end
983
          end
984
        end
985
 
986
        -- Move into final position for mining the next row
987
        if (onNearSideOfQuarry == diggingAway) then
988
          if (startedLayerToRight == true) then
989
            turtleTurn(direction.LEFT)
990
          else
991
            turtleTurn(direction.RIGHT)
992
          end
993
        else
994
          if (startedLayerToRight == true) then
995
            turtleTurn(direction.RIGHT)
996
          else
997
            turtleTurn(direction.LEFT)
998
          end
999
        end
1000
      end
1001
 
1002
      -- Dig to the other side of the quarry
1003
      local blocksMined
1004
      for blocksMined = 0, (quarryWidth - 1) do
1005
        if (blocksMined > 0) then
1006
          -- Only move forward if this is not the first space
1007
          turtleForward()
1008
        end
1009
1010
        -- If the current block is (0,0), then record the fact that the 
1011
        -- turtle has been through this block and what it's orientation was and update the layer
1012
        -- that it should return via to get back to the surface (it no longer needs to go down
1013
        -- a level to prevent losing ores). 
1014
        if ((currX == 0) and (currZ == 0)) then
1015
          -- Am at (0, 0). Remember this, and what direction I was facing so that the quickest route
1016
          -- to the surface can be taken
1017
          levelToReturnTo = miningLevel
1018
          haveBeenAtZeroZeroOnLayer = true
1019
          orientationAtZeroZero = currOrient
1020
        end
1021
1022
        -- If currently at bedrock, just move down until the turtle can't go any
1023
        -- further. This allows the blocks within the bedrock to be mined
1024
        if (miningLevel == bottomLayer) then
1025
          -- Temporarily turn off looking for chests to increase bedrock mining speed (this
1026
          -- means that the program doesn't support chests below level 5 - but I think
1027
          -- they they don't exist anyway)
1028
          local lookForChestsPrev = lookForChests
1029
          lookForChests = false
1030
1031
          -- Manually set the flag to determine whether the turtle should try to move first or
1032
          -- dig first. At bedrock, is very rarely any space
1033
1034
          -- Just above bedrock layer, dig down until can't dig any lower, and then
1035
          -- come back up. This replicates how the quarry functions
1036
          lastMoveNeededDig = true
1037
          local moveDownSuccess = turtleDown()
1038
          while (moveDownSuccess == true) do
1039
            moveDownSuccess = turtleDown()
1040
          end
1041
1042
          -- Know that we are moving back up through air, therefore set the flag to force the
1043
          -- turtle to try moving first 
1044
          lastMoveNeededDig = false
1045
1046
          -- Have now hit bedrock, move back to the mining layer
1047
          writeMessage("Moving back to mining layer", messageLevel.DEBUG)
1048
          writeMessage("currY: "..currY..", bottomLayer: "..bottomLayer, messageLevel.DEBUG)
1049
          while (currY < bottomLayer) do
1050
            turtleUp()
1051
          end
1052
1053
          -- Now back at the level above bedrock, again reset the flag to tell the turtle to 
1054
          -- try digging again (because it is rare to find air at bedrock level)
1055
          lastMoveNeededDig = false
1056
1057
          -- Reset the look for chests value
1058
          lookForChests = lookForChestsPrev
1059
        elseif ((blocksMined > 0) and ((currX ~= 0) or (currZ ~= 0))) then
1060
          -- This isn't the first block of the row, nor are we at (0, 0) so we need to check the
1061
          -- block below
1062
1063
          -- Check the block down for being a noise block (don't need to check the first
1064
          -- block as it has already been checked in the outer loop)
1065
          if (isNoiseBlock(turtle.compareDown) == false) then
1066
            -- If we are not looking for chests, then just dig it (it takes 
1067
            -- less time to try to dig and fail as it does to do detect and
1068
            -- only dig if there is a block there)
1069
            if (lookForChests == false) then
1070
              turtle.digDown()
1071
              ensureInventorySpace()
1072
            elseif (turtle.detectDown() == true) then
1073
              if (isChestBlock(turtle.compareDown) == true) then
1074
                -- There is a chest block below. Move back and approach
1075
                -- from the side to ensure that we don't need to return to
1076
                -- start through the chest itself (potentially losing items) 
1077
                turtleBack()
1078
                turtleDown()
1079
                emptyChest(turtle.suck)
1080
                turtleUp()
1081
                turtleForward()
1082
                turtle.digDown()
1083
                ensureInventorySpace()
1084
              else
1085
                turtle.digDown()
1086
                ensureInventorySpace()
1087
              end
1088
            end
1089
          end
1090
        end
1091
       
1092
        -- Check the block above for ores (if we're not a (0, 0) in which case
1093
        -- we know it's air)
1094
        if ((currX ~= 0) or (currZ ~= 0)) then
1095
          if (isNoiseBlock(turtle.compareUp) == false) then
1096
            -- If we are not looking for chests, then just dig it (it takes 
1097
            -- less time to try to dig and fail as it does to do detect and
1098
            -- only dig if there is a block there)
1099
            if (lookForChests == false) then
1100
              turtle.digUp()
1101
              ensureInventorySpace()
1102
            elseif (turtle.detectUp() == true) then
1103
              -- Determine if it is a chest before digging it
1104
              if (isChestBlock(turtle.compareUp) == true) then
1105
                -- There is a chest block above. Empty it before digging it
1106
                emptyChest(turtle.suckUp)
1107
                turtle.digUp()
1108
                ensureInventorySpace()
1109
              else
1110
                turtle.digUp()
1111
                ensureInventorySpace()
1112
              end
1113
            end
1114
          end
1115
        end
1116
      end
1117
 
1118
      -- Am now at the other side of the quarry
1119
      onNearSideOfQuarry = not onNearSideOfQuarry
1120
    end
1121
 
1122
    -- If we were digging away from the starting point, will be digging
1123
    -- back towards it on the next layer
1124
    diggingAway = not diggingAway
1125
  end
1126
 
1127
  -- Return to the start
1128
  returnToStartAndUnload(false)
1129
 
1130
  -- Face forward
1131
  turtleSetOrientation(direction.FORWARD)
1132
end
1133
 
1134
-- ********************************************************************************** --
1135
-- Main Function                                          
1136
-- ********************************************************************************** --
1137
-- Process the input arguments - storing them to global variables
1138
local args = { ... }
1139
local paramsOK = true
1140
1141
turtleId = os.getComputerLabel()
1142
rednet.open("right")
1143
1144
if (#args == 1) then
1145
  quarryWidth = tonumber(args[1])
1146
  local x, y, z = gps.locate(5)
1147
  startHeight = y
1148
  if (startHeight == nil) then
1149
    writeMessage("Can't locate GPS", messageLevel.FATAL)
1150
    paramsOK = false
1151
  end
1152
elseif (#args == 2) then
1153
  quarryWidth = tonumber(args[1])
1154
  startHeight = tonumber(args[2])
1155
else
1156
  writeMessage("Usage: OreQuarry <diameter> <turtleY>", messageLevel.FATAL)
1157
  paramsOK = false
1158
end
1159
1160
if (paramsOK == true) then
1161
  if ((startHeight < 6) or (startHeight > 128)) then
1162
    writeMessage("turtleY must be between 6 and 128", messageLevel.FATAL)
1163
    paramsOK = false
1164
  end
1165
 
1166
  if ((quarryWidth < 2) or (quarryWidth > 64)) then
1167
    writeMessage("diameter must be between 2 and 64", messageLevel.FATAL)
1168
    paramsOK = false
1169
  end
1170
end
1171
 
1172
if (paramsOK == true) then
1173
  writeMessage("---------------------------------", messageLevel.INFO)
1174
  writeMessage("** Ore Quarry v0.4 by AustinKK **", messageLevel.INFO)
1175
  writeMessage("---------------------------------", messageLevel.INFO)
1176-
  writeMessage("currX", messageLevel.INFO)
1176+
  writeMessage("%currX%", messageLevel.INFO)
1177-
  writeMessage("currY", messageLevel.INFO)
1177+
  writeMessage("%currY%", messageLevel.INFO)
1178-
  writeMessage("currZ", messageLevel.INFO)
1178+
  writeMessage("%currZ%", messageLevel.INFO)
1179
 
1180
  -- Set the turtle's starting position
1181-
  currX = x
1181+
  currX = x + 0
1182
  currY = startHeight
1183-
  currZ = z
1183+
  currZ = z + 0
1184
  currOrient = direction.FORWARD
1185
 
1186
  -- Calculate which blocks in the inventory signify noise blocks
1187
  determineNoiseBlocksCountCount()
1188
1189
 
1190
  if ((noiseBlocksCount == 0) or (noiseBlocksCount == 15)) then
1191
    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)
1192
  else
1193
    -- Create a Quary
1194
    turtle.select(1)
1195
    currentlySelectedSlot = 1
1196
    createQuarry()
1197
  end
1198
end