COMPUTERS DON’T THINK. They aren’t capable of having any thoughts at all; they can only ever do what programmers tell them to do. You can, however, make computers look like they are thinking and making decisions by themselves. By programming your computer to “understand” what’s happening and giving it rules to “decide” what to do next, you can open a door to a lot of fun.
In this adventure, you are going to program a block friend who will follow you around, provided you don’t get too far away. You are also going to create a flying saucer that chases you until it can get above you and beam you aboard.
You learn how to make a block move and follow the path it decides is best, as well as how to use the Python random
module to make it look as if the computer is thinking. You also create shapes using the MinecraftShape
functions in the minecraftstuff
module (which is included in your starter kit).
Minecraft can be a lonely world. But your player doesn’t have to be alone—you can create a block that will follow him around, talk to him and be his friend (see Figure 7-1).
To program a block friend you need to think like her! She will be happy if she’s near your player, so she will want to follow him around and try to sit next to him. She’ll stay happy as long as your player is close to her; if your player walks off, she will walk after him. If your player gets too far away, it will make the block friend sad and she will stop following him until he comes back and gives her a hug (by standing next to her).
The block friend program has two distinct parts:
While the block friend is moving towards the target, she should be travelling “on top” of the land, not floating in the air or burrowing under the ground! You do this by using the function mc.getHeight(x,z)
and passing it a horizontal position (x,z). It returns the vertical position (y) of the first block down from the sky that isn’t AIR
.
Start by creating a new program for the block friend:
BlockFriend.py
in the MyAdventures
folder.minecraft
, block
, minecraftstuff
, math
and time
modules:
import mcpi.minecraft as minecraft
import mcpi.block as block
import mcpi.minecraftstuff as minecraftstuff
import math
import time
def distanceBetweenPoints(point1, point2):
xd = point2.x - point1.x
yd = point2.y - point1.y
zd = point2.z - point1.z
return math.sqrt((xd*xd) + (yd*yd) + (zd*zd))
TOO_FAR_AWAY = 15
Minecraft
and MinecraftDrawing
objects:
mc = minecraft.Minecraft.create()
mcdrawing = minecraftstuff.MinecraftDrawing(mc)
"happy"
:
blockMood = "happy"
getHeight()
function to find out the y position, so the block is sitting on top of the land:
friend = mc.player.getTilePos()
friend.x = friend.x + 5
friend.y = mc.getHeight(friend.x, friend.z)
mc.setBlock(friend.x, friend.y, friend.z,
block.DIAMOND_BLOCK.id)
mc.postToChat("<block> Hello friend")
target = friend.clone()
while True:
distanceBetweenPoints()
function:
pos = mc.player.getTilePos()
distance = distanceBetweenPoints(pos, friend)
if blockMood == "happy":
if distance < TOO_FAR_AWAY:
target = pos.clone()
else:
blockMood = "sad"
mc.postToChat("<block> Come back. You are too far ↩
away. I need a hug!")
if
statement that checks if the block is "happy"
, you need to tell the program if the block friend is "sad"
, to wait until the player is within one block’s distance (a hug) before changing the block’s mood to "happy"
.
elif blockMood == "sad":
if distance <= 1:
blockMood = "happy"
mc.postToChat("<block> Awww thanks. Lets go.")
The block friend appears next to your player; if you walk too far away the friend asks you to come back.
Now that your block friend is talking to you to let you know how she feels, the next step is to update the program to make the block friend follow the player:
if friend != target:
getLine()
function in MinecraftDrawing
:
line = mcdrawing.getLine(
friend.x, friend.y, friend.z,
target.x, target.y, target.z)
The getLine()
function works in the same way as drawLine()
(see Adventure 6). However, instead of drawing the line in blocks, it returns a list of points (x, y, z) that make a line between the two sets of x, y, z coordinates passed as parameters.
for nextBlock in line[:-1]:
mc.setBlock(friend.x, friend.y, friend.z, block.AIR.id)
friend = nextBlock.clone()
friend.y = mc.getHeight(friend.x, friend.z)
mc.setBlock(friend.x, friend.y, friend.z,
block.DIAMOND_BLOCK.id)
time.sleep(0.25)
target = friend.clone()
When the for
loop finishes, the block friend has reached her target, so set the target to be the block friend’s own current position. This way she won’t try to move again.
The program moves the block friend by clearing her from her previous last position (which it does by setting the block to AIR
), updating the block friend’s position to be the next block in the line, then re-creating the block friend in that position.
time.sleep(0.25)
The block friend now follows the player until they get too far away from each other. When that happens, the block friend comes to a stop and your player has to go back and stand next to the block friend before she starts following again.
You can download the complete code for the block friend program from the Adventures in Minecraft companion website at www.wiley.com/go/adventuresinminecraft2e
.
The problem with the block friend program you just created is that it is predictable; she's always going to do the same thing. After you’ve run the program only a couple of times, you’ll know exactly what she's going to do and when, so things are going to get boring very quickly. To really give your block friend a ‘mind', you need to give her an air of unpredictability.
By using random numbers, you can simulate unpredictability—in other words, make something look as if it’s acting unpredictably. You do this by making a program choose to do things based on a probability; for example, you might tell it to do a particular action 1 every 100 times. By adding more rules based on different odds, you can make the program much more difficult to predict. Before changing the block friend program to use random numbers, let’s explore the code to create random numbers and probability checks. You may remember random numbers being introduced in Adventure 3.
The Python random
module contains the function random.randint(startNumber, endNumber)
which is used to generate a random number between two specific numbers (startNumber
, endNumber
).
The following code prints a random number between 1 and 10 each time it is run. If you want to see the results, you should create a new Python program:
import random
randomNo = random.randint(1,10)
print(randomNo)
By adding an if
statement to check when the random number is 10, you create a probability check that will be true approximately 1 time in every 10:
import random
if random.randint(1,10) == 10:
print("This happens about 1 time in 10")
else
print("This happens about 9 times out of 10")
If you were to run this program 100 times you would expect to see “This happens about 1 time in 10” printed about 10 times (see Figure 7-3), but you might only see it 9 or 11 times, or maybe even not at all. It's unpredictable!
If you use random numbers and a probability check in your block friend program, you can make it less predictable. You can even make the block friend “unfriend” your player!
Add some new rules to the block friend program so that if the block friend is “sad” there is a 1 in 100 chance she will decide she has had enough of waiting and will not follow your player if he comes back and gives her a hug:
BlockFriend.py
program from the MyAdventures
folder.BlockFriendRandom.py
.random
module to the import
statements at the top of the program (the code in bold):
import mcpi.minecraft as minecraft
import mcpi.block as block
import mcpi.minecraftstuff as minecraftstuff
import math
import time
import random
"hadenough"
when the block is "sad"
:
elif blockMood == "sad":
if distance <= 1:
blockMood = "happy"
mc.postToChat("<block> Awww thanks. Lets go.")
if random.randint(1,100) == 100:
blockMood = "hadenough"
mc.postToChat("<block> Thats it. I have had ↩
enough.")
When your player gets too far away from the block friend, if you wait long enough (for a 1 in 100 chance to come true), the block friend decides she has had enough and ‘unfriends’ the player! Once that happens, the block friend says, “That’s it. I have had enough”, and will sit there forever.
In the block friend program, you made one block move around and follow the player. But what if you wanted to make a lot of blocks move around? Or how about a shape made out of moving blocks, like a car or an alien spaceship?
This is where things become much more difficult, because you need to keep track of lots of blocks. Each time you want to make it move you would need to set all the blocks to AIR
and then re-create the blocks in their new position. If there are a lot of blocks, the shape stops moving properly and slows to a crawl.
The minecraftstuff
module contains functions called MinecraftShape
, which has been written especially to create shapes and move them around. It does this by keeping track of all the blocks that make up a shape; when the shape is moved, it only changes the blocks that have changed rather than changing all of them.
To use MinecraftShape
, you have to tell it what the shape looks like by creating a list of blocks that make up the shape. Each of the blocks in the shape has a position (x, y, z) and a block type.
Figure 7-4 shows a simple shape made up of seven blocks, together with the positions of the blocks. In this case, “position” isn’t the same as the position in Minecraft. You’ll see that the centre of the wooden horse is 0,0,0 and each block is relative to the centre, so the block to the right is 1,0,0.
Now you’re going to create a program that uses MinecraftShape
to create the wooden horse in Figure 7-4 and make it move:
WoodenHorse.py
in the MyAdventures
folder.minecraft
, block
, minecraftstuff
and time
modules:
import mcpi.minecraft as minecraft
import mcpi.block as block
import mcpi.minecraftstuff as minecraftstuff
import time
Minecraft
object:
mc = minecraft.Minecraft.create()
MinecraftShape
where in the Minecraft world to create the wooden horse. Get the player’s position and add 1 to the z and y coordinates, so it isn’t directly on top of the player:
horsePos = mc.player.getTilePos()
horsePos.z = horsePos.z + 1
horsePos.y = horsePos.y + 1
MinecraftShape
, passing the Minecraft object and the position where the shape should be created as parameters:
horseShape = minecraftstuff.MinecraftShape(mc, horsePos)
horseShape.setBlock(0,0,0,block.WOOD_PLANKS.id)
horseShape.setBlock(-1,0,0,block.WOOD_PLANKS.id)
horseShape.setBlock(1,0,0,block.WOOD_PLANKS.id)
horseShape.setBlock(-1,-1,0,block.WOOD_PLANKS.id)
horseShape.setBlock(1,-1,0,block.WOOD_PLANKS.id)
horseShape.setBlock(1,1,0,block.WOOD_PLANKS.id)
horseShape.setBlock(2,1,0,block.WOOD_PLANKS.id)
The position of the blocks is the same as shown in Figure 7-4.
WoodenHorse.py
program to make the horse move by adding the following code to the bottom of the program:
for count in range(0,10):
time.sleep(1)
horseShape.moveBy(1,0,0)
horseShape.clear()
The moveBy(x,y,z)
function tells the shape to “move by” the number of blocks in x, y, z. So in this example, the horseShape
is moved by one block across (x). The clear()
function removes the shape, setting all the blocks to AIR
.
You can download the complete code for the wooden horse from the Adventures in Minecraft companion website at www.wiley.com/go/adventuresinminecraft2e
.
You’ll find shapes useful now, because you’re going to create an alien spaceship! Later, you use them again to create obstacles.
Aliens are about to invade Minecraft. A spaceship comes down from the sky directly above your player, who is in mortal danger—these aliens are not friendly and they won’t give up until they have completed their mission.
In the next program, you use MinecraftShape
and the programming techniques you used in the block friend program to create an alien spaceship (see Figure 7-5) which hovers above the surface of the world, chasing your player, trying to get above him. And when it succeeds, it beams him onboard.
You create the alien spaceship using MinecraftShape
, just like in the wooden horse program with each block in the shape having its own relative position and block type. Figure 7-6 shows the positions of the shape’s blocks from the side and from above.
Like the block friend program, the code for the alien invasion is in two parts. The first part is the rules to decide what the spaceship does next; the second part is the code that moves the alien spaceship towards the player.
When the alien spaceship is chasing the player, it taunts him by posting messages (like “you can’t run forever”) to the chat. The messages are picked at random from a list of taunts (see Figure 7-7).
The rules to decide what the alien spaceship does next is based on one of three modes:
Once the alien spaceship has captured the player, the program builds a dismal room in which to hold him and change his position to be inside it (see Figure 7-8). The aliens then post messages to the player before beaming him back by changing his position back to what it was and clearing the room.
Use the following steps to create the alien invasion program:
AlienInvasion.py
in the MyAdventures
folder.minecraft
, block
, minecraftstuff
and time
modules:
import mcpi.minecraft as minecraft
import mcpi.block as block
import mcpi.minecraftstuff as minecraftstuff
import time
distanceBetweenPoints()
function:
def distanceBetweenPoints(point1, point2):
xd = point2.x - point1.x
yd = point2.y - point1.y
zd = point2.z - point1.z
return math.sqrt((xd*xd) + (yd*yd) + (zd*zd))
HOVER_HEIGHT
is the number of blocks the alien spaceship hovers over the player; ALIEN_TAUNTS
is a list of the taunts that are posted to the chat while the aliens are chasing the player:
HOVER_HEIGHT = 15
ALIEN_TAUNTS = ["<aliens>You cannot run forever",
"<aliens>Resistance is useless",
"<aliens>We only want to be friends"]
You can change the aliens’ taunts—see how creative you can be! Or add more if you like.
Minecraft
and MinecraftDrawing
objects:
mc = minecraft.Minecraft.create()
mcdrawing = minecraftstuff.MinecraftDrawing(mc)
alienPos = mc.player.getTilePos()
alienPos.y = alienPos.y + 50
mode = "landing"
MinecraftShape
(refer to Figure 7-6 for a reminder of how this is done):
alienShape = minecraftstuff.MinecraftShape(mc, alienPos)
alienShape.setBlock(-1,0,0,block.WOOL.id, 5)
alienShape.setBlock(0,0,-1,block.WOOL.id, 5)
alienShape.setBlock(1,0,0,block.WOOL.id, 5)
alienShape.setBlock(0,0,1,block.WOOL.id, 5)
alienShape.setBlock(0,-1,0,block.GLOWSTONE_BLOCK.id)
alienShape.setBlock(0,1,0,block.GLOWSTONE_BLOCK.id)
while
loop, which continues to loop while the mode is not "missionaccomplished"
or, to put it another way, exits when the mode is "missionaccomplished"
:
while mode != "missionaccomplished":
playerPos = mc.player.getTilePos()
"landing"
, set the alien target (where the alien spaceship travels to) to be above the player’s position and set the mode to "attack"
:
if mode == "landing":
mc.postToChat("<aliens> We do not come in peace - ↩
please panic")
alienTarget = playerPos.clone()
alienTarget.y = alienTarget.y + HOVER_HEIGHT
mode = "attack"
"attack"
, check to see whether the alien spaceship is above the player. If it is, beam him inside the ship and set the mode to "missionaccomplished"
. Otherwise, if the player has got away, set the alien target to be the player’s current position and post a taunt to the chat:
elif mode == "attack":
#check to see if the alien ship is above the player
if alienPos.x == playerPos.x and alienPos.z == ↩
playerPos.z:
mc.postToChat("<aliens>We have you now!")
#create a room
mc.setBlocks(0,50,0,6,56,6,block.BEDROCK.id)
mc.setBlocks(1,51,1,5,55,5,block.AIR.id)
mc.setBlock(3,55,3,block.GLOWSTONE_BLOCK.id)
#beam up player
mc.player.setTilePos(3,51,5)
time.sleep(10)
mc.postToChat("<aliens>Not very interesting at all - ↩send it back")
time.sleep(2)
#send the player back to the original position
mc.player.setTilePos(
playerPos.x, playerPos.y, playerPos.z)
#clear the room
mc.setBlocks(0,50,0,6,56,6,block.AIR.id)
mode = "missionaccomplished"
else:
#the player got away
mc.postToChat(
ALIEN_TAUNTS[random.randint(0,len(ALIEN_TAUNTS)-1)])
alienTarget = playerPos.clone()
alienTarget.y = alienTarget.y + HOVER_HEIGHT
When the player is beamed aboard the spaceship, a room is built 50 blocks over the spawn position and the player’s position is set to be inside the room. (Just like Doctor Who’s Tardis, the inside is bigger than the outside!). Messages are then posted to the chat, before the player’s position is set to be back where he started and the room is cleared.
while
loop:
if alienPos != alienTarget:
line = mcdrawing.getLine(
alienPos.x, alienPos.y, alienPos.z,
alienTarget.x, alienTarget.y, alienTarget.z)
for nextBlock in line:
alienShape.move(
nextBlock.x, nextBlock.y, nextBlock.z)
time.sleep(0.25)
alienPos = alienTarget.clone()
while
loop. When the mode has been set to "missionaccomplished"
and the while
loop finishes, the last line of the program makes the alien spaceship disappear:
alienShape.clear()
You can download the complete code for the alien invasion from the Adventures in Minecraft companion website at www.wiley.com/go/adventuresinminecraft2e
.
In this adventure you have used algorithms and rules to simulate a friend and an alien invasion—how about taking it further and simulating other things such as a flock of birds (or blocks), waves across the oceans in Minecraft or a complete cellular system such as Conway’s Game of Life made out of blocks.
To find out more about Conway’s Game of Life visit en.wikipedia.org/wiki/Conway's_Game_of_Life
.
Quick Reference Table | |
Command |
Description |
import random |
Imports the Python |
random.randint(start, end) |
Creates a random number between the start and end numbers |
import minecraftstuff |
Imports the |
mc.getHeight(x,z) |
Gets the height (y coordinate) of the land (i.e. the first block down from the sky that isn’t |
mcdrawing = minecraftstuff. ↩ MinecraftDrawing(mc) |
Creates the |
copyOfPosition = position.clone() |
Creates a copy (clone) of a Minecraft position |
mcdrawing.getLine(x1, y1, z1, x2, y2, z2) |
Gets all the blocks in a line between two points |
shape = minecraftstuff. ↩ MinecraftShape(mc, pos) shape.setBlock(1,0,0,block.DIRT.id) shape.setBlock(0,0,1,block.DIRT.id) |
Creates a |
shape.moveBy(x,y,z) |
Moves a |
shape.move(x,y,z) |
Moves a |
shape.clear() |
Clears the |
shape.draw() |
Draws the |
Achievement Unlocked: Abducted by your own artificial intelligence!