image

IT’S WHEN YOU can start to process large amounts of data that computer programming gets really exciting. Your program then becomes a set of rules that govern how that data is read, processed and visually represented—the data becomes the really important part.

In this adventure, you first look at how to create text files that define mazes. The mazes will then be automatically built in the Minecraft world for you and your friends to solve; you'll wonder how such a small Python program can build something so big in the Minecraft world.

You are then going to develop this simple idea into a complete virtual 3D scanning and printing facility called the duplicator room. Anything you build inside the duplicator room can be saved to a file, recalled later and teleported to any location in your Minecraft world, and even loaded into Minecraft worlds on other computers! You will be able to build your own library of objects and build them in Minecraft so quickly that your friends will all want duplicating machines of their own!

Reading Data from a File

Computer programs are generally quite unintelligent—they repeatedly follow instructions that you give them. If you don’t change any input to your program, it will do exactly the same thing every time you run it. The Minecraft programs you have been making have been quite interactive because they change what they do depending on what happens in the game world, which is the input to the program.

Interesting Things You Can Do with Data Files

Another interesting way to make computer programs do different things every time you run them is to store the input data in a text file and have the computer read that file when it starts. You can then create any number of text files and even have a menu so you can choose which file to open depending on what you want to do with the program. This is called a data-driven program because it is mostly the data in the external text file that defines what the program does.

If you think about it, many of the programs you already use on your computer use data files. The word processor you use to type up your homework stores your homework in a data file; when you take a photo with a digital camera it is stored in a data file; and your image editor program reads that photo from the data file. Even Minecraft uses data files behind the scenes for tasks like saving and loading the world, and for the texture packs used to build all of the different blocks in the world from. Using data files with a program is a much more flexible way of working because it means that the program can be used for lots of different things without forcing you, the user, to modify the program every time you want it to do something slightly different. You can also share those data files with your friends, if they have the same programs as you.

Your first step in writing a data-driven program is to learn how to use Python to open and read text files from the computer filing system.

Making a Hint-Giver

To learn how to open and read text files, you are going to write a simple hint-giver program. This program reads a file you have prepared of useful Minecraft hints and tips and displays one of the hints on the Minecraft chat at random intervals. You’re going to need a text file with tips in it, so your first task is to create this file. You can create this file in the normal Python editor, just like you do with your Python programs.

Start up Minecraft, IDLE, and if you are working on a PC or a Mac, start up the server, too. You should have had a bit of practice with starting everything by now, but refer to Adventure 1 if you need any reminders.

  1. In IDLE, create a new text file by choosing File ⇒ New File from the menu. Save the file as tips.txt.
  2. Now list four or five tips, using one line of text for each tip. Here are a few example Minecraft tips, but it’s much more fun if you make up your own. Make sure you press the Enter key at the end of each line, so that this creates a newline inside the file for each line. (You find out more about newlines later in this adventure.)

    Build yourself a house before the mobs come and get you
    Use flowing water to fill underwater channels
    Build up with sand then knock out the bottom block
    Use both first-person and third-person views
    Double tap space bar to fly into the sky

  3. Save this file in your MyAdventures folder. You don’t run this file, as it is not a Python program but a text file you’re going to tell your Python program to use.

Now you have a data file with tips in it—in other words, input data—it’s time to write the program that processes that input data. This program read a random tip from the file and display it after a random interval on the Minecraft chat. Then, while you are playing your game, helpful tips will pop up on the chat.

  1. Start a new file with File ⇒ New File and then save it as tipChat.py.
  2. Import the necessary modules:

    import mcpi.minecraft as minecraft
    import time
    import random

  3. Connect to the Minecraft game:

    mc = minecraft.Minecraft.create()

  4. Set a constant for the filename, so that you can easily change it to read a different file later:

    FILENAME = "tips.txt"

  5. Open the file in read mode (see the following Digging into the Code for a fuller explanation of this line):

    f = open(FILENAME, "r")

  6. Read all the lines of the file into a list called tips. Remember that you have already used lists in Adventure 4 (magic bridge builder):

    tips = f.readlines()

  7. Close the file when you have finished with it. It is good practice to always close a file when you have finished with it, because when the file is open it cannot be used by other programs, such as the editor you used to enter the text into the file in the first place:

    f.close()

  8. Now write a game loop that waits a random length of time between three and seven seconds:

    while True:
    time.sleep(random.randint(3,7))

  9. Now tell the program to choose a random message from the tips list and display it on the chat. You need to use the strip() function to strip out unwanted newline characters. You’ll be looking at newline characters later on, so don’t worry too much about understanding this for now.

    msg = random.choice(tips)
    mc.postToChat(msg.strip())

