From chopping trees and crafting stone bricks to farming wheat, turtles are now doing most of your chores for you. The last resources you need are those you mine from deep underground, such as iron, gold, diamond, coal, redstone, and lapis lazuli.
Your turtles can do this mining for you. Turtles come with a mining program named excavate, which you can use to mine a square hole straight down to the bedrock blocks at the bottom of the Minecraft world. However, as shown in Figure 15-1, you can easily fall into these deep holes, which makes them dangerous.
Figure 15-1: The excavate program creates deep, dangerous holes.
In this chapter, we’ll write a mining program called stairminer that digs stairs into the earth, as shown in Figure 15-2. Near the surface, the turtle will mine mostly dirt and stone blocks. However, as it mines deeper, your turtle will find ore and diamond blocks.
Figure 15-2: The stairminer program makes safe stairs while it mines.
After you’ve set up the stairminer program, you’ll be able to mine ore without fear of falling into an abyss! These stairs also let you safely climb down into your mines so you can dig new tunnels or build an underground base.
Let’s start by designing the stair-mining algorithm. Instead of just mining straight down to bedrock, the stairminer program cuts a stair-shaped pattern into the earth so your turtle won’t make deadly holes you could fall into. The turtle starts on the surface, as shown in Figure 15-3.
Figure 15-3: The turtle on the surface, before mining
We want the algorithm to make the turtle dig a hole one space deep, then move one space forward, dig a hole two spaces deep, then move one space forward, dig a hole three spaces deep, and so on. The turtle should continue to dig until it reaches bedrock or a target depth the player specifies.
We’ll need to translate this behavior into specific actions for the turtle to follow, and we’ll also want to make sure the program is efficient. Every time the turtle mines down, it must return to the surface before digging down again. Because each move uses fuel, we want to limit the turtle’s movement and ensure the turtle is digging as much as possible. We’ll do this by programming the turtle not only to dig on its way down but also to dig on its way up. As a result, the turtle will use its fuel as efficiently as possible by mining columns every time it moves.
Let’s look at the actions the stair-mining algorithm involves and figure out how we can minimize the amount of fuel the turtle uses at each part of the process.
As shown in Figure 15-4, the turtle mines the block at the X to create the first stair step in the algorithm’s first part. Because the turtle doesn’t move in this part, it doesn’t use any fuel. The turtle only needs to dig the first stair step once at the beginning of the program, so the algorithm doesn’t repeat this part.
Figure 15-4: The turtle starts by digging below itself.
Next, the turtle moves forward one block to the next column it will mine, as shown in Figure 15-5. The turtle will repeat this action every time it starts a new column at the surface of the mine.
Figure 15-5: After digging the first column, the turtle moves forward one space to start the next column.
When the turtle is in position to mine the next column, it digs down one block more than it previously mined and moves into the empty space at the bottom of the column. Figure 15-6 shows this action.
Figure 15-6: The turtle digs down from the surface for the next column.
After the turtle finishes mining down, it needs to mine the next column from the bottom up. The turtle mines the block in front of it and moves forward one space at the bottom of the column. When the turtle is in the next column, it needs to mine the block below it to make the column one block deeper than the previous column, as shown in Figure 15-7. Mining the two blocks doesn’t use fuel, so the turtle uses only one unit of fuel in this action. The turtle repeats this action every time it starts a new column from inside the mine.
Figure 15-7: The turtle digs the block in front of it, moves one space forward, and then digs the block below it.
In the algorithm’s last part, the turtle needs to mine up to complete the rest of the column. The turtle digs up to the surface, as shown by the orange arrow in Figure 15-8.
Figure 15-8: The turtle digs up, returning to the surface.
The turtle continues to repeat all the actions except the initial one where the turtle dug down one space at the first column. In other words, the turtle moves forward, digs down, starts a new column, and mines up, repeating the purple, green, and orange steps shown in Figure 15-9. The turtle repeats this mining pattern two columns at a time, mining down and then up. It continues until it reaches bedrock or the targeted depth.
Figure 15-9: The stair pattern mined by the turtle
Before we can create the stairminer program, which uses the stair-mining algorithm we just outlined, we need to write some helper functions to handle situations where sand and gravel blocks might fall into the area the turtle is mining.
You might think that calling turtle.dig() always clears the space in front of a turtle, but it doesn’t. If there are sand or gravel blocks above the block the turtle is digging, those blocks will fall down and block the turtle’s path. Because the stair-mining algorithm we designed sometimes assumes the turtle has cleared a space in front of or above the turtle, you need a function that tells the turtle to keep digging until blocks stop falling into the space the turtle is trying to clear.
Figure 15-10 shows three sand blocks stacked on top of each other in front of the turtle. The turtle must call turtle.dig() three times to clear the sand.
To clear blocks, we’ll write the digUntilClear() and digUpUntilClear() functions, which are nearly identical and clear blocks in front of the turtle and above the turtle, respectively. Because we can use these functions for many different turtle programs, we’ll add them to the hare module. From the command shell, run edit hare. Move the cursor to the bottom of the file and continue the code by adding the following lines:
Figure 15-10: The turtle must dig three times to clear the space in front of it because the sand blocks will keep falling down.
hare
...snip...
216. -- digUntilClear() keeps digging until
217. -- there are no more blocks (used when
218. -- sand or gravel can fall into the path)
219. function digUntilClear()
220. while turtle.detect() do
221. if not turtle.dig() then
222. return false
223. end
224. end
225. return true
226. end
227.
228. -- digUpUntilClear() keeps digging up until
229. -- there are no more blocks (used when
230. -- sand or gravel can fall into the path)
231. function digUpUntilClear()
232. while turtle.detectUp() do
233. if not turtle.digUp() then
234. return false
235. end
236. end
237. return true
238. end
After entering these instructions, save the program and exit the editor. You can also download this program by running pastebin get wwzvaKuW hare.
The digUntilClear() function repeatedly calls turtle.dig() until no blocks remain in front of the turtle. The function returns the value true if the space in front of the turtle is empty, and it returns false if the space can’t be cleared (for example, if the turtle encounters unmineable bedrock blocks).
hare
216. -- digUntilClear() keeps digging until
217. -- there are no more blocks (used when
218. -- sand or gravel can fall into the path)
219. function digUntilClear()
220. while turtle.detect() do
221. if not turtle.dig() then
222. return false
223. end
224. end
225. return true
226. end
The function needs to continue calling turtle.dig() as long as there is a block in front of the turtle, so on line 220 the function uses a while loop to instruct the turtle to dig indefinitely. This loop keeps looping as long as turtle.detect() returns true, which indicates the turtle detects a block in front of it.
Line 221 calls turtle.dig(), which returns false if the turtle can’t dig the block in front of it. The turtle.dig() function returns false for two reasons: when there is an empty space with nothing to dig or when there is a bedrock block that can’t be mined. The execution doesn’t enter the while loop if an empty space is detected because turtle.detect() would have returned false, making not turtle.detect() a true condition. This means that when turtle.dig() returns false in the while loop, the turtle must be facing a bedrock block. In that case, the turtle can’t possibly clear the block in front of it.
Otherwise, the loop keeps looping as long as a block is in front of the turtle. Once the space is clear, the execution exits the loop at line 224. Line 225 returns true from the function.
The digUpUntilClear() function’s code on lines 228 to 238 is almost identical to digUntilClear() except it calls turtle.detectUp() instead of turtle.detect() and turtle.digUp() instead of turtle.dig().
With our helper functions in place to make the stair-mining algorithm possible, let’s write the stairminer program!
The stairminer program mines a stair-shaped pattern into the ground in front of the turtle. From the command shell, run edit stairminer and enter the following code:
stairminer
1. --[[Stair Miner program by Al Sweigart
2. Mines in a stair pattern.]]
3.
4. os.loadAPI('hare')
5.
6. local cliArgs, targetDepth, columnDepth, result, errorMessage
7.
8. cliArgs = {...}
9. targetDepth = tonumber(cliArgs[1])
10.
11. -- display "usage" info
12. if targetDepth == nil or cliArgs[1] == '?' then
13. print('Usage: stairminer <depth>')
14. return
15. end
16.
17. turtle.digDown()
18.
19. columnDepth = 2
20. while true do
21. -- move forward
22. hare.digUntilClear()
23. turtle.forward()
24.
25. -- mine while descending
26. for i = 1, columnDepth do
27. -- check for bedrock
28. result, errorMessage = turtle.digDown()
29. if errorMessage == 'Unbreakable block detected' then
30. print('Hit bedrock. Done.')
31. return
32. else
33. turtle.down()
34. end
35. end
36.
37. -- check if done
38. print('Current depth: ' .. columnDepth)
39. if columnDepth >= targetDepth then
40. print('Done.')
41. return
42. end
43.
44. -- move forward
45. hare.digUntilClear()
46. turtle.forward()
47. turtle.digDown()
48.
49. -- check if there's enough fuel to go up and back down again
50. while turtle.getFuelLevel() < (columnDepth * 2) do
51. -- try to burn fuel items in the inventory
52. for slot = 1, 16 do
53. turtle.select(slot)
54. turtle.refuel()
55. end
56.
57. if turtle.getFuelLevel() < (columnDepth * 2) then
58. print('Please load more fuel...')
59. os.sleep(10)
60. end
61. end
62.
63. -- check for a full inventory
64. while hare.selectEmptySlot() == false do
65. print('Please unload the inventory...')
66. os.sleep(10)
67. end
68.
69. -- mine while ascending
70. for i = 1, columnDepth do
71. hare.digUpUntilClear()
72. turtle.up()
73. end
74.
75. columnDepth = columnDepth + 2
76. end
After you’ve entered all of these instructions, save the program and exit the editor.
Set the turtle on the ground, and then run the stairminer program by passing an integer argument for the target depth. For example, stairminer 6 will make the turtle mine a stair-shaped path six blocks deep. Note that the stairminer program rounds up to the nearest even number because it will always move down one column and then move up the next column. So, stairminer 9 will mine a stair-shaped path 10 blocks deep, the same as if stairminer 10 had been run.
If you get errors when running this program, carefully compare your code to the code in this book to find any typos. If you still cannot fix your program, delete the file by running delete stairminer and then download it by running pastebin get PGH1WYpH stairminer.
The first four lines of stairminer consist of comments describing the program and a call to os.loadAPI() to load the hare module. After the program is set up, we declare several variables we’ll use in the program.
stairminer
6. local cliArgs, targetDepth, columnDepth, result, errorMessage
Line 6 uses a local statement to create five variables. The cliArgs variable contains the command line arguments the program uses when it executes. The targetDepth and columnDepth variables keep track of how far down the turtle should go and how far the turtle has gone up or down a column, respectively. The result and errorMessage variables store the return values from turtle.digDown() calls.
Next, the program stores the command line argument in the targetDepth variable. If the player didn’t enter a number for the command line argument or they entered '?', the program displays a usage message.
stairminer
8. cliArgs = {...}
9. targetDepth = tonumber(cliArgs[1])
10.
11. -- display "usage" info
12. if targetDepth == nil or cliArgs[1] == '?' then
13. print('Usage: stairminer <depth>')
14. return
15. end
The command line arguments are stored in the {...} table, which line 8 stores in a variable named cliArgs. When the player doesn’t enter a number for the command line argument (for example, if the player runs stairminer hello), the tonumber(cliArgs[1]) expression on line 9 evaluates to nil, which sets targetDepth to nil. Line 12 checks if the value in targetDepth is nil or if the command line argument is '?'. If either condition on line 12 is true, the code on lines 13 and 14 runs and shows the program’s usage message.
Recall that the turtle doesn’t have to move down to dig the first column. The turtle starts at the surface, digs down, and then moves forward to the next column. Line 17 tells the turtle to dig downward, creating a one-block-deep column. This action makes the first step in the stair-shaped path.
stairminer
17. turtle.digDown()
The first column only needs to be dug once, so the stair-mining algorithm never repeats this first action. The turtle digs the rest of the columns using a while loop and alternates between digging down and digging up. The turtle digs down for the first two columns, and then it digs up for the third column. After the first three columns, the turtle digs down for every even column and up for every odd column. Let’s look at the code that tells the turtle to dig the even columns.
The columnDepth variable stores the number of blocks the turtle should dig for the column it’s currently mining.
stairminer
19. columnDepth = 2
20. while true do
21. -- move forward
22. hare.digUntilClear()
23. turtle.forward()
Because the turtle digs the first column before the loop, columnDepth starts at 2 and increases at the end of each iteration of the while loop that begins on line 20. Inside the while loop, line 22 clears the block in front of the turtle by using hare.digUntilClear(). If sand or gravel blocks keep falling in front of the turtle, hare.digUntilClear() ensures that all the blocks are cleared before line 23 tells the turtle to move forward one space. When the program finishes line 23, the turtle is in position to dig the rest of the next column.
After the turtle has moved forward, we create a for loop to run in the while loop.
stairminer
25. -- mine while descending
26. for i = 1, columnDepth do
27. -- check for bedrock
28. result, errorMessage = turtle.digDown()
29. if errorMessage == 'Unbreakable block detected' then
30. print('Hit bedrock. Done.')
31. return
32. else
33. turtle.down()
34. end
35. end
The for loop on line 26 tells the turtle to dig downward by calling turtle.digDown() on line 28. However, the digging could fail if the turtle is at the bottom of the Minecraft world, where there are unbreakable bedrock blocks. On line 28, if bedrock is below the turtle, turtle.digDown() returns false for its first return value (which we store in the result variable) and the string 'Unbreakable block detected' for its second return value (which we store in errorMessage). Line 29 checks for the 'Unbreakable block detected' string in errorMessage, and if the string is there, line 30 displays the message Hit bedrock. Done. to the player. When the turtle hits bedrock, it can’t continue to dig the stairs, so the program terminates on line 31.
Otherwise, if no bedrock is detected below the turtle, the turtle can move down and the program continues to the else statement on line 32. Line 33 is inside the else statement’s block and moves the turtle down into the dug-out space. Line 34 ends the else statement’s block, and line 35 ends the for loop’s block. The code inside the for loop from lines 26 to 35 makes the turtle dig down columnDepth times (or until the turtle reaches bedrock).
After digging the column and exiting the for loop, the program runs through the rest of the while loop.
stairminer
37. -- check if done
38. print('Current depth: ' .. columnDepth)
39. if columnDepth >= targetDepth then
40. print('Done.')
41. return
42. end
Line 38 prints columnDepth, which is the number of spaces down the turtle moved in the for loop on lines 26 to 35. The if statement on line 39 checks whether columnDepth (the depth of the column the turtle will dig) is equal to or greater than the targetDepth. If the condition is true, line 40 tells the player the program is done, and the return statement on line 41 terminates the program. Remember: A return statement outside of all functions, like the one on line 41, will terminate a ComputerCraft program.
If the turtle hasn’t reached bedrock after digging down a column, the turtle should clear the space in front of it, move forward, and dig down once. These actions create a stair below the turtle for the next column.
stairminer
44. -- move forward
45. hare.digUntilClear()
46. turtle.forward()
47. turtle.digDown()
On line 45, the turtle calls hare.digUntilClear() to dig the block in front of it. Line 46 moves the turtle forward by calling turtle.forward(). Line 47 digs the block that’s now below the turtle by calling turtle.digDown(). Figure 15-11 shows the state of the stair pattern being carved by the turtle after line 47 executes. The turtle will then mine the column it is currently in from the bottom up.
But before the turtle can mine upward, we need the turtle to check whether it has enough fuel to continue the stair-mining process. Let’s look at how that fuel check works.
Figure 15-11: The stair mine after line 47 executes
When the turtle mines down the even columns, you can easily follow it down the stairs and retrieve the turtle if it runs out of fuel. But if the turtle runs out of fuel as it’s mining up the odd columns, it might be floating too far above your head to reach it. For example, you don’t want the turtle to run out of fuel when it is halfway up a tall column, like the one shown in Figure 15-12.
Figure 15-12: If the turtle runs out of fuel halfway up a deep column, it can be difficult to reach.
The turtle needs to check that it has enough fuel to reach the surface and then go back down. Because the for loop on lines 26 to 35 makes the turtle move down columnDepth number of times, the turtle also needs to move up columnDepth number of times to complete a column up to the surface. This means the turtle must move columnDepth * 2 to go down the current column and back up to the surface again. (Recall that the turtle only uses one unit of fuel per movement. Digging and turning don’t use fuel.)
If the turtle doesn’t have at least columnDepth * 2 units of fuel, it should start consuming any fuel items it has in its inventory. As the turtle carves out the stair-shaped mine, it often mines coal or other blocks it can use as fuel. When the turtle runs low on fuel, we can have the turtle use the mined coal in its inventory to power itself up. The while loop that begins on line 50 checks the turtle’s fuel level by calling getFuelLevel().
stairminer
49. -- check if there's enough fuel to go up and back down again
50. while turtle.getFuelLevel() < (columnDepth * 2) do
If the turtle has less fuel than columnDepth * 2, the execution enters the while loop’s block and begins searching for fuel in the turtle’s inventory.
A for loop inside the while loop searches for fuel in each of the turtle’s inventory slots.
stairminer
51. -- try to burn fuel items in the inventory
52. for slot = 1, 16 do
53. turtle.select(slot)
54. turtle.refuel()
55. end
The for loop on line 52 makes the slot variable iterate over the numbers 1 to 16. On each iteration of the for loop, line 53 selects slot number slot in the turtle’s inventory and line 54 attempts to have the turtle consume the items in the slot as fuel. If the items aren’t usable as fuel, the turtle.refuel() function does nothing.
After consuming any fuel in its inventory, the turtle rechecks whether it has less than columnDepth * 2 fuel. If it still needs more fuel, the turtle prints the message Please load more fuel... and waits 10 seconds for the player to manually put fuel items into its inventory.
stairminer
57. if turtle.getFuelLevel() < (columnDepth * 2) then
58. print('Please load more fuel...')
59. os.sleep(10)
60. end
61. end
Line 57 checks the turtle’s fuel level using the same condition that appeared in the while statement on line 50. If the turtle consumed more than columnDepth * 2 units of fuel, the program skips the if statement’s block. When the if statement’s block is skipped, the execution reaches the end of the while loop on line 61 and goes back to line 50 to recheck the while loop’s condition. The while loop on line 50 uses the same condition as the if statement on line 77, so the condition on line 50 will also be false. This is how the execution moves past the while loop when the turtle has enough fuel.
However, if the condition on line 57 is true because the turtle doesn’t have enough fuel, line 58 displays the message Please load more fuel... to the player and then pauses for 10 seconds by calling os.sleep(10) on line 59. After the pause, the execution reaches the end of the while loop and goes back to line 50 to recheck the condition.
If the player has put more fuel items in the turtle’s inventory, those items are consumed on the next iteration of the while loop. If the player hasn’t added items to the turtle’s inventory, the turtle’s fuel level stays the same, and the program goes through the while loop again. When the while loop runs again, the program displays the message Please load more fuel... and waits another 10 seconds. Until the player puts enough fuel in the turtle’s inventory to make getFuelLevel() return a number larger than columnDepth * 2, the turtle continues waiting for fuel.
After the program ensures that the turtle has enough fuel, it needs to check to make sure the turtle’s inventory isn’t full before it can begin digging up the next column. Let’s look at how this check works next.
If the turtle passes its fuel check, the execution leaves the while loop and is ready to perform its inventory check. The turtle drops any mined blocks if its inventory is already full, which is a waste of blocks. The turtle should check whether it has a full inventory while it is at the bottom of a column so that if it’s full, the player can reach the turtle to unload it.
The selectEmptySlot() function in the hare module selects the first empty slot it finds in the turtle’s inventory. However, we don’t need to select an empty inventory slot for stair mining. Because the selectEmptySlot() function returns false if there are no empty inventory slots, we can use it to determine if the turtle’s inventory is full.
stairminer
63. -- check for a full inventory
64. while hare.selectEmptySlot() == false do
65. print('Please unload the inventory...')
66. os.sleep(10)
67. end
The while loop on line 64 uses selectEmptySlot() to check whether the inventory has at least one empty slot. If the turtle doesn’t have an empty slot, the execution enters the while loop’s block, line 65 displays a Please unload the inventory... message, and line 66 waits 10 seconds. This code is similar to what lines 58 and 59 did for the fuel check. The while loop only stops looping after the player unloads the turtle’s inventory and selectEmptySlot() returns true. After the inventory check, the program continues to line 70 and the turtle mines upward back to the surface.
Because the turtle is columnDepth spaces beneath the surface, it needs to mine columnDepth blocks up to return to the surface. The for loop on line 70 causes the turtle to dig and move upward.
stairminer
69. -- mine while ascending
70. for i = 1, columnDepth do
71. hare.digUpUntilClear()
72. turtle.up()
73. end
Line 71 calls digUpUntilClear() before moving up. We use the function hare.digUpUntilClear() instead of turtle.digUp() when digging up because sand or gravel could be above the turtle. When the loop ends, the turtle will be back at the surface.
Line 75 increments columnDepth by 2 because the loop makes the turtle mine two columns at a time, and so we need to increase columnDepth by two blocks at each iteration.
stairminer
75. columnDepth = columnDepth + 2
76. end
By increasing columnDepth by 2, the turtle moves two units deeper the next time it digs downward. Line 76 is the end statement for the while loop on line 20. When the execution loops back to line 20, lines 22 and 23 will move the turtle forward over the next space, where it will continue to mine down and then back up to the surface. The turtle only stops when columnDepth is equal to or greater than targetDepth (which the player supplied as a command line argument) or when the turtle hits bedrock.
After running the stairminer program, you’ll have a stair-shaped hole that gives you safe access deep underground, and your inventory will contain all the blocks the turtle mined while making it.
The stairminer program uses a complex algorithm, but once you write it, you can leave the drudgery of mining to your turtles. Even if you don’t need the blocks that the stairminer program mines, you could use this program to carve out the staircases for a subterranean lair with stone brick walls (crafted from your cobblestone generator) lining the sides. As long as you can program instructions for the turtle, the possibilities are endless!
By learning how to code and speaking the computer’s language, you can get an army of turtles to automate many tasks in Minecraft. Programming is a useful and fun skill, and I hope you’ll continue to experiment more on your own. When it comes to programming, there’s always more to learn, so get going and invent your own turtle programs. Good luck!