3. Tilt Pixels game
3.1. Game design
![coordinates](../_images/microbit_coords.png)
- Set up the game object (initialize an instance of the class)
Set between 2 and 4 random pixels to be found.
Start from a random pixel and display it brightly then faintly.
- Repeat the following steps:
Use the accelerometer to detect a tilt and move the pixel.
- If all the pixels have been found then:
Display the location of the hidden pixels as well as the visited pixels.
Scroll the score.
Play again if both buttons are pressed.
TiltPixels()
, for the game object.set_game
method is used outside the class itself.from microbit import *
game = TiltPixels()
game.run_game()
while True:
if button_a.was_pressed() and button_b.was_pressed():
game.set_game()
game.run_game()
else:
sleep(2000)
3.2. The TiltPixels class
TiltPixels
class groups together the game attributes and game methods.- class TiltPixels
- Set up the game object to control the game, including the hidden and visited pixels.Initial x, y values for the initial pixel could be passed here as an argument.
game = TiltPixels(2,2)
would place the initial pixel in the center of the 5 by 5 grid.There is no need to do so since the game has an __init__ method to start at a random pixel.
from microbit import *
import random
game = TiltPixels()
3.3. The TiltPixels outline
Tasks
Here is the outline to the TiltPixels class. See if you can write code for each method to get the game working.
from microbit import *
import random
class TiltPixels:
"""TiltPixels game: tilt to find the hidden pixels"""
def __init__(self, x_pos=random.randint(0, 4), y_pos=random.randint(0, 4)):
# call set_game
def set_game(self, x_pos=random.randint(0, 4), y_pos=random.randint(0, 4)):
# sets or resets the game variables.
def set_pixels_to_find():
# creates a set of tuples of (x, y) coordinates for 2 to 4 hidden pixels.
def acc_x_change(self):
# returns -1 to move to the left, 0 for no change and 1 to move to the right.
def acc_y_change(self):
# returns -1 to move to the top, 0 for no change and 1 to move to the bottom.
def tilt(self):
# moves a bright pixel in the direction of tilt.
def prepare_move(self, x_delta, y_delta):
# updates the new pixel and adds it to the pixels_filled set.
def show(self):
# sets the brightness of the new pixel to bright, then dim.
def are_all_found(self):
# checks if all the hidden pixels have been visited by tilting.
def answer(self):
# displays the hidden pixels brightly and the visited pixels dimly.
def set_score(self, time):
# calculates the score.
def show_score(self):
# scrolls the score.
def run_game(self):
# runs the game.
game = TiltPixels()
game.run_game()
while True:
if button_a.was_pressed() and button_b.was_pressed():
game.set_game()
game.run_game()
else:
sleep(2000)
3.4. The TiltPixels instance variables
- __init__(x_pos=random.randint(0, 4), y_pos=random.randint(0, 4))
- The __init__() method is called when the game object is created.It calls
self.set_game(x_pos, y_pos)
.The starting pixel is at the coordinates:(x_pos, y_pos)
.x_pos
is the starting x value which by default will be a random integer from 0 to 4.y_pos
is the starting y value which by default will be a random integer from 0 to 4.
- def set_game(self, x_pos=random.randint(0, 4), y_pos=random.randint(0, 4))
- Sets the following variables:
self.x_pos
is the x position of the current pixel.self.y_pos
is the y position of the current pixel.self.tilt_sensitivity
is the amount of tilt needed to move the pixel.self.game_speed
is the sleep time between pixel moves.``self.score``is set to 0.self.pixels_filled
is initialized as a set with the starting pixel tuple:(x_pos, y_pos)
. A set is used to make it easy to keep track of the visited pixels. A set is used instead of a list because sets don’t allow duplicate values to be stored. When the microbit is tilted, each pixel will be added to the set.self.pixels_to_get
stores the set of hidden pixels created usingset_pixels_to_find
.Calls the following methods:self.show()
displays the pixel at (x_pos, y_pos).
class TiltPixels:
"""TiltPixels game: tilt to find the hidden pixels"""
def __init__(self, x_pos=random.randint(0, 4), y_pos=random.randint(0, 4)):
self.set_game(x_pos, y_pos)
def set_game(self, x_pos=random.randint(0, 4), y_pos=random.randint(0, 4)):
self.x_pos = x_pos
self.y_pos = y_pos
self.tilt_sensitivity = 100
self.game_speed = 200
self.score = 0
self.pixels_filled = {(x_pos, y_pos)}
self.pixels_to_get = self.set_pixels_to_find()
self.show()
3.6. Accelerometer
- acc_x_change()
- Return an integer that will be used to move the pixel left or right.Values are: -1 to move to the left, 0 for no change and 1 to move to the right.A sensitivity of 100 can be exceeded with a small tilt.
class TiltPixels:
...
def acc_x_change(self):
sensitivity = self.tilt_sensitivity
acc_x = accelerometer.get_x()
if acc_x < -sensitivity:
xd = -1
elif acc_x > sensitivity:
xd = 1
else:
xd = 0
return xd
- acc_y_change()
- Return an integer that will be used to move the pixel left to right.Values are: -1 to move to the top, 0 for no change and 1 to move to the bottom.A sensitivity of 100 can be exceeded with a small tilt.
class TiltPixels:
...
def acc_y_change(self):
sensitivity = self.tilt_sensitivity
acc_y = accelerometer.get_y()
if acc_y < -sensitivity:
yd = -1
elif acc_y > sensitivity:
yd = 1
else:
yd = 0
return yd
3.7. Tilt
while True
loop calls game.tilt()
pixels_filled
.- tilt()
- Calls the prepare_move method and the show method.
class TiltPixels:
...
def tilt(self):
self.prepare_move(self.acc_x_change(),self.acc_y_change())
self.show()
3.7.1. Prepare move
- prepare_move(x_delta, y_delta)
- Updates the x_pos and y_pos values for the new pixel and adds it to the pixels_filled set.x_delta is the integer returned from
acc_x_change()
.y_delta is the integer returned fromacc_y_change()
.
pixels_filled.add((self.x_pos, self.y_pos)
adds the new tuple (x, y) to the set pixels_filled
. Because sets can’t include duplicate values, any previously visited pixels are only stored once.class TiltPixels:
...
def prepare_move(self, x_delta, y_delta):
self.x_pos = min(4, max(0, self.x_pos + x_delta))
self.y_pos = min(4, max(0, self.y_pos + y_delta))
self.pixels_filled.add((self.x_pos, self.y_pos))
3.7.2. Show
- show()
- Set the brightness of the new pixel to 9, then 2.
class TiltPixels:
...
def show(self):
display.set_pixel(self.x_pos, self.y_pos, 9)
sleep(50)
display.set_pixel(self.x_pos, self.y_pos, 2)
3.8. are_all_found
- are_all_found()
- Returns True if all the hidden pixels have been visited, or False if not.It uses the issubset method to check if all the values in the set pixels_to_get are in the set pixels_filled.
class TiltPixels:
...
def are_all_found(self):
return self.pixels_to_get.issubset(self.pixels_filled)
3.9. Answer and score
- answer()
- Loop through the set of hidden pixels and set their brightness to 9.
- set_score()
- Calculate the game score by finding the difference between the number of pixels visited and the number of hidden pixels, subtracting that from 100 and subtracting the time taken.The higher the number the better. Scores above 90 are very difficult to achieve.
- show_score()
- Scrolls the score.
class TiltPixels:
...
def answer(self):
# display.clear()
for i in self.pixels_to_get:
display.set_pixel(i[0], i[1], 9)
sleep(2000)
def set_score(self, time):
self.score = (100 - (len(self.pixels_filled) -
len(self.pixels_to_get)) - int(time / 1000))
def show_score(self):
scores = "score = " + str(self.score)
display.scroll(scores, delay=80)
3.10. Run game
- run_game()
- Turn on pixels as the microbit is tilted until the hidden pixels are found.
class TiltPixels:
...
def run_game(self):
start_time = running_time()
game_over = False
while game_over is False:
self.tilt()
sleep(self.game_speed)
if self.are_all_found():
now = running_time()
game_over = True
self.answer()
self.set_score(now - start_time)
self.show_score()
3.11. Game code
from microbit import *
import random
class TiltPixels:
"""TiltPixels game: tilt to find the hidden pixels"""
def __init__(self, x_pos=random.randint(0, 4), y_pos=random.randint(0, 4)):
self.set_game(x_pos, y_pos)
def set_game(self, x_pos=random.randint(0, 4), y_pos=random.randint(0, 4)):
self.x_pos = x_pos
self.y_pos = y_pos
self.tilt_sensitivity = 100
self.game_speed = 200
self.score = 0
self.pixels_filled = {(x_pos, y_pos)}
self.pixels_to_get = self.set_pixels_to_find()
self.show()
@staticmethod
def set_pixels_to_find():
pixels = set()
for _ in range(random.randint(2, 4)):
x = random.randint(0, 4)
y = random.randint(0, 4)
pixels.add((x, y))
return pixels
def answer(self):
# display.clear()
for i in self.pixels_to_get:
display.set_pixel(i[0], i[1], 9)
sleep(2000)
def are_all_found(self):
return self.pixels_to_get.issubset(self.pixels_filled)
def set_score(self, time):
self.score = (100 - (len(self.pixels_filled) -
len(self.pixels_to_get)) - int(time / 1000))
def show_score(self):
scores = "score = " + str(self.score)
display.scroll(scores, delay=80)
def prepare_move(self, x_delta, y_delta):
self.x_pos = min(4, max(0, self.x_pos + x_delta))
self.y_pos = min(4, max(0, self.y_pos + y_delta))
self.pixels_filled.add((self.x_pos, self.y_pos))
def show(self):
display.set_pixel(self.x_pos, self.y_pos, 9)
sleep(50)
display.set_pixel(self.x_pos, self.y_pos, 2)
def acc_x_change(self):
sensitivity = self.tilt_sensitivity
acc_x = accelerometer.get_x()
if acc_x < -sensitivity:
xd = -1
elif acc_x > sensitivity:
xd = 1
else:
xd = 0
return xd
def acc_y_change(self):
sensitivity = self.tilt_sensitivity
acc_y = accelerometer.get_y()
if acc_y < -sensitivity:
yd = -1
elif acc_y > sensitivity:
yd = 1
else:
yd = 0
return yd
def tilt(self):
self.prepare_move(self.acc_x_change(),self.acc_y_change())
self.show()
def run_game(self):
start_time = running_time()
game_over = False
while game_over is False:
self.tilt()
sleep(self.game_speed)
if self.are_all_found():
now = running_time()
game_over = True
self.answer()
self.set_score(now - start_time)
self.show_score()
game = TiltPixels()
game.run_game()
while True:
if button_a.was_pressed() and button_b.was_pressed():
game.set_game()
game.run_game()
else:
sleep(2000)
Tasks
Use subclasses to complete the following:
Modify the code to use a button press to peek at the answer for half a second while still playing the game.
Modify the code so that the A and B buttons move the pixel left to right instead of tilting left to right. Keep the tilting in the y-direction.
Write code to use the A and B buttons to adjust the game speed in steps of 100 with a minimum of 100 and a maximum of 800.
Add a default parameter for the game speed to the __init__ method and set_game method to enable setting of the game speed. Run the first game with a game speed of 1000. Use a for-loop to decrement (lower) the game speed down to 200 in steps of 200 so that 5 games are played.