Save and run your program. What happens? As you walk around the Minecraft world and play your game, every so often a tip pops up on the chat, just like in Figure 5-1. Note how the Minecraft chat builds up messages on the screen, and gradually they disappear over time.

image

FIGURE 5-1 Random tips pop up on the chat as you play Minecraft.

Building Mazes from a Data File

Now that you know how to make your programs read from data files, you are ready to get a bit more adventurous. You know from earlier adventures that it is easy to place blocks in the Minecraft world. What if you could build blocks using lists of blocks stored in a data file? That’s exactly what you are going to do here: You are going to build a complete 3D maze in Minecraft, where the maze data is stored in an external file! But first, you need to decide how the maze data will be represented inside the file.

The mazes that you design are 3D because they are built in the Minecraft 3D world, but really they are just 2D mazes built out of 3D blocks—there is only one layer to your maze. This means that the data file needs to store x and z data for each block in the maze, so it is rectangular.

You need to decide how blocks themselves are represented in the data file. You use walls and air. To keep things simple, use a 1 to represent a wall and a 0 to represent air. You can always change the block types inside your Python program that are used for walls later.

Understanding CSV Files

For this program, you use a special type of text file called a CSV file, to represent your mazes as files in your computer’s filing system.

CSV is a very simple and widely used format. Here is a sample of a CSV file that stores part of a table from a database with details about Minecraft gamer characters. In this CSV file, the first row (or line) in the file has the names of the fields, and all other lines have data separated by commas:

Name,Handle,Speciality
David,w_geek,Coding in Python
Roma,physics_gurl,Designing big buildings
Ryan,mr_teck,Minecraft robots
Craig,rrrrrrrr,TNT expert

This CSV file has one header row, which has three field names called Name, Handle and Speciality. There are four data rows, each with three fields of data.

For your maze data file, you don’t need a header row because each column in the table represents the same type of data; that is, it stores a number 0 for a space and a number 1 for a wall. You can download this sample maze file from the companion website, but here it is if you want to type it in:

  1. Create a new text file by choosing File ⇒ New File. Use File ⇒ Save As and name the file maze1.csv. Make sure you save this file in your MyAdventures folder.
  2. Type in the following lines very carefully, making sure there are exactly the same number of columns on each line. If you look very carefully at this data you should already be able to see the structure of this maze! There are 16 numbers per line and 16 lines, so perhaps use a pencil to tick off each line as you type it in so you don’t lose your place:

    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
    1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1
    1,0,0,1,0,0,0,0,1,0,1,0,1,0,0,1
    1,1,0,1,0,1,1,0,0,0,0,0,1,0,1,1
    1,1,0,1,0,1,1,1,1,1,1,1,1,0,1,1
    1,1,0,0,0,1,1,1,1,1,0,0,0,0,1,1
    1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1
    1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1
    1,0,1,1,1,1,0,0,0,0,0,1,1,1,1,1
    1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
    1,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1
    1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1
    1,0,1,0,1,1,1,1,0,1,1,1,1,1,0,1
    1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1
    1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1

  3. Save this file. You will use it once you have written the Python program that reads and processes the data in this file.

Building a Maze

