Simple letter game testing dexterity and using several features of the microbit, such as button events, the display and the system timer

Dependencies:   microbit

Fork of microbit-letter-game by Anna Bridge

Simple letter game

Overview

This is a simple game which demonstrates a number of built in microbit features and some general programming ideas.

The basic idea of the game is to test hand eye coordination based on visual stimulus. After a start up sequence, a random letter from A-Z is displayed on the microbit display. The player must then determine if the letter is a vowel or a consonant and press an associated button accordingly (left for vowel, right for consonant). There is a pre-determined time limit during which the correct button must be pressed. Each time the player reaches the next level this time limit reduces. The player scores 1 point for each correct press. Once an incorrect button is pressed or the time limit exceeded, the game is over and the players score is displayed.

Start up sequence

The start up sequence consists of a scrolling message: "GET READY ...", followed by an animated shrinking then expanding box.

Levelling up

The next level is reached when the player scores a predetermined number of points. To indicate a levelling up the shrinking and expanding box animation is displayed.

Game over

Once the game is over , a scrolling message: "GAME OVER. SCORE = " is displayed.

Game mechanics

There were a number of areas to be considered when designing the game,

  • How to display a random letter
  • How to get a button press and disable presses at certain times
  • How to measure a response time
  • How to write text to the display
  • How to animate a picture on the display
  • How to allow easy configuration of difficulty level.

Random letters

The microbit has a function to print a single character:

    uBit.display.printChar(character)

Character can be a single quoted value, e.g. 'A' or the ASCII code of a character, e.g. 65 (ASCII code for 'A').

Thus to display a random character in the range A-Z we can generate a random number in the range 65 - 90 and then call the above function with that value.

The microbit has a random number generator:

uBit.random(x)

This produces a random number in the range 0 to x. However we want a value in the range 65 to 90 , thus:

char letter = uBit.random(26) + 65;
uBit.display.printChar(letter);

Getting button presses

There are a couple of ways of getting button presses:

  • Checking for a button press synchronously
  • Using the Message bus to asynchronously identify button presses

There are 3 button types

  1. buttonA (left button)
  2. buttonB (right button)
  3. buttonAB (both buttons at the same time)

Synchronous button presses

This can be achieved by calling the function

ubit.buttonA.isPressed()

This will check if button A is currently being pressed. This is not a very practical way of checking for presses as it would involve writing a blocking loop which waits for one of the buttons to be pressed. E.g.

While (!ubit.buttonA.isPressed() && !ubit.buttonB.isPressed()
{
    ubit.sleep(100);   // put the microbit to sleep for 100 milliseconds
}

Asynchronous button presses

This is a better programming model approach. This allows the microbit operating system to worry about whether a button has been pressed or not. All we have to do is write a function that we want to be called if a button is pressed and tell the operating system to call that. On the microbit this is done by using what is called the 'messageBus'. E.g.

uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_EVT_ANY, onButtonA);

This tells the operating system to look for any events (MICROBIT_EVT_ANY) coming from the peripheral identified by MICROBIT_ID_BUTTON_A and if seen to call the function onButtonA. This is called registering onButtonA as an interrupt service routine (ISR). When any event is detected as originating from button A (this could be a press, a hold, a double press etc) the current piece of code being executed is interrupted and onButtonA is called. Once onButtonA has finished, the original piece of code will continue running from the point at which it was interrupted.

Committer:
AnnaBridge
Date:
Wed Apr 13 16:20:53 2016 +0000
Revision:
0:c0ff9a7daaac
Child:
2:ab61446893c8
First version

Who changed what in which revision?

