Arduino SpeedMath Game
SpeedMath Game is a fun way to test your abilities at how fast you can do math in your head both visually and auditorily.
Components and supplies
1
9V battery (generic)
1
Potentiometer 1k Ohms
1
Arduino UNO
1
Breadboard (generic)
1
Resistor 220 ohm
1
RGB LCD Shield Kit, 16x2 Character Display
1
Infrared IR Obstacle Sensor
1
Button Pad 4x4 - LED Compatible
1
Speaker: 0.25W, 8 ohms
1
9V Battery Clip
1
RGB Diffused Common Cathode
3
Resistor 330 ohm
1
Jumper wires (generic)
Project description
Code
Code
c_cpp
1/* ------------------------------------------------------------------------------------------------------------------------------------------ */ 2/* SpeedMath Game */ 3/* ------------------------------------------------------------------------------------------------------------------------------------------ */ 4/* SpeedMath Game is a fun way to test your abilities at how fast you can do math in your head. */ 5/* The game contains multiple ways of displaying arithmetic problems, including visually, and auditorily. */ 6/* ------------------------------------------------------------------------------------------------------------------------------------------ */ 7/* Instructions: */ 8/* 1. Start the game by waving at the IR sensor. The LED would turn green. */ 9/* 2. The game has 3 levels (Easy, Medium, and Hard). */ 10/* 3. Click on 1 on the keypad to play the easy level, 2 for the medium level, and 3 for the hard level. */ 11/* 4. There are 10 questions in each game. */ 12/* 5. Make sure to answer quickly, the game is set by a timer for every question. The timer is displayed on the LCD. */ 13/* 6. If you click the wrong key, you can click on "D" on they keypad to erase the last typed number. */ 14/* 7. To check your answer and move on to the next question before the timer ends, click on "#" on the keypad. */ 15/* 8. If your answer is correct, the green RGB LED lights up. If it is wrong, the LED turns red. */ 16/* 9. You can adjust the brightness of the LED by turning the knob of the potentiometer. */ 17/* 10. If you choose the easy level, you have 20 seconds to answer a "+", "-", "*", or "" math question displayed on the LCD. */ 18/* 11. If you choose the medium level, only the operation is displayed on the LCD. You have to observe how many times the blue LED blinks. */ 19/* 12. The hard level is similar, except that you have to listen carefully to the speaker and count how many times it "buzzes"! */ 20/* 13. For medium and hard levels, a duration of a second separates the first number from the second. */ 21/* 14. In these two levels, you only have 15 and 10 seconds to perform math operations in your head on the two numbers and type your answers. */ 22/* 15. At the end of each game, your game score is displayed on the LCD, and then your total game scores. */ 23/* 16. If you decide to stop the game at any time, you can click on "*" on the keypad. */ 24/* 17. Don't worry, you can play the game again at any time by waving at the IR sensor. */ 25/* 18. Have fun! */ 26/* ------------------------------------------------------------------------------------------------------------------------------------------ */ 27/* Made by Mariam Alzaabi */ 28/* 23/11/2020 */ 29/* ------------------------------------------------------------------------------------------------------------------------------------------ */ 30 31#include <Keypad.h> //Keypad library 32#include <LiquidCrystal_I2C.h> //LCD library 33#include <EEPROM.h> //EEPROM library 34 35// Defining Arduino pins 36const byte bluePin = 10; //PB2 attach pin D12 (PB2) Arduino to pin Blue of RGB LED 37const byte greenPin = 11; //PB3 attach pin D12 (PB3) Arduino to pin Green of RGB LED 38const byte redPin = 12; //PB4 attach pin D12 (PB4) Arduino to pin Red of RGB LED 39const byte speaker = 13; //PB5 attach pin D13 (PB5) Arduino to speaker pin 40const byte potentiometerPin = A0; //attach pin A0 Arduino to potentiometer pin 41 42// Creating pointers to port D and B registers 43byte *ptr_to_PORTD; 44byte *ptr_to_PIND; 45byte *ptr_to_DDRB; 46byte *ptr_to_PORTB; 47 48// New shapes created for the LCD screen 49byte smileyFace[] = { //Smiley face used for answers checking 50 B00000, 51 B00000, 52 B01010, 53 B00000, 54 B10001, 55 B01110, 56 B00000, 57 B00000 58}; 59byte sadFace[] = { //Sad face used for answers checking 60 B00000, 61 B00000, 62 B01010, 63 B00000, 64 B01110, 65 B10001, 66 B00000, 67 B00000 68}; 69byte blankChar[] = { //Blank character used for M/H game levels 70 B11111, 71 B11111, 72 B11111, 73 B11111, 74 B11111, 75 B11111, 76 B11111, 77 B11111 78}; 79 80// Creating object from LCD library to control LCD 81LiquidCrystal_I2C lcd(0x27, 16, 2); 82 83// Configuring Keypad using keypad library 84const byte ROWS = 4; //four rows 85const byte COLS = 4; //four columns 86 87// Defining the keymap 88char keys[ROWS][COLS] = { 89 {'1', '2', '3', 'A'}, 90 {'4', '5', '6', 'B'}, 91 {'7', '8', '9', 'C'}, 92 {'*', '0', '#', 'D'} 93}; 94 95byte rowPins[ROWS] = {2, 3, 4, 5}; //connect to the row pinouts of the keypad 96byte colPins[COLS] = {6, 7, 8, 9}; //connect to the column pinouts of the keypad 97 98// Creating the keypad 99Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); 100 101// Code for game implementation 102namespace Game { 103class SpeedMath 104{ 105 public: 106 String input = ""; //used to get a string of number inputs from the user 107 int num1, num2, correctValue, op; //initialization of int type 108 String operation = ""; //type of operation used in the game ("+", "-", "*", or "/") 109 int numChar = 0; //used for determining the number of characters used in typing a question 110 111 char difficulty; //level of difficuty chosen by the user (1-E, 2-M, or 3-H) 112 byte score = 0; //score is initially 0 113 byte numQuestions = 10; //number of questioned asked, max is 255 114 bool playMode = false; //if user is in play mode 115 bool isExited = false; //if the game has already been exited 116 bool levelChosen = false; //if the level is chosen by the user 117 bool setUp = false; //if the game has already been exited 118 bool gameStopped = false; //if the game has been stopped 119 unsigned long delayStart = 0; //time the delay started 120 bool delayRunning = false; //true if still waiting for delay to finish 121 int totScore = 0; //total score of the player 122 int addTotScore = 0; //address of the total score 123 124 // Function used to generate random questions for the easy level game 125 void generateEasy(void) { 126 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 127 generateNumbers(1, 100, 1, 100, 1, 20, 1, 100); //the range of randomly generated numbers 128 lcd.print(String(num1) + operation + String(num2) + "="); //print the question to the LCD 129 numChar = countDigit(num1) + countDigit(num2) + 2; //count the number of characters displayed on the LCD 130 delayStart = millis(); //start delay 131 delayRunning = true; //delay is not finished yet 132 } 133 134 // Function used to generate random questions for the medium level game 135 void generateMed(void) { 136 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 137 generateNumbers(1, 10, 1, 10, 1, 10, 1, 10); //the range is [1, 10] 138 lcd.print("Look carefully!"); //print to the LCD screen for 2 seconds 139 delay(2000); 140 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 141 for (int i = 1; i <= num1; i++) { //used for blinking num1 142 analogWrite(bluePin, 255); //writes an analog value (PWM wave) to blue pin for 250 ms 143 delay(250); 144 analogWrite(bluePin, 0); //turn it off for 250 ms 145 delay(250); 146 } 147 delay(1000); //wait for a second before blinking num2 148 for (int i = 1; i <= num2; i++) { //used for blinking num2 149 analogWrite(bluePin, 255); //writes an analog value (PWM wave) to blue pin for 250 ms 150 delay(250); 151 analogWrite(bluePin, 0); //turn it off for 250 ms 152 delay(250); 153 } 154 delay(1000); //wait for a second before displaying the operation 155 lcd.createChar(0, blankChar); //create a custom character for displaying the question 156 lcd.home(); //positions the cursor in the upper-left of the LCD 157 lcd.write(0); //write the custom character to the LCD 158 lcd.print(operation); //print the operation to the LCD 159 lcd.write(0); //write the custom character to the LCD again 160 lcd.print("="); //print the operation to the LCD 161 numChar = 4; //number of characters on the LCD 162 delayStart = millis(); //start delay 163 delayRunning = true; //delay not finished yet 164 } 165 166 // Function used to generate random questions for the hard level game 167 void generateHard(void) { 168 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 169 generateNumbers(1, 10, 1, 10, 1, 10, 1, 10); //the range is [1, 10] 170 lcd.print("Listen carefully!"); //print to the LCD for 2 seconds 171 delay(2000); 172 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 173 for (int i = 1; i <= num1; i++) { //used for "buzzing" num1 174 tone(speaker, 1000); //start speaker sound for 250ms 175 delay(250); 176 noTone(speaker); //Stop buzzing 177 } 178 delay(1000); //wait for a second before "buzzing" num2 179 for (int i = 1; i <= num2; i++) { //used for "buzzing" num1 180 tone(speaker, 1000); //start speaker sound for 250ms 181 delay(250); 182 noTone(speaker); //Stop buzzing 183 } 184 delay(1000); //wait for a second before displaying the operation 185 lcd.createChar(0, blankChar); //create a custom character for displaying the question 186 lcd.home(); //positions the cursor in the upper-left of the LCD 187 lcd.write(0); //write the custom character to the LCD 188 lcd.print(operation); //print the operation to the LCD 189 lcd.write(0); //write the custom character to the LCD again 190 lcd.print("="); //print the operation to the LCD 191 numChar = 4; //number of characters on the LCD 192 delayStart = millis(); //start delay 193 delayRunning = true; //delay not finished yet 194 } 195 196 // Function used to reset values after each question 197 void clean() { 198 num1 = num2 = correctValue = 0; 199 input = ""; 200 numChar = 0; 201 } 202 203 // Function used to count the number of digits in a number 204 int countDigit(int n) { 205 int count = 0; 206 while (n != 0) { 207 n = n / 10; 208 ++count; 209 } 210 return count; 211 } 212 213 // Function used to generate the numbers for the game in the range [1, 255] 214 void generateNumbers(byte minAdd, byte maxAdd, byte minSub, byte maxSub, byte minMul, byte maxMul, byte minDiv, byte maxDiv) { 215 op = random(1, 5); //generate a random number [1,4] for the type of operation 216 // Switch statement used for performing mathematical calculation 217 switch (op) { 218 case (1): //if the random number is "1", perform addition 219 operation = "+"; //type of operation is addition 220 num1 = random(minAdd, maxAdd); //generate a random number [1-99] for the first operand 221 num2 = random(minAdd, maxAdd); //generate a random number [1-99] for the second operand 222 correctValue = num1 + num2; //perform addition 223 break; 224 case (2): //if the random number is "2", perform subtraction 225 operation = "-"; //type of operation is subtraction 226 num1 = random(minSub, maxSub); //generate a random number [1-99] for the first operand 227 num2 = random(minSub, maxSub); //generate a random number [1-99] for the second operand 228 while (num1 < num2) { //calculation results cannot be negative 229 num2 = random(minSub, maxSub); 230 } 231 correctValue = num1 - num2; //perform subtraction 232 break; 233 case (3): //if the random number is "3", perform multiplication 234 operation = "x"; //type of operation is multiplication 235 num1 = random(minMul, maxMul); //generate a random number [1-19] for the first operand 236 num2 = random(minMul, maxMul); //generate a random number [1-19] for the second operand 237 correctValue = num1 * num2; //perform multiplication 238 break; 239 case (4): //if the random number is "4", perform division 240 operation = "/"; //type of operation is division 241 num1 = random(minDiv, maxDiv); //generate a random number [1-99] for the first operand 242 num2 = random(minDiv, maxDiv); //generate a random number [1-99] for the second operand 243 while ((num1 < num2) || ((num1 % num2) != 0)) { //calculation results cannot be decimal 244 num2 = random(minDiv, maxDiv); 245 } 246 correctValue = num1 / num2; //perform division 247 break; 248 } 249 } 250 251 // Function used for initializing the game 252 void initGame() { 253 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 254 int potValue = analogRead(potentiometerPin) / 4; //measure the potentiometer value (max 255) 255 setColor(0, potValue, 0); //light up the RGB LED with green color 256 lcd.print("Hello!"); //print to the LCD screen 257 tone(speaker, 3000, 1000); //start speaker sound for 1 second 258 delay(1000); 259 setColor(0, 0, 0); //turn off green color 260 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 261 lcd.print("Difficulty "); //print to the LCD screen 262 lcd.setCursor(2, 1); //sLevelet cursor to the third position from the bottom 263 lcd.print("1-E 2-M 3-H"); //print to the LCD screen 264 } 265 266 // Function used for setting up the game 267 void setUpGame() { 268 lcd.backlight(); //turn on blacklight 269 setUp = true; //the game has been set up 270 isExited = false; //the game has not been exited 271 initGame(); //initialize the game 272 char key; 273 do { //keep looping until a level is chosen and the user has not chosen to exit the game 274 key = keypad.getKey(); //value of a key being pressed 275 if ((key == '1') || (key == '2') || (key == '3')) { //if a level is chosen 276 delay(1000); //wait for a second before starting 277 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 278 levelChosen = true; //level has been chosen 279 difficulty = key; //sets difficulty level 280 playMode = true; //user is now in play mode 281 playGame(); //play the game 282 } 283 else if (key == '*') { //if user has chosen to stop the game 284 stopGame(); //stop the game 285 } 286 } while (((key != '1') && (key != '2') && (key != '3') && (levelChosen != true)) && (!isExited)); 287 } 288 289 // Function for playing the game based on difficulty level 290 void playGame() { 291 numQuestions -= 1; //decrement number of questions 292 // Switch statement used for generating questions based on difficulty level 293 switch (difficulty) { 294 case ('1'): //if difficulty is easy 295 generateEasy(); //generate random easy questions 296 break; 297 case ('2'): //if difficulty is medium 298 generateMed(); //generate random medium questions 299 break; 300 case ('3'): //if difficulty is hard 301 generateHard(); //generate random hard questions 302 break; 303 } 304 } 305 306 // Function used to check if the inputted values match the correct values 307 void checkAnswer() { 308 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 309 int potValue = analogRead(potentiometerPin) / 4; //measure the potentiometer value (max 255) 310 if (input.toInt() == correctValue) { //if they match 311 score += 1; //increment the score value 312 tone(speaker, 4500, 800); //start speaker sound for 1 second 313 lcd.createChar(0, smileyFace); //create a custom character (smiley face) 314 lcd.home(); //positions the cursor in the upper-left of the LCD 315 lcd.print("Correct!"); //print to the LCD screen 316 lcd.write(0); //write the custom character to the LCD 317 setColor(0, potValue, 0); //light up the RGB LED with green color 318 } else { //if they do not match 319 tone(speaker, 500, 800); //start speaker sound for 1 second 320 lcd.createChar(0, sadFace); //create a custom character (sad face) 321 lcd.home(); //positions the cursor in the upper-left of the LCD 322 lcd.print("Incorrect!"); //print to the LCD screen 323 lcd.write(0); //write the custom character to the LCD 324 setColor(potValue, 0, 0); //light up the RGB LED with red color 325 } 326 delay(1000); //wait for 1 second 327 setColor(0, 0, 0); //turn off RGB LED color 328 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 329 clean(); //reset values 330 } 331 332 //Function to check if there are questions left 333 void continueGame() { 334 if (numQuestions > 0) { //if there are questions left 335 checkAnswer(); //check the answer 336 playGame(); //keep playing the game 337 } else if (numQuestions == 0 && playMode == true) { //if no questions left 338 checkAnswer(); //check the answer 339 playMode = false; //user has finished the game 340 lcd.setCursor(5, 0); //set cursor to the sixth position from the top 341 lcd.print("Score:" + String(score) + "/10"); //print to the LCD screen 342 delay(1500); //wait for 1.5 seconds 343 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 344 lcd.print("Total Score"); //print to the LCD screen 345 lcd.setCursor(0, 1); //set cursor to the first position from the bottom 346 totScore = EEPROM.get(addTotScore, totScore) + score; //update total score 347 EEPROM.put(addTotScore, totScore); //write total score to the EEPROM 348 lcd.print(String(EEPROM.get(addTotScore, totScore))); //print total score on the LCD from EEPROM 349 } 350 } 351 352 // Function for stopping the game 353 void stopGame() { 354 if (!isExited) { //stop the game if it has not been already stopped 355 clean(); //reset values 356 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 357 setColor(255, 0, 0); //light up the RGB LED with red color 358 lcd.setCursor(6, 0); //set cursor to the seventh position from the top 359 lcd.print("Good"); //print to the LCD screen 360 lcd.setCursor(6, 1); //set cursor to the seventh position from the bottom 361 lcd.print("Bye!"); //print to the LCD screen 362 delay(2000); //clear everything after 2 seconds 363 setColor(0, 0, 0); //turn off red color 364 lcd.clear(); //clear LCD screen and position the cursor in the upper-left corner 365 clearGame(); //reset values 366 } 367 } 368 369 // Function for resetting values after every game 370 void clearGame() { 371 lcd.noBacklight(); //turn off backlight 372 isExited = true; //the game has been stopped 373 setUp = false; //the game can be set up when it is on again 374 numQuestions = 10; //the number of questions is back to 10 375 difficulty = NULL; //difficulty can be chosen again later 376 levelChosen = false; //level can be chosen again later 377 score = 0; //reset the score 378 } 379 380 // Function used when a number is pressed on the Keypad 381 void numPress(char num) { 382 if (playMode) { //if the user is in play mode 383 lcd.setCursor(numChar, 0); //set the cursor to the position after the printed characters 384 input += num; //convert the numbers to a string of numbers 385 numChar += 1; //increment the number of characters displayed on the LCD 386 lcd.write(num); //write the number to the LCD 387 tone(speaker, 1000, 300); //start speaker sound for 1 second 388 } 389 } 390 391 // Function used to delete a character typed during the game 392 void deleteChar() { 393 if (input.length() != 0) { //if there are characters typed by the user 394 numChar -= 1; //decrement the number of characters displayed on the LCD 395 input.remove(input.length() - 1); //remove the last character from the input string 396 lcd.setCursor(numChar, 0); //set the cursor to the last position 397 lcd.print(" "); //hide the character from the LCD 398 lcd.setCursor(numChar, 0); //set the cursor to the last position 399 } 400 } 401 402 // Use PWM to control the brightness of the RGB LED [0-255] 403 void setColor(int redValue, int greenValue, int blueValue) { 404 analogWrite(redPin, redValue); //writes an analog value (PWM wave) to red pin 405 analogWrite(greenPin, greenValue); //writes an analog value (PWM wave) to green pin 406 analogWrite(bluePin, blueValue); //writes an analog value (PWM wave) to blue pin 407 } 408 409 //Function used to set up the timer based on difficulty level 410 void setUpTimer() { 411 // Switch statement used for setting the timer based on difficulty level 412 switch (difficulty) { 413 case ('1'): //if difficulty is easy 414 setTimer(20); //sets a timer of 20 seconds 415 break; 416 case ('2'): //if difficulty is medium 417 setTimer(15); //sets a timer of 10 seconds 418 break; 419 case ('3'): //if difficulty is hard 420 setTimer(10); //sets a timer of 10 seconds 421 break; 422 } 423 } 424 425 //Function used to create the timer 426 void setTimer(unsigned long inSeconds) { 427 if (playMode) { //if the game is in play mode 428 if (inSeconds < 86400) { //maximum is 23h:59m:59s = 86399 seconds 429 bool secondPrinted = false; //can only be printed once 430 // Check if delay has timed out after 5 seconds 431 if (delayRunning && ((millis() - delayStart) >= (inSeconds * 1000))) { 432 delayRunning = false; //this code can only run once 433 continueGame(); //continue playing the game 434 } 435 else { //if the timer has not timed out 436 for (int i = 1; i <= inSeconds; i++) { //check if another second has passed 437 if ((delayRunning && ((millis() - delayStart) >= ((inSeconds - i) * 1000))) && !secondPrinted) { 438 secondPrinted = true; //only check for the first correct condition 439 displayTimer(i); //display the timer on the LCD 440 } 441 } 442 } 443 } 444 } 445 } 446 447 //Function used to display the timer on the LCD 448 void displayTimer(int totalSecond) { 449 lcd.setCursor(0, 1); //set the cursor to the first position from the bottom 450 char timeFormat[8]; //for formatting the time 451 452 //Return a formatted string in the form HH:MM:SS 453 sprintf(timeFormat, "%02d:%02d:%02d", totalSecond / 3600, (totalSecond % 3600) / 60, totalSecond % 60); 454 lcd.print(timeFormat); //print it to the LCD 455 } 456}; 457} 458 459// Initializing an object of class SpeedMath 460Game::SpeedMath my_game; 461 462// Setup code here, to run once 463void setup() { 464 lcd.init(); //initialize the LCD 465 ptr_to_PORTD = 0x2B; //PORTD (port D) register address 466 ptr_to_PIND = 0x29; //PIND (port D) register address 467 *ptr_to_PORTD = B00000010; //sets IR pin in PD1 as input 468 ptr_to_DDRB = 0x24; //DDRB (port B) register address 469 ptr_to_PORTB = 0x25; //PORTB (port B) register address 470 *ptr_to_DDRB = B00111100; //sets speaker pin in PB5 and RGB pins in PB2/3/4 as output 471 randomSeed(analogRead(0)); //seeds the random number generator (AnalogRead on pin 0) 472} 473 474// Main code, to run repeatedly 475void loop() { 476 // Set up a timer based on difficulty level 477 my_game.setUpTimer(); 478 byte statusIRSensor = *ptr_to_PIND; //digitalRead(IRSensor); 479 if (!my_game.setUp && ((statusIRSensor & B00000010) == LOW)) //if the game has not been set up and an object has been detected 480 { 481 my_game.setUpGame(); //set up the game 482 } 483 char key = keypad.getKey(); //value of a key being pressed 484 if (key) { //if a key has been pressed 485 // Switch statement used when a key is pressed on the Keypad 486 switch (key) { 487 case 'A': //if any of these are pressed nothing happens 488 case 'B': 489 case 'C': 490 break; 491 case 'D': //if 'D' is pressed 492 my_game.deleteChar(); //delete the last character typed 493 break; 494 case '#': //if '#' is pressed 495 my_game.continueGame(); //check the answer then continue the game if there are questions left 496 break; 497 case '*': //if '*' is pressed 498 my_game.stopGame(); //stop the game 499 break; 500 default: //if a number is pressed 501 my_game.numPress(key); //write the number to the LCD and retrieve a string of the numbers pressed 502 break; 503 } 504 } 505} 506
Downloadable files
Schematic
Schematic
Schematic
Schematic
Comments
Only logged in users can leave comments