Now that you have a data file describing your maze, the final step is to write the Python program that reads data from this file and builds the maze in Minecraft using blocks.

  1. Start a new file with File ⇒ New File and then use File ⇒ Save As to save it as csvBuild.py.
  2. Import the necessary modules:

    import mcpi.minecraft as minecraft
    import mcpi.block as block

  3. Connect to the Minecraft game:

    mc = minecraft.Minecraft.create()

  4. Define some constants. The GAP constant is the block type that is used in the spaces. This is normally air, but you could experiment with different block types to make the maze interesting. The WALL constant is the block type that the walls of the maze are built with, so choose this carefully as some block types won’t work very well as walls. The FLOOR constant is used to build the layer that the maze stands on:

    GAP = block.AIR.id
    WALL = block.GOLD_BLOCK.id
    FLOOR = block.GRASS.id

  5. Open the file containing your maze data. The FILENAME constant is used so that it is easy for you to change the filename later to read in different maze files:

    FILENAME = "maze1.csv"
    f = open(FILENAME, "r")

  6. Get the player position and work out the coordinates of the bottom corner of the maze. Adding 1 to the x and z makes sure that the maze doesn’t get built on top of your player’s head. You could vary the y coordinate to build the maze in the sky if you wanted to:

    pos = mc.player.getTilePos()
    ORIGIN_X = pos.x+1
    ORIGIN_Y = pos.y
    ORIGIN_Z = pos.z+1

  7. The z coordinate varies at the end of each line of data, so start it off at the origin of the maze. It varies a little later on in the program:

    z = ORIGIN_Z

  8. Loop around every line of the file. See Digging into the Code, which explains what the readlines() function does. The for loop is actually looping through every line in the file, one by one. Each time round the loop, the line variable holds the next line that has been read from the file:

    for line in f.readlines():

  9. Split the line into parts, wherever there is a comma. See Digging into the Code, which explains what the split() function does. Remember that all of the lines that are part of the body of the for loop have to be indented one level:

    data = line.split(",")

  10. Look at this step very carefully: You now have another for loop. This is called a nested loop, because one loop is nested inside another. Your program has to reset the x coordinate at the start of every new row of the maze, so this has to be done outside of the for loop that draws a whole row:

    x = ORIGIN_X
    for cell in data:

  11. Each number read in from the row is actually read in as a piece of text (a string), so for this program to work, you have to put quotes ("") around the number. This if/else statement chooses whether to build a gap or a wall, based on the number just read back from the CSV file. A 0 builds a gap, and anything else builds a wall. Make sure your indentation is correct here: The if statement is indented twice because it is part of the for cell in data loop, which is part of the for line in f.readlines() loop. The b variable here is useful as it makes the program a bit smaller and simpler. (Try rewriting this part without using the b variable to see what I mean!)

    if cell == "0":
    b = GAP
    else:
    b = WALL
    mc.setBlock(x, ORIGIN_Y, z, b)
    mc.setBlock(x, ORIGIN_Y+1, z, b)
    mc.setBlock(x, ORIGIN_Y-1, z, FLOOR)

  12. Update the x coordinate at the end of the for cell loop. This must be indented so that it lines up with the previous mc.setBlock() as it is still part of the body of the for cell in data loop.

    x = x + 1

  13. Update the z coordinate at the end of the loop that processes each row of data (this must be indented one level only, as it is part of the for line in f.readlines() loop).

    z = z + 1

Save your program and double-check that all the indentation is correct. If the indentation is wrong, the program doesn't work correctly, and you get some very strange-shaped mazes!

Run your program and a fantastic (and quite hard to solve) maze will be built in front of you. Walk around the maze and see if you can solve it without breaking down any walls or flying. Figure 5-2 shows what the maze looks like from ground level.

image

FIGURE 5-2 A ground level view of the maze inside Minecraft

If you get stuck, you can always cheat a bit and fly into the sky to get a 3D bird’s-eye view of the maze, as shown in Figure 5-3.

image

FIGURE 5-3 A bird’s-eye view of the maze inside Minecraft

Building a 3D Block Printer

Building mazes is great fun, but there is so much more you can do with data files now that you know the basics! Why stop at building with only two block types on a single layer? You are now going to use your maze program as the basis of your very own 3D printer and 3D scanner that will duplicate trees, houses—in fact, anything you can build in Minecraft—and “print” them all around the Minecraft world at the touch of a button! This is a block builder really, but I like to call this a 3D printer because of the way you can sometimes see the blocks building up row at a time, just like how a computer printer prints onto a piece of paper row at a time, or how a 3D printing machine builds up structures a layer at a time.

You’re getting quite good at building programs now, and each new program you build is larger than the last one. Just like in the previous adventure, where you built your program out of functions, you are going to build this program up in steps.

Hand-Crafting a Small Test Object to 3D Print

In your maze program, data was stored in a CSV file, where each line in the file represents a line of blocks inside the Minecraft world. Each column inside that line (separated by commas) corresponds to one block in that line of blocks. This is a 2D structure because it stores a block for each x and z coordinate of a rectangle region. The mazes you have built here are really only 2D mazes, as they only have one layer. They are just built inside the 3D Minecraft world by using 3D blocks.

