Cutting wires in sequence...out of sequence?

no, but i now see that when all the wires are cut, Deactivate() is called which sets state to Inactive. the Defused state is never used

seems that the code is a bit overcomplicated (which makes it harder to understand and debug)

it's also unconfirmed whether cutting a wire (removing an alligator clip) make the pin LOW or HIGH? the code seems to expect it to be HIGH

That's not like most bombs I've seen that have lotsa wires.

Of course neither is cutting wires on a Hollywood bomb in exactly the right order likely to be forgiven if you make a mistake.

And most bombs are a little bit better wired than with alligator clips.

So maybe the wires should be sacrificed for each use, need to find a wires cutter somewhere in the room, or area, and they'd need to strip and twist back into continuity an incorrectly snipped wire.

It kills me that people get paid to sit around all morning thinking through things like this. I've enjoyed work, but I didn't get to call it fun very often.

a7

CIA and MI6 and Interpole and CSIS will probably be on my door step if real bombs start appearing with 5 coloured switches.

:scream:

Make it simple and build some with six wires. :sunglasses:

...and the winner is.....BREAK. I put it in one spot and it helped a bit, then added it in two more places and presto-change-o! I also used the "poor man's" debug and it's smooth sailing. Below is the final code. I'm sure it could be written more concisely, but it does the job. Thanks everyone!

/**
 * "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
#define SD_ChipSelectPin 10
#include <SD.h>
#include <TMRpcm.h>
#include <SPI.h>

TMRpcm tmrpcm;


// 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;

void Activate() {
  state = State::Active;
  // Set the detonation time to the appropriate time in the future
  detonationTime = millis() + (unsigned long)gameDuration*60*1000;
  Serial.println("Bomb activated!");
}

void Deactivate() {
  state = State::Inactive;
}

void Detonate() {
  state = State::Exploded;
}



void setup() {
  tmrpcm.speakerPin = 9;
  Serial.begin(9600);

  if (!SD.begin(SD_ChipSelectPin)) {
    Serial.println("SD fail");
  }

  // 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();
  tmrpcm.play("slow.wav");
  tmrpcm.volume(5);
};


void loop() {
  // "poor man's" debouncing
  delay(25);
  // 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
        tmrpcm.setVolume(5);
        tmrpcm.play("fast.wav");
        Serial.println("  WRONG WIRE");
        break;
      }
    }
    // 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");
      lastState[i] = LOW;
      break;
    }
  }

  // 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;
      break;
    }
  }

  // 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("BOOM!");
      tmrpcm.setVolume(5);    // play explosion sound effect
      tmrpcm.play("expl.wav");
    }
    else if(numWires <= wiresCutCounter) {
      Deactivate();
      Serial.println("Bomb defused!");
      tmrpcm.setVolume(5);   // Play Sousa music
      tmrpcm.play("Sous.wav");
    }
  }
}

@emily79 yay!

Now I never argue with success, and I probably won't spend time seeing if I am correct, but as far as I can tell the three instances of break; in your latest sketch cannot possibly explain the change from "doesn't work" to "works".

So, interesting. Did you change anything else beside the poor man's thing and the breaks?

a7

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);
}

Nice.

Calls for the addition of an LCD.

The wokwi does have a TM1637 four digit LED display…

I hope I forget this before I get back to the lab. :expressionless:

Note: this is not a real explosive device!

a7

Thank you?