Commando Jump Game For The Micro:bit In Python
Written by Mike James   
Friday, 15 April 2016
Article Index
Commando Jump Game For The Micro:bit In Python
Top Down Programming
Complete Listing

Starting The Program - Top Down

There is always a problem in getting started. A blank sheet of paper, or its digital equivalent a blank screen, is scary - for most people anyway. There is an old but still incredibly useful approach to programming, and many other things, called "top down programming". 

What you do is assume that you have already solved all of the problems in writing your program. So, for example, in our game we can pretend that we have a function that starts the game, one that plays it and a function that ends it. If you don't know about functions you need to look them up, but at their simplest they are a way of grouping a block of code under a name and from then on you can use the block of code by simply using its name. 

For example, start a new MicroPython project and enter:

from microbit import *

def startGame():
print("startGame")

def playGame():
print("playGame")

def endGame():
print("endGame")

startGame()
playGame()
endGame()

 

The first lines of the program define three new functions, startGame, playGame and endGame. All each one does at the moment is print its name to the serial console. In the jargon these are called "stubs" and the idea is that the stub stands in for the real function that you haven't written yet. 

Not particularly useful but we can now write our program as:

startGame()
playGame()
endGame()

Again you may not be too impressed, but we have a program that we can compile run and test. We have a start. If you do run the program you will see on the console:

 consolestub

 

You can see that your three functions are being used and in the right order - you have a working program and you only just started. This is the joy of top down programming.

Of course you now have to fill out the definitions of each of the functions to actually do something, but this isn't unreasonable. Another advantage is that you can fill in one of the stubs and still run and test the program while the other stubs remain stubs.

This is called stepwise refinement. 

Refining the startGame function

The easiest of the functions to refine is startGame. Let's add some code to show the player a short countdown to the start of the game and then display the wall which constitutes the playing area of the game:

def startGame():
  countDown()
  drawPlayArea()

This may at first seem like procrastination of the worst sort, but as your arithmetic teacher should have told you:

"put off doing any working out as long as possible". 

This is equally true of top down stepwise refinement. 

Now we do need to write some code to create the functions - there does come a time when you can't split something down into yet more functions.

The countDown function is simply: 

def countDown():
  for t in range(5,0,-1):
    display.scroll(str(t))
  display.scroll("!")

This uses a for loop with a negative increment to count down from 5 to 1 and then shows an exclamation mark to indicate the start. Notice the use of str(t) to convert from a number to a string. 

The drawPlayArea function is just as easy:

def drawPlayArea():
  display.clear()
  for y in range(1,5):
   display.set_pixel(2,y,9)

This displays a "wall" of full on LED in the middle of the screen x=2 and y runs from 1 to 4. 

Notice that both of these functions are simple and this is another advantage of top down programming. It means you only ever have to deal with small functions that do simple things and not a huge program made up of lots of lines of code. 

The playGame function

The playGame function is obviously going to the the most complex of the set. Again we try to simplify it by using other functions. We need to put a time limit on how long the player has and we need to keep track of the commando's vertical position. We are also going to be counting the number of times the player presses button A. The commando, which is just a single LED and so appears as a "block", is going to move up one place for every ten presses.

The playGame function starts:

def playGame():
  position=4
  button_a.reset_presses()
  t1=running_time()
  press=0

The variable t1 holds the time at which the player starts to play. 

Next we start a while loop that runs until the time is up. The logic is that we display the commando's position and read the number of times button A has been pressed. If the count gets to 10 then move the command up one. 

Given we have only five LEDs to switch on and off, the steps that the command takes are going to seem big. However the LEDs have 10 brightness levels. Why not make the commando move more smoothly by dimming one LED by one level and brightening the one above by a level for every press. This would make the commando seem to slide between LEDs with each button press.

This is more complicated than making a sudden jump after 10 presses, but the effect is worth it. The commando seems to move smoothly up the wall and it gives the player more feedback on what is happening.

To do this we need to implement the flashMan function:

def flashMan(x,y,p,vx,vy):
  display.set_pixel(x,y,0)
  display.set_pixel(x+vx,y+vy,0)
  sleep(100)
  display.set_pixel(x,y,9-p)
  display.set_pixel(x+vx,y+vy,p)
  sleep(100)

The flashMan function sets the LEDs at x,y and x+vx and y+vy off. It waits 100 milliseconds and then puts the same LEDs on, but the one at x,y is set to 9-p and the one at x+vx,y+vy is set to p. You can see that p is the proportion that the brightness is split between the two LEDs.

For example suppose p is 0 then the first LED is set to 9 and the second to 0. If p is 3 then the first is set to 6 and the second to 3. Finally is p is 9 the first is set to 0 and the second to 9. As p varies from 0 to 9 the position of the brightness moves from the first to the second LED.

What are vx and vy?

They simply set where the second LED is with respect to the first corresponding to the direction that the commando is heading in.  If vx=0 and vy=-1 the commando is going up, remember the LEDs are 0 at the top and 5 at the bottom. 

Using this function we can now write the while loop:

while(running_time()-t1<20000):
  flashMan(4,position,press,0,-1)
  press=button_a.get_presses()
  if press>=10:
    position=position-1
    button_a.reset_presses()
    press=0
    if position==0:
      break
  return position

The condition in the while loop means that it stops after 20,000 milliseconds or 20 seconds. You will need to reduce this after you finish testing to something more challenging. Say 5000 or 5 seconds. 

The first thing we do is flash the command at the current position. Notice that vx and vy are set to move up and that the proportion of brightness is set by press - the number of times the button had been pressed. 

Next we update press and if it is 10 or greater we need to move the commando up one complete LED. We also need to reset the button count ready for the next ten presses. Finally we check to see if the commando has reached the top of the wall. If so the break stops the while loop.

Finally the function returns the commandos position when the while loop ends - why? The simple reason is that other functions might, and do, need to know how the game ended and this is signaled by the commando's position - at the top of the wall (position==0) or not. 

The complete function is:

def playGame():
  position=4
  button_a.reset_presses()
  t1=running_time()
  press=0
  while(running_time()-t1<20000):
    flashMan(4,position,press,0,-1)
    press=button_a.get_presses()
    if press>=10:
      position=position-1
      button_a.reset_presses()
      press=0
      if position==0:
        break
  return position

You can see that the logic is complicated but it isn't easy to see how it could be simplified.



Last Updated ( Saturday, 23 April 2016 )