To build 3D shapes, you need to build up in the y dimension too. If you think of a 3D object as just multiple layers or slices, then your 3D program is not all that different from your maze program. All you need to do for a cube that is 5 by 5 by 5 blocks in size is to store five layers of information.

There is a problem with this. Your Python program doesn't know how many lines to expect until it has processed the whole file. It could assume a certain size, but it would be nice to design a flexible CSV file format that can work with any size of object.

To solve this problem, you add some metadata in the first line of the file that describes the size of the object.

You are now going to create a sample object that is a tiny 3D cave so that you can test the 3D printing capabilities of your program. You can download this sample object as shown in Figure 5-4 from the companion Wiley website by downloading the CodeFiles.zip file, or just type it in here like you did with your maze.

image

FIGURE 5-4 You build a small stone cave by writing a CSV data file.

  1. Start a new file with File ⇒ New File, and save it as object1.csv.
  2. Type the first metadata line that describes the size of the object. Your test object is 5 by 5 by 5. The first number is the x-size (width), the second number is the y-size (height) and the third number is the z-size (depth):

    5,5,5

  3. Type the first layer of the test object, making sure that you leave a blank line before the first line of data. You need all the block positions filled in on the bottom layer, which is why every number is set to a 1:

    1,1,1,1,1
    1,1,1,1,1
    1,1,1,1,1
    1,1,1,1,1
    1,1,1,1,1

  4. You are building a hollow cube with an open front, so put in three layers with that format. Make sure you put a blank line before these numbers:

    1,1,1,1,1
    0,0,0,0,1
    0,0,0,0,1
    0,0,0,0,1
    1,1,1,1,1

  5. Use copy and paste to put in two more identical hollow layers below this one, making sure you leave exactly one blank line between each square section of numbers.
  6. Finally, put a solid top on the object. To do this, use copy and paste to copy the rows of data from step 3, again making sure that there is a blank line before the first row of numbers of this layer.

Save your file, but don’t run it—you can’t run it as it is not a Python program but a data file that will be used by the program you are now going to write.

Writing the 3D Printer

Now that you have written your test data, it is time to write the 3D printer program that builds the hollow cave inside the Minecraft world. This program is very similar to your maze program, but this one has three nested loops: one loop for each of the x, y and z dimensions.

  1. Start a new file with File ⇒ New File, and use File ⇒ Save As from the menu to save it as print3D.py.
  2. Import the necessary modules:

    import mcpi.minecraft as minecraft
    import mcpi.block as block

  3. Connect to the Minecraft game:

    mc = minecraft.Minecraft.create()

  4. Create a constant that is the name of your data file. You can easily change this later on if you want to read different files with different objects in them:

    FILENAME = "object1.csv"

  5. Define a print3D() function. You reuse this function later on in the final project, so make sure you name it correctly:

    def print3D(filename, originx, originy, originz):

  6. Just as you did with your maze builder program, open the file in read mode and read in all the lines into a Python list:

    f = open(filename, "r")
    lines = f.readlines()

  7. The first item in the list is at index 0. This is the first line of the file that holds the metadata. Split this line into parts by using the split() function. Then store those three numbers into the variables sizex, sizey and sizez. You have to use the int() function here because when you read lines from a file they come in as strings, but you need them to be numbers so you can calculate with them later:

    coords = lines[0].split(",")
    sizex = int(coords[0])
    sizey = int(coords[1])
    sizez = int(coords[2])

  8. Set a lineidx variable to remember the index of the line in the lines[] list that you are processing. Because your program scans through the lines list reading out data for different layers of the 3D object, you can’t use this as the loop control variable:

    lineidx = 1

  9. The first for loop scans through each of the vertical layers of the data read in from the file. The first few lines of the file are for the layer that is built at the lowest y layer. You can put a postToChat here so that the progress of the printing process is visible inside Minecraft:

    for y in range(sizey):
    mc.postToChat(str(y))

  10. Skip over the blank line between each layer in the file by adding 1 to the lineidx variable. Remember that these blank lines are only in the file so that it is easier for people to read it, but your program has to skip over them.

    lineidx = lineidx + 1

  11. Start a nested for loop that reads out the next line in the file and splits it whenever there is a comma. Be careful of the indentation here; the for has to be indented two levels (one for the function and one for the for y loop) and the code inside this for x loop needs to be indented three levels.

    for x in range(sizex):
    line = lines[lineidx]
    lineidx = lineidx + 1
    data = line.split(",")

  12. Now start your third for loop that scans through each of the blocks in the line just read and builds them inside Minecraft. The for z loop is indented three levels and the body of the loop is indented four times, so take very special care of the indentation; otherwise you'll end up with some very strange-shaped objects!

    for z in range(sizez):
    blockid = int(data[z])
    mc.setBlock(originx+x, originy+y, originz+z, ↩
             blockid)

  13. Finally, type the lines of the main program, which are not indented at all. These lines read the player position and then ask the print3D() function to print your 3D object just to the side of where you're standing:

    pos = mc.player.getTilePos()
    print3D(FILENAME, pos.x+1, pos.y, pos.z+1)

