// ===== Chasing the Light - Integrated Game Sketch =====
// Uses variable names, functions, and approach exactly as in user's snippets.
// Game rules:
// - 5 patterns (AB, CD, AC, BD, ABCD)
// - random pattern shown, player must move joystick to corresponding input
// and HOLD that input for 3 seconds to capture (or hold joystick button)
// - up to 9 successful captures (single-digit 7-seg)
// - any mistake or timeout -> game over -> flashing zero
// - restart button only works while zero is flashing (game over)
// ----------------- from snippet: restart button -----------------
const int TRUE = 1;
const int FALSE = 0;
const int restartButton = 6; // button is connected to pin 6 on the Arduino
// KF note: reads LOW(0V) when button NOT pressed and HIGH(5V) when button IS pressed
bool gameGo; // TRUE (1) for game going, FALSE (0) for game over
bool gameRestart; // TRUE (1) for game restarting, FALSE (0) for game over
// ----------------- from snippet: joystick & leds -----------------
// LEDs named A-D: use the same "led" pins as your randomness snippet
const int aled = 4; // A
const int bled = 2; // B
const int cled = A3; // C
const int dled = A5; // D
// array of LED pins in the order A B C D (same as your snippet)
int leds[4] = { aled, bled, cled, dled };
// KF note: LEDs go 5V to led/resistor to pin so LOW is on and HIGH is OFF
// function to turn on LED connected to pin number it receives
void ledOn(int pin)
{
digitalWrite(pin, LOW); // pin LOW = LED on (as per your hardware wiring)
}
// function to turn off LED connected to pin number it receives
void ledOff(int pin)
{
digitalWrite(pin, HIGH); // pin HIGH = LED off
}
// joystick pins (from your joystick snippet)
const int xPin = A0; // horizontal (blue wire)
const int yPin = A1; // vertical (green wire)
const int switchPin = 5; // joystick button (magenta wire)
int xValue; // holds horizontal value
int yValue; // holds vertical value
int switchState; // holds state of button: pressed (LOW) or unpressed (HIGH)
// horizontal & vertical maximum constants (from your joystick snippet)
const int LEFT = 0; // x left
const int RIGHT = 1023; // x right
const int UP = 0; // y up
const int DOWN = 1023; // y down
// ----------------- patterns array (from your randomness snippet) -----------------
// In the patterns array: A=0, B=1, C=2, D=3; -1 sentinel
int patterns[5][4] = {
{0, 1, -1, -1}, // 0 => A B (top)
{2, 3, -1, -1}, // 1 => C D (bottom)
{0, 2, -1, -1}, // 2 => A C (left)
{1, 3, -1, -1}, // 3 => B D (right)
{0, 1, 2, 3} // 4 => A B C D (all)
};
// helper: print pattern visually in serial (kept exactly like your function)
void printPatternVisual(int patternIndex) // function receives index of the pattern to display
{
bool A = false;
bool B = false;
bool C = false;
bool D = false;
for (int x = 0; x < 4; x++)
{
int ledIndex = patterns[patternIndex][x];
if (ledIndex == -1) break;
if (ledIndex == 0) A = true;
if (ledIndex == 1) B = true;
if (ledIndex == 2) C = true;
if (ledIndex == 3) D = true;
}
Serial.println("--------");
Serial.print(A ? "A " : "_ ");
Serial.println(B ? "B" : "_");
Serial.print(C ? "C " : "_ ");
Serial.println(D ? "D" : "_");
Serial.println("--------");
Serial.println();
}
// showPattern: turn on the LEDs for the chosen pattern and leave them on
// (we will handle timing / checks outside so we don't block unnecessarily)
void showPattern(int patternIndex)
{
// turn on only the LEDs in the pattern
for (int x = 0; x < 4; x++)
{
int ledIndex = patterns[patternIndex][x];
if (ledIndex == -1) break;
ledOn(leds[ledIndex]);
}
// print the visual to serial for debugging
printPatternVisual(patternIndex);
}
// turn off all four LEDs (utility)
void turnOffAllLeds()
{
for (int i = 0; i < 4; i++) ledOff(leds[i]);
}
// ----------------- 7-segment display pins and functions (from your snippet) -----------------
// define pins for each of the LEDs in the 7-segment display (use EXACT names)
const int B = 13;
const int A = 12;
const int F = 11;
const int G = 10;
const int E = 9;
const int D = 8;
const int C = 7;
const int MAX_ROUNDS = 9; // maximum round number possible that the single digit display can show
int roundNum = 0; // round number / score (0..9)
// Note: in your snippet "below, HIGH turns LED on and LOW turns LED off" for 7-seg
// (We will follow that same approach when writing segments on 7-seg)
// utility: display digits 0-9 on 7-seg using same logic (HIGH ON, LOW OFF)
void displayDigit(int n)
{
// Ensure n is in 0..9 range
if (n < 0) n = 0;
if (n > 9) n = 9;
// Default to all off first (LOW = off for 7-seg in your snippet init)
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, LOW);
// Now light the segments needed (HIGH = on, per your snippet)
switch (n)
{
case 0:
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
// G off
break;
case 1:
digitalWrite(A, LOW);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, LOW);
break;
case 2:
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, LOW);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, LOW);
digitalWrite(G, HIGH);
break;
case 3:
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, HIGH);
break;
case 4:
digitalWrite(A, LOW);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
break;
case 5:
digitalWrite(A, HIGH);
digitalWrite(B, LOW);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, LOW);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
break;
case 6:
digitalWrite(A, HIGH);
digitalWrite(B, LOW);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
break;
case 7:
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, LOW);
break;
case 8:
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
break;
case 9:
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, LOW);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
break;
}
}
// show blank (all segments off) - useful for flashing
void displayBlank()
{
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, LOW);
}
// flash zero continuously until restart button is pressed
// This will block until restart pressed, because game must wait for player restart
void flashZeroAndWaitForRestart()
{
Serial.println("GAME OVER - FLASHING 0. PRESS RESTART TO PLAY AGAIN.");
// Only the restart button should restart when flashing (as requested).
// restartButton reads HIGH when pressed per your comment.
while (true)
{
// show 0
displayDigit(0);
delay(400);
// blank
displayBlank();
delay(400);
// read restart button - only enable reset while flashing
if (digitalRead(restartButton) == TRUE)
{
// debounce little
delay(30);
if (digitalRead(restartButton) == TRUE)
{
// perform restart
Serial.println("GAME RESTARTING NOW...");
// Small delay and break to restart
delay(250);
break;
}
}
}
}
// ----------------- utility: interpret joystick into pattern index -----------------
// returns:
// 0 -> pattern 0 (A B) [UP]
// 1 -> pattern 1 (C D) [DOWN]
// 2 -> pattern 2 (A C) [LEFT]
// 3 -> pattern 3 (B D) [RIGHT]
// 4 -> pattern 4 (A B C D) [BUTTON]
// -1 -> no extreme / no action
int readJoystickAction()
{
xValue = analogRead(xPin);
yValue = analogRead(yPin);
switchState = digitalRead(switchPin); // LOW when pressed (per your joystick snippet)
// Check button first (user pressed joystick button) -> corresponds to pattern 4
if (switchState == LOW)
{
return 4; // ABCD
}
// Now check directional extremes exactly as your snippet uses (==)
if (yValue == UP) // up = pattern 0 (A B)
{
return 0;
}
if (yValue == DOWN) // down = pattern 1 (C D)
{
return 1;
}
if (xValue == LEFT) // left = pattern 2 (A C)
{
return 2;
}
if (xValue == RIGHT) // right = pattern 3 (B D)
{
return 3;
}
// no matching extreme
return -1;
}
// ----------------- main game config -----------------
const unsigned long HOLD_REQUIRED_MS = 3000UL; // 3 seconds hold required to capture
const int ROUNDS_PER_GAME = MAX_ROUNDS; // 9 (from your 7-seg capacity)
// ----------------- setup -----------------
void setup()
{
// ----- serial for debug -----
Serial.begin(9600);
// ----- pins -----
// LED pins
for (int x = 0; x < 4; x++)
{
pinMode(leds[x], OUTPUT);
ledOff(leds[x]); // turn led off initially
}
// joystick pins
pinMode(xPin, INPUT);
pinMode(yPin, INPUT);
pinMode(switchPin, INPUT_PULLUP); // as in your joystick snippet
// restart button
pinMode(restartButton, INPUT); // as in your restart snippet
// 7-seg pins
for (int p = 7; p <= 13; p++)
{
pinMode(p, OUTPUT);
digitalWrite(p, LOW); // consistent with your snippet initialization
}
// seed randomness
randomSeed(analogRead(A2));
// initial game state
gameGo = TRUE; // start with game ready
gameRestart = FALSE;
roundNum = 0;
// welcome message
delay(500);
Serial.println("\n\nCHASING THE LIGHT - GAME START\n");
delay(500);
}
// ----------------- main loop -----------------
void loop()
{
if (gameGo == TRUE)
{
Serial.println("GAME PLAYING...");
// play up to ROUNDS_PER_GAME rounds or until a mistake
while (roundNum < ROUNDS_PER_GAME && gameGo == TRUE)
{
// pick random pattern 0..4
int pattern = random(0, 5);
Serial.print("NEXT PATTERN (index): ");
Serial.println(pattern);
// show the pattern on LEDs (they stay on while we check player)
showPattern(pattern);
// Now wait for the player to produce and HOLD the correct action for HOLD_REQUIRED_MS
unsigned long actionStartTime = millis();
unsigned long holdStartTime = 0;
bool holding = false;
bool captured = false;
// We'll poll frequently (not blocking delays), until timeout
while (millis() - actionStartTime < HOLD_REQUIRED_MS)
{
int playerAction = readJoystickAction(); // returns pattern index or -1
if (playerAction == pattern)
{
if (!holding)
{
// start hold timer when first match detected
holdStartTime = millis();
holding = true;
Serial.println("MATCH DETECTED - HOLDING...");
}
else
{
// still holding: check if hold time satisfied
if (millis() - holdStartTime >= HOLD_REQUIRED_MS)
{
captured = true;
Serial.println("CAPTURED!");
break;
}
// otherwise keep holding
}
}
else
{
// not matching right now, reset hold state
if (holding)
{
// player released or moved away, reset
holding = false;
Serial.println("HOLD LOST - must start hold again");
}
// also if playerAction is an incorrect action (not -1 and not equal to pattern),
// that's an immediate mistake per "any miscapture -> game over".
if (playerAction != -1 && playerAction != pattern)
{
Serial.print("MISCAPTURE (wrong action): ");
Serial.println(playerAction);
captured = false;
holding = false;
// force immediate game over
break;
}
}
// tiny delay to not spam readings; still responsive
delay(25);
} // end waiting for hold
// turn off pattern LEDs after attempt
turnOffAllLeds();
// decide outcome for this round
if (captured)
{
// increment score and update 7-seg display
roundNum++;
if (roundNum > MAX_ROUNDS) roundNum = MAX_ROUNDS; // safety cap
Serial.print("ROUND SUCCESS! Score now: ");
Serial.println(roundNum);
displayDigit(roundNum); // show the score on 7-seg
delay(600); // little pause before next round
}
else
{
// either timeout or miscapture (wrong action) => game over
Serial.println("FAILED TO CAPTURE - GAME OVER.");
gameGo = FALSE; // indicate game over
roundNum = 0; // we'll flash zero (per user request) - 7-seg shows flashing 0
// Go to the game over flashing function which waits for restart
flashZeroAndWaitForRestart();
// when flashZeroAndWaitForRestart returns, the restart button was pressed.
// Reset state to allow a fresh game to start.
gameRestart = TRUE;
if (gameRestart == TRUE)
{
// reset everything to initial state
Serial.println("RESETTING GAME STATE...");
// reset variables
roundNum = 0;
gameGo = TRUE;
gameRestart = FALSE;
// clear LEDs and display blank
turnOffAllLeds();
displayBlank();
delay(300);
Serial.println("GAME RESTARTED. Good luck!");
// continue outer while (new game)
}
else
{
// shouldn't reach here - but fall back
Serial.println("ERROR: couldn't restart properly.");
}
} // end else (not captured)
} // end while rounds
// If the player reached MAX_ROUNDS success (win)
if (roundNum >= ROUNDS_PER_GAME && gameGo == TRUE)
{
// player won the game (9 captures)
Serial.println("END GAME - WINNER!");
// show the winner state as your original snippet does for MAX_ROUNDS + 1 blinking 9
// We'll blink the 9 to celebrate
for (int i = 0; i < 8; i++)
{
displayBlank();
delay(300);
displayDigit(9);
delay(300);
}
// After celebrating, go to flashing zero/wait for restart to allow new game
gameGo = FALSE;
roundNum = 0;
flashZeroAndWaitForRestart();
// after restart pressed:
gameRestart = TRUE;
if (gameRestart == TRUE)
{
roundNum = 0;
gameGo = TRUE;
gameRestart = FALSE;
displayBlank();
Serial.println("GAME RESTARTED AFTER WIN.");
}
}
} // end if gameGo == TRUE
// If game is not going (someone pressed restart earlier or after win) then allow restart button only while flashing,
// but we've handled restart inside flashZeroAndWaitForRestart() which blocks until restart.
// Keep loop small otherwise:
delay(50);
}