14PROGRAMMING A ROBOTIC FARM

In Survival mode, you need to make sure you’re fed, but hunting for and gathering food is time consuming. Farming and stocking food are better solutions, but maintaining a large farm can be quite a chore. However, you can manage a giant crop field with one well-programmed farming turtle, as shown in Figure 14-1.

Figure 14-1: One turtle can work a large-scale wheat farm.

In this chapter, you’ll write an algorithm that tells your turtles how to plant, harvest, and store crops.

SETTING UP A WHEAT FIELD

To create a wheat-farming algorithm, you must understand how to make a wheat field in Minecraft. To do this, you’ll need a hoe, a flat area of dirt or grass blocks, a bucket, access to a water source such as an ocean or river (the water source doesn’t need to be near the grass), fence blocks, and a chest.

First, go to your water source with your empty bucket and right-click the water with the bucket to fill it. Then, go to your flat area of dirt or grass blocks and use a hoe to till the top of the flat area until you have a 9 × 9 area of tilled blocks. Because tilled blocks revert back to dirt if water isn’t nearby, you’ll need to dig a hole in the middle of your 9 × 9 field and then right-click the hole with the water bucket. Doing so will fill the hole with water. This single water block can water all dirt blocks up to four blocks away from it in any direction, creating a 9 × 9 field for your farm. You can place many of these 9 × 9 fields next to each other to create larger farms.

You should also place a fence around the farm to keep out cows, sheep, and monsters that would block the turtle’s path, and you should place a chest one block off the ground in the bottom-left corner of the farm for the turtle to store its harvest. Figure 14-2 shows what your field should look like.

NOTE

A single turtle can manage a rectangular area of any size. Just be sure to have one water block for each 9 × 9 portion of the field to keep the crops irrigated. You need only one fence around the rectangular field (no matter its size) to keep out mobs and animals.

Figure 14-2: A 9 × 9 wheat field with water in the middle and a turtle and chest in the bottom-left corner

Now that your farm is set up, let’s look at the algorithm the farmwheat program uses to run the farm.

DESIGNING THE WHEAT-FARMING ALGORITHM

Later in this chapter, we’ll write the farmwheat program, but first we need to plan the algorithm the program should use. Before the turtle can start farming the field, it needs to check two things. First, the turtle checks that it is starting next to a chest. If it can’t find a chest, it has nowhere to drop the wheat that it harvests. Because the farmwheat program will reuse the hare.sweepField() function we wrote in Chapter 13, the turtle also needs to check that it has enough fuel to complete a sweep of the field.

After the turtle confirms that it has a chest next to it and enough fuel, it will use the hare.sweepField() function to move over the entire area of the field. Because wheat matures at different rates (most crops grow to maturity within 30 to 60 minutes, or two to three Minecraft days), the whole wheat field probably won’t be mature at the same time. Therefore, we’ll need to design a function to pass to hare.sweepField() that will determine what the turtle should do at each space in the field.

The turtle needs to perform one of three actions:

Plant a seed if no wheat is planted in the space

Do nothing if wheat is planted but hasn’t matured yet

Harvest the wheat if it is mature and plant a seed

After the turtle performs one of these three actions on each space, the turtle will return to its original position, which we programmed into the hare.sweepField() function in Chapter 13. In this case, the turtle’s starting position is next to the chest. The turtle turns to face the chest, drops any harvested wheat into it, and then turns to face the field again. Then the turtle sleeps for 10 minutes to give the planted wheat time to mature before sweeping the field again.

Now that you understand how the algorithm works, you can use it to create the farmwheat program. We’ll start by adding a helper function called findBlock() to hare, which the turtle will use to confirm that it is near a chest.

EXTENDING THE HARE MODULE

The findBlock() function causes the turtle to spin around to see if a certain block is nearby. This function will help our farming turtle locate a nearby chest for storing the harvested wheat. We’ll add the findBlock() function to the hare module because it will also be useful to other programs.

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:

After you’ve entered all of these instructions, save and exit the editor. You can also download this program by running pastebin get wwzvaKuW hare.