Save your program and run it to see what happens! Run around to different locations inside Minecraft and run the program. Every time you run the program, a hollow stone cave should be built just near your player! Figure 5-5 shows how easy it is to build lots of stone caves very quickly.

image

FIGURE 5-5 3D printing lots of stone caves inside Minecraft

Building a 3D Block Scanner

Your 3D printer is already very powerful. You can create a big library of CSV files for different objects you want to build, and then, whenever you need to, just run your print3D.py program with a different FILENAME constant to stamp the object all over your Minecraft world.

However, building larger and more complex objects becomes very difficult if you have to enter all the numbers into the CSV file by hand. Minecraft itself is the best program for building complex objects—so, what if you could use Minecraft to create these CSV data files for you? Here, you are going to run up to a tree and hug it, and then make an identical copy of the tree so you can duplicate it all over the Minecraft world. Fortunately, 3D scanning works a bit like 3D printing in reverse, so this not quite as hard as you might think.

  1. Start a new file with File ⇒ New File, save it with File ⇒ Save As and call the program scan3D.py.
  2. Import the necessary modules:

    import mcpi.minecraft as minecraft
    import mcpi.block as block

  3. Connect to the Minecraft game:

    mc = minecraft.Minecraft.create()

  4. Create some constants for the name of the CSV file you want to scan to and the size of the area that you want to scan:

    FILENAME = "tree.csv"
    SIZEX = 5
    SIZEY = 5
    SIZEZ = 5

  5. Define a scan3D() function. You use this function in a later program, so make sure it is named correctly:

    def scan3D(filename, originx, originy, originz):

  6. Open the file, but this time open it in write mode using the "w" file mode inside the open() function:

    f = open(filename, "w")

  7. The first line of the file has to contain the metadata, so write the x, y and z sizes in the first line. See Digging into the Code to understand what the " " at the end of the line is and why you need it.

    f.write(str(SIZEX) + "," + str(SIZEY) + ","
    + str(SIZEZ) + " ")

  8. The scanner program is just the reverse of the printing program, again using three nested loops. Here is this part of the program all in one go, so that it is easy for you to get the indents correct. See Digging into the Code for an explanation of how the comma-separated lines are created:

    for y in range(SIZEY):
    f.write(" ")
    for x in range(SIZEX):
    line = ""
    for z in range(SIZEZ):
    blockid = mc.getBlock(originx+x,
    originy+y,
    originz+z)
    if line != "":
    line = line + ","
    line = line + str(blockid)
    f.write(line + " ")
    f.close()

  9. Finally, the main program is not indented at all. This just reads the player position, calculates a cube space, where the player is standing at the centre of that space, and asks the scan3D() function to scan that whole space to your CSV file.

    pos = mc.player.getTilePos()
    scan3D(FILENAME, pos.x-(SIZEX/2), pos.y, pos.z-(SIZEZ/2))

Save your program. Double-check that all your indentation is correct before you run it.

Now run up to a tree and hug it (stand as close as you can to its trunk), and run your scan3D.py program. What happens?

Did anything happen at all? Remember that this program scans an object to a CSV file, so you have to look at the CSV file tree.csv which is in the MyAdventures folder, to see what numbers have been stored in it. Open the file tree.csv by choosing File ⇒ Open from the editor window and choosing All Files in the file type drop-down menu. Figure 5-6 shows a section of the tree.csv file that I captured after hugging a tree on my computer! Because the scanning space is quite small, you might regularly get only half a tree scanned, but you can always change the SIZE constants to scan a bigger area.

image

FIGURE 5-6 Contents of CSV file after hugging and scanning a tree

