Introduction: Reaction Game

About: Coder by trade, tinkerer by hobby. Spends time working, running, family and computer games ...

My son has won a place on the World Scout Jamboree 2019 and I was looking at ways to help him fund raise. One of the options was to create some 'fair ground' style games that could be taken to various events and charge people to play them.

I had always fancied having a play with the Batak games you get in expensive gyms, but as a non-gym member I have never had a chance. Looking at the cost of hire, I'm not sure I could afford it anyway!

I therefore decided to build my own version!

The final product contains a number of different games which can be chosen without any additional set up.

Parts you will need:

Tools you will need:

  • Soldering iron (solder, stand, solder remover etc.)
  • 1.2mm drill bit
  • 1.5mm drill bit
  • Drill and drill bits
  • 25mm flat drill bit
  • Wire stripper/cutter
  • G-Clamps

Step 1: Basic Design

A reaction game is very simple. It is a timer, a display and how every many illuminated buttons that you require.

The main parts of the wiring for the reaction game is a collection of two independent circuits that are repeated. These two circuits are:

1. Button

There are many tutorials on adding buttons to an Arduino including on the official Arduino website.

2. Independently lit LEDs (12v)

For the LEDs, as we are using 12v LEDs, we require an external power supply that is separated from the Arduino which can only work with up to 5v. For a previous project (a photo booth that I hope to write up at some point) I used MOSFETs to interface between a Raspberry PI and some LED strip lights. I used this excellent tutorial regarding the connection between the PI and the LEDs. For this project I have simply repeated the exercise multiple times.

Output Displays

The third part of the game is the LED matrix display to display the score and time remaining. For this I used 4 of the linked MAX7219 matrix displays. To get them working I used the linked tutorial which links to the driver library, and gives examples of using the displays.

Step 2: Circuit Board - Button Connectors

The instructable has been written while building my second of the reaction games that I have made. During the building of the first reaction game I made a few minor errors, so have improved on the design slightly. (Ideally I would like to make a PCB for all of the electronics, and I plan to do that when I have more time).

  1. Drill two 6mm holes in the vero board at a convenient location. If using the same vero board, then I recommend the same location as I have used. This avoids all the major routes, including the large route that I use for ground. The holes are used for mounting the board to the back of the reaction game. In my original design I didn't do that, and ended up hot gluing the circuit board. That is fine until it goes wrong and I am in trouble!
  2. Using a 1.5mm drill bit, increase the size of the pins along one edge. The 4 pin female connectors that I use to connect each button to the board have significantly wider pins. As the 4 pin connectors span 2 holes each, only every other hole needs widening.
  3. Push all 8 of the female connectors to the board, and solder into place.

Step 3: Circuit Board - Main Wiring

This section details the wiring of the main components to the board.

  1. A 16 pin male header component it wired centrally near the bottom of the board. This is the interface between the buttons, LEDs and the Arduino
  2. Because of the vero board that is being used, it is easy to split the separate circuits (LED and button) into groupings. The female connector spans 8 holes. This means that the LED can use 4 holes and the button can use 4 holes to keep the circuitry in the same 'vertical'. Therefore all 8 circuits can be repeated with a space of 8 holes apart.
  3. To make wiring slightly easier I tended to wire all of the same parts for each circuit in turn (as shown in the pictures)

Step 4: Board

To mount the electronics I bought the largest board available that I could transport (610 mm x 1220 mm x 9 mm).

  1. I marked the locations of all the buttons.
    1. Each corner button is 100 mm x 100 mm from the corner.
    2. The top and bottom middle buttons are in the middle, but 100 mm from the edge
    3. The two buttons in the middle (left and right) are equal distance from the other buttons on the appropriate side.
  2. After marking the locations of the buttons I used a 25 mm flat head bit to drill out the holes for the buttons.
  3. I marked the locations of the displays
    1. A friend has a oscillating multi-tool, so I borrowed him to cut the rectangular holes for the display.
  4. Due to the thickness and design of the displays I used, I used a router to remove about 5mm deep of the MDF.
  5. I cut 3 support legs for the board. The legs are cut with an angle, allowing the board to be used both lying down on a table or standing up against a wall. The middle legs requires a slot for cables to be passed through.
    • On version 1 of the project I only used 2 legs, but that led to a lot of bouncing of the board when angled in the 'table top' (lying flat) position.
  6. Glue the legs into position.
  7. I painted the board using blackboard paint. This is mainly to allow us to draw on the board using chalk to advertise the game, and also write down the different game names on the buttons.
  8. Finally I hot glued two bolts to the board (near the top of the board) to mount the electronics via the 2 holes that were drilled earlier in the PCB. I also hot glued a ring of glue on (now topside of) the bolt to minimise the risk of short circuits.

