Python Minecraft II – Hello World

So in this post  for the Naas-Sallins Coder Dojo we are going to look at writing a program which will allow us to alter the state of the python minecraft world which we looked at in the last post. The first thing we need to do is amend the saveModule.py file in our pymc folder to allow for the two new blocks we added. If we look at lines 9 and 11 we have the following

        self.coordDictSave = { str(main.GRASS):'GRASS', str(main.SAND):'SAND', str(main.BRICK):'BRICK', str(main.STONE):'STONE' }

        self.coordDictLoad = { 'GRASS':main.GRASS, 'SAND':main.SAND, 'BRICK':main.BRICK, 'STONE':main.STONE }

we can see the structure of the save and load lines. We don’t really need to know everything the code does to guess that we can extend it to add our 2 blocks DOJO & DOJO2 so we get

        self.coordDictSave = { str(main.GRASS):'GRASS', str(main.SAND):'SAND', str(main.BRICK):'BRICK', str(main.STONE):'STONE',str(main.DOJO):'DOJO',str(main.DOJO2):'DOJO2' }

        self.coordDictLoad = { 'GRASS':main.GRASS, 'SAND':main.SAND, 'BRICK':main.BRICK, 'STONE':main.STONE, 'DOJO':main.DOJO, 'DOJO2':main.DOJO2 }

Now when we run our main.py program we can save the world by pressing F5. This creates a file called savegame.sav in our pymc folder.

This file has the following structure.

[-8, -3, 21]=>STONE
[52, -2, -39]=>GRASS
[66, -2, 19]=>GRASS

each line in the file represents the coordinates and texture of every block in our world. We can think of these numbers as the x, y and z of our world, where x is length, y is height and z is depth. Once we know the structure of our save file it gives the opportunity to write code to change the file and the world itself.

Our first step is to make a copy of the save file which we will call savegame.bak, this is so that we can start with a default state as we test our code. In our pymc folder create and edit a new file called world.py and add the following code

import shutil

shutil.copyfile('savegame.bak','savegame.sav')
world=open('savegame.sav','a')
world.close()

In this snippet we import the shutil library which allows us to perform operations on files on our computer. Our next line simply copies our default world state from our backup savegame.bak to savegame.sav . We then show how to open and close that file. The ‘a’ in the open statement says that we are going to open the file for append, which just means that we are going to add on to the end of the file.

Next we are going to alter our world by adding lines to the savegame.sav file. In this example we are going to create a large ‘hello world’ message in blocks. So we are going to create a function which will draw each letter.


def do_h(startx,starth,starty,texture):
    """ draw the letter H """
    myletter = []
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+4,starty,texture))
    return(myletter)

This function is different than the ones we had in earlier posts, if we look at the first line we can see that we have some values in between the brackets after our name do_h. These values are called parameters and they represent the variables that we want this function to do its work on. In this case they are startx, starth, starty, texture which represent the beginning coordinates of the first block we want to place in our world along with the texture we want to use. After that we create a new list called myletter and then begin to create the lines which will draw our letter H. We do this by adding onto the beginning position of our first block. For example if we want to add a block on top of our first (which will be part of our first leg of the letter H) we have

startx,starth+1,starty,texture

so you can see that all we are changing  is the height of the block by one in comparison to the first. To explain the rest of the line, mylist.append simply means that we add onto our list. The next bit

("[%d,%d,%d]=>%s\n" % (startx+3,starth+4,starty,texture))

says to replace the placeholders (%d for digits and %s for words) with the following variables. Really all this is saying is that we want to create a line that will have the same structure as our savegame.sav file. In general our function creates a list of the lines we want to add to our file given a start position and texture. In order to actually add this letter H to our world we need to call the function so our code becomes

import shutil
def do_h(startx,starth,starty,texture):
    """ draw the letter H """
    myletter = []
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+4,starty,texture))
    return(myletter)

shutil.copyfile('savegame.bak','savegame.sav')
world=open('savegame.sav','a')

nextletter = do_h(20,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)

world.close()

Here we can see that in order to create our H we call the function do_h and send it our values for startx, starty, startz and texture. The values passed to our function when we call it need to be in the format and order that our function expects in order for it to work. By changing these values we can alter where the letter will be placed in our world.   The variable next letter takes on the value returned by our function and we then loop through that list writing the details to our world file. If we run this program we will then be able to see our floating letter when we run our world using main.py. All that is required to spell out Hello World is to define a function for each letter and then call it with the correct starting coordinates.
helloworld

Its a fair bit to take in but once you get the hang of it you can create all sorts of objects by code alone. The full Hello world code is below. Note how once we have defined the letter, we only need to change the coordinates to position it somewhere else.

import shutil

def do_e(startx,starth,starty,texture):
    """ draw the letter E """
    myletter = []
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+4,starty,texture))
    return(myletter)
def do_h(startx,starth,starty,texture):
    """ draw the letter H """
    myletter = []
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+4,starty,texture))
    return(myletter)