Building a Duplicating Machine

You now have all the building blocks of programs you need in order to design and build your own 3D duplicating machine that will be envy of all your friends. With it, you will be able to jump into the Minecraft world and make a magic duplicating room materialise, in which you can build any object you like. You can then save these objects to files, load them into the room to edit them or magically duplicate them all over the Minecraft world. Finally, you can escape from the world and make the duplicating room vanish completely, leaving no traces of your magic behind you.

Just like in some of the earlier adventures, you are going to use your existing programs and stitch them together into a much bigger program. Because this program has a number of features, you are going to add a menu system to it so it is easy to control.

Writing the Framework of the Duplicating Machine Program

The first thing to do is to write the framework of the program that makes it all hang together. You start with some dummy functions that just print their name when you call them, and gradually fill in their detail using your existing functions from other programs. You may remember that this is the same way you built up your treasure hunt game in Adventure 4.

  1. Start a new file with File ⇒ New File and save it as duplicator.py.
  2. Import the necessary modules:

    import mcpi.minecraft as minecraft
    import mcpi.block as block
    import glob
    import time
    import random

  3. Connect to the Minecraft game:

    mc = minecraft.Minecraft.create()

  4. Set some constants for the size of your duplicating machine. Don’t set these too big, or the duplicator will take too long to scan and print objects. Also set an initial default position for your duplicating room:

    SIZEX = 10
    SIZEY = 10
    SIZEZ = 10
    roomx = 1
    roomy = 1
    roomz = 1

  5. Define some dummy functions for all the features that this program needs to have. You fill in the detail of these soon:

    def buildRoom(x, y, z):
    print("buildRoom")

    def demolishRoom():
    print("demolishRoom")

    def cleanRoom():
    print("cleanRoom")

    def listFiles():
    print("listFiles")

    def scan3D(filename, originx, originy, originz):
    print("scan3D")

    def print3D(filename, originx, originy, originz):
    print("print3D")

  6. The dummy menu function is special because it returns a value at the end of the function. For now, you can generate a random option and return it so that it is possible to test the early versions of your program, but soon you write a proper menu here. You are writing a dummy menu so that you can test the structure of your program first, and you soon fill this in with a proper menu:

    def menu():
    print("menu")
    time.sleep(1)
    return random.randint(1,7)

  7. Write the main game loop that displays a menu and then uses the function required for that feature. See Digging into the Code for more information about how the anotherGo variable is used:

    anotherGo = True
    while anotherGo:
    choice = menu()
    if choice == 1:
    pos = mc.player.getTilePos()
    buildRoom(pos.x, pos.y, pos.z)
    elif choice == 2:
    listFiles()
    elif choice == 3:
    filename = input("filename?")
    scan3D(filename, roomx+1, roomy+1, roomz+1)
    elif choice == 4:
    filename = input("filename?")
    print3D(filename, roomx+1, roomy+1, roomz+1)
    elif choice == 5:
    scan3D("scantemp", roomx+1, roomy+1, roomz+1)
    pos = mc.player.getTilePos()
    print3D("scantemp", pos.x+1, pos.y, pos.z+1)
    elif choice == 6:
    cleanRoom()
    elif choice == 7:
    demolishRoom()
    elif choice == 8:
    anotherGo = False

Save your test program and run it. What happens? Figure 5-7 shows what happened when I ran this program on my computer. You can see that the program is telling you what it is doing—it is choosing a random item from the menu, and then it is using the function that handles that menu option. As each function at the moment in your program only prints its name, that is all that you see. You see a different sequence of words on your screen to those in Figure 5-7 because the menu() function chooses a random choice each time it is used.

image

FIGURE 5-7 The results of running your test program

Displaying the Menu

A menu system is a very useful feature to add to your programs if they have lots of options for you to choose from. This menu system prints all the available options and then loops around, waiting for you to enter a number in the correct range. If you enter a number that is out of range, it just prints the menu again to give you another try.

Modify the menu function and replace it with the following (make sure that you get the indentation correct):

def menu():
while True:
print("DUPLICATOR MENU")
print(" 1. BUILD the duplicator room")
print(" 2. LIST files")
print(" 3. SCAN from duplicator room to file")
print(" 4. LOAD from file into duplicator room")
print(" 5. PRINT from duplicator room to player.pos")
print(" 6. CLEAN the duplicator room")
print(" 7. DEMOLISH the duplicator room")
print(" 8. QUIT")
choice = int(input("please choose: "))
if choice < 1 or choice > 8:
print("Sorry, please choose a number between 1 and 8")
else:
return choice

