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 17:21:22 2016 +0000
Revision:
2:ab61446893c8
Parent:
0:c0ff9a7daaac
Added state checking to effectively disable button presses until we are ready for them

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 2:ab61446893c8 32 and then a small animation showing shrinking/expanding 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 2:ab61446893c8 37 Each time you level up, the time to respond decreases. When you level up the shrinking/
AnnaBridge 2:ab61446893c8 38 expanding 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 2:ab61446893c8 43 These can all be tweaked by updating the #defines.
AnnaBridge 0:c0ff9a7daaac 44
AnnaBridge 0:c0ff9a7daaac 45 */
AnnaBridge 0:c0ff9a7daaac 46
AnnaBridge 0:c0ff9a7daaac 47 #include "MicroBit.h"
AnnaBridge 0:c0ff9a7daaac 48 #include <stdio.h>
AnnaBridge 0:c0ff9a7daaac 49
AnnaBridge 0:c0ff9a7daaac 50 void level_up();
AnnaBridge 0:c0ff9a7daaac 51 void start();
AnnaBridge 0:c0ff9a7daaac 52 void game_over();
AnnaBridge 0:c0ff9a7daaac 53 void display_letter();
AnnaBridge 0:c0ff9a7daaac 54 void check_press(unsigned long delay);
AnnaBridge 0:c0ff9a7daaac 55 void onButtonA(MicroBitEvent e);
AnnaBridge 0:c0ff9a7daaac 56 void onButtonB(MicroBitEvent e);
AnnaBridge 0:c0ff9a7daaac 57
AnnaBridge 0:c0ff9a7daaac 58
AnnaBridge 0:c0ff9a7daaac 59 MicroBit uBit;
AnnaBridge 0:c0ff9a7daaac 60
AnnaBridge 0:c0ff9a7daaac 61 #define INITIAL_LIMIT 2000 //ms
AnnaBridge 0:c0ff9a7daaac 62 #define SPEEDUP 200 //ms
AnnaBridge 0:c0ff9a7daaac 63 #define LEVEL_UP 20 // level/speed up each time score accumulates by this amount
AnnaBridge 0:c0ff9a7daaac 64
AnnaBridge 0:c0ff9a7daaac 65 typedef enum
AnnaBridge 0:c0ff9a7daaac 66 {
AnnaBridge 0:c0ff9a7daaac 67 NONE,
AnnaBridge 0:c0ff9a7daaac 68 VOWEL,
AnnaBridge 0:c0ff9a7daaac 69 CONSONANT,
AnnaBridge 0:c0ff9a7daaac 70 LATE_PRESS
AnnaBridge 0:c0ff9a7daaac 71 } Letter_type;
AnnaBridge 0:c0ff9a7daaac 72
AnnaBridge 2:ab61446893c8 73 typedef enum
AnnaBridge 2:ab61446893c8 74 {
AnnaBridge 2:ab61446893c8 75 BUTTONS_ENABLED,
AnnaBridge 2:ab61446893c8 76 BUTTONS_DISABLED
AnnaBridge 2:ab61446893c8 77 } Button_state;
AnnaBridge 2:ab61446893c8 78
AnnaBridge 0:c0ff9a7daaac 79 Letter_type buttonPress = NONE;
AnnaBridge 0:c0ff9a7daaac 80 Letter_type type = NONE;
AnnaBridge 0:c0ff9a7daaac 81 unsigned long time_val;
AnnaBridge 0:c0ff9a7daaac 82 unsigned long limit;
AnnaBridge 0:c0ff9a7daaac 83 unsigned score = 0;
AnnaBridge 2:ab61446893c8 84 Button_state b_state = BUTTONS_DISABLED;
AnnaBridge 0:c0ff9a7daaac 85
AnnaBridge 0:c0ff9a7daaac 86 void level_up()
AnnaBridge 0:c0ff9a7daaac 87 {
AnnaBridge 0:c0ff9a7daaac 88 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 89 uBit.display.print(outer, 0, 0);
AnnaBridge 0:c0ff9a7daaac 90 uBit.sleep(200);
AnnaBridge 2:ab61446893c8 91 MicroBitImage middle("0,0,0,0,0\n0,255,255,255,0\n0,255,0,255,0\n0,255,255,255,0\n0,0,0,0,0\n");
AnnaBridge 0:c0ff9a7daaac 92 uBit.display.print(middle, 0, 0);
AnnaBridge 0:c0ff9a7daaac 93 uBit.sleep(200);
AnnaBridge 0:c0ff9a7daaac 94 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 95 uBit.display.print(inner, 0, 0);
AnnaBridge 0:c0ff9a7daaac 96 uBit.sleep(200);
AnnaBridge 0:c0ff9a7daaac 97 uBit.display.print(middle, 0, 0);
AnnaBridge 0:c0ff9a7daaac 98 uBit.sleep(200);
AnnaBridge 0:c0ff9a7daaac 99 uBit.display.print(outer, 0, 0);
AnnaBridge 0:c0ff9a7daaac 100 uBit.sleep(200);
AnnaBridge 0:c0ff9a7daaac 101 uBit.display.clear();
AnnaBridge 0:c0ff9a7daaac 102 uBit.sleep(300);
AnnaBridge 0:c0ff9a7daaac 103 }
AnnaBridge 0:c0ff9a7daaac 104
AnnaBridge 0:c0ff9a7daaac 105 void start()
AnnaBridge 0:c0ff9a7daaac 106 {
AnnaBridge 0:c0ff9a7daaac 107 score = 0;
AnnaBridge 0:c0ff9a7daaac 108 limit = INITIAL_LIMIT;
AnnaBridge 0:c0ff9a7daaac 109 uBit.display.clear();
AnnaBridge 0:c0ff9a7daaac 110 uBit.display.scroll("GET READY...", 100);
AnnaBridge 0:c0ff9a7daaac 111 uBit.sleep(500);
AnnaBridge 0:c0ff9a7daaac 112 level_up();
AnnaBridge 0:c0ff9a7daaac 113 uBit.sleep(500);
AnnaBridge 0:c0ff9a7daaac 114 display_letter();
AnnaBridge 2:ab61446893c8 115 b_state = BUTTONS_ENABLED;
AnnaBridge 0:c0ff9a7daaac 116 }
AnnaBridge 0:c0ff9a7daaac 117
AnnaBridge 0:c0ff9a7daaac 118 void game_over()
AnnaBridge 0:c0ff9a7daaac 119 {
AnnaBridge 0:c0ff9a7daaac 120 char val[5];
AnnaBridge 0:c0ff9a7daaac 121 uBit.display.clear();
AnnaBridge 0:c0ff9a7daaac 122 uBit.display.scroll("GAME OVER!", 100);
AnnaBridge 0:c0ff9a7daaac 123 uBit.display.scroll("SCORE = ");
AnnaBridge 0:c0ff9a7daaac 124 sprintf(val,"%u", score);
AnnaBridge 0:c0ff9a7daaac 125 uBit.display.scroll(val, 100);
AnnaBridge 0:c0ff9a7daaac 126 }
AnnaBridge 0:c0ff9a7daaac 127
AnnaBridge 0:c0ff9a7daaac 128
AnnaBridge 0:c0ff9a7daaac 129 void display_letter()
AnnaBridge 0:c0ff9a7daaac 130 {
AnnaBridge 0:c0ff9a7daaac 131 // random number from 65 - 90 ie ASCII A-Z
AnnaBridge 0:c0ff9a7daaac 132 char letter = uBit.random(26) + 65;
AnnaBridge 0:c0ff9a7daaac 133
AnnaBridge 0:c0ff9a7daaac 134 switch(letter)
AnnaBridge 0:c0ff9a7daaac 135 {
AnnaBridge 0:c0ff9a7daaac 136 case 'A':
AnnaBridge 0:c0ff9a7daaac 137 case 'E':
AnnaBridge 0:c0ff9a7daaac 138 case 'I':
AnnaBridge 0:c0ff9a7daaac 139 case 'O':
AnnaBridge 0:c0ff9a7daaac 140 case 'U':
AnnaBridge 0:c0ff9a7daaac 141 type = VOWEL;
AnnaBridge 0:c0ff9a7daaac 142 break;
AnnaBridge 0:c0ff9a7daaac 143
AnnaBridge 0:c0ff9a7daaac 144 default:
AnnaBridge 0:c0ff9a7daaac 145 type = CONSONANT;
AnnaBridge 0:c0ff9a7daaac 146 }
AnnaBridge 0:c0ff9a7daaac 147
AnnaBridge 0:c0ff9a7daaac 148 uBit.display.printChar(letter,500);
AnnaBridge 0:c0ff9a7daaac 149 //uBit.sleep(limit);
AnnaBridge 0:c0ff9a7daaac 150 uBit.display.clear();
AnnaBridge 0:c0ff9a7daaac 151 time_val = uBit.systemTime();
AnnaBridge 0:c0ff9a7daaac 152 }
AnnaBridge 0:c0ff9a7daaac 153
AnnaBridge 0:c0ff9a7daaac 154 void check_press(unsigned long delay)
AnnaBridge 0:c0ff9a7daaac 155 {
AnnaBridge 0:c0ff9a7daaac 156 if (buttonPress == type && delay <= limit)
AnnaBridge 0:c0ff9a7daaac 157 {
AnnaBridge 0:c0ff9a7daaac 158 score++;
AnnaBridge 0:c0ff9a7daaac 159 if ((score % LEVEL_UP) == 0 && limit > SPEEDUP)
AnnaBridge 0:c0ff9a7daaac 160 {
AnnaBridge 0:c0ff9a7daaac 161 // Increase speed
AnnaBridge 0:c0ff9a7daaac 162 limit -= SPEEDUP;
AnnaBridge 0:c0ff9a7daaac 163 level_up();
AnnaBridge 2:ab61446893c8 164 uBit.sleep(500);
AnnaBridge 0:c0ff9a7daaac 165 }
AnnaBridge 0:c0ff9a7daaac 166 buttonPress = NONE;
AnnaBridge 0:c0ff9a7daaac 167 display_letter();
AnnaBridge 0:c0ff9a7daaac 168 }
AnnaBridge 0:c0ff9a7daaac 169 else
AnnaBridge 0:c0ff9a7daaac 170 {
AnnaBridge 0:c0ff9a7daaac 171 game_over();
AnnaBridge 0:c0ff9a7daaac 172 uBit.sleep(1000);
AnnaBridge 0:c0ff9a7daaac 173 start();
AnnaBridge 2:ab61446893c8 174 }
AnnaBridge 0:c0ff9a7daaac 175 }
AnnaBridge 0:c0ff9a7daaac 176
AnnaBridge 0:c0ff9a7daaac 177 void onButtonA(MicroBitEvent e)
AnnaBridge 0:c0ff9a7daaac 178 {
AnnaBridge 2:ab61446893c8 179 if (b_state == BUTTONS_ENABLED)
AnnaBridge 0:c0ff9a7daaac 180 {
AnnaBridge 2:ab61446893c8 181 b_state = BUTTONS_DISABLED;
AnnaBridge 2:ab61446893c8 182 if (e.value == MICROBIT_BUTTON_EVT_CLICK)
AnnaBridge 2:ab61446893c8 183 {
AnnaBridge 2:ab61446893c8 184 unsigned long delay = uBit.systemTime() - time_val;
AnnaBridge 2:ab61446893c8 185 buttonPress = VOWEL;
AnnaBridge 2:ab61446893c8 186 check_press(delay);
AnnaBridge 2:ab61446893c8 187 }
AnnaBridge 2:ab61446893c8 188
AnnaBridge 2:ab61446893c8 189 b_state = BUTTONS_ENABLED;
AnnaBridge 0:c0ff9a7daaac 190 }
AnnaBridge 0:c0ff9a7daaac 191 }
AnnaBridge 0:c0ff9a7daaac 192
AnnaBridge 0:c0ff9a7daaac 193 void onButtonB(MicroBitEvent e)
AnnaBridge 0:c0ff9a7daaac 194 {
AnnaBridge 2:ab61446893c8 195 if (b_state == BUTTONS_ENABLED)
AnnaBridge 0:c0ff9a7daaac 196 {
AnnaBridge 2:ab61446893c8 197 b_state = BUTTONS_DISABLED;
AnnaBridge 2:ab61446893c8 198 if (e.value == MICROBIT_BUTTON_EVT_CLICK)
AnnaBridge 2:ab61446893c8 199 {
AnnaBridge 2:ab61446893c8 200 unsigned long delay = uBit.systemTime() - time_val;
AnnaBridge 2:ab61446893c8 201 buttonPress = CONSONANT;
AnnaBridge 2:ab61446893c8 202 check_press(delay);
AnnaBridge 2:ab61446893c8 203 }
AnnaBridge 2:ab61446893c8 204 b_state = BUTTONS_ENABLED;
AnnaBridge 2:ab61446893c8 205 }
AnnaBridge 0:c0ff9a7daaac 206 }
AnnaBridge 0:c0ff9a7daaac 207
AnnaBridge 0:c0ff9a7daaac 208
AnnaBridge 0:c0ff9a7daaac 209 int main()
AnnaBridge 0:c0ff9a7daaac 210 {
AnnaBridge 0:c0ff9a7daaac 211
AnnaBridge 0:c0ff9a7daaac 212 // Initialise the micro:bit runtime.
AnnaBridge 0:c0ff9a7daaac 213 uBit.init();
AnnaBridge 0:c0ff9a7daaac 214
AnnaBridge 0:c0ff9a7daaac 215 // Set up button event handlers
AnnaBridge 0:c0ff9a7daaac 216 uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_EVT_ANY, onButtonB);
AnnaBridge 0:c0ff9a7daaac 217 uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_EVT_ANY, onButtonA);
AnnaBridge 0:c0ff9a7daaac 218
AnnaBridge 0:c0ff9a7daaac 219 start();
AnnaBridge 0:c0ff9a7daaac 220 while(1)
AnnaBridge 0:c0ff9a7daaac 221 {
AnnaBridge 0:c0ff9a7daaac 222 uBit.sleep(100);
AnnaBridge 0:c0ff9a7daaac 223 }
AnnaBridge 0:c0ff9a7daaac 224
AnnaBridge 0:c0ff9a7daaac 225 // If main exits, there may still be other fibers running or registered event handlers etc.
AnnaBridge 0:c0ff9a7daaac 226 // Simply release this fiber, which will mean we enter the scheduler. Worse case, we then
AnnaBridge 0:c0ff9a7daaac 227 // sit in the idle task forever, in a power efficient sleep.
AnnaBridge 0:c0ff9a7daaac 228 release_fiber();
AnnaBridge 0:c0ff9a7daaac 229 }