def do_l(startx,starth,starty,texture):
    """ draw the letter L """
    myletter = []
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth,starty,texture))
    return(myletter)
def do_o(startx,starth,starty,texture):
    """ draw the letter O """
    myletter = []
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+4,starty,texture))
    return(myletter)
def do_w(startx,starth,starty,texture):
    """ draw the letter W """
    myletter = []
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+4,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+4,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+4,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+4,starth+4,starty,texture))
    return(myletter)
def do_r(startx,starth,starty,texture):
    """ draw the letter R """
    myletter = []
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth,starty,texture))
    return(myletter)
def do_d(startx,starth,starty,texture):
    """ draw the letter O """
    myletter = []
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+3,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+1,starth+4,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+2,starth+4,starty,texture))
    #myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+1,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+2,starty,texture))
    myletter.append("[%d,%d,%d]=>%s\n" % (startx+3,starth+3,starty,texture))
    return(myletter)

shutil.copyfile('savegame.bak','savegame.sav')
world=open('savegame.sav','a')

nextletter = do_h(20,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)
nextletter = do_e(25,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)
nextletter = do_l(29,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)
nextletter = do_l(33,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)
nextletter = do_o(37,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)
nextletter = do_w(43,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)
nextletter = do_o(49,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)
nextletter = do_r(54,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)
nextletter = do_l(59,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)
nextletter = do_d(63,4,-8,'DOJO2')
for i in nextletter:
    world.write(i)
world.close()

 

Simple Python Minecraft Clone

This is the latest in a series for the Naas-Sallins Coder Dojo about using the Python programming language. This time we are going to look at taking the source code for a simple Python minecraft clone by Michael Fogleman and making some amendments.

Our First step is getting the pyglet graphic library installed. Michael has some instructions on his page but in general on the command line type [to get to the command line on windows press the windows key and r and then type cmd before pressing enter]

pip install pyglet

This will download and install the library for you, if you run into trouble try these tips or wait until the next dojo session and I will try to help out.

Once you have pyglet installed the next step is to get the python code we are going to play with. We are going to use a fork of the original project by another github user (bison) which contains some code to save the state of the world and then reload it. So download this zip file and extract its contents to a new folder, call that folder something like pymc. Our folder should contain the following files

LICENSE
main.py
README.md
saveModule.py
texture.png

So step one is to check that everything works by running main.py. You can do this from the command line by changing to your pymc folder and typing python main.py or by loading the file into the pythonwin editor and clicking run. If you get something like

basicworld

then congrats, everything is working as it should! Play instructions are below.

 Moving

– W: forward
– S: back
– A: strafe left
– D: strafe right
– Mouse: look around
– Space: jump
– Tab: toggle flying mode

Building

– Selecting type of block to create:
– 1: brick
– 2: grass
– 3: sand
– Mouse left-click: remove block
– Mouse right-click: create block

Quitting

– ESC: release mouse, then close window

Okay so next we are going to learn how to add our own blocks which involves firstly editing the image file texture.png and then editing the main.py file to allow for the new blocks that we are going to add. To start copy texture.png to a new file called texture2.png then open this in an image editor like mspaint. You should see

textureblank

we are going to edit the two squares shown. For example with mspaint I changed them to

textureedit

which is the coderdojo logo and a badly hand drawn one! Don’t worry you can come back to this as often as you like.

So now we need to edit the main.py code to allow for these new blocks. Open the file in your editor and have a quick look at it.  Don’t panic! We have 845 lines of code but we only need to change a few of them.

Lets start by telling the program to use our new texture2.png. In your editor search for texture.png (ctrl+f in pythonwin), it is on line 55. Change this to read

TEXTURE_PATH = 'texture2.png'

Just below that we have 4 lines which tell the program which section of texture2.png to use for the top bottom and side of each block. Our 2 blocks will use the same texture for all sides so lets add our two lines for these blocks which I’m calling DOJO and DOJO2

GRASS = tex_coords((1, 0), (0, 1), (0, 0))
SAND = tex_coords((1, 1), (1, 1), (1, 1))
BRICK = tex_coords((2, 0), (2, 0), (2, 0))
STONE = tex_coords((2, 1), (2, 1), (2, 1))
DOJO = tex_coords((3, 1), (3, 1), (3, 1))
DOJO2 = tex_coords((3, 0), (3, 0), (3, 0))

In order to be able to place these blocks we need to add them to the inventory of blocks so that when a player presses 4 or 5 it changes the block to place to DOJO or DOJO2. If we have a look through our code or search for inventory we find on line 455

        # A list of blocks the player can place. Hit num keys to cycle.
        self.inventory = [BRICK, GRASS, SAND]

Here we can see a comment telling what the code does and then our list of textures. Lets edit this to

        # A list of blocks the player can place. Hit num keys to cycle.
        self.inventory = [BRICK, GRASS, SAND, DOJO, DOJO2]

If we run this new main.py we should be able to place our new blocks by pressing 4 or 5
newblockworld