Save the program and run it. What does your program do differently from the one with the dummy menu? Figure 5-8 shows what the real menu looks like.

image

FIGURE 5-8 The menu system

Building the Duplicator Room

The duplicator room is going to be built out of glass, and it has a missing front on it so that your player can easily climb into it and create and delete blocks.

Replace the buildRoom() function with the following code. Be careful of the long lines that use setBlocks(), but note that I have not used the ↩ arrow here, because Python allows you to split these across multiple lines (see Digging into the Code for an explanation of why you can sometimes split lines and sometimes cannot):

def buildRoom(x, y, z):
global roomx, roomy, roomz
roomx = x
roomy = y
roomz = z
mc.setBlocks(roomx, roomy, roomz,
roomx+SIZEX+1, roomy+SIZEY+1, roomz+SIZEZ+1,
block.GLASS.id)
mc.setBlocks(roomx+1, roomy+1, roomz,
roomx+SIZEX, roomy+SIZEY, roomz+SIZEZ,
block.AIR.id)

Save your program and test it again. Test option 1 on the menu to make sure that you can build the duplicator room. Run to another location in the Minecraft world and choose option 1 again to see what happens! Figure 5-9 shows the duplicator room after it has just been built.

image

FIGURE 5-9 The duplicator room built in front of you

Demolishing the Duplicator Room

When you have run your duplicator program many times, you probably end up with lots of old duplicator rooms built all over the Minecraft world. Only the latest one that you have created is actually a working room, and after some time your world fills up so much that you won’t know which room is the right one! To solve this problem, you're adding a feature to your program that demolishes the duplicator room so that your Minecraft world doesn’t get cluttered with all these old rooms!

Demolishing the duplicator room is just like using your clearSpace.py program from Adventure 3. All you need to know is the outer coordinates of the room. Because the room is one block bigger all around the outside of the duplicating space defined by SIZEX, SIZEY and SIZEZ, this is quite simple to do with a little bit of maths.

Modify the demolishRoom() function to look like this.

def demolishRoom():
mc.setBlocks(roomx, roomy, roomz,
roomx+SIZEX+1, roomy+SIZEY+1, roomz+SIZEZ+1,
block.AIR.id)

Save your program and run it again. Now it’s easy to build or demolish your duplicator room. Just use option 1 on your menu to build it and option 7 to demolish it.

Scanning from the Duplicator Room

You have written this part of the program before, in your scan3D.py program, so you can use that with some small modifications.

  1. Replace the scan3D() function with the following code. This code is almost identical to the scan3D.py program, but the line in bold has been added to make it show the progress of the scanning on the Minecraft chat. Scanning a big room can take a long time, so it is nice to have some indication of how far through the process your program has got. You can use copy and paste to bring in the code from your earlier program to save some typing time here:

    def scan3D(filename, originx, originy, originz):
    f = open(filename, "w")
    f.write(str(SIZEX) + "," + str(SIZEY) + "," + ↩
    str(SIZEZ) + " ")
    for y in range(SIZEY):
    mc.postToChat("scan:" + str(y))
    f.write(" ")
    for x in range(SIZEX):
    line = ""
    for z in range(SIZEZ):
    blockid = mc.getBlock(originx+x, originy+y, ↩
             originz+z)
    if line != "":
    line = line + ","
    line = line + str(blockid)
    f.write(line + " ")
    f.close()

  2. Save the program and test it again by jumping into the duplicating room, building something and then choosing option 3 from the menu. Open up the file that it creates to check that the object has been scanned properly. Figure 5-10 shows a portion of a scanned file. Note that there are lots of zeros; this is because it has scanned all the AIR blocks as well.
image

FIGURE 5-10 The contents of the scanned file

Cleaning the Duplicator Room

Fancy a spring clean? Sometimes it is useful to have a fresh start and just clear the room. You could do this by demolishing the room and rebuilding it, but it’s really easy to add a clean feature, as it is only the coordinates that differ from the demolishRoom() function.

  1. Modify the cleanRoom() function so that it looks like the following code. Note how the start coordinates are all bigger than the edge of the room, and the end coordinates are not as far over as in the demolishRoom() function. This way, it clears the inner space of the room without destroying its walls:

    def cleanRoom():
    mc.setBlocks(roomx+1, roomy+1, roomz+1,
    roomx+SIZEX, roomy+SIZEY, roomz+SIZEZ, block.AIR.id)

  2. Save your program and run it again. Create something inside the room and then choose option 6 to test whether you can clear the room quickly.

