Sniff is a "Scratch-like" programming language that's designed to help Scratchers move gently from Scratch to more conventional languages. They can start writing programs, without having to learn a new language because Sniff is based on Scratch. They learn a little more about variables, compiling, syntax errors (!), and they can have fun controlling real hardware while they're doing it.

Monday, 16 February 2015

Adventures in Minecracraft (Chapters 1&2)

With the release of the new Raspberry Pi I thought it was time to revisit one its compelling features: minecraft-pi (It's other must have feature is Mathematica, but that's a bit outside the scope here!). Minecraft on the Pi is a bit old and buggy compared to other versions, but it's free, and includes a simple API to allow you to manipulate blocks from your own code. Usually that's Python, but Sniff has also supported Minecraft-Pi for some time now. The code got a few bug fixes in Release 14.

By co-incidence Martin O'Hanlon and David Whale's book "Adventures in Minecraft" was on special offer on Amazon, so it seemed like a great idea to get the book, and see how it worked out rewriting the examples in Sniff.

"Adventures in Minecraft" is about learning to code in Python, using Minecraft as a pretty effective carrot. While I could pick issue with some things in the book, the fundamental principle is really great. Having interfaced Sniff to Minecraft some time ago, once I'd built a tower I was really at a loss of what to do with it. The book contains lots of great ideas for the kinds of things you can code for minecraft, which are completly worth the price of the book, even if you're not planning on following it in a step by step fashion.

Working through the examples and implementing them in Sniff demonstrated some of the differences between the Sniff and Python approach. In several cases the Python code was apparently shorter but relied on some behind the scenes Python magic feature that simply can't be explained in a few paragraphs to a novice programmer. The Sniff version had to work a little harder, but the result is something that is fairly self evident and self contained. Another way of looking at is that the Sniff version is something that a student could work towards and figure out for themselves, where as some of the Python solutions are based on simply the knowledge that there's some magic to do that written by someone else.

Adventure 1

Python

import mcpi.minecraft as minecraft

mc = minecraft.Minecraft.create()
mc.postToChat("Hello Minecraft World")

Sniff

make world minecraft device
make message string

when start
.set message to  "Hello Minecraft World"
.tell world to "show"

Adventure 1 is mainly about setup, and getting a first program working. The code is pretty much identical in both languages, though of course the syntax is different. In Python we import a module, connect to the world and post a message. In Sniff we do pretty much the same, except that we pass the text in the variable message, rather than as a parameter. The big strength of Sniff is that it avoid all those pesky dots. As a C programmer I know they're referencing elements of structures (or objects), but I can't really see myself sensibly explaining the reasoning behind minecraft.Minecraft.create to someone on their first day...

Sniff also has the option to connect to a remote server. I ran all my code on a Mac, but talking to Minecraft on a Pi. Just set the networkPeer before your first instruction to the world.

make world minecraft device
make networkPeer string
make message string

when start
.set networkPeer to "raspberrypi.local."
.set message to  "Hello Minecraft World"
.tell world to "show"

In Release 15 we should also have (optional) connection management, so you can connect and disconnect from different servers, or even connect to two minecraft worlds at the same time, so you could teleport objects from one to the other!

Adventure 2

make world minecraft device
make message string
make mcX number
make mcY number
make mcZ number

when start
.tell world to "getPos"
.set message to join "x=" [mcX]
.set message to join message join " y=" [mcY]
.set message to join message join " z=" [mcZ]
.tell world to "show"


In Chapter 2 we move on to checking the players position. This lays the foundations of getting to grips with the world coordinate system. We tell the world to "getPos", which sets up the three variables mcX,mcY and mcZ. From these we assemble the message, and send it back to the world.

import mcpi.minecraft as minecraft
mc = minecraft.Minecraft.create()
pos = mc.player.getTilePos()
mc.postToChat("x="+str(pos.x) + " y="+str(pos.y) +" z="+str(pos.z))

Here's the Python version. It's shorter but not necessarily clearer. Sniff code tends to be long and thin - there are more lines, but that means each does less, which makes each line easier to understand.

The Python code uses modules, objects, structures, dynamic typing, and operator overloading. That's not a criticism of Minecraft, or the Adventures book, but rather its just the Python way.  Of course they're all concepts that programmers should know, but that's a lot to throw at a non-programmer in their second coding lesson. Of course you could hope that your students aren't too bright, won't notice, maybe don't ask too many questions or will accept "because I say so" as an appropriate answer...

Here's the code for the welcome-home example from Adventure 2.

make world minecraft device
make message string
make mcX number
make mcY number
make mcZ number

when start
.forever
..wait 1 secs
..tell world to "getPos"
..if mcX>10 and mcX<11 and mcZ>12 and mcZ<13
...set message to "welcome home"
...tell world to "show"


and in Python

import mcpi.minecraft as minecraft
import time

mc = minecraft.Minecraft.create()
while True:
    time.sleep(1)
    pos = mc.player.getTilePos()
    if pos.x == 10 and pos.z == 12:
        mc.postToChat("welcome home")

The main difference here is that in Python getTilePos returns an integer, so we can compare it to 10 whereas in Sniff we use getPos which returns the float position. The python api also has a getPos() function which returns the accurate position, but in Sniff we decided to just have one function to return position. As such, we have to test that the player is in a region 10<x<11 rather than just x=10 (as an aside the Scratch guys were clever enough to avoid using =  to mean assignment. "=" in Scratch and Sniff means the same as it does in maths, and we never write X=X+1, which is obviously not true!).

Here's the final Rent example from Adventure 2 (read the book for a full explantation!):
make world minecraft device
make networkPeer string
make message string
make mcX number
make mcY number
make mcZ number

make xmin number
make xmax number
make zmin number
make zmax number

make rent number
make inField number

when start
.set networkPeer to "raspberrypi.local."
.set xmin to 10
.set xmax to 20
.set zmin to 10
.set zmax to 20
.forever
..tell world to "getPos"
..if mcX>xmin and mcX<xmax and mcZ>zmin and mcZ<zmax
...change rent by 1
...set message to join "You own rent:" [rent]
...tell world to "show"
...change inField by 1
..else
...set inField to 0
..
..if inField>3
...set message to "Too Slow"
...tell world to "show"
...set mcX to xmax+2
...set mcY to 10
...set mcZ to zmax+2
...tell world to "setPos"
..wait 1 secs

We mark out an area, and if the player is in the area we "charge rent". If they remain in the area for more than 3 seconds consecutively, they get teleported into the air, and outside the area.

I'll leave it there. The book is well worth getting, and contains lots of great project ideas. I'll post code for the later chapters in future posts (and try and avoid discussing Sniff Vs Python).

No comments:

Post a comment