Heh. I saw the faster ticking comment and hacked up report()-ing a countdown timer that tweaks the global detonation time and speeds up when mistakes are made.
This will drop into your sim:
// https://wokwi.com/projects/374394134602244097
// https://forum.arduino.cc/t/cutting-wires-in-sequence-out-of-sequence/1162990
/**
"Cut The Wires" defuse bomb puzzle
Players must "cut" the correct wires in order(via releasing alligator clips) to stop
the ticking clock. If the clock reaches zero (after 60 minutes), the bomb "explodes"
with sound effects. If wires are "cut" out of order, the ticking will get faster.
*/
#define DEBUG
// CONSTANTS
const byte numWires = 5;
const int wirePins[numWires] = {2, 3, 4, 5, 6};
// Define the number of steps in the sequence that the player must follow
// GLOBALS
int lastState[numWires];
// What is the order in which wires need to be cut
// 0 indicates the wire should not be cut
int wiresToCut[numWires] = {0, 1, 2, 3, 4}; // (blue, red, yellow, green)
byte wiresCutCounter = 1;
// Keep track of the current state of the device
enum State {Inactive, Active, Defused, Exploded};
State state = State::Inactive;
//This is the timestamp at which the bomb will detonate
//It is calculated by adding on the specified number of minutes in the game time
// to the value of millis() when the code is initialised.
unsigned long detonationTime;
//The game length (in minutes)
int gameDuration = 60;
bool fastMode = false;
void Activate() {
state = State::Active;
// Set the detonation time to the appropriate time in the future
// detonationTime = millis() + (unsigned long)gameDuration*60*1000;
detonationTime = millis() + 30000; // twenty-five seconds
Serial.println("Bomb activated!");
}
void Deactivate() {
state = State::Inactive;
Serial.println("inactive. is that mean dead now until reset?");
}
void Detonate() {
state = State::Exploded;
}
void setup() {
Serial.begin(9600);
Serial.println("restarted\n\n");
// Initialise wire pins
for (int i = 0; i < numWires; i++) {
pinMode(wirePins[i], INPUT_PULLUP);
lastState[i] = digitalRead(wirePins[i]);
}
// Set the detonation time to the appropriate time in the future
detonationTime = millis() + (unsigned long)gameDuration * 60 * 1000;
// Print the initial state of the wires
for (int i = 0; i < numWires; i++) {
Serial.println(F("Wire "));
Serial.print(i);
Serial.println(digitalRead(wirePins[i]) ? " Unconnected" : " Connected");
}
// Arm the bomb!
Activate();
};
void loop() {
delay(25); // was there a bouncing problem? this killed it if there was
// First, see if any of the wires have been recently cut
for (int i = 0; i < numWires; i++) {
// If the previous reading was LOW, but now it's HIGH, that means this wire must have been cut
if (digitalRead(wirePins[i]) == HIGH && lastState[i] == LOW) {
Serial.println("Wire ");
Serial.print(i);
Serial.println(" cut");
lastState[i] = HIGH;
// Was this the correct wire to cut?
if (wiresCutCounter == wiresToCut[i]) {
// Go on to the next counter
wiresCutCounter++;
}
// Incorrect wire cut
else {
// Play faster ticking sound effect
fastMode = true;
Serial.println(" WRONG WIRE");
}
}
// If the previous reading was LOW, but now it's HIGH, that means this wire must just have been cut
else if (digitalRead(wirePins[i]) == LOW && lastState[i] == HIGH) {
Serial.println(" Wire ");
Serial.println(i);
Serial.println( " reconnected");
fastMode = false;
lastState[i] = LOW;
}
}
// Now, test the current state of all wires against the solution state
//First, assume that the correct wires have all been cut
bool allWiresCut = true;
// Then, loop over the wires array
for (int i = 0; i < numWires; i++) {
// Every wire that has a number > 0 in the wiresToCut array should be cut (at some point), after which they will read HIGH,
// So if any of them still read LOW, that means that there is at least one wire still to be cut
if (wiresToCut[i] != 0 && lastState[i] == LOW) {
allWiresCut = false;
}
}
// What to do next depends on the current state of the device
if (state == State::Active) {
// Retrieve the current timestamp
unsigned long currentTime = millis();
if (currentTime > detonationTime) {
Detonate();
Serial.println("time is up, so BOOM!");
}
else if (allWiresCut == true ) {
Deactivate();
Serial.println("Bomb defused!");
}
else {
}
}
report();
}
void report(void) {
const unsigned long interval = 250;
static unsigned long last = 0;
unsigned long now = millis();
if (now - last < interval) return;
last += interval;
if (fastMode) {
detonationTime -= 1000;
}
char buff[50];
signed long time = now - detonationTime;
char timeSign = time < 0 ? '-' : '+';
time = abs(time);
snprintf(buff, 50, "%c%02ld:%02ld.%03ld", timeSign, time / 60000L, (time % 60000L) / 1000L, time % 1000);
Serial.println(buff);
}
Edit: Here's a updated report() that prints the status as well:
void report(void) {
const unsigned long interval = 250;
static unsigned long last = 0;
unsigned long now = millis();
if (now - last < interval) return;
last += interval;
if (fastMode) {
detonationTime -= 1000;
}
char buff[50];
signed long time = now - detonationTime;
char timeSign = time < 0 ? '-' : '+';
char * stateStrings[] = {"Inactive", "Active", "Defused", "Exploded"};
time = abs(time);
snprintf(buff, 50, "%c%02ld:%02ld.%03ld %s",
timeSign,
time / 60000L,
(time % 60000L) / 1000L,
time % 1000,
stateStrings[state]);
Serial.println(buff);
}