Sunday 3 November 2013

Minecraft Coding a Traffic Jam

I was doing a workshop on Minecraft Coding on the Rapsberry Pi for a group of teachers at Edge Hill University and I wanted to create a stretching challenge.

The idea of the workshop was that the starting point would be to create a working traffic light, which change through the colours in sequence e.g. Red, Red/Amber, Green, Amber, Red, but for those who completed the challenge early the stretch would be to expand on this to 2 traffic lights, a road and so on.

I created a series of programs in Python which would simulate a road system, traffic lights, junctions, cars, etc in Minecraft as examples which the attendees could take away.

My finished traffic jam.

I created 5 programs to show how I went from creating 1 traffic light to my traffic jam.  Each program led on from the next.  All the code is on githubhttps://github.com/martinohanlon/minecraft-traffic.

1light.py - is a really basic implementation of a traffic light, a single function which sets the blocks in turn.

```#minecraft traffic lights - simple
```
```#import the minecraft.py module from the minecraft directory
import minecraft.minecraft as minecraft
#import minecraft block module
import minecraft.block as block
#import time, so delays can be used
import time

#Connect to minecraft
mc = minecraft.Minecraft.create()

#clear area
mc.setBlocks(-10,0,-10,60,50,50,block.AIR.id)
#put grass on the floor
mc.setBlocks(-10,-1,-10,60,-1,10,block.GRASS.id)

#build traffic light
# pole straight up
mc.setBlocks(0,0,0,0,5,0,block.IRON_BLOCK.id, 15)
# create 3 lights out of wool
# wool values (black - 15, red - 14, yellow - 4, green - 13)
# set all the lights to off (black)
mc.setBlock(1,5,0,block.WOOL.id,15)
mc.setBlock(1,4,0,block.WOOL.id,15)
mc.setBlock(1,3,0,block.WOOL.id,15)

#loop forever
while(True):
#set to stop
mc.setBlock(1,5,0,block.WOOL.id,14)
mc.setBlock(1,4,0,block.WOOL.id,15)
mc.setBlock(1,3,0,block.WOOL.id,15)
# sleep for half a second
time.sleep(0.5)
#set to stop, prepare
mc.setBlock(1,5,0,block.WOOL.id,14)
mc.setBlock(1,4,0,block.WOOL.id,4)
mc.setBlock(1,3,0,block.WOOL.id,15)
time.sleep(0.5)
#set to go
mc.setBlock(1,5,0,block.WOOL.id,15)
mc.setBlock(1,4,0,block.WOOL.id,15)
mc.setBlock(1,3,0,block.WOOL.id,13)
time.sleep(0.5)
#set to prepare
mc.setBlock(1,5,0,block.WOOL.id,15)
mc.setBlock(1,4,0,block.WOOL.id,4)
mc.setBlock(1,3,0,block.WOOL.id,15)
time.sleep(0.5)
#back to stop```

1light-functions.py - is effectively the same program as 1light.py, but I have split the code into 3 functions:
• createTrafficLight() - creates the pole and 3 'lights' on the top
• go() - sets the traffic light to go
• stop() - sets the traffic light to stop
2lights.py - creates 2 traffic lights, which work in unison, one setting to stop, the other setting to go.  To do this I created a class called TrafficLight.  The main program then created 2 instances of this class and called stop, go methods of the class to change the lights

#minecraft traffic lights - classes

#import the minecraft.py module from the minecraft directory
import minecraft.minecraft as minecraft
#import minecraft block module
import minecraft.block as block
#import time, so delays can be used
import time

class TrafficLight():
def __init__(self, mc, x, y, z):
#build traffic light
# pole straight up
mc.setBlocks(x,y,z,x,y+5,z,block.IRON_BLOCK.id, 15)
# create 3 lights out of wool
# wool values (black - 15, red - 14, yellow - 4, green - 13)
# set all the lights to off (black)
mc.setBlock(x+1,y+5,z,block.WOOL.id,15)
mc.setBlock(x+1,y+4,z,block.WOOL.id,15)
mc.setBlock(x+1,y+3,z,block.WOOL.id,15)
#set to stop
mc.setBlock(x+1,y+5,z,block.WOOL.id,14)
#store x,y,z
self.x = x
self.y = y
self.z = z
#store mc
self.mc = mc

def go(self):
#set to stop, prepare        self.mc.setBlock(self.x+1,self.y+5,self.z,block.WOOL.id,14)
self.mc.setBlock(self.x+1,self.y+4,self.z,block.WOOL.id,4)
self.mc.setBlock(self.x+1,self.y+3,self.z,block.WOOL.id,15)
time.sleep(0.5)
#set to go
self.mc.setBlock(self.x+1,self.y+5,self.z,block.WOOL.id,15)
self.mc.setBlock(self.x+1,self.y+4,self.z,block.WOOL.id,15)
self.mc.setBlock(self.x+1,self.y+3,self.z,block.WOOL.id,13)
time.sleep(0.5)