Step 5: Wire the Buttons and Assemble

  1. Cut appropriate lengths of cable for each button. These cables will vary in length due to the distance of the board/buttons.
  2. On one end, screw the stripped wires into a connecting block. Keep all 4 wires in the same order for each connecting block as this will simplify the wiring later, and also make it easier to fix any issues. The order I used was:
    • RED - LED 12v
    • BLACK - LED Ground
    • YELLOW - Switch to pin
    • GREY - Switch 5v
  3. On the other end I crimped connectors to keep the connections nice and clean. I did run out of connectors so not all of mine have been done
  4. Once all of the connectors have been wired, assemble the buttons as shown. My LEDs were not marked, so getting them in the right orientation involved some trial and error, but that isn't possible until after the code has been completed
  5. My buttons had two small pieces of plastic on the back to stop the button rotating when in the board. This required two small holes to be drilled in the front of the board for each button. The easiest way of doing this I found was to put the button in and twist it by 45 degrees. After taking the button out, it was possible to see the two lines drawn by the pins. Drill the one end of each of these lines with a small 3mm drill bit. Make sure you drill the same end of each (e.g. the clockwise end of both lines!)

Step 6: Wiring the Arduino

The instructions below are for wiring the Arduino MEGA.

  • A 16 pin Male to Female ribbon jumper cable is required to be routed from the 16 pin header on the PCB to the digital data pins on the Arduino as shown in the diagram.
  • Two pins connect from the Arduino to the PCB. 5v goes to the small circuit on the PCB which has the RED wire. The Ground pin goes from the Arduino to the PCB,
  • The Display is wired as shown in the wiring diagram.
  • Finally the 12v wires for the LEDs are wired to a female jack connector.

The Displays

The displays are paired up and put in the holes (making sure they are put in the right way round). I use duct tape to secure the displays in the board.

The reason I use duct tape and don't permanently fix the displays in place is that I want the ability to play the game either standing up against a wall, or lying flat on a table. This is why the struts at the back are cut at an angle. However these two positions require the displays to be switched around so that they are the correct way round each time. Therefore by using the duct tape method they can be changed easily.

Step 7: Coding the Arduino

I am not going to give a huge tutorial on Arduino programming as there are some excellent tutorials, but I will step through my code giving a generic overview. I am sure I can improve on the code and also add in a number of additional games.

This project requires the HCMAX7219 libraries. There may be multiple versions available, but I used the ones referenced by the Hobby Components Blog. This is the reason I would recommend getting the matrix displays from here as it seems cheeky using their work without donating!

Once downloaded, edit the HCMAX7219.h file and change the following line to 4:

(line #31)

#define NUMBEROFDRIVERS 4

This value determines how many of the LED matrix displays are daisy chained together. For the one player game we use 4, for the two player game we use 8.

Attached to the Instructable is my code.

The code contains two arrays, one for the LEDs and one for the buttons:

const int LED[8] = { 2, 4, 6, 8, 22, 24, 26, 28 };
const int BUTTON[8] = { 3, 5, 7, 9, 23, 25, 27, 29 };

By using arrays, it is possible to index a specific button, and return the appropriate pin values.

The program consists of the following main parts:

  1. Setup - Run once to set up the game when the Arduino is powered on
  2. Loop - Constantly run
    1. ScreenSaver - Randomly light the buttons until a button is pressed. There are 3 screen savers in the program which are randomly chosen
    2. CountDown - When a button is pressed, perform a short countdown, flashing the lights faster and faster so that the player is ready to start
    3. PlayGame - Play the actual game

Setup

  • Setup randomises the seed by reading 'noise' from an analog port.
  • If DEBUG is set, it opens the serial port. When running with DEBUG set, there can be a significant delay in reading the buttons, so turn off the DEBUG when running the game properly
  • Set up all the input and output pins appropriately
/*<br>  Set up the game
*/
void setup() {
  // Set up the seed
  randomSeed(analogRead(0));<br>  if (DEBUG)
    Serial.begin(9600);
  
  // Set up the pins
  for (int i = 0; i < NUMBER_BUTTONS; i++)
  {
    pinMode(LED[i], OUTPUT);
    pinMode(BUTTON[i], INPUT);
  }
}

Loop

  • The main loop starts the 'screen saver', which waits for a button press
  • Display the count down of flashing the lights faster and faster before starting
  • Play the game
/*<br>  The main loop
*/
void loop() 
{
  Debug("DEBUG ON - ", 0);
  // Run the screen saver
  ScreenSaver();<br>  // Countdown to start
  CountDown();<br>  // Play the game
  PlayGame();
}

Screen Saver

The 'screen saver' simply flashes the buttons on in different patterns. This was useful for debugging the electronics, as well as 'advertising' that the reaction game is working and ready to play. I created 3 different screen savers, from simply randomly showing lights, building up and breaking down all the lights, and showing the lights in different lines that 'bounce' around. Each screen saver is in its own function that turns on and turns off the appropriate lights while waiting for a button to be pressed. As we have 8 buttons, each button corresponds to a bit in a byte. This allows the SetLEDs function to use bitwise operations to turn on and off the lights.

The Build up and Lines screen savers use a lookup table that contains the byte patterns to display. The random screen saver simply picks a random b=number between 1 and 255 and displays the lights that correspond to that number.

/*<br>  Randomly does a screen saver
*/
void ScreenSaver()
{
  int screenSaver = random(3);
  bool response = false;
  
  while (!response)
  {
    switch(screenSaver)
    {
      case 0:
        Debug("Screen saver random: ", screenSaver);
        response = ScreenSaverRandom();
        break;
      case 1:
        Debug("Screen saver build up: ", screenSaver);
        response = ScreenSaverBuildUp();
        break;
      case 2:
        Debug("Screen saver lines: ", screenSaver);
        response = ScreenSaverLines();
        break;
      default:
        Debug("Screen saver default: ", screenSaver);
        response = ScreenSaverRandom();
    }
  }
}

Setting the LEDs

The following two functions are used to set the LEDs on and off (depending on the requirements of that is calling them).

SetLED writes to a specific pin, setting the pin to HIGH or LOW.

SetLEDs uses a byte pattern to set multiple LEDs on and off.

/*<br>  Set a button
*/
void SetLED (int button, int led)
{
  if (button != HIGH)
    digitalWrite(led, LOW);
  else
    digitalWrite(led, HIGH);
}<br><br>/*
  Set the LED pattern using bits
  0000 0001 - LED 01
  0000 0010 - LED 02
  0000 0100 - LED 03
  0000 1000 - LED 04
  0001 0000 - LED 05
  0010 0000 - LED 06
  0100 0000 - LED 07
  1000 0000 - LED 08
*/
void SetLEDs (int pattern)
{
  Debug("SetLEDs: ", pattern);
  int bitPattern = 0x01;
  
  for(int i=0;i<NUMBER_BUTTONS;i++) {<br>    if ((pattern & bitPattern) != 0)<br>      SetLED(HIGH, LED[i]);<br>    else<br>      SetLED(LOW, LED[i]);<br><br>    bitPattern = bitPattern << 1;<br>  }<br>}

Check for a button press

The following function returns true is any button has been pressed, or false if none have.

/*<br>  Check to see if any button is pressed
*/
bool CheckButtons()
{
  for (int i=0; i< NUMBER_BUTTONS; i++)
  {
    if(digitalRead(BUTTON[i]) > 0)
    {
      Debug("CheckButtons() - pressed: ", i);
      return true;
    }
  }  Debug("CheckButtons() - NOT PRESSED - ", -1);
  return false;
}

Play the game

This function is the main one player game. It simply loops for 31 seconds, updating the time, turning on a single LED, and waiting for the appropriate button to be pressed. When the appropriate button has been pressed, it increments the score, randomly choses a different button and repeats.

The game actually lasts for 31 seconds, but when using 30 seconds the perception is that you are getting 29 seconds. For a 31 second game, the timer shows 30 seconds and then waits a second to decrement. When it reaches 0 the game stops. If you were to use a 30 second timer, the game would show 30 seconds and immediately change to 29 seconds. This seems like you aren't getting the full 30 seconds (although you are). I found it better to give an extra second than make the user think they were being cheated!

Once the time has elapsed it shows the players score for 10 seconds, updates the high score if appropriate and returns to the screen saver (which shows the high score for the game).

/*<br>  Play the game
*/
void PlayGame()
{
  unsigned long endTime = millis() + 31000;
  int score = 0;
  
  Debug("endTime: ", endTime);
  Debug("millis(): ", millis());  long randButton = random(8);  
  while (millis() < endTime)
  {
    // Set the button to press as lit
    SetLED(HIGH, LED[randButton]);
    
    UpdateDisplay((endTime - millis())/1000, score);
    
    // Check to see if the button was pressed
    if(digitalRead(BUTTON[randButton]))
    {
      score ++;
      // Turn off the button
      SetLED(LOW, LED[randButton]);
      
      // Get the new button and check it has changed
      int newButton = random(8);
      while(newButton == randButton)
      {
        newButton = random(8);
      }
      
      // Values differ, so continue
      randButton = newButton;
    }
  }
  
  // Clear all the LEDs
  SetLEDs(0);
  
  // Wait 10 seconds to show the score
  delay(10000);
  
  // If a new high score, set it
  if (score > highScore)
    highScore = score;
}

Updating the display

This function updates the display during the game. The function converts the time and score to strings, prefixing any 1 character values with a '0' to pad them out. It then sends the buffer to the HCMAX7219 library to print the values, and then refreshes the display.

The high score function does very similar work, but shows the value HI and the high score.

void UpdateDisplay(unsigned long time, int score)<br>{
  // Update the time, make sure it is 2 digits
  String timeStr = String(time);
  if(timeStr.length() < 2)
    timeStr = "0" + timeStr;<br><br>  // Add the score which must be 2 digits
  String scoreStr = String(score);
  if(scoreStr.length() < 2)
    scoreStr = "0" + scoreStr;<br><br>  // Combine the string
  String numberStr = timeStr + scoreStr;
  
  char charBuf[5];
  numberStr.toCharArray(charBuf, 5);<br><br>  // Display the string
  HCMAX7219.printMatrix(charBuf,32);
  HCMAX7219.Refresh();
}

Improvements made for the 2 player game

I have made a few improvements for the 2 player game that should be moved back to the one player game. These include:

  1. The high score is stored in the EEPROM so that when the game is turned off it saves the high score. When the game is first turned on, the setup has been modified to look for button 7 being pressed. If button 7 is pressed on start up it resets the high score to 0.
  2. The CheckButtons function was changed to return -1 if no button was pressed, or the value of the button that was pressed. This allows the main menu to check the value returned and chose a different game to be played in the main loop.

Step 8: Optional Improvements

As I progressed on the original project I was continuously improving, updating and refining what I was doing.

Second board for 2 player games

Originally I was looking at the one player reaction game, but when play testing the game, several other ideas came up that could be implemented. The first one is the two player option. By having a second board, a significant number of additional games could be coded. By using a Arduino Mega, it is possible to run the two boards off one processor.

Improve the number of game types

There are a significant number of game types available. Most are a variation on a theme

  1. Reaction game - similar to 'whack a mole'. If it lights up, press the button, get a point, repeat for 30 seconds.
  2. Reaction minus game - same as game #1, but you lose a point for each press you get wrong. You can only go back to zero though (display isn't large enough for negative numbers).
  3. Multiple button reaction game - Similar to #1, but multiple buttons light up and all have to be cleared before a new random set of buttons are displayed.
  4. Simon says - Although all my buttons are a single colour, it is possible to light up the lights in a given order and make the player repeat the given pattern

Add displays to the buttons

If you could add a matrix display to each button, and a larger matrix display in the middle, you could ask simple questions with multiple choice answers. Even without a display on each button, you could number each button and ask simple math questions.

Step 9: Conclusion

During the write up I was doing some research to document my decisions and was surprised by the cost of hire of the Batak games, as these reaction games are so easy to make. This game can be modified in many ways including adding more buttons, (the MEGA should be able to handle 20+ for a single player game), larger displays to allow questions and answers, and many more game types.

I hope people find the instructable useful!