The findBlock() function is very simple to use. You pass findBlock() a string for the name ID of the block you’re looking for. For example, later on we’ll pass the function the name ID 'minecraft:chest' to search for a chest. The findBlock() function causes the turtle to turn right up to four times using the for loop on line 205 and has the turtle inspect the block currently in front of it at each turn by using the turtle.inspect() function.

The turtle.inspect() function returns two values. It returns the value true if a block is in front of the turtle to inspect or false if a block is not there. When there is a block to inspect, the turtle also returns data about the block. On line 206, we store the boolean returned from turtle.inspect() in the variable result, and we store the block data in the variable block. Every time the turtle inspects, it checks whether a block exists and whether the block has the same name ID as the one passed to findBlock(). If the turtle finds the block it’s looking for, it stops turning and returns true. Otherwise, the turtle continues turning until it turns a total of four times. If the turtle doesn’t find the block it’s looking for after four turns, the findBlock() function returns false. With our helper function ready, we can start coding the main farmwheat program.

WRITING THE FARMWHEAT PROGRAM

Let’s implement the farming algorithm. From the command shell, run edit farmwheat and enter the following code:

After you’ve entered all of these instructions, save and exit the editor.

RUNNING THE FARMWHEAT PROGRAM

To run the farmwheat program, you must make sure water is within four blocks of each dirt block in the field and that a chest is behind the turtle’s starting space in the bottom-left corner of the field, as shown in Figure 14-3. You must also equip the turtle with a diamond hoe so it can till the dirt or grass blocks. After the turtle is set up, enter farmwheat 9 9 into the command shell to have the turtle farm a 9 × 9 field.

Figure 14-3: The turtle must start one block above the ground with a chest behind it.

Keep in mind that if you move too far away from the turtle, the turtle shuts down. If the turtle shuts down in the middle of the wheat field, you’ll need to move the turtle back to the bottom-left corner of the field and run the program again to make the turtle continue farming. The turtle will continue sweeping over the field, checking and planting wheat as before.

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 farmwheat and then download it by running pastebin get SfcB8b55 farmwheat.

SETUP FOR THE FARMWHEAT PROGRAM

The first five lines of the program consist of the usual comments that describe who wrote the program and what it does. Line 7 loads the hare module so the program can call its functions.

Then the program reads in the command line arguments to get the length and width of the wheat field. If the command line arguments aren’t provided, the program displays a usage message.

Because command line arguments are always strings but the length and width variables will always contain numbers, lines 11 and 12 pass the first and second command line arguments to the tonumber() function. The return values are stored in length and width.

If there is no first or second command line argument, tonumber() will return nil to store in length or width, respectively. When there is no second command line argument or if the first command line argument is '?', the condition for the if statement on line 15 will be true and lines 16 and 17 will execute. Line 16 displays the usage message, and the return statement on line 17 terminates the program.

Next, the program performs more setup steps to make sure the turtle has everything it needs to run the farmwheat program.

Line 20 displays a reminder to the user that they can terminate the program by holding down CTRL-T. The turtle then tries to find the chest with a call to hare.findBlock('minecraft:chest'). If it can’t find a chest, line 24 terminates the program with an error message because there’s no point in continuing the program if there is no chest to store the wheat in.

Because the chest is behind the turtle and the wheat field, the turtle needs to turn left twice (on lines 28 and 29) to face the field again.

WRITING FUNCTIONS TO USE IN THE MAIN PROGRAM

Let’s write a function named checkWheatCrop() that tells the turtle what to do at each space on the field it sweeps, and let’s write another function named plantWheatSeed() that contains the instructions to plant wheat seeds. We’ll also write a third function named storeWheat() that instructs the turtle on what it should do after it finishes harvesting wheat. These three functions will be called from the main part of the farmwheat program.

CHECKING THE CROP

The checkWheatCrop() function inspects the space below the turtle and determines the action the turtle should take. We’ll pass checkWheatCrop() to sweepField() so the function is called at each space the turtle goes over on the field.

The following code instructs the turtle to plant a seed if there currently is no wheat under the turtle.