def stop(self):
#set to prepare
self.mc.setBlock(self.x+1,self.y+5,self.z,block.WOOL.id,15)
self.mc.setBlock(self.x+1,self.y+4,self.z,block.WOOL.id,4)
self.mc.setBlock(self.x+1,self.y+3,self.z,block.WOOL.id,15)
time.sleep(0.5)
#set to stop
self.mc.setBlock(self.x+1,self.y+5,self.z,block.WOOL.id,14)
self.mc.setBlock(self.x+1,self.y+4,self.z,block.WOOL.id,15)
self.mc.setBlock(self.x+1,self.y+3,self.z,block.WOOL.id,15)
# sleep for half a second
time.sleep(0.5)

#MAIN PROGRAM

#Connect to minecraft
mc = minecraft.Minecraft.create()

#clear area
mc.setBlocks(-5,0,-5,5,50,5,block.AIR.id)

#create traffic light
trafficLight1 = TrafficLight(mc,0,0,0)
trafficLight2 = TrafficLight(mc,0,0,3)

#loop forever
while(True):
trafficLight2.stop()
trafficLight1.go()
time.sleep(2)
trafficLight1.stop()
trafficLight2.go()
time.sleep(2)

road.py - to create a road and a junction I extended the program to include new classes for Road() and Junction(), the road now becomes the controlling class, allowing you to create junctions on the road.  In order to get the traffic lights changing in unison and allowing the elements of the road to work independently I also implemented threading.

car.py - is where I introduced the Car() class, again cars are controlled by the Road() class and every instance of the Car() class runs in its own thread.   Each car works independently checking before it moves whether it is going to enter a closed junction or go into the back of another car.  The main program creates a new car on the road every 3 seconds.

#minecraft traffic lights - class

#import the minecraft.py module from the minecraft directory
import minecraft.minecraft as minecraft
#import minecraft block module
import minecraft.block as block
#import time, so delays can be used
import time
#import threading so I can make asynchronous calls!
#import random so I can randomly create cars
import random

def __init__(self, mc, x, y, z, width, lenght):
mc.setBlocks(x, y-1, z, x+lenght, y-1, z+width-1, block.BEDROCK.id)
#create line down the middle
mc.setBlocks(x, y-1, z+(width/2), x+lenght, y-1, z+(width/2), block.WOOL.id, 0)
#store values
self.x = x
self.y = y
self.z = z
self.width = width
self.lenght = lenght
#create empty junctions & cars list
self.junctions = []
self.cars= []

def run(self):
#start up junctions
for junction in self.junctions:
#tell the junction to run in the background
junction.daemon
junction.start()

def stop(self):
#stop junctions
for junction in self.junctions: junction.stop()
#stop cars
for car in self.cars: car.stop()
#wait for junctions to stop
for junction in self.junctions: junction.join()
#wait for cars to stop
for car in self.cars: car.join()

#create junction at position down the road
self.junctions.append(junction)

def startCar(self, direction):
#create car
car = Car(mc, self, direction)
self.cars.append(car)
#tell car to run in background
car.daemon
car.start()

#store variables
self.mc = mc
self.direction = direction
#set x,y,z
#set z & x position, left or right side, top or bottom of road depending on direction
if direction == 1:
if direction == -1:

def run(self):
lenghtOfCar = 4
self.running = True
ableToMove = True
#find the end of the road, depending on which way the car is going
#loop until i meet the end of the road
while(self.x != endOfRoad and self.running == True):
#draw the car
if ableToMove: self.drawCar()
#sleep for a bit
time.sleep(0.5)
#move the car
#where will the car be moving too?
frontOfCar = self.x + self.direction
#if im going forwards add 3 to the x, as my car's x is at the back
if self.direction == 1: frontOfCar = frontOfCar + lenghtOfCar
ableToMove = True
#am I going to enter a junction which is closed?
if self.direction == 1 and junction.x1 == frontOfCar and junction.open == False: ableToMove = False
if self.direction == -1 and junction.x2 == frontOfCar and junction.open == False: ableToMove = False
#am I going to hit another car?
if self.direction == 1 and frontOfCar >= car.x and frontOfCar <= (car.x + lenghtOfCar) and self.z == car.z and car.running == True: ableToMove = False
if self.direction == -1 and frontOfCar <= (car.x + lenghtOfCar) and frontOfCar >= car.x and self.z == car.z and car.running == True: ableToMove = False
#clear car and add 1 to the car's position
if ableToMove:
self.clearCar()
self.x = self.x + self.direction

self.running = False

def stop(self):
self.running = False

def drawCar(self):
self.mc.setBlocks(self.x, self.y, self.z, self.x + 3, self.y + 2, self.z, block.IRON_BLOCK.id)
self.mc.setBlock(self.x, self.y, self.z, block.WOOL.id, 15)
self.mc.setBlock(self.x+3, self.y, self.z, block.WOOL.id, 15)

