/*
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 modified to handle up to 6 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.
*/
/*
* Environment definitions
*/
#define MAX_PLAYERS 6 // Updated for 6 players
#define TIMEOUT_PERIOD 5000
#define DELAY_TIME 250
#define NO_ANSWER 0
#define CORRECT_ANSWER 1
#define WRONG_ANSWER -1
/* 6 player modified
* Hardware definitions
*/
#define TOP_LED A5
#define TOP_SWITCH 12
#define CORRECT_ANSWER_BUTTON 4
#define WRONG_ANSWER_BUTTON 6
#define READY_FOR_ANSWER_LIGHT 5
/*
* Housekeeping definitions
*/
int buzzedInPlayer = -1;
int buzzedInPlayerCount = 0; // Initialized to 0
unsigned long timeout = 0; // Initialized to 0
int blockedPlayers = 0; // Initialized to 0
int moderator;
/* Cursor control for display */
char ESC_CHAR = 27;
#define PUT_CURSOR_MIDSCREEN \
// Removed serial cursor control as it's not needed
/*
* 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 long timePenalty; // Changed to unsigned long for time tracking
}; // End struct playerStruct_t
struct player_t player[MAX_PLAYERS];
// Reset player time.
void resetPlayerTime() {
for (int i = 0; i < MAX_PLAYERS; i++) {
player[i].timePenalty = 0; // Reset time penalty to 0
}
} // End resetPlayerTime
// Reset to the initial state.
void resetAll() {
moderator = NO_ANSWER;
phase = QUESTION_READING;
for (int i = 0; i < MAX_PLAYERS; i++) {
digitalWrite(player[i].light, LED_OFF);
player[i].button = NOT_PUSHED;
resetPlayerTime();
}
buzzedInPlayer = -1;
buzzedInPlayerCount = 0; // Reset player count
} // 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) {
while (digitalRead(CORRECT_ANSWER_BUTTON) == PUSHED) {
delay(DEBOUNCE_TIME);
}
return CORRECT_ANSWER;
} else if (digitalRead(WRONG_ANSWER_BUTTON) == PUSHED) {
while (digitalRead(WRONG_ANSWER_BUTTON) == PUSHED) {
delay(DEBOUNCE_TIME);
}
return WRONG_ANSWER;
} else {
return NO_ANSWER;
}
}
void readPlayers() {
// States of all player buttons are saved into the player array.
for (int 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() {
digitalWrite(READY_FOR_ANSWER_LIGHT, LED_OFF);
// Removed serial messages related to the waiting for question
while (!(readModerator() == CORRECT_ANSWER)) {
for (int i = 0; i < MAX_PLAYERS; i++) {
digitalWrite(player[i].light, LED_ON);
delay(250);
digitalWrite(player[i].light, LED_OFF);
}
}
// Removed serial messages related to players waiting
}
void setup() {
for (int 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);
// Set the moderator buttons as inputs
pinMode(WRONG_ANSWER_BUTTON, INPUT);
pinMode(CORRECT_ANSWER_BUTTON, INPUT);
digitalWrite(WRONG_ANSWER_BUTTON, HIGH); // Enable pullup
digitalWrite(CORRECT_ANSWER_BUTTON, HIGH); // Enable pullup
resetAll();
// Removed serial begin as it is no longer needed
PUT_CURSOR_MIDSCREEN; // This can be removed as well since it's not used
waitingForQuestion();
}
void loop() {
switch (phase) {
case QUESTION_READING:
{
readPlayers();
// Apply Penalty for eager button pressers
for (int i = 0; i < MAX_PLAYERS; i++) {
if ((player[i].button == PUSHED) && (player[i].timePenalty == 0)) {
player[i].timePenalty = DELAY_TIME;
// Removed serial message for player penalty
}
}
moderator = readModerator();
if (moderator == WRONG_ANSWER) {
phase = WAITING_FOR_RING_IN;
timeout = millis() + TIMEOUT_PERIOD;
digitalWrite(READY_FOR_ANSWER_LIGHT, LED_ON);
for (int i = 0; i < MAX_PLAYERS; i++) {
player[i].timePenalty += millis();
}
// Removed serial messages for waiting for ring in
blockedPlayers = 0; // Reset blocked players count
}
break;
} // End case QUESTION_READING
case WAITING_FOR_RING_IN:
{
if (blockedPlayers >= MAX_PLAYERS) {
// Removed serial messages for all players ringing in
phase = QUESTION_READING;
resetAll();
waitingForQuestion();
break;
} else {
buzzedInPlayer = -1;
if (timeout >= millis()) {
readPlayers();
for (int 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;
// Removed serial message for player number
}
}
} else {
// Removed serial message for timeout
phase = QUESTION_READING;
resetAll();
waitingForQuestion();
break;
}
break;
} // End else
} // End case WAITING_FOR_RING_IN
case WAITING_FOR_ANSWER:
{
if (timeout < millis()) {
if (buzzedInPlayerCount <= MAX_PLAYERS) {
// Removed serial message for waiting 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;
// Removed serial message for player number timing out
}
}
moderator = readModerator();
if (moderator == CORRECT_ANSWER) {
digitalWrite(player[buzzedInPlayer].light, LED_OFF);
// Removed serial message for correct answer
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);
// Removed serial message for wrong answer
resetPlayerTime();
}
break;
} // End case WAITING_FOR_ANSWER
default:
{
// Removed default case serial message
}
} // End switch (phase)
}
/*
Here are the connections for an Arduino Nano:
### Connections:
1. **LEDs for Players**:
- `TOP_LED` (A7): Connect to the common cathode of the LED for the top player. The other players' LEDs will connect to pins A6, A5, A4, A3, A2, and A1 respectively.
- **Player LEDs**:
- Player 1 LED: A7
- Player 2 LED: A6
- Player 3 LED: A5
- Player 4 LED: A4
- Player 5 LED: A3
- Player 6 LED: A2
2. **Player Buttons**:
- `TOP_SWITCH` (12): Connect to the button for the top player. The other players' buttons will connect to pins 11, 10, 9, 8, 7, and 6 respectively.
- **Player Buttons**:
- Player 1 Button: 12
- Player 2 Button: 11
- Player 3 Button: 10
- Player 4 Button: 9
- Player 5 Button: 8
- Player 6 Button: 7
3. **Moderator Buttons**:
- `CORRECT_ANSWER_BUTTON` (A0): Connect to the button used for the correct answer.
- `WRONG_ANSWER_BUTTON` (A1): Connect to the button used for the wrong answer.
4. **Ready for Answer Light**:
- `READY_FOR_ANSWER_LIGHT` (5): Connect to an LED that indicates readiness for answers.
### Pull-Up Resistors:
For the buttons, make sure to enable the internal pull-up resistors by setting the pins to `HIGH` in your code, as you did with `digitalWrite(buttonPin, HIGH);`. This way, when the button is pressed, it will read `LOW`.
### Summary of Connections:
- **LEDs**: A7 to A2 (for players)
- **Buttons**: 12 to 7 (for players)
- **Moderator Buttons**: A0 and A1
- **Ready Light**: Pin 5
*/