The turtle.inspectDown() function has the turtle inspect the space directly under it. If there is no wheat, the result variable on line 35 is set to false and line 38 tills the dirt block. Line 39 calls plantWheatSeed(), which we’ll create in the next section. The plantWheatSeed() function plants a wheat seed on the tilled soil block beneath the turtle.

If the space under the turtle isn’t empty (that is, the space contains wheat or a seed), the turtle checks whether the space contains mature wheat.

Line 40 checks whether block is equal to the value nil and then checks whether the block['name'] value is the same as a wheat block’s ID name. If nothing was under the turtle, the turtle.inspectDown() call on line 35 would have set block to nil. This would cause block['name'] on line 40 to be an error because block would be nil instead of a table value. This potential error is why line 40 first checks that block doesn’t equal nil.

The table value in block also has a 'metadata' key whose value indicates how much the wheat has grown. When the wheat seed has just been planted, the 'metadata' value equals 0. When the value equals 7, the wheat has matured and the last condition on line 40 would be true.

In sum, line 40 checks for three different conditions: whether there is a block under the turtle, whether this block is a wheat block, and whether this wheat block has matured. If any of these conditions returns false, the execution moves to line 46, which is the end of the block, and the turtle does nothing.

If all the conditions on line 40 are true, line 42 harvests the wheat by calling turtle.digDown(). Whether mining ore, chopping wood, or harvesting wheat, the turtle.dig() functions use the turtle’s equipped tool to collect blocks. Line 44 calls plantWheatSeed(), which replaces the wheat that the turtle just harvested. We’ll examine this function next.

PLANTING SEEDS

Because we need the same wheat-planting code on lines 39 and 44, we put the code in its own plantWheatSeed() function to avoid having to enter it twice. When you write your own Lua programs, your programs will be more readable if you use functions to eliminate duplicate code.

The first task that the plantWheatSeed() function on line 51 performs is to select wheat seeds in the turtle’s inventory by passing 'minecraft:wheat_seeds' to the call to hare.selectItem() so the selected slot contains wheat seeds.

If there are no seeds, the turtle displays a warning message, doesn’t plant a seed, and continues on through the rest of the program. The program doesn’t need to terminate because the turtle gets more seeds as it harvests mature wheat crops. But if the selectItem() call on line 52 returns true, the turtle’s inventory will have seeds selected.

The else code block that starts on line 54 plants a seed and displays a message to the player.

On line 55, turtle.placeDown() plants a seed if seeds are in the current slot and tilled soil is under the turtle. Then, line 56 displays Planted seed. to the user, and line 58 ends the plantWheatSeed() function’s code block.

STORING WHEAT

After the turtle sweeps the field, it needs to drop any harvested wheat into the chest. To do this, the program calls the storeWheat() function when the sweep has finished. This function makes the turtle first find and then face a chest.

Line 65 calls hare.findBlock() and passes it 'minecraft:chest', which causes the turtle to spin around, stopping when it finds a chest. If the turtle can’t find a chest, the hare.findBlock() function call returns false and line 66 terminates the program with an error message.

Otherwise, the program continues and assumes the turtle is facing a chest. The turtle must select an inventory slot that contains wheat and drop it into the chest by calling turtle.drop().

Line 70 is a while loop whose condition is based on the value returned by hare.selectItem('minecraft:wheat'). As long as wheat is in the turtle’s inventory, this function call returns true and the while loop continues to loop. Inside this loop, line 71 shows the player the number of wheat items in the current slot and line 72 attempts to drop them into the chest in front of the turtle.

If the chest is already full, the call to turtle.drop() on line 72 returns false. In this case, line 73 terminates the program with an error message. Otherwise, the loop keeps looping until there is no more wheat, and then the execution moves on to line 78.

When the turtle is facing the chest, the field is behind it. To face the field again, the turtle needs to turn around.

Lines 78 and 79 turn the turtle to the left twice so that it is facing the field again. Line 80 ends the storeWheat() function.

We now have all three functions that will be called from the main program: checkWheatCrop(), plantWheatSeed(), and storeWheat(). Let’s start farming!

FARMING WITH A LOOP

