Tuesday, 13 January 2015

Minecraft API - Player's Direction

One of the questions I get asked a lot about the Minecraft: Pi edition APi is "how can I get the direction the player is facing?" and I have always had to say "sorry you can't do that".

While I can't change the API for Minecraft: Pi edition I can change RaspberryJuice - so I decided to add functions to allow you to find out where the player is looking.  You can download the RaspberryJuice plugin Canarymod and Bukkit.

I have also created a new Adventures in Minecraft starterkit which includes the new version of RapsberryJuice and everything you need to use the new api functions.



The 3 new functions in the api are:
  • player.getRotation() - return the angle of rotation between 0 and 360
  • player.getPitch() - returns the angle of pitch between -90 and 90
  • player.getDirection() - returns a unit-vector of x,y,z pointing in the direction the player is facing
The functions also work with the entities as well so you can use entity.getRotation(), entity.getPitch() and entity.getDirection()

Here are the couple of code examples I demo in the video.

Get the players rotation and pitch angles:
#import the minecraft module
import mcpi.minecraft as minecraft

#create a connection to minecraft
mc = minecraft.Minecraft.create()

while True:
    #get the players rotational angle
    angle = mc.player.getRotation()
    #get the player up and down angle
    pitch = mc.player.getPitch()
    mc.postToChat(str(angle) + " : " + str(pitch))

Create a block in front of the player using getDirection():
import mcpi.minecraft as minecraft
import mcpi.block as block
import time

#how far in front of the player the block will be
BLOCKDISTANCE = 5

mc = minecraft.Minecraft.create()

while True:
    #get the position
    pos = mc.player.getPos()
    #get the direction
    direction = mc.player.getDirection()
    #calc the position of the block in front of the player
    x = round(pos.x + (direction.x * BLOCKDISTANCE))
    y = round(pos.y + (direction.y * BLOCKDISTANCE) + 1)
    z = round(pos.z + (direction.z * BLOCKDISTANCE))
    mc.setBlock(x,y,z,block.DIAMOND_BLOCK)
    time.sleep(0.1)
    mc.setBlock(x,y,z,block.AIR)

I hope you find the new api functions useful.

Tuesday, 30 December 2014

BBC iPlayer RSS Feeds - 4 million / day

When the BBC decided to turn off its iPlayer RSS feeds with no up front warning, I was annoyed, really annoyed, which isn't like me.

Why was I annoyed? I think it was the total arrogance of the "Yeah sorry you used to use them, we don't do that anymore, try this new thing", "but the new things not ready yet?", "Yeah sorry about that".

In short a fairly sizable group of people used these RSS feeds either directly or indirectly via 3rd party tools to access iPlayer content and it was the only reliable and open data method for getting information about iPlayer content.

I was interested, other than me, who else used these RSS feeds, so I made a freedom of information (FOI) request to the BBC and asked them.  It turns out for the 30 days prior to them being shutdown they were accessed 4,000,000 (yes 4 MILLION) times a day!  

The response from the BBC goes on to state that they suspect that a fair number of the downloads where from bots, yep probably, but those bots where doing so to deliver services to people and it could be that 1 bot download serviced many people.

The moral of the story is that you can't expect services to be provided forever regardless of who is providing them and how many people use them or assume that there will be a replacement.

Below is my request and subsequent response from the BBC.

Subject: FOI Request - BBC iPlayer RSS Feeds Usage
From: "Martin O'Hanlon" 
To: foi@bbc.co.uk

Dear Sir,

I am requesting information under the Freedom of Information act about the
usage of BBC iPlayer RSS feeds.