def clearCar(self):
self.mc.setBlocks(self.x, self.y, self.z, self.x + 3, self.y + 2, self.z, block.AIR.id)

def __init__(self, mc, x1, y1, z1, x2, y2, z2, timeOpen, timeClosed):
#create junction
mc.setBlocks(x1,y1-1,z1,x2,y2-1,z2,block.BEDROCK.id)
#create lines
mc.setBlocks(x1,y1-1,z1,x2,y2-1,z1,block.WOOL.id,0)
mc.setBlocks(x2,y1-1,z1,x2,y2-1,z2,block.WOOL.id,0)
mc.setBlocks(x2,y1-1,z2,x1,y2-1,z2,block.WOOL.id,0)
mc.setBlocks(x1,y1-1,z2,x1,y2-1,z1,block.WOOL.id,0)
#create traffic lights
self.trafficLight1 = TrafficLight(mc, x1, y1, z1-1, -1)
self.trafficLight2 = TrafficLight(mc, x2, y2, z2+1, 1)
#set to open
self.openJunction()
#store times
self.timeOpen = timeOpen
self.timeClosed = timeClosed
#store variables
self.x1 = x1
self.y1 = y1
self.z1 = z1
self.x2 = x2
self.y2 = y2
self.z2 = z2

def run(self):
#start the Junction
self.running = True
while(self.running):
self.openJunction()
time.sleep(self.timeOpen)
self.closeJunction()
time.sleep(self.timeClosed)

def stop(self):
#stop the junction
self.running = False

def openJunction(self):
#set lights to go
light1 = self.trafficLight1.go()
light2 = self.trafficLight2.go()
#wait for lights to finish changing
light1.join()
light2.join()
#set open status to True
self.open = True

def closeJunction(self):
#set lights to stop
light1 = self.trafficLight1.stop()
light2 = self.trafficLight2.stop()
#wait for lights to finish changing
light1.join()
light2.join()
#set open status to False
self.open = False

class TrafficLight():
def __init__(self, mc, x, y, z, direction):
#build traffic light
# pole straight up
mc.setBlocks(x,y,z,x,y+5,z,block.IRON_BLOCK.id, 15)
# create 3 lights out of wool
# wool values (black - 15, red - 14, yellow - 4, green - 13)
# set all the lights to off (black)
mc.setBlock(x+direction,y+5,z,block.WOOL.id,15)
mc.setBlock(x+direction,y+4,z,block.WOOL.id,15)
mc.setBlock(x+direction,y+3,z,block.WOOL.id,15)
#set to stop
mc.setBlock(x+direction,y+5,z,block.WOOL.id,14)
#store x,y,z
self.x = x
self.y = y
self.z = z
#store direction
self.direction = direction
#store mc
self.mc = mc

def go(self):

def setToGo(self):
#set to stop, prepare
self.mc.setBlock(self.x+self.direction,self.y+5,self.z,block.WOOL.id,14)
self.mc.setBlock(self.x+self.direction,self.y+4,self.z,block.WOOL.id,4)
self.mc.setBlock(self.x+self.direction,self.y+3,self.z,block.WOOL.id,15)
time.sleep(0.5)
#set to go
self.mc.setBlock(self.x+self.direction,self.y+5,self.z,block.WOOL.id,15)
self.mc.setBlock(self.x+self.direction,self.y+4,self.z,block.WOOL.id,15)
self.mc.setBlock(self.x+self.direction,self.y+3,self.z,block.WOOL.id,13)
time.sleep(0.5)

def stop(self):

def setToStop(self):
#set to prepare
self.mc.setBlock(self.x+self.direction,self.y+5,self.z,block.WOOL.id,15)
self.mc.setBlock(self.x+self.direction,self.y+4,self.z,block.WOOL.id,4)
self.mc.setBlock(self.x+self.direction,self.y+3,self.z,block.WOOL.id,15)
time.sleep(0.5)
#set to stop
self.mc.setBlock(self.x+self.direction,self.y+5,self.z,block.WOOL.id,14)
self.mc.setBlock(self.x+self.direction,self.y+4,self.z,block.WOOL.id,15)
self.mc.setBlock(self.x+self.direction,self.y+3,self.z,block.WOOL.id,15)
time.sleep(0.5)

if __name__ == "__main__":
#MAIN PROGRAM

#Connect to minecraft
mc = minecraft.Minecraft.create()

#clear area
mc.setBlocks(-10,0,-10,60,50,10,block.AIR.id)
#put grass on the floor
mc.setBlocks(-10,-1,-10,60,-1,10,block.GRASS.id)

#create junction, 20 blocks down, open for 10 seconds, closed for 5
#loop until Ctrl C
try:
while(True):
#create a car in a random direction, unless its 0 then we wont create a car
direction = random.randint(-1, 1)
if direction != 0:
#sleep for a bit
time.sleep(3)
except KeyboardInterrupt:
print("stopped")
finally:
#stop everything