Let’s write the main loop of the program with the functions defined on lines 32 to 80. In this loop, the turtle first checks that it has enough fuel. If the turtle has enough fuel to sweep the field it’s on, it will plant and harvest the wheat field, store any harvested wheat in a chest, and then wait 10 minutes to allow the wheat to grow before repeating the whole process.

The loop’s first step checks that the turtle has enough fuel to move across the entire field and back to the starting space.

On line 84, the while loop’s condition is true. Therefore, the loop will loop forever, so the program terminates only if error() is called or the player holds down CTRL-T.

To check how much fuel a turtle needs, we need to come up with an equation to calculate the fuel units the turtle needs to sweep the field. To keep this calculation simple, we’ll overestimate the turtle’s fuel needs.

First, we figure out how many units of fuel the turtle needs to travel down each column. Turtles use one unit of fuel each time they move, and moving down one column that is length blocks long requires length - 1 moves, so that would be length - 1 units of fuel. However, the turtle also uses fuel whenever it moves to the next column of the field. So the turtle needs length - 1 + 1, or length, units of fuel to move along each column.

We then multiply the length number by the number of rows in the field. There are width number of rows in the field, so we’ll calculate the total amount of fuel the turtle needs to sweep the entire field as length * width.

In addition, the turtle needs enough fuel to move back to its starting position. If the turtle ends up at the far end of the field from its starting position, it needs to move down one column (length number of moves) and down one row (width number of moves). So the final formula for the amount of fuel the turtle needs to do one sweep of the field is length * width + length + width. Figure 14-4 shows a diagram of the formula.

Of course, this formula is an overestimate because the turtle could end up on the closer side of the field, where it only needs to move down one row to get back to its starting position. Also, the leftmost path of the red arrow should be length - 1 blocks long to account for the starting block, not length blocks long, and the blue arrow should be length - 1 blocks long as well to account for the corner block. But this formula is acceptable because it’s better to overestimate the amount of fuel needed rather than underestimate it.

If the turtle doesn’t have enough fuel, the condition on line 86 returns false and line 87 terminates the program with the error() function and a message telling the player the turtle needs more fuel. Otherwise, if the condition on line 86 returns true, the turtle can begin farming by calling hare.sweepField() on line 92.

As in the previous chapters, the hare.sweepField() function takes the length and width of the field and controls the turtle’s movements across the entire field. Line 92 passes checkWheatCrop to the sweepField() function. Remember not to enter checkWheatCrop() because if you add parentheses to the function name, Lua will call checkWheatCrop() and pass its return value to hare.sweepField() instead of passing the function.

The hare.sweepField() function returns after the turtle has harvested and planted the entire field. When the turtle is done sweeping, it needs to place the wheat it has in its inventory into the chest next to the starting space. We wrote the storeWheat() function to do this, which we call on line 93.

We want the turtle to plant and harvest continually, but the wheat that was just planted needs time to grow. If the turtle immediately starts sweeping the field again, none of the wheat will have time to grow and the turtle will waste fuel. Instead, we make the turtle pause for 10 minutes on line 95 and display a message to the player telling them this.

farmwheat

95. print('Sleeping for 10 minutes...')96. os.sleep(600)97. end

Line 96 calls os.sleep(), passing 600 to the function so the turtle pauses for 600 seconds (600 seconds divided by 60 seconds per minute equals 10 minutes). Line 97 ends the while loop that began on line 84.

BONUS ACTIVITY: GIANT WHEAT FIELDS

In this chapter, we’ve made the turtle farm a 9 × 9 area, but the farmwheat program can handle farms of any size. Create a giant wheat field like the one in Figure 14-1. Then run the program with command line arguments based on the size of your larger wheat field to make the turtle farm the entire area!

TIPS FOR AUTOMATING OTHER KINDS OF FARMING

Much of the code that runs in the farmwheat program is in hare.sweepField(). Because you can pass hare.sweepField() a function to call at each space in the field, it can accomplish a variety of tasks.

If you want to make other types of farms with ComputerCraft, you can write and pass functions that reuse the hare.sweepField() code to farm other crops. In the following sections, I provide some tips for writing different kinds of farming programs.

FARMING VEGETABLES