UserRevisionLine numberNew contents of line
AnnaBridge 0:c0ff9a7daaac 1 /*
AnnaBridge 0:c0ff9a7daaac 2 The MIT License (MIT)
AnnaBridge 0:c0ff9a7daaac 3 Copyright (c) 2016 British Broadcasting Corporation.
AnnaBridge 0:c0ff9a7daaac 4 This software is provided by Lancaster University by arrangement with the BBC.
AnnaBridge 0:c0ff9a7daaac 5 Permission is hereby granted, free of charge, to any person obtaining a
AnnaBridge 0:c0ff9a7daaac 6 copy of this software and associated documentation files (the "Software"),
AnnaBridge 0:c0ff9a7daaac 7 to deal in the Software without restriction, including without limitation
AnnaBridge 0:c0ff9a7daaac 8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
AnnaBridge 0:c0ff9a7daaac 9 and/or sell copies of the Software, and to permit persons to whom the
AnnaBridge 0:c0ff9a7daaac 10 Software is furnished to do so, subject to the following conditions:
AnnaBridge 0:c0ff9a7daaac 11 The above copyright notice and this permission notice shall be included in
AnnaBridge 0:c0ff9a7daaac 12 all copies or substantial portions of the Software.
AnnaBridge 0:c0ff9a7daaac 13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
AnnaBridge 0:c0ff9a7daaac 14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
AnnaBridge 0:c0ff9a7daaac 15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
AnnaBridge 0:c0ff9a7daaac 16 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
AnnaBridge 0:c0ff9a7daaac 17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
AnnaBridge 0:c0ff9a7daaac 18 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
AnnaBridge 0:c0ff9a7daaac 19 DEALINGS IN THE SOFTWARE.
AnnaBridge 0:c0ff9a7daaac 20 */
AnnaBridge 0:c0ff9a7daaac 21
AnnaBridge 0:c0ff9a7daaac 22 /* The letter game....
AnnaBridge 0:c0ff9a7daaac 23
AnnaBridge 0:c0ff9a7daaac 24 This simple game demonstrates the use of:
AnnaBridge 0:c0ff9a7daaac 25 1) The random function
AnnaBridge 0:c0ff9a7daaac 26 2) Events on the message bus to handle button presses
AnnaBridge 0:c0ff9a7daaac 27 3) Driving the display for both characters, numbers and animations.
AnnaBridge 0:c0ff9a7daaac 28 4) The system timer
AnnaBridge 0:c0ff9a7daaac 29
AnnaBridge 0:c0ff9a7daaac 30 The start of the game will be preceeded by the following message:
AnnaBridge 0:c0ff9a7daaac 31 "Get Ready.."
AnnaBridge 0:c0ff9a7daaac 32 and then a small animation showing shrinking boxes.
AnnaBridge 0:c0ff9a7daaac 33 Once the game starts a random letter from A-Z will be displayed on the screen.
AnnaBridge 0:c0ff9a7daaac 34 If it is a consonant then you press the right button.
AnnaBridge 0:c0ff9a7daaac 35 If it is a vowel then you press the left button.
AnnaBridge 0:c0ff9a7daaac 36 You must press the corresponding button within the time period for the current level.
AnnaBridge 0:c0ff9a7daaac 37 Each time you level up, the time to respond decreases. When you level up the shrinking
AnnaBridge 0:c0ff9a7daaac 38 box animation will be displayed again.
AnnaBridge 0:c0ff9a7daaac 39
AnnaBridge 0:c0ff9a7daaac 40 The initial allowed time is defined as INITIAL_LIMIT.
AnnaBridge 0:c0ff9a7daaac 41 The amount by which the time allowed decreases at each level is defined by SPEEDUP.
AnnaBridge 0:c0ff9a7daaac 42 The point at which a level up occurs is defined as LEVEL_UP.
AnnaBridge 0:c0ff9a7daaac 43
AnnaBridge 0:c0ff9a7daaac 44 */
AnnaBridge 0:c0ff9a7daaac 45
AnnaBridge 0:c0ff9a7daaac 46 #include "MicroBit.h"
AnnaBridge 0:c0ff9a7daaac 47 #include <stdio.h>
AnnaBridge 0:c0ff9a7daaac 48
AnnaBridge 0:c0ff9a7daaac 49 void level_up();
AnnaBridge 0:c0ff9a7daaac 50 void start();
AnnaBridge 0:c0ff9a7daaac 51 void game_over();
AnnaBridge 0:c0ff9a7daaac 52 void display_letter();
AnnaBridge 0:c0ff9a7daaac 53 void check_press(unsigned long delay);
AnnaBridge 0:c0ff9a7daaac 54 void onButtonA(MicroBitEvent e);
AnnaBridge 0:c0ff9a7daaac 55 void onButtonB(MicroBitEvent e);
AnnaBridge 0:c0ff9a7daaac 56
AnnaBridge 0:c0ff9a7daaac 57
AnnaBridge 0:c0ff9a7daaac 58 MicroBit uBit;
AnnaBridge 0:c0ff9a7daaac 59
AnnaBridge 0:c0ff9a7daaac 60 #define INITIAL_LIMIT 2000 //ms
AnnaBridge 0:c0ff9a7daaac 61 #define SPEEDUP 200 //ms
AnnaBridge 0:c0ff9a7daaac 62 #define LEVEL_UP 20 // level/speed up each time score accumulates by this amount
AnnaBridge 0:c0ff9a7daaac 63
AnnaBridge 0:c0ff9a7daaac 64 typedef enum
AnnaBridge 0:c0ff9a7daaac 65 {
AnnaBridge 0:c0ff9a7daaac 66 NONE,
AnnaBridge 0:c0ff9a7daaac 67 VOWEL,
AnnaBridge 0:c0ff9a7daaac 68 CONSONANT,
AnnaBridge 0:c0ff9a7daaac 69 LATE_PRESS
AnnaBridge 0:c0ff9a7daaac 70 } Letter_type;
AnnaBridge 0:c0ff9a7daaac 71
AnnaBridge 0:c0ff9a7daaac 72 Letter_type buttonPress = NONE;
AnnaBridge 0:c0ff9a7daaac 73 Letter_type type = NONE;
AnnaBridge 0:c0ff9a7daaac 74 unsigned long time_val;
AnnaBridge 0:c0ff9a7daaac 75 unsigned long limit;
AnnaBridge 0:c0ff9a7daaac 76 unsigned score = 0;
AnnaBridge 0:c0ff9a7daaac 77
AnnaBridge 0:c0ff9a7daaac 78 void level_up()
AnnaBridge 0:c0ff9a7daaac 79 {
AnnaBridge 0:c0ff9a7daaac 80 MicroBitImage outer("255,255,255,255,255\n255,0,0,0,255\n255,0,0,0,255\n255,0,0,0,255\n255,255,255,255,255\n");
AnnaBridge 0:c0ff9a7daaac 81 uBit.display.print(outer, 0, 0);
AnnaBridge 0:c0ff9a7daaac 82 uBit.sleep(200);
AnnaBridge 0:c0ff9a7daaac 83 MicroBitImage middle("0,0,0,0,0\n0,255,255,255,0\n0,255,0,255,0\n0,255,0,255,0\n0,0,0,0,0\n");
AnnaBridge 0:c0ff9a7daaac 84 uBit.display.print(middle, 0, 0);
AnnaBridge 0:c0ff9a7daaac 85 uBit.sleep(200);
AnnaBridge 0:c0ff9a7daaac 86 MicroBitImage inner("0,0,0,0,0\n0,0,0,0,0\n0,0,255,0,0\n0,0,0,0,0\n0,0,0,0,0\n");
AnnaBridge 0:c0ff9a7daaac 87 uBit.display.print(inner, 0, 0);
AnnaBridge 0:c0ff9a7daaac 88 uBit.sleep(200);
AnnaBridge 0:c0ff9a7daaac 89 uBit.display.print(middle, 0, 0);
AnnaBridge 0:c0ff9a7daaac 90 uBit.sleep(200);
AnnaBridge 0:c0ff9a7daaac 91 uBit.display.print(outer, 0, 0);
AnnaBridge 0:c0ff9a7daaac 92 uBit.sleep(200);
AnnaBridge 0:c0ff9a7daaac 93 uBit.display.clear();
AnnaBridge 0:c0ff9a7daaac 94 uBit.sleep(300);
AnnaBridge 0:c0ff9a7daaac 95 }
AnnaBridge 0:c0ff9a7daaac 96
AnnaBridge 0:c0ff9a7daaac 97 void start()
AnnaBridge 0:c0ff9a7daaac 98 {
AnnaBridge 0:c0ff9a7daaac 99 score = 0;
AnnaBridge 0:c0ff9a7daaac 100 limit = INITIAL_LIMIT;
AnnaBridge 0:c0ff9a7daaac 101 uBit.display.clear();
AnnaBridge 0:c0ff9a7daaac 102 uBit.display.scroll("GET READY...", 100);
AnnaBridge 0:c0ff9a7daaac 103 uBit.sleep(500);
AnnaBridge 0:c0ff9a7daaac 104 level_up();
AnnaBridge 0:c0ff9a7daaac 105 uBit.sleep(500);
AnnaBridge 0:c0ff9a7daaac 106 display_letter();
AnnaBridge 0:c0ff9a7daaac 107 }
AnnaBridge 0:c0ff9a7daaac 108
AnnaBridge 0:c0ff9a7daaac 109 void game_over()
AnnaBridge 0:c0ff9a7daaac 110 {
AnnaBridge 0:c0ff9a7daaac 111 char val[5];
AnnaBridge 0:c0ff9a7daaac 112 uBit.display.clear();
AnnaBridge 0:c0ff9a7daaac 113 uBit.display.scroll("GAME OVER!", 100);
AnnaBridge 0:c0ff9a7daaac 114 uBit.display.scroll("SCORE = ");
AnnaBridge 0:c0ff9a7daaac 115 sprintf(val,"%u", score);
AnnaBridge 0:c0ff9a7daaac 116 uBit.display.scroll(val, 100);
AnnaBridge 0:c0ff9a7daaac 117 }
AnnaBridge 0:c0ff9a7daaac 118
AnnaBridge 0:c0ff9a7daaac 119
AnnaBridge 0:c0ff9a7daaac 120 void display_letter()
AnnaBridge 0:c0ff9a7daaac 121 {
AnnaBridge 0:c0ff9a7daaac 122 // random number from 65 - 90 ie ASCII A-Z
AnnaBridge 0:c0ff9a7daaac 123 char letter = uBit.random(26) + 65;
AnnaBridge 0:c0ff9a7daaac 124
AnnaBridge 0:c0ff9a7daaac 125 switch(letter)
AnnaBridge 0:c0ff9a7daaac 126 {
AnnaBridge 0:c0ff9a7daaac 127 case 'A':
AnnaBridge 0:c0ff9a7daaac 128 case 'E':
AnnaBridge 0:c0ff9a7daaac 129 case 'I':
AnnaBridge 0:c0ff9a7daaac 130 case 'O':
AnnaBridge 0:c0ff9a7daaac 131 case 'U':
AnnaBridge 0:c0ff9a7daaac 132 type = VOWEL;
AnnaBridge 0:c0ff9a7daaac 133 break;
AnnaBridge 0:c0ff9a7daaac 134
AnnaBridge 0:c0ff9a7daaac 135 default:
AnnaBridge 0:c0ff9a7daaac 136 type = CONSONANT;
AnnaBridge 0:c0ff9a7daaac 137 }
AnnaBridge 0:c0ff9a7daaac 138
AnnaBridge 0:c0ff9a7daaac 139 uBit.display.printChar(letter,500);
AnnaBridge 0:c0ff9a7daaac 140 //uBit.sleep(limit);
AnnaBridge 0:c0ff9a7daaac 141 uBit.display.clear();
AnnaBridge 0:c0ff9a7daaac 142 time_val = uBit.systemTime();
AnnaBridge 0:c0ff9a7daaac 143 }
AnnaBridge 0:c0ff9a7daaac 144
AnnaBridge 0:c0ff9a7daaac 145 void check_press(unsigned long delay)
AnnaBridge 0:c0ff9a7daaac 146 {
AnnaBridge 0:c0ff9a7daaac 147 if (buttonPress == type && delay <= limit)
AnnaBridge 0:c0ff9a7daaac 148 {
AnnaBridge 0:c0ff9a7daaac 149 score++;
AnnaBridge 0:c0ff9a7daaac 150 if ((score % LEVEL_UP) == 0 && limit > SPEEDUP)
AnnaBridge 0:c0ff9a7daaac 151 {
AnnaBridge 0:c0ff9a7daaac 152 // Increase speed
AnnaBridge 0:c0ff9a7daaac 153 limit -= SPEEDUP;
AnnaBridge 0:c0ff9a7daaac 154 level_up();
AnnaBridge 0:c0ff9a7daaac 155 }
AnnaBridge 0:c0ff9a7daaac 156 buttonPress = NONE;
AnnaBridge 0:c0ff9a7daaac 157 display_letter();
AnnaBridge 0:c0ff9a7daaac 158 }
AnnaBridge 0:c0ff9a7daaac 159 else
AnnaBridge 0:c0ff9a7daaac 160 {
AnnaBridge 0:c0ff9a7daaac 161 game_over();
AnnaBridge 0:c0ff9a7daaac 162 uBit.sleep(1000);
AnnaBridge 0:c0ff9a7daaac 163 start();
AnnaBridge 0:c0ff9a7daaac 164 }
AnnaBridge 0:c0ff9a7daaac 165
AnnaBridge 0:c0ff9a7daaac 166 }
AnnaBridge 0:c0ff9a7daaac 167
AnnaBridge 0:c0ff9a7daaac 168 void onButtonA(MicroBitEvent e)
AnnaBridge 0:c0ff9a7daaac 169 {
AnnaBridge 0:c0ff9a7daaac 170 if (e.value == MICROBIT_BUTTON_EVT_CLICK)
AnnaBridge 0:c0ff9a7daaac 171 {
AnnaBridge 0:c0ff9a7daaac 172 unsigned long delay = uBit.systemTime() - time_val;
AnnaBridge 0:c0ff9a7daaac 173 buttonPress = VOWEL;
AnnaBridge 0:c0ff9a7daaac 174 check_press(delay);
AnnaBridge 0:c0ff9a7daaac 175 }
AnnaBridge 0:c0ff9a7daaac 176 }
AnnaBridge 0:c0ff9a7daaac 177
AnnaBridge 0:c0ff9a7daaac 178 void onButtonB(MicroBitEvent e)
AnnaBridge 0:c0ff9a7daaac 179 {
AnnaBridge 0:c0ff9a7daaac 180 if (e.value == MICROBIT_BUTTON_EVT_CLICK)
AnnaBridge 0:c0ff9a7daaac 181 {
AnnaBridge 0:c0ff9a7daaac 182 unsigned long delay = uBit.systemTime() - time_val;
AnnaBridge 0:c0ff9a7daaac 183 buttonPress = CONSONANT;
AnnaBridge 0:c0ff9a7daaac 184 check_press(delay);
AnnaBridge 0:c0ff9a7daaac 185 }
AnnaBridge 0:c0ff9a7daaac 186
AnnaBridge 0:c0ff9a7daaac 187 }
AnnaBridge 0:c0ff9a7daaac 188
AnnaBridge 0:c0ff9a7daaac 189
AnnaBridge 0:c0ff9a7daaac 190 int main()
AnnaBridge 0:c0ff9a7daaac 191 {
AnnaBridge 0:c0ff9a7daaac 192
AnnaBridge 0:c0ff9a7daaac 193 // Initialise the micro:bit runtime.
AnnaBridge 0:c0ff9a7daaac 194 uBit.init();
AnnaBridge 0:c0ff9a7daaac 195
AnnaBridge 0:c0ff9a7daaac 196 // Set up button event handlers
AnnaBridge 0:c0ff9a7daaac 197 uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_EVT_ANY, onButtonB);
AnnaBridge 0:c0ff9a7daaac 198 uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_EVT_ANY, onButtonA);
AnnaBridge 0:c0ff9a7daaac 199
AnnaBridge 0:c0ff9a7daaac 200 start();
AnnaBridge 0:c0ff9a7daaac 201 while(1)
AnnaBridge 0:c0ff9a7daaac 202 {
AnnaBridge 0:c0ff9a7daaac 203 uBit.sleep(100);
AnnaBridge 0:c0ff9a7daaac 204 }
AnnaBridge 0:c0ff9a7daaac 205
AnnaBridge 0:c0ff9a7daaac 206 // If main exits, there may still be other fibers running or registered event handlers etc.
AnnaBridge 0:c0ff9a7daaac 207 // Simply release this fiber, which will mean we enter the scheduler. Worse case, we then
AnnaBridge 0:c0ff9a7daaac 208 // sit in the idle task forever, in a power efficient sleep.
AnnaBridge 0:c0ff9a7daaac 209 release_fiber();
AnnaBridge 0:c0ff9a7daaac 210 }