On (or around) the 29th October, access to the BBC iPlayer RSS Feeds was
removed (http://iplayerhelp.external.bbc.co.uk/tv/feeds).

Could you provide information relating to the use of the RSS Feeds prior to
them being removed.

By month, for the 12 months previous could you state the number of access
requests for the RSS feeds supplied under http://feeds.bbc.co.uk/iplayer/.

If you are unable to provide 12 months of data by month, could you provide
the information for the last 30 days.

Kind regards

Martin O'Hanlon

-------------------------------

From: FOI Enquiries <FOIEnquiries@bbc.co.uk>
To: "'Martin O'Hanlon'" 
Subject: Response to your request for information - RFI20141782
Date: Fri, 28 Nov 2014 14:49:53 +0000

Dear Mr O'Hanlon

Please find attached the response to your request for information, referenc=
e RFI20141782.

Kind regards

BBC Information Policy and Compliance
BC2 B6, 201 Wood Lane, London W12 7TP

You can download the attached from here, or view the contents below:

Martin O'Hanlon
Via email: martin@ohanlonweb.com

28th November 2014

Dear Martin,

Freedom of Information Act 2000 – RFI20141782

Thank you for your request under the Freedom of Information Act (‘the Act’) of 30th
October 2014, seeking:

“I am requesting information under the Freedom of Information act about the usage of BBC iPlayer
RSS feeds.

On (or around) the 29th October, access to the BBC iPlayer RSS Feeds was removed
(http://iplayerhelp.external.bbc.co.uk/tv/feeds).

Could you provide information relating to the use of the RSS Feeds prior to them being removed.
By month, for the 12 months previous could you state the number of access requests for the RSS
feeds supplied under http://feeds.bbc.co.uk/iplayer/.

If you are unable to provide 12 months of data by month, could you provide the information for the
last 30 days.”

I estimate that to deal with your request for 12 months’ worth of data, given the amount of
data this involves, would take more than two and a half days; under section 12 of the Act, we
are allowed to refuse to handle the request if it would exceed the appropriate limit. The
appropriate limit has been set by the Regulations (SI 2004/3244) as being £450 (equivalent to
two and a half days work, at an hourly rate of £25).

With regard to the 30 days’ worth information, the following is the total number of requests
for urls under http://feeds.bbc.co.uk/iplayer which relates to iPlayer RSS feeds (date followed
by the total):

28/09/2014 - 4139102
29/09/2014 - 3969429
30/09/2014 - 3688324
01/10/2014 - 3884073
02/10/2014 - 3883081
03/10/2014 - 3925662
04/10/2014 - 4159130
05/10/2014 - 4222500
06/10/2014 - 4131642
07/10/2014 - 3985815
08/10/2014 - 4012385
09/10/2014 - 3915717
10/10/2014 - 3882014
11/10/2014 - 4048638
12/10/2014 - 4208782
13/10/2014 - 4116161
14/10/2014 - 3970894
15/10/2014 - 4140721
16/10/2014 - 3840215
17/10/2014 - 3929542
18/10/2014 - 4183741
19/10/2014 - 4298197
20/10/2014 - 4059930
21/10/2014 - 4046123
22/10/2014 - 3941945
23/10/2014 - 3922209
24/10/2014 - 3962244
25/10/2014 - 4070949
26/10/2014 - 4307425
27/10/2014 - 4074942
28/10/2014 - 4050387

Please note that the data above does not give any indication of what was making the request
i.e. whether human or bot (a software application which runs automated tasks over the
internet). The rather flat level probably indicates that there was a very large amount of
automated processing going on, so this should not be confused with actual users doing
something with the information in the feeds.

You may request an internal review of our decision that your request exceeds the
appropriate limit. Please contact us at the address above, explaining what you would like us to
review and including your reference number. If you are not satisfied with the internal review,
you can appeal to the Information Commissioner. The contact details are: Information
Commissioner's Office, Wycliffe House, Water Lane, Wilmslow, Cheshire, SK9 5AF, Tel:
0303 123 1113 (local rate) or 01625 545 745 (national rate) or see http://www.ico.gov.uk/

Yours sincerely,

Kate Leece
Head of Legal and Business Affairs
BBC Future Media


Sunday, 21 December 2014

Minecraft - QR Codes

This isn't a particularly new idea and it has certainly been done before... but I like it.

Point your phone at this...

I saw a tweet from @ImmersiveMind (aka Stephen Reid) linking to a new blog post about creating qr codes in Minecraft as a way of showing how to link the real world with the digital.

That afternoon I had 30 minutes to wait before I was picked up to be taken out for festive activities - could I code a Minecraft QR generator in time? It turns out I could:


I used the fantastic python module qrcode to create the QR, which has a useful function, get_matrix(), to allow you to get the QR as a 2 dimensional list of True and False's.  I then used this matrix to create white and black wool - dead easy!

The qrcode module can be installed using pip and requires PIL (python imaging library) to work - the first step is to install pip and PIL and then use pip to install qrcode:
sudo apt-get install python-pip python-imaging
sudo pip install qrcode
You can get the code from github:
git clone https://github.com/martinohanlon/minecraft-qrcode
Run it:
python minecraft-qrcode/minecraft-qrcode.py
The code:
from qrcode import *
from mcpi.minecraft import *
from mcpi.block import *

def getQR(inputString):
    qr = QRCode(version=1,
                error_correction=ERROR_CORRECT_L,
                border=1)
    qr.add_data(inputString)
    qr.make()
    return qr.get_matrix()

mc = Minecraft.create()
pos = mc.player.getTilePos()

qrMatrix = getQR("http://www.stuffaboutcode.com")

rowNo = 0
for row in qrMatrix:
    rowNo -= 1
    columnNo = 0
    for column in row:
        columnNo -= 1
        if column:
            #black
            blockColour = 15
        else:
            #white
            blockColour = 0

        mc.setBlock(pos.x + rowNo, pos.y + columnNo, pos.z, WOOL.id, blockColour)

Thursday, 18 December 2014

GPIO Xmas Tree Reaction Game

In order to bring a bit of Christmas based fun to the office (I am normally a bit of a 'bah humbug') I have created a reaction game using a Raspberry Pi and a GPIO Xmas Tree from pocketmoneytronics.


The game is pretty simple, random leds are lit up on the xmas tree, the player has to press the button when the green led on the top of the tree is lit up.  The quicker you are, the higher you score.

A random animation is played when the tree is waiting for the next player, you press the button to start, all the leds are lit up and then its time to play.  At the end the players position is displayed on the tree, with 1st place lighting up the top of the tree.

Have a go
You will need a Raspberry Pi, a GPIO Xmas Tree and a button:
  1. Plug the GPIO Xmas tree into the far left set of pins on the GPIO header
  2. Connect the button up between 3.3V and GPIO 4.

Update - 23/12/2014 - 2 player
The GPIO Xmas Tree Reaction game has gone 2 player...  You go head to head versus a friend, the quickest to the green led wins and an led on either the left or right of the tree is lit up to show you who won.


If you want to play head to head:
  1. Connect the button up between 3.3V and GPIO22
  2. Press the 2nd button to start the 2 player game, pressing the 1st button, still starts the 1 player game
Get the code
Download the code from github.com/martinohanlon/GPIOXmasTreeGame and run the game - open a terminal and run:
git clone https://github.com/martinohanlon/GPIOXmasTreeGame
cd GPIOXmasTreeGame/xmastreegame
python xmastreegame.py

How does it work
There was only 1 particular challenge to creating the game (other than my dodgy soldering which meant I ruined the first tree I brought)  - doing more than one thing at a time!

The libraries supplied by pocketmoneytronics are really good and there are some great examples, the problem I had is that when you tell the tree to "light up leds 1 & 4", that is all your program can do, it blocks because the tree uses Charlieplexing and the libraries don't support threading.

Charliewhat?  In summary, each gpio pin is actually controlling 2 leds and when you light up 2 leds on the tree the program is actually turning the leds on and off independently really quickly, so quickly that its tricking your eyes into thinking that both leds are actually turned on.

This is why you cant just "turn on led 1 and led 4", the tree doesn't work that way.

To get around this, I made a threaded version of the pocketmoneytronics tree.py module.

Using the original libraries, you would have used the following code to light up all the leds for 1 second:
tree.setup()
#turn all the leds on for 1 second
#the program stops here and nothing can happen until the leds turns off
tree.leds_on_and_wait(ALL, 1)
tree.cleanup()
Using my threaded class you would use:
#create the XmasTree object
tree = XmasTree()
#start the tree object
tree.start()
#turn all leds on
tree.leds_on(ALL)
#the program can now do what it wants and the leds will stay on
sleep(1)
tree.stop()
The rest of the program was pretty easy to create, wait for a button to be pressed, light up led's randomly with a random delay in between, get the difference in time between turning on the yellow led and the button being pressed and hey presto, a Christmas themed game.

The code
All the code is here github.com/martinohanlon/GPIOXmasTreeGame.
import threading
import RPi.GPIO as GPIO
from time import sleep, time
from ThreadedTree import XmasTree
from random import getrandbits, randint
from os.path import isfile

#CONSTANTS
#leds
L0 = 1
L1 = 2
L2 = 4
L3 = 8
L4 = 16
L5 = 32
L6 = 64
ALL = 1+2+4+8+16+32+64
#leds as a list
LEDS = [L0,L1,L2,L3,L4,L5,L6]
#leds as a list descending down the tree
LEDSDESC = [L0,L6,L5,L4,L2,L1,L3]
#gpio pin the game button is connected too
NEWGAMEBUTTONPIN = 4
#gpio pin which will cause the game to stop if trigger
STOPGAMEBUTTONPIN = 17

class TreeRandom(threading.Thread):
    def __init__(self, xmasTree):
        #setup threading
        threading.Thread.__init__(self)
        #setup properties
        self.stopped = False
        self.running = False
        self.xmasTree = xmasTree
        
    def run(self):
        self.running = True
        while not self.stopped:
            ledsToLight = 0
            #loop through all the lights, randomly pick which ones to light
            for led in LEDS:
                if getrandbits(1) == 1:
                    ledsToLight = ledsToLight + led
            #turn the leds on
            self.xmasTree.leds_on(ledsToLight)
            #delay
            sleep(1)
        #when its stopped turn the leds off
        self.xmasTree.leds_on(0)
        self.running = False

    def stop(self):
        #stop the animation
        self.stopped = True
        #wait for it to stop running
        while self.running:
            sleep(0.01)

class TreeGame():
    def __init__(self, xmasTree, scoresFile):
        self.scoresFile = scoresFile
        self.scores = self._loadScores()
        #print self.scores

    def play(self):
        #turn on all leds
        xmasTree.leds_on(ALL)
        #wait a bit
        sleep(2)
        #get a random number, which will be how many leds will be lit before the green one
        steps = randint(7,14)
        for step in range(0,steps):
            #light a random red led
            ledToLight = LEDS[randint(1,6)]
            xmasTree.leds_on(ledToLight)
            #wait for a random time between 0.5 and 1 second
            timeToSleep = randint(5,10) / 10.0
            sleep(timeToSleep)
        #light the green led
        xmasTree.leds_on(L0)
        #get the time
        startTime = time()
        #wait for button to be released (if its pressed)
        while(GPIO.input(NEWGAMEBUTTONPIN) == 1):
            sleep(0.001)
        #wait for the button to be pressed
        while(GPIO.input(NEWGAMEBUTTONPIN) == 0):
            sleep(0.001)
        #get the time
        endTime = time()
        timeDiff = endTime - startTime
        #put the score in the score list and find the position
        # loop through all the scores
        for score in range(0,len(self.scores)):
            # is this time less than the current score?
            if timeDiff < self.scores[score]:
                #record the players position
                position = score
                self.scores.insert(score,timeDiff)
                break
        #save to the score file
        self._saveScores()
        #flash the position
        self._displayPosition(position)

    def _displayPosition(self,position):
        #if there position was less than 6, flash it on the tree
        # else flash all the lights
        if position <= 6:
            ledToLight = LEDSDESC[position]
        else:
            ledToLight = ALL
        #flash the position
        for count in range(15):
            xmasTree.leds_on(ledToLight)
            sleep(0.2)
            xmasTree.leds_on(0)
            sleep(0.2)

    # load the scores files
    def _loadScores(self):
        scores = []
        #does the file exist?  If so open it
        if isfile(self.scoresFile):
            with open(self.scoresFile, "r") as file:
                for score in file:
                    scores.append(float(score))
        else:
            #no file so put an initial score which is massive
            scores.append(999)

        return scores

    # save the scores file
    def _saveScores(self):
        with open(self.scoresFile, "w") as file:
            for score in self.scores:
                file.write(str(score)+"\n")


#main program
if __name__ == "__main__":

    #setup GPIO
    GPIO.setmode(GPIO.BCM)

    #setup the new game button
    GPIO.setup(NEWGAMEBUTTONPIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    #setup the stop game button
    GPIO.setup(STOPGAMEBUTTONPIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

    #create threaded tree object
    xmasTree = XmasTree()
    #start the xmas tree
    xmasTree.start()
    #create tree game oject
    treeGame = TreeGame(xmasTree, "scores.txt")

    try:
        stopGame = False
        #loop until the stop game pin is set
        while(not stopGame):
            #run the xmas tree random animation
            treeRandom = TreeRandom(xmasTree)
            treeRandom.start()
            
            #wait until a button is pressed to either start a new game or stop the game
            while(GPIO.input(NEWGAMEBUTTONPIN) == 0 and GPIO.input(STOPGAMEBUTTONPIN) == 0):
                sleep(0.01)
            
            #new game
            if GPIO.input(NEWGAMEBUTTONPIN) == 1:                
                #stop the animation
                treeRandom.stop()
                #run the game
                treeGame.play()
                #game over, start the animation again
                sleep(1)

            #stop game
            elif GPIO.input(STOPGAMEBUTTONPIN) == 1:
                stopGame = True
        
    finally:
        #stop tree random animation
        treeRandom.stop()
        #stop xmas tree
        xmasTree.stop()
        #cleanup gpio
        GPIO.cleanup()


Monday, 24 November 2014

Minecraft Sat Nav

A couple of months back the ordnance survey created version 2 of their Minecraft map of Great Britain, its got loads more detail than the original, and is even more brilliant than there first one.

It is however a pain to get around...  Welcome "Minecraft Sat Nav", utterly ridiculous, totally pointless but at the same time brilliant.

Imagine you are exploring Minecraft Great Britain and you suddenly realise you need to get to Macclesfield but dont know the way, simple fire up Minecraft Sat Nav (patent pending!) and type navigate Macclesfield and it will give you a street by street navigation between your location and a town which was previously one of the world's biggest producers of silk!


How does it work?  Here are some facts:
  1. Its a python program
  2. It uses a Canarymod minecraft server to host the map
  3. The RaspberryJuice plugin is used to talk to Minecraft
  4. It uses the MapQuest open API's to get the locations and directions
  5. I reverse engineered Ordance Survey's 'conversion tool' to work out how to turn eastings and northings into Minecraft co-ordinates
  6. I used Hannah Fry's awesome python code to turn latitude and longitude into eastings and northings
  7. Its got a low tech 'retro styled' command line interface
You want to have a go yourself?  Here's a guide:
  1. Buy yourself a copy of Adventures in Minecraft ;) - honestly you really cant go wrong and it'll teach you what you need to know to make your own Minecraft Sat Nav!
  2. Setup a Canarymod server with RaspberryJuice
  3. Download the Ordnance Survey Minecraft GB map
  4. Replace the default world in canarymod with the Minecraft GB map
  5. Download the Minecraft Sat Nav program
  6. Run the MinecraftSatNav.py python program
The commands are really simple:
  • teleport <location> e.g. teleport london
  • navigate <destination> e.g. navigate fort william
  • navigateFrom <start>,<dest> e.g. navigate sheffield, grindleford
  • exit
Enuf said..

Thursday, 13 November 2014

Minecraft: Pi Worksheet

I quite often run workshops about programming Minecraft on the Raspberry Pi either at Raspberry Jam's or Pycon or schools or just about anywhere else you can power on a Pi and if you have attended one of these you will have no doubt used my worksheet.



I have been asked a few times if I can share it, so, I have put the Minecraft: Pi Edition Worksheet on google docs and made it public.  Please feel free to use it, share it, copy it - although a link back is always welcome :)

Tuesday, 11 November 2014

Adventures in Minecraft Arrives

I've been working with David Whale (blog.whaleygeek.co.uk) since February writing a book called "Adventures in Minecraft" and today it started arriving through people's doors!

It's a book written especially for 11-15 year old's and its a fun way to not only get into programming using Python but also learn how to do amazing things with Minecraft.

All the adventures, projects and programs in the book work with Minecraft on the PC and Apple Mac as well as Minecraft: Pi Edition on the Raspberry Pi.


I have created a small mini site, including a forum for the readers of the book to come together get support, share ideas and I personally can't wait to see the projects you create.

Its available from Wiley, Amazon (UK, US) and loads of other book stockists.

Minecraft programming experts David Whale and Martin O′Hanlon walk you step–by–step through everything you need to know to:
  • Get started writing Minecraft programs in Python on your PC, Mac, or Raspberry Pi
  • Build houses and other structures in the blink of an eye, and make a 3D duplicating machine
  • Write interactive games like a field that charges you rent, and a treasure hunt using magic vanishing bridges
  • Build custom game control panels using simple electronic circuits
  • Easily build huge 2D and 3D structures such as spheres and pyramids
  • Build intelligent objects like a massive Minecraft clock, and program an alien invasion
  • Plan and write a complete interactive arena game
Using the programming skills you learn from this book, writing Minecraft programs offers endless possibilities to create anything you can imagine.

To make your journey that much easier, the Adventures in Minecraft companion website, wiley.com/go/adventuresinminecraft supplies you with a video for each adventure in the book, downloadable code files, helpful programming reference tables, a bonus adventure, and badges to collect for your Minecraft accomplishments.

Myself and David are really proud of what we have created and think it really is a great way to get into creating your own Minecraft mods, tools and games.

Ria at SilkStream who got a copy of the book early has written up a review.