You can easily substitute vegetables for your wheat crops by changing a few values and names in your code. For example, checkWheatCrop() and plantWheatSeed() could be renamed checkVegCrop() and plantVeg(). Potatoes and carrots in Minecraft don’t have seeds. Instead, you plant potato and carrot items directly into tilled soil using the turtle.placeDown() function, which produces multiple items when you harvest the mature plants. Figure 14-5 shows a turtle farming carrots.

You’ll also need to replace the Minecraft name IDs the program checks for. For example, instead of calling hare.selectItem('minecraft:wheat_seeds'), your vegetable-farming program needs to call hare.selectItem('minecraft:potato') and hare.selectItem('minecraft:carrot').

Figure 14-5: A turtle programmed to farm carrots

Your vegetable-farming program shouldn’t store all the carrots and potatoes it harvests in a chest, because it needs items to plant the next time the turtle sweeps the field.

MILKING COWS AND SHEARING SHEEP

Turtles can milk cows with an empty bucket or use shears to shear sheep for wool. You don’t need to equip the turtle with these items. Instead, you put them in the turtle’s currently selected slot. To provide enough space for the turtle to hover over the cows and sheep, the turtle must be two spaces above the ground (unlike one space for the other farms). Figure 14-6 shows the turtle hovering two spaces off the ground so it can sweep over cows and sheep.

Figure 14-6: Turtles must be two spaces off the ground to milk cows or shear sheep.

To milk a cow, the current slot must contain an empty bucket. While the turtle is above the cow, call the turtle.placeDown() function to fill the bucket with milk. To shear sheep, the current slot must have shears. While the turtle is above the sheep, call the turtle.placeDown() function to shear the sheep. The turtle automatically picks up any sheared wool.

The cows and sheep will move around the field, but you can use fences to contain them. It’s possible they might evade the turtle as it sweeps across the field, but the turtle will find them more often than not. In addition, because cows and sheep are not blocks, calling turtle.inspectDown() won’t work. Instead, the turtle will have to blindly call turtle.placeDown() with the empty bucket or shears in the current slot to milk or shear the livestock.

GATHERING CHICKEN EGGS

Chickens produce eggs every five to ten minutes, but like all dropped items, eggs will disappear after five minutes if they’re not picked up. You can program turtles to sweep a field of chickens and pick the eggs off the ground.

To create a chicken farm, place chickens in a fenced-off rectangular field that the turtle sweeps. Then pass the turtle.suckDown function (without the parentheses) to the hare.sweepField() function to make the turtle pick up any items the chickens drop. Figure 14-7 features an egg-gathering turtle. Like livestock, eggs are not blocks, so turtle.inspectDown() won’t identify them.

Figure 14-7: A turtle gathers eggs dropped by chickens.

FARMING CACTI AND SUGAR CANE

Cacti and sugar cane have specific growing requirements. For example, they both need at least a block of empty space separating them in order to grow, so you might need to adjust your code to skip rows to create a cacti or sugar cane farm.

Figure 14-8: Cacti only grow on sand and must have a block of space separating them.

Sugar cane only grows on sand or dirt blocks that are adjacent to water, so every other row of your farm needs to be water blocks, as shown in Figure 14-9.

Figure 14-9: A turtle tends a sugar cane farm. Note that sugar cane must be planted next to water.

Both crops also grow up to three blocks in height. When a turtle hovers four blocks over a field of cacti or sugar cane, calling the turtle.digDown() function harvests any cactus or sugar cane that has grown to its maximum height. The Minecraft IDs for cactus and sugar cane are 'minecraft:cactus' and 'minecraft:reeds', respectively.

WHAT YOU LEARNED

By reusing the hare.sweepField() code, you can easily program turtles to automatically harvest several different kinds of crops. In this chapter, you learned how to use turtles to hoe dirt blocks, plant seeds, and detect mature wheat plants. Calling turtle.inspectDown() returns a table value that has a 'metadata' key whose value is a number from 0 to 7. When this number value is 7, the wheat is fully mature. The turtle uses this value to detect when the wheat is ready to be harvested with the diamond hoe.

The algorithm in hare.sweepField() has been useful for many different tasks. In Chapter 15, you’ll develop a new algorithm for mining a stair-shaped hole into the earth.