Introduction: Jeopardy Ring-in Buttons With Built-in Rules

About: Way back in history, I went to college to become an engineer. I earned a BE/EE and worked as an EE for about nine months, until computers started to emerge as a career field. Fifteen years later, I was ready t…

There are several good Jeopardy/Game Show lockout buttons/buzzers, but none incorporate the timing rules of Jeopardy. I am learning about the Arduino and I thought that this would be a great project to practice with.

The rules include a question reading period with no timeout to reading the question. During this period, any player who tries to ring in receives a 250 ms penalty. When the question is finished, players have a five second window to ring in and a five second timeout to answer. After an answer timeout or a wrong answer, there is another five second window to ring in and the flow proceeds until a player answers correctly or all players have rung in and have answered incorrectly or the ring-in window has timed out.

Initially, I built my project on a solder-less breadboard. When I got the connections worked out I moved to a Radio Shack Universal Component PC Board with 780 Holes which I happened to have bought several years ago. My intention was to use as much from my junk box as possible. I recommend that, if you plan to make this, you consider using the MakerShield Kit from Maker Shed. It is more expensive but it will have the parts you need and save time.

I have not included a lot of detailed instructions on wiring the PC board, since I don't recommend that you use the same board as I did and the circuit is fairly simple. The circuit and the breadboard layout are included.

Step 1: Gather Materials

As I said, much of what went into this came from my junk box. I did have to purchase the push-button switches. The minimum parts list is:

1 Blue LED
1 Green LED
1 Red LED
1 Yellow LED
1 White LED
6 Pushbutton (Radio Shack Catalog #: 275-1556 or Catalog #: 275-1548)
6 10k Ω Resistor
4 220 Ω Resistor
1 Arduino (processor ATmega; variant Arduino UNO R3)
1 solder-less breadboard
jumper wires

In addition, if you want to go beyond breadboarding, you might need:
5 LED Holders (Radio Shack Catalog #: 276-079)
1 Project Box (I used a piece of sheet metal bent to a box size of 5"x8"x3", with wood ends. This size provided plenty of room.)
4 3.5MM Mono Male Plug (Ebay)
4 Panel 3.5mm Female Mono Headphone Jack (Ebay)
4 5" sections 1/2" PVC pipe
4 1/2" PVC end caps
  20 gauge or 22 gauge ZIP Cord or Speaker Wire 

1 MakerShield Kit (MakerShed)
or
1 prototyping breadboard
1 set Shield stacking headers for Arduino (Adafruit)

After I had built the project, I noticed some other projects which had used the MakerShield board with a solder-less breadboard on its top. This is a great idea because it allows you to easily put the project together and then reuse the MakerShield when you move on to the next project.

Step 2: The Ring-In Buttons


The Ring-In Buttons each consist of a 5" length of 1/2" PVC pipe, a 1/2" PVC pipe cap, a push-button switch and the wire to the game console (the Project Box). Begin with drilling a hole on the end of the pipe cap for the switch. The switches I used require a 1/4" hole. The thickness of the end of the cap will prevent the threads of the switch from protruding sufficiently to screw on the mounting nut, so the end must be sanded or ground down. I used a disc sander.

Step 3: Let the Wiring Begin

If you are the adventuresome kind, take the schematic and go at it. I prefer to breadboard a project to be sure that I haven't overlooked something. I used the free software from http://fritzing.org/ to create the breadboard layout and schematic. It is great software since, as you are creating, the auto-route feature performs a check which is a great help in potential missing connections. It may not help if you simply put a wire on the wrong pin.

The software does not contain a model for every possible component and I have not progressed to the point where I can create a new one. Consequently, the switch model is meant to be mounted on a PC board or breadboard, not hanging in mid-air or mounted on a chassis somewhere. It also has a PC board layout feature that I am starting to get used to, but have not mastered.

If you follow the breadboard layout, cross checking with the schematic, you should be able to upload the Arduino sketch below and, after setup, see the player LEDs light in chase-mode.

Step 4: Beyond the Breadboard

When you have confidence that you want to commit the design to something more permanent, you will need a project box. If you are like me, cheap frugal, you might take a piece of sheet metal (I thought I had some aluminum but it turned out to be brass) something about 20 or 22 gauge should work, cut it to 8"x13" and bend it to have a center area of 5"x8" with two 3"x8" 90 degree flaps. I have an old 18" "bending brake" to do the bending. There are instructables in how to build your own, but it might be more expeditious to consider buying one. Harbor Freight sells one for about thirty dollars. Complete the box with two pieces of wood to fit on the ends. Don't forget to make a hole or slot for the power or USB cord to go through.

If you are not into that sort of thing, just go buy a project box of about the same size.

For the circuit board, I recommend the MakerShield kit. I did not use it, but my final attempt was very similar to it, just using stuff I had around. My first plan was to use wire wrapping. I was pretty good at it, thirty years ago, but not now and I don't recommend it.

In the build, the layout is not critical. Neatness is very helpful. if you are consistent in placement, when you have components, like resistors, performing a similar function, laid out with similar connections, if something looks out of place,it is worth double-checking.

Step 5: Uploading

//
// This sketch implements a "Jeopardy" style contestant response tiebreaker.
//
// Each contestant has a pushbutton switch.
// After setup, the waitingForQuestion function is invoked. This uses a
// local loop, rather than the Arduino loop, to wait for the moderator
// to begin. While waiting, the player lights are illuminated in chase mode.

// When the moderator presses the switch S6 button, the QUESTION_READING
// state begins. There is no time limit here. In this phase the player
// LEDs will light in chase mode. During this time, if a player button is
// pressed, a penalty of 0.25 seconds is applied for the next phase. When
// the moderator presses the switch S5 button and releases it, the
// READY_FOR_ANSWER_LIGHT is turned on, player LEDs will be extinguished and
// the game will proceed to the WAITING_FOR_RING_IN state.

// In WAITING_FOR_RING_IN, the READY_FOR_ANSWER_LIGHT is
// illuminated. There is a five second timeout which, at its expiration,
// causes the game to resetAll to clear variables, the waitingForQuestion
// function is invoked and the state changes to QUESTION_READING. When
// a player presses a button, if the player is not BLOCKED from a prior
// wrong answer and any delay time imposed in the QUESTION_READING phase,
// that player's light is turned and READY_FOR_ANSWER_LIGHT is turned off.
// The game proceeds to the WAITING_FOR_ANSWER state.

// In WAITING_FOR_ANSWER, after a five second timeout or
// a player giving a wrong answer, the game proceeds to
// WAITING_FOR_RING_IN, if there are players not yet buzzed in. If a
// player gives a correct answer or all players have attempted to answer,
// resetAll clears variables, the waitingForQuestion function is invoked
// and the state changes to QUESTION_READING.
//

// Note: This program is set up to handle up to four players. If there are fewer
// players, it will handle them, although when those players have rung in and
// incorrectly answered, the program will time out, instead of immediately
// recognizing that there are no remaining players. For the traditional three
// player purists or those who cannot wait the extra five seconds, MAX_PLAYERS
// can be changed to three and the program recompilied and uploaded. It will then
// only recognize players one through three.

// 2012.12.18 Initial release.

/*
* Environment definitions
*/
#define MAX_PLAYERS 4
#define TIMEOUT_PERIOD 5000
#define DELAY_TIME 250
#define NO_ANSWER 0
#define CORRECT_ANSWER 1
#define WRONG_ANSWER -1

/*
* Hardware definitions
  */
#define TOP_LED 13
#define TOP_SWITCH 9
// The CORRECT_ANSWER_BUTTON is switch S6
#define CORRECT_ANSWER_BUTTON 3
// The WRONG_ANSWER_BUTTON is switch S5
#define WRONG_ANSWER_BUTTON 4
#define READY_FOR_ANSWER_LIGHT 5

/*
* Housekeeping definitions
*/
int buzzedInPlayer = -1;
int buzzedInPlayerCount = 0;
unsigned long timeout = 0;
int blockedPlayers = 0;
int moderator;

char ESC_CHAR = 27;
#define PUT_CURSOR_MIDSCREEN\
    Serial.print(ESC_CHAR);\
    Serial.print("[2J");\
    delay(2);\
    Serial.print(ESC_CHAR);\
    Serial.print("[10;1H");


/*
*  Button press definitions
*/
#define NOT_PUSHED HIGH
#define PUSHED     LOW
#define BLOCKED   -1

/*
*  LED state definitions
*/
#define LED_ON  HIGH
#define LED_OFF LOW

/*
* Game States
*/
#define QUESTION_READING 1
#define WAITING_FOR_RING_IN 2
#define WAITING_FOR_ANSWER 3
int phase = QUESTION_READING;
#define DEBOUNCE_TIME 10

struct player_t {
    int light;
    int buttonPin;
    int button;
    unsigned timePenalty;
} ;// End struct playerStruct_t

struct player_t player[MAX_PLAYERS];

// Reset player time.
void resetPlayerTime()
{
   int i;

   for (i = 0; i < MAX_PLAYERS; i++)
   {
       player[i].timePenalty = 0;
   };
} // End resetAll

// Reset to the initial state.
void resetAll()
{
   int i;
   moderator = NO_ANSWER;

   phase = QUESTION_READING;

   for (i = 0; i < MAX_PLAYERS; i++)
   {
       digitalWrite(player[i].light, LED_OFF);
       player[i].button = NOT_PUSHED;
       resetPlayerTime();
   };
   buzzedInPlayer = -1;
   buzzedInPlayerCount = 0;
} // End resetAll

int readModerator()
{
// Button release is the trigger for a button having been pushed.
// Debounce is used to sense the button release.
    if (digitalRead(CORRECT_ANSWER_BUTTON) == PUSHED) {
        for(;;) {
            delay(DEBOUNCE_TIME);
            if (digitalRead(CORRECT_ANSWER_BUTTON) == NOT_PUSHED) {
                break;
            }
        }
        return CORRECT_ANSWER;
    } else if (digitalRead(WRONG_ANSWER_BUTTON) == PUSHED) {
        for(;;) {
            delay(DEBOUNCE_TIME);
            if (digitalRead(WRONG_ANSWER_BUTTON) == NOT_PUSHED) {
                break;
            }
        }
        return WRONG_ANSWER;
    } else {
        return NO_ANSWER;
    }
}

void readPlayers()
{
// States of all player buttons are saved into the player array.
    int i;
    for (i = 0; i < MAX_PLAYERS; i++)
    {
        if (player[i].button != BLOCKED) {
            if (digitalRead(player[i].buttonPin) == PUSHED) {
                player[i].button = PUSHED;
            } else {
                player[i].button = NOT_PUSHED;
            }
        }
    }
}

void waitingForQuestion()
{
    int i;

    digitalWrite(READY_FOR_ANSWER_LIGHT, LED_OFF);
    Serial.println("\tModerator, Press S6 and begin reading");
    Serial.println("\tModerator, Press S5 when you have finished the Question");
    while (!(readModerator() == CORRECT_ANSWER)) {
        for (i = 0; i < MAX_PLAYERS; i++) {
            digitalWrite(player[i].light, LED_ON);
            delay(250);
            digitalWrite(player[i].light, LED_OFF);
        }
    }
    PUT_CURSOR_MIDSCREEN
    Serial.println("\tPlayers, Wait for question to finish");
}
void setup()
{
    int i;
    for (i = 0; i < MAX_PLAYERS; i++) {

        // Assign the LEDs, cathodes connected to ground
        player[i].light = TOP_LED - i;
        pinMode(player[i].light, OUTPUT);

        // Assign the pushbutton switches; connect the input to ground when closed.
        player[i].buttonPin = TOP_SWITCH - i;
        pinMode(player[i].buttonPin, INPUT);
        digitalWrite(player[i].buttonPin, HIGH);  // Enable pullup

    }

    pinMode(READY_FOR_ANSWER_LIGHT, OUTPUT);

    pinMode( WRONG_ANSWER_BUTTON, INPUT );
    digitalWrite( WRONG_ANSWER_BUTTON, HIGH);    // Enable pullup

    pinMode( CORRECT_ANSWER_BUTTON, INPUT );
    digitalWrite( CORRECT_ANSWER_BUTTON, HIGH);    // Enable pullup

    resetAll();

    Serial.begin(9600);           // set up Serial library at 9600 bps

    PUT_CURSOR_MIDSCREEN
    waitingForQuestion();
}

void loop()
{
    int i;

    switch (phase) {
    case QUESTION_READING: {
    // Moderator has pressed the reset button
    // or the questioned has been answeredd
    // or all players have answered incorrectly.
    // No timing for this phase.
    // Any Buzz-ins will suffer 0.25 second delay when next state entered.
        readPlayers();
        // Apply Penalty for eager button pressers
        for (i = 0; i < MAX_PLAYERS; i++) {
            if ((player[i].button == PUSHED) &&
                (player[i].timePenalty == 0)) {
                player[i].timePenalty = DELAY_TIME;
                Serial.print("\tPlayer ");
                Serial.print(i+1);
                Serial.println(" gets a penalty");
            }
        }
        moderator = readModerator();
        if (moderator == WRONG_ANSWER) {
            phase = WAITING_FOR_RING_IN;
            timeout = millis() + TIMEOUT_PERIOD;
            digitalWrite(READY_FOR_ANSWER_LIGHT, LED_ON);
            for (i = 0; i < MAX_PLAYERS; i++) {
                player[i].timePenalty += millis();
            }
            PUT_CURSOR_MIDSCREEN
            Serial.println("\tWaiting for Ring In");
            Serial.println();
            blockedPlayers = 0;
        }
        break;
    } // End case QUESTION_READING

    case WAITING_FOR_RING_IN: {
    // Moderator has pressed the ANY BUTTON while in QUESTION_READING state.
    // Five seconds for this phase.
        if (blockedPlayers >= MAX_PLAYERS) {
            PUT_CURSOR_MIDSCREEN
            Serial.println("\tAll players have rung in");
            Serial.println("\tModerator, Please read the next Question");
            phase = QUESTION_READING;
            resetAll();
            waitingForQuestion();
            break;
        } else {
            buzzedInPlayer = -1;
            if (timeout >= millis()) {
                readPlayers();
                for (i = 0; i < MAX_PLAYERS; i++) {
                    if ((player[i].timePenalty <= millis()) &&
                        player[i].button == PUSHED) {
                        buzzedInPlayer = i;
                        buzzedInPlayerCount += 1;
                        digitalWrite(player[buzzedInPlayer].light, LED_ON);
                        digitalWrite(READY_FOR_ANSWER_LIGHT, LED_OFF);
                        phase = WAITING_FOR_ANSWER;
                        timeout = millis() + TIMEOUT_PERIOD;
                        player[i].button = BLOCKED;
                        blockedPlayers += 1;
                        PUT_CURSOR_MIDSCREEN
                        Serial.print("\tPlayer Number ");
                        Serial.println(i+1);
                    }
                }
            } else {
                Serial.println("\tTimed out waiting for a RING_IN ");
                Serial.println("\tModerator, Please read the next Question");
                phase = QUESTION_READING;
                resetAll();
                waitingForQuestion();
                break;
            }
            break;
        } // End else
    } // End case WAITING_FOR_RING_IN

    case WAITING_FOR_ANSWER: {
    // A player has PUSHED the button while in WAITING_FOR_RING_IN state.
    // Five seconds for this phase.
        if (timeout < millis()) {
            if (buzzedInPlayerCount <= MAX_PLAYERS) {
                PUT_CURSOR_MIDSCREEN
                Serial.println("\tWAITING_FOR_RING_IN");
                timeout = millis() + TIMEOUT_PERIOD;
                digitalWrite(player[buzzedInPlayer].light, LED_OFF);
                digitalWrite(READY_FOR_ANSWER_LIGHT, LED_ON);
                phase = WAITING_FOR_RING_IN;
                PUT_CURSOR_MIDSCREEN
                Serial.print("\tPlayer Number ");
                Serial.print(buzzedInPlayer+1);
                Serial.println(" timed out waiting for an answer ");
                }
        }
        moderator = readModerator();
        if (moderator == CORRECT_ANSWER) {
            digitalWrite(player[buzzedInPlayer].light, LED_OFF);
            PUT_CURSOR_MIDSCREEN
            Serial.print("\tPlayer Number ");
            Serial.print(buzzedInPlayer+1);
            Serial.println(", that is correct");
            Serial.println("\tModerator, Please read the next Question");
            phase = QUESTION_READING;
            resetAll();
            waitingForQuestion();
        } else
        if (moderator == WRONG_ANSWER) {
            phase = WAITING_FOR_RING_IN;
            timeout = millis() + TIMEOUT_PERIOD;
            digitalWrite(player[buzzedInPlayer].light, LED_OFF);
            digitalWrite(READY_FOR_ANSWER_LIGHT, LED_ON);
            Serial.println("\tOooo, sorry, that is wrong");
            Serial.println("\tWaiting for Ring In");
            resetPlayerTime();
        }
        break;
    } // End case WAITING_FOR_ANSWER

    default: {
        Serial.print("default: phase = ");
        Serial.println(phase);
    }

     } // End switch (phase)
}

Step 6: Playing the Game

This is the step we have been waiting for. Powering the box may be done with the USB cable used for uploading the sketch or with a nine volt battery and its adapter plugged into the the external power input. If you are using the USB port connected to a computer, I have included code which will assist in tracking the game flow.

For the computer interaction, you will need a telnet client. Windows users might try TeraTerm Pro, my favorite or Putty, both free applications. Both of these support VT100 emulation (without VT100 emulation the output may contain unexpected characters and will not be formatted properly). Mac and Linux users are on you own. Whichever client you use, select SERIAL at a baud rate of 9600.

Whether you use the telnet client or not, the play is the same. The participants are the moderator (which may actually be two people, one to read the questions and one to operate the console) and one (yes, one) to four players.

The game steps are:
  1. The console will begin lighting the player LEDs in chase mode indicating the Waiting For Question period, which has no timeout.
  2. Moderator presses switch S6 and holds until the chase lights stop, which begins the Question Reading period.
  3. During Question Reading, if a player presses a ring-in button, a 0.25 second ring-in penalty is given to the player. The player is blocked from ringing in for 0.25 seconds once the ring-in period starts.
  4. Moderator finishes the question and presses S5 which begins the Ring-In period.
  5. The Ring-In period lasts for five seconds.
  6. If no eligible player rings in within the five seconds, the question is finished and the game reverts to Waiting For Question period, step 1.
  7. The LED of the first player to ring in will light and the Answer period will begin.
  8. The Answer period will last up to five seconds.
  9. If no answer is given and there is at least one player eligible to ring in, the current player is blocked from ringing in on this question and the game reverts to the Ring-In period, step 5.
  10. If an incorrect answer is given the moderator presses switch S5 (Wrong Answer) and if there is at least one player eligible to ring in, the current player is blocked from ringing in on this question and the game reverts to the Ring-In period, step 5.
  11. If an incorrect answer or no answer is given and there is not at least one player eligible to ring in, the question is finished and the game reverts to Waiting For Question period, step 1.
  12. If the correct answer is given, the question is finished and the game reverts to Waiting For Question period, step 1.
Play continues until someone pulls the plug.

Step 7: Conclusion

Thank you for viewing my Instructable. I hope you enjoyed it. Let me know if you build it and tell me what modifications or enhancements you have made.

I had some ideas for enhancements:
  • Add a speaker and output sounds, for example, a "Time's Up" beep.
  • Create a host computer application which would parse the text it receives from the Arduino and does more than simply print the text.
    • Have it deliver more intricate sounds than just a beep
    • Make it interactive so that there could be a Final Jeopardy phase
    • Make it portable, to work with Macs and Linux
  • Add a small LCD screen to the console, to display the text that would normally go to the host computer
That's all, for now.

Have fun and SHARE.