Commit f1af0c6b authored by glenpike's avatar glenpike
Browse files

Merge remote-tracking branch 'upstream/master'

parents 5cbef7e3 0d3316a6
......@@ -376,7 +376,10 @@
inkscape:groupmode="layer"
id="layer1">
<g
id="g6484">
id="g6484"
inkscape:export-filename="/home/dave/code/dbscode/doc/images/box.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150">
<path
inkscape:connector-curvature="0"
id="path3003"
......@@ -484,7 +487,10 @@
sodipodi:role="line">size z</tspan></text>
</g>
<g
id="g6505">
id="g6505"
inkscape:export-filename="/home/dave/code/dbscode/doc/images/axes.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150">
<path
inkscape:connector-curvature="0"
id="path5389"
......@@ -530,7 +536,10 @@
sodipodi:role="line">x</tspan></text>
</g>
<g
id="g6515">
id="g6515"
inkscape:export-filename="/home/dave/code/dbscode/doc/images/toblerone.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150">
<path
inkscape:connector-curvature="0"
id="path3003-3"
......@@ -648,7 +657,10 @@
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
id="g6538">
id="g6538"
inkscape:export-filename="/home/dave/code/dbscode/doc/images/cylinder.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150">
<path
transform="translate(324.64286,163.92857)"
d="m 160,507.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
......@@ -752,7 +764,10 @@
sodipodi:type="arc" />
</g>
<g
id="g6554">
id="g6554"
inkscape:export-filename="/home/dave/code/dbscode/doc/images/cone.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150">
<path
transform="translate(54.64286,363.92857)"
d="m 160,507.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
......@@ -841,7 +856,10 @@
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
id="g6568">
id="g6568"
inkscape:export-filename="/home/dave/code/dbscode/doc/images/sphere.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150">
<path
d="m 410,802.36218 a 60,60 0 1 1 -120,0 60,60 0 1 1 120,0 z"
sodipodi:ry="60"
......
# dBsCode Minecraft Cheat Sheet
![](https://github.com/nebogeo/dbscode/raw/master/doc/images/dbscode.png)
# Minecraft Programming Cheat Sheet
This is for the dBsCode taster sessions, learning programming via
procedural architecture in Minecraft using the Raspberry Pi. This is a
......@@ -65,7 +67,7 @@ with a radius of 10 blocks.
###cylinder(blocktype, position_point, radius, height):
example: `cylinder(STONE_BRICK,point(0,0,0),6,20)`
Builds a cylinder of stone brick in the center of the world radius 6, height 20.
Builds a cylinder of stone brick in the centre of the world radius 6, height 20.
![A cylinder image](https://github.com/nebogeo/dbscode/raw/master/doc/images/cylinder.png "How a cylinder works")
......
![](https://github.com/nebogeo/dbscode/raw/master/doc/images/dbscode.png)
# An infinite Minecraft house generator
![Houses](https://github.com/nebogeo/dbscode/raw/master/doc/images/infinite-house.jpg "Some houses")
Before you start:
* Launch Minecraft and create a new world.
* Launch Geany
## Step 1 Make a roof
* In Geany, from the file menu select "new", and then save as `house.py` in the "pi"
directory.
* We need to import the dBsCode commands we'll be using and clear an
area in the Minecraft world for working in. Create this program:
from dbscode_minecraft import *
bulldoze()
* **Test** Press F5 to run the program (this will also save your
program for you). After a few seconds you should see a "flatworld" type
of environment.
* Lets start with the roof. Build a prism by adding this to the end of your script:
toblerone(WOOD,point(0,5,0),point(10,6,8))
* **Test** Press F5 and navigate to the centre of the world (using the
coordinates at the top left of the Minecraft window). You should see a
toblerone shape. This shape is drawn from block position 0,5,0 and is of
size 10,6,8.
* We now need to hollow out the shape we've just drawn in order to make
space for the walls:
toblerone(AIR,point(0,3,0),point(10,6,8))
This shape has the same dimensions but drawn 2 blocks lower, and
as the material is set to AIR, it "cuts way" from the previous shape,
leaving a roof structure.
## Step 2 Build the walls
* Add this line so it comes after the roof toblerones:
box(BRICK_BLOCK,point(2,0,1),point(7,7,6))
* Press F5 to check this - it fills the area below the roof but we have
a hole just underneath the top. We can fill this with another box:
box(BRICK_BLOCK,point(4,7,1),point(3,2,6))
* This house is not much good as it's solid, so we can hollow it out
with an air box in the middle:
box(AIR,point(3,0,2),point(5,7,4))
* You can check this worked by destroying a few bricks and breaking
in. Lets quickly add a door:
box(AIR,point(4,0,1),point(3,4,1))
## Step 3 Make a house function
Functions are one of the founding ideas behind programming - they can
amplify your actions, and allow you to solve difficult problems by
breaking them into small ones.
* We use 'def' to build functions. Collect together all the code you've
just written so it looks like this - the spaces at the start of the
lines are important - there are 4 of them, or just press the "tab" key
once:
def house():
toblerone(WOOD,point(0,5,0),point(10,6,8))
toblerone(AIR,point(0,3,0),point(10,6,8))
box(BRICK_BLOCK,point(2,0,1),point(7,7,6))
box(BRICK_BLOCK,point(4,7,1),point(3,2,6))
box(AIR,point(3,0,2),point(5,7,4))
box(AIR,point(4,0,1),point(3,4,1))
Now we can "call" this function by simply adding this to the bottom of your
program (there shouldn't be any spaces before this):
house()
Press F5 - your house should appear as normal. So far so good, but we
haven't found out what the point of the function is yet...
* We can add a position "parameter" to the function. We use parameters
to pass information into a function by adding them to the brackets at
the top and refering to them inside. Change your existing function and
add "pos" to all of the positions of the shapes:
def house(pos):
toblerone(WOOD,pos+point(0,5,0),point(10,6,8))
toblerone(AIR,pos+point(0,3,0),point(10,6,8))
box(BRICK_BLOCK,pos+point(2,0,1),point(7,7,6))
box(BRICK_BLOCK,pos+point(4,7,1),point(3,2,6))
box(AIR,pos+point(3,0,2),point(5,7,4))
box(AIR,pos+point(4,0,1),point(3,4,1))
We can now pass in the position when we call the function, to draw a
house at any position:
house(point(5,0,10))
Add another two:
house(point(-5,0,5))
house(point(8,0,0))
* Add as many houses as you like.
* What happens if you make them overlap?
## Step 4 Make your houses look different
* Lets change the function to add a 'roof' parameter so our houses can
have different roof materials. Also swap the `WOOD` in the first
toblerone to be `roof` :
def house(roof,pos):
toblerone(roof,pos+point(0,5,0),point(10,6,8))
toblerone(AIR,pos+point(0,3,0),point(10,6,8))
box(BRICK_BLOCK,pos+point(2,0,1),point(7,7,6))
box(BRICK_BLOCK,pos+point(4,7,1),point(3,2,6))
box(AIR,pos+point(3,0,2),point(5,7,4))
box(AIR,pos+point(4,0,1),point(3,4,1))
Now we need to add materials to your houses:
house(COBBLESTONE,point(5,0,10))
house(LAPIS_LAZULI_BLOCK,point(10,0,10))
house(MELON,point(13,0,-8))
* Lets go further and change the shape of the houses, by adding
height. We need to move the roof up and change the size of the walls:
def house(roof,pos,height):
toblerone(roof,pos+point(0,5+height,0),point(10,6,8))
toblerone(AIR,pos+point(0,3+height,0),point(10,6,8))
box(BRICK_BLOCK,pos+point(2,0,1),point(7,7+height,6))
box(BRICK_BLOCK,pos+point(4,7+height,1),point(3,2,6))
box(AIR,pos+point(3,0,2),point(5,7+height,4))
box(AIR,pos+point(4,0,1),point(3,4,1))
house(COBBLESTONE,point(5,0,10),1)
house(LAPIS_LAZULI_BLOCK,point(10,0,10),5)
house(MELON,point(13,0,-8),20)
The melon roofed house is a tall one!
* What happens if you pass in height as a minus number?
## Step 5 For loops: making *loads* of houses
If we want to make loads of houses, writing them all manually as
`house(blah blah)' is a pain. One way we can make this easier is by
using a "for" loop. Delete the houses at the bottom of your program and
add this:
for i in range(0,5):
house(MELON,point(i*10,0,0),10)
This will repeat the `house` line 5 times (make sure you add 4 spaces
before), each time with i being a number described by the `range`
function - so 0 to 5 in this case. We set the X position of the house
by multiplying this by 10 (so each house in the row appears spaced by 10
blocks)
* Try changing 5 to something bigger!
## Step 6 Randomness: making all your houses different
* Randomness is a surprisingly important area and is used a lot in
computer games as well as programming in general. Here we can use it to
make every one of our houses different. Change the `house` function call
in the loop to:
for i in range(0,10):
house(MELON,point(i*10,0,0),random_range(0,20))
`rand_range` provides a random number between 0 and 20. Try running it
multiple times - it should change each time.
* We can also change the block material for each house using the
function `choose_one` which randomly picks between parameters which you
pass in - as it's quite long lets break the line to make it easier to read:
for i in range(0,10):
house(choose_one(BRICK_BLOCK,COBBLESTONE,BEDROCK,SANDSTONE),
point(i*10,0,0),random_range(0,20))
* Try changing the position of the houses with `rand_range`
## Challenges
* Can you change the width and height of the houses?
* Add windows - using GLASS brick type.
* These houses definitely need chimneys too.
![](https://github.com/nebogeo/dbscode/raw/master/doc/images/dbscode.png)
# Auto-Minecraft-Castle
![A castle](https://github.com/nebogeo/dbscode/raw/master/doc/images/auto-castle.jpg "A castle")
Before you start:
* Launch Minecraft and create a new world.
* Launch Geany
## Step 1 Make a battlement function
* In Geany, from the file menu select "new", and then save as `castle.py` in the "pi"
directory.
* We need to import the dBsCode commands we'll be using and clear an
area in the Minecraft world for working in. Create this program:
from dbscode_minecraft import *
bulldoze()
* **Test** Press F5 to run the program (this will also save your
program for you). After a few seconds you should see a "flatworld" type
of environment.
* Lets make a function to create a battlement. We'll reuse this to
create the bits of our castle we need. It'll use parameters so we can
specify the position, length and height of a section.
def battlement_x(pos, length, height):
box(STONE_BRICK,pos,point(length,height,1))
box(STONE_BRICK,pos+point(0,height,-1),point(length,2,3))
for i in range(0,length/2):
box(STONE_BRICK,pos+point(i*2,height+2,-1),point(1,1,3))
We create two boxes, one for the wall itself, the other for a thicker
top section. Then we use a for loop to create all the knobbly bits at
the top (called crenels according to wikipedia). The loop automatically
creates enough to fill the battlement whatever length it is.
Test it with something like:
battlement_x(point(0,0,0),10,5)
Press F5 to check your battlement!
## Step 2 Make a battlement in the other direction
We also need a battlement in the Z direction. Don't type this out,
copy/paste `battlement_x` and change it. Hint - you mainly need to swap
the first and last parameters of the point functions.
def battlement_z(pos, length, height):
box(STONE_BRICK,pos,point(1,height,length))
box(STONE_BRICK,pos+point(-1,height,0),point(2,2,length))
for i in range(0,length/2):
box(STONE_BRICK,pos+point(-1,height+2,i*2),point(3,1,1))
Let's test that both of these work:
battlement_x(point(0,0,0),10,5)
battlement_z(point(0,0,0),10,5)
Press F5. You should see two battlements going at 90 degrees to each
other from the centre of the world.
## Step 3 Make a castle_walls function
We can now use these two functions to create one that draws a complete
set of walls of any size and position that you pass in.
def walls(pos,size):
battlement_x(pos,size.x, size.y)
battlement_z(pos,size.z, size.y)
battlement_x(pos+point(0,0,size.z),size.x, size.y)
battlement_z(pos+point(size.x,0,0),size.z, size.y)
Test this with something like:
walls(point(0,0,0),point(10,5,10))
The first and last (X and Z) numbers of the size control the wall
rectangle while the middle (Y) number controls the height.
## Step 4 Make a tower function
This one is very simple - we can use the walls command with a tall
height as a tower:
def tower(pos,height):
walls(pos,point(5,height,5)
Try making lots of towers to test this.
## Step 5 Build a castle function
If we position towers at the corner of a set of walls, we start to get
something more castle like. We need to shift the towers slightly so they
are centred correctly:
def castle(pos,size):
walls(pos,size)
tower(pos+point(-2,0,-2),size.y*2)
tower(pos+point(size.x-2,0,size.z-2),size.y*2)
tower(pos+point(-2,0,size.z-2),size.y*2)
tower(pos+point(size.x-2,0,-2),size.y*2)
## Challenges
* Can you make a super-castle function by nesting multiple castles
inside each other (like in the screen shot above)?
* We haven't tried changing the block material. Can you find a way of
doing this so castles can be built from different block types you
control?
* Is there a way of using randomness to make your castles more
interesting?
![](https://github.com/nebogeo/dbscode/raw/master/doc/images/dbscode.png)
# Huge skyscrapers and dungeons
![Skyscrapers](https://github.com/nebogeo/dbscode/raw/master/doc/images/skyscraper.jpg "Skyscrapers")
![Tunnels](https://github.com/nebogeo/dbscode/raw/master/doc/images/tunnels.jpg "Tunnels")
Before you start:
* Launch Minecraft and create a new world.
* Launch Geany
## Step 1 Make a function that calls itself
* In Geany, from the file menu select "new", and then save as
`skyscraper.py` in the "pi" directory.
* We need to import the dBsCode commands we'll be using and clear an
area in the Minecraft world for working in. Create this program:
from dbscode_minecraft import *
bulldoze()
* **Test** Press F5 to run the program (this will also save your program
for you). After a few seconds you should see a "flatworld" type of
environment.
* In this project, we're going to see how "recursion" can be used to
make very complex structures without very much code. Lets start with a
row of blocks.
def row(pos,count):
if count>0:
box(SANDSTONE,pos,point(1,1,1))
row(pos+point(3,0,0),count-1)
This function is a bit like a for loop, in that it will repeat the box
command as many times as count is set to at the start. If we pass 10 in,
it checks it with the if, draws a cube and calls itself at the end with
the count as 9 - and so on until it reaches 0, when it's finished (the
"if" only runs it's following code running if count is greater than 0).
Let's try it:
row(point(0,0,0),5)
You should see 10 blocks. If the program seems to be running forever,
click in the section at the bottom of the geany window and press "ctrl"
and "c" to stop it and check the code.
## Step 2 Make a tree
We can use the same principle to do more interesting things. Change your
`row` function into a tree one:
def tree(pos,count):
if count>0:
box(SANDSTONE,pos,point(1,count,1))
tree(pos+point(-1,count,0),count-1)
tree(pos+point(1,count,0),count-1)
tree(point(0,0,0),5)
Here we are calling ourselves twice at the end, you can think of these
like branches first moving to the left then the right, with -1 and 1
being added to the X position in the tree call. We're also using the
current count to change how big the box is as it goes down the tree.
## Step 3 Test out the third dimension
As well as moving -1 and 1 in the X, lets add it to the Y as well - add
this inside the the tree function, after the 2 others:
tree(pos+point(0,count,-1),count-1)
tree(pos+point(0,count,1),count-1)
Try increasing the 1 and -1's in the points to 2 and -2 - this will
explode the tree - your function should now look like this:
def tree(pos,count):
if count>0:
box(SANDSTONE,pos,point(1,count,1))
tree(pos+point(-2,count,0),count-1)
tree(pos+point(2,count,0),count-1)
tree(pos+point(0,count,-2),count-1)
tree(pos+point(0,count,2),count-1)
* Try change the amount it explodes, and making different for different
dimensions.
## Step 4 Adding direction and randomness
Let's briefly go back to 2 dimensions and try adding direction (a `d`
parameter), this is needed so we can make longer branches that point in
different directions. We'll add `d` to the `pos` in the tree call and to
the size of the box too:
def tree(pos,d,count):
if count>0:
box(SANDSTONE,pos,d+point(1,1,1))
tree(pos+d,point(-2,0,0),count-1)
tree(pos+d,point(2,0,0),count-1)
tree(pos+d,point(0,2,0),count-1)
tree(point(0,0,0),point(0,1,0),5)
Try running this, it doesn't look too interesting - the problem is that
there are far too many branches being drawn all on top of each other. We
can remove some of them using a random chance, let's write a quick
function to make this easier:
def chance(percent):
return random_range(0,100)<percent
This returns `True` if the parameter is less than a random number
between 0 and 100. 50 will result in a 50% chance. We'll use it with
`if` to give out tree a 50% chance of branching in any direction:
def tree(pos,d,count):
if count>0:
box(SANDSTONE,pos,d+point(1,1,1))
if chance(50): tree(pos+d,point(-2*count,0,0),count-1)
if chance(50): tree(pos+d,point(2*count,0,0),count-1)
if chance(50): tree(pos+d,point(0,2*count,0),count-1)
This will be different each time you run the program, so try it a few
times as some of the trees will be better than others.
## Step 5 Going large!
Lets make it all much bigger and add a thickness to the branches.
* Put `count` in the box size.
* Change the 2 and -2's with 4 and -4 to make it go further.
* Add back the third dimension with two more branches.
def chance(percent):
return random_range(0,100)<percent
def tree(pos,d,count):
if count>0:
box(SANDSTONE,pos,d+point(count,count,count))
if chance(50): tree(pos+d,point(-4*count,0,0),count-1)
if chance(50): tree(pos+d,point(4*count,0,0),count-1)
if chance(50): tree(pos+d,point(0,0,-4*count),count-1)
if chance(50): tree(pos+d,point(0,0,4*count),count-1)
if chance(50): tree(pos+d,point(0,4*count,0),count-1)
tree(point(0,0,0),point(0,1,0),7)
## Step 6 Convert it into a dungeon
Nearly all the shapes we've been making are made by placing blocks into
the world, but it's just as easy to make shapes by taking them away. With
4 changes we can make this into a dungeon generator:
* Change `SANDSTONE` to `AIR`
* Change `point(0,4*count,0)` in the last tree branch to
`point(0,-4*count,0)`. This will make it go down instead of up.
* Change 1 to -5 where the tree is first called.
* Add a huge box to clear the ground
`box(SANDSTONE,point(-100,-101,-100),point(200,100,200))`
def chance(percent):
return random_range(0,100)<percent
def tree(pos,d,count):
if count>0:
box(AIR,pos,d+point(count,count,count))
if chance(50): tree(pos+d,point(-4*count,0,0),count-1)
if chance(50): tree(pos+d,point(4*count,0,0),count-1)
if chance(50): tree(pos+d,point(0,0,-4*count),count-1)
if chance(50): tree(pos+d,point(0,0,4*count),count-1)
if chance(50): tree(pos+d,point(0,-4*count,0),count-1)
box(SANDSTONE,point(-100,-101,-100),point(200,100,200))
tree(point(0,0,0),point(0,-5,0),5)
## Challenges
* What happens if you remove the bulldoze or huge SANDSTONE box and
repeatedly run the program?
* We haven't tried changing the block types - can you make the tree
structure change material as it gets built?
* Can you add lights and decoration to the dungeon?
* Can you find a way to combine both above and below ground structures?
pandoc -c github.css 01-house.md > rendered/01-house.html
pandoc -c github.css 02-auto-castle.md > rendered/02-auto-castle.html
pandoc -c github.css 03-skyscraper-tunnels.md > rendered/03-skyscraper-tunnels.html
pandoc -c github.css ../docs.md > rendered/docs.html
body {
font-family: Helvetica, arial, sans-serif;