Printing from the Duplicator Room

Printing (duplicating) the object that is in the room is really easy, because you have already written the print3D.py program that does this, and it is identical. Here it is again so you can see it all in one place:

def print3D(filename, originx, originy, originz):
f = open(filename, "r")
lines = f.readlines()
coords = lines[0].split(",")
sizex = int(coords[0])
sizey = int(coords[1])
sizez = int(coords[2])
lineidx = 1
for y in range(sizey):
mc.postToChat("print:" + str(y))
lineidx = lineidx + 1
for x in range(sizex):
line = lines[lineidx]
lineidx = lineidx + 1
data = line.split(",")
for z in range(sizez):
blockid = int(data[z])
mc.setBlock(originx+x, originy+y, originz+z, ↩
         blockid)

Save the program and run it again. Test that you can build something in the room, run to somewhere in the Minecraft world and then choose option 5 from the menu. The contents of the room are scanned then printed just to the side of where your player is standing. You should be able to run to anywhere in the Minecraft world and duplicate your objects many times. Try duplicating objects in the sky and under water to see what happens! Figure 5-11 shows the results of running this program.

image

FIGURE 5-11 A stone shape in the room and printed next to your player

Listing Files

Your last task is to write a useful little function that lists all the files in your filing system that are CSV files. You could just use the File Manager, but it is nice to add this feature to your program so that you have everything you need in one place:

  1. Modify the listFiles() function to use the glob.glob() function to read in a list of all files and print them. See Digging into the Code for an explanation of how this works.

    def listFiles():
    print(" FILES:")
    files = glob.glob("*.csv")
    for filename in files:
    print(filename)
    print(" ")

  2. Save the program and run it again. Scan a few objects into CSV files and then choose option 2 from the menu to make sure that they are all listed. Figure 5-12 shows the files I created when I ran this on my computer.
image

FIGURE 5-12 Viewing the list of CSV files you have created

Quick Reference Table

Command

Description

f = open("data.txt", "r")

tips = f.readlines()

f.close()

for t in tips:

print(t)

Reading lines from a file

f = open("scores.txt", "w")

f.write("Victoria:26000 ")

f.write("Amanda:10000 ")

f.write("Ria:32768 ")

f.close()

Writing lines to a file

import glob

names = glob.glob("*.csv")

for n in names:

print(n)

Getting a list of matching filenames

a = " hello "

a = a.strip()

print(a)

Stripping unwanted white space from strings

Further Adventures in Data Files

In this adventure, you learnt how to read from and write to data files. This opens up endless opportunities for saving and restoring parts of the Minecraft world, and even bringing in large amounts of real-world data from other sources, such as websites. You built your own 3D mazes with lots of winding tunnels and dead ends, and finished by building a fully functional 3D scanner and printer, complete with a full menu system. This technique of writing a menu system is useful for many other programs, too!

There are many sources of “live data” on the Internet. One data source that I found when researching for this book was the Nottingham City Council car park data here: http://data.nottinghamtravelwise.org.uk/parking.xml. This data is updated every five minutes and tracks cars as they enter and exit the car parks. I have written a Python program and put it on my github page that processes this data and prints out useful information here: https://github.com/whaleygeek/pyparsers. See if you can use this to write a Minecraft game that builds car parks with cars in them, inside your Minecraft world. Your game could be a car-parking challenge where you have to run around the Minecraft world and try to find a spare parking space in a limited time!

Everything you ever wanted to know about 3D mazes is explained in detail on this fantastic web page: http://www.astrolog.org/labyrnth/algrithm.htm. This page includes lots of example multilayer 3D mazes that are stored in normal text files. Look through the site and see if you can find a file for a multi-layer maze, and modify your maze program to use the techniques you learned with your 3D duplicating machine to build a huge multilayered maze. You might even experiment with some of the suggestions on this website to write a Python program that automatically generates mazes for you!

image

Achievement Unlocked: Shattering the laws of physics and magically materialising and dematerialising huge objects all over the Minecraft world at the push of a button.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset