Cutting wires in sequence...out of sequence?

Hi everyone. I am creating an escape room puzzle based on Alastair Aitchison's "Cut the Wires" project. I started with his code but cut a lot out because he was using LED lights, WiFi, and special effects lighting, which I am not. I added in 4 sound files using TMRpcm library to use during different stages.

I have it working almost perfectly, except the end result of "defusing the bomb" happens no matter the order of the 4 correct wires. I have 5 wires available, with only 4 needing to be cut, and of that, I did specify in the beginning of my code what the order should be. Any ideas on how to tweak my code so it ONLY defuses when wires from Pins 3, 4, 5, then 6 are cut in that exact order???

Attached is my wiring diagram and code. Thanks in advance for any suggestions!


```cpp
/**
 * "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() {
  // 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");
      }
    }
    // 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;
    }

  }

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

Did his code work?

There's stuff in what you posted that looks like the order is necessary, perhaps you cut too deeply and inadvertently damaged the logic. It could have been something seemingly trivial.

Is there a concept of reattaching a cut wire, or is it Boom! at the first mistake, or is cutting disarming, the counter goes on but the bomb doesn't go off?

Please post the original code.

a7

Suggest you create a counter that starts at zero (0).

Read the first correct wire using state change detection.
If the count = 0, and 1st is cut, increment counter to one (1).
If you are sitting in count 0, and any other wire is cut, boom …

If the counter = 1, and the next correct wire is cut, counter goes to two (2).
If wrong wire is cut when counter = 1, boom …

etc.



You can also use a State Machine, Google it …

1 Like

Hello emily79

Welcome to the worldbest Arduino forum ever.

I assume that you have written the programme by yourself, then it is quite easy to find the error:

There's a trick to figuring out why something isn't working:

Use a logic analyzer to see what happens.
Put Serial.print statements at various places in the code to see the values of variables and determine whether they meet your expectations.

Have a nice day and enjoy coding in C++.

1 Like

The code works, but you have to use it exact.

Launch the program, close all the switches and then reset the Arduino with its button.

Now open switches one at a time, but close any switch when it is opened in error before you change any other switches.

I added

void loop() {
    delay(25);

as a Poor Man's debouncing - real wires being cut and spliced back may need a longer debounce, or it doesn't matter at all. Can't run that to ground at this time.

I reduced the time to solve to 25 seconds just because life is too short.

If you fix mistakes right away, then if you cut the four wires in da correct ordre (right to left, start with the second switch), the bmb will defuse.

I do not think I made any other change to the logic. I added printing to see the flow.

Try it here

Wokwi_badge bomb bomb boom


It's a horrible program, could be organized much better. I can't be sure it isn't able to be "gamed" in one way or annother. I do see that recovering from multiple mistakes is something that does not work and may in fact be a way to confuse the code.

TBH I went blind getting this far, so I hope it is useful. I tried to find the original code so I could do my own super-competent stripping (I am very good at stripping :wink: ), but it seems to be behind a Patreon paywall, so Bzzzt! That may even me sharing it here would make someone mad... not the spirit of these fora, perhaps it is available for free. Which would be about the right prince for it.

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

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
      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");
      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 {

    }
  }
}
``
<br>
I may be totally off here, but I am seeing defusation and explosions, so.

a7
1 Like

shouldn't each wire cut be checked against the sequence and if wrong detonate?

look this over

byte wiresCutCounter;
void loop ()
{
    for (int i=0; i < NumWires; i++) {
     // if (digitalRead(wirePins[i]) == HIGH && lastState[i] == LOW) {
        if (digitalRead(wirePins[i]) == LOW && lastState[i] == HIGH) {
            Serial.print ("Wire ");
            Serial.print (i);
            Serial.println (" cut");
            lastState[i] = LOW;

            if (LOW == lastState [i]) {
                if (i != wiresToCut [wiresCutCounter++])
                    detonate ();

                if (NumWires <= wiresCutCounter)
                    deactivate ();
            }
        }
    }

    // time out
    if (state == State::Active) {
        if (millis () > detonationTime) {
            detonate ();
            Serial.println ("BOOM!");
        }
    }
}

i assume you are removing (cutting) the wires. aren't the pins pulled HIGH and go LOW when "cut"?

No. The game has a means to make mistakes, and try again. Detonation is not immediate.

Gameplay consists in cutting a wire, and if it is not correct, reconnecting the wire to try another wire to see if it is next.

A very common puzzle, usually seen with 4 - 10 buttons.

You do the maths on how long, at worst, it would take to discover the correct sequence. Remember, this is not flying blind trying to get the right from N! "Combinations".

This one is a bit different to what I've seen mostly. Usually a mistake resets progress. Wwith cutting wires, the game designer decided to leave progress as much as has been made, and allow for an "undo" of a wrongly selected next wire.

a7

A digital write HIGH to an input pin turns on it’s internal pull-up.

I don't have time to, but a fast-forward scan of this

shows all the fun way this is made into a game.

a7

looks like there's nothing that triggers checking, so it seems that if the wrong wire is cut, an LED or buzzer can be turned on.

and there's just a timer to finish which goes "boom" when expires

it seems that what is lacking is something the sets state to Defused when the correct sequence occurs, possibly

    if (wiresToCut[i] !=0 && lastState[i] == LOW) {
      breakl
    state  = Defused;

instead of

I did indeed pay $10 to Patreon in order to have access to all of Alistair's codes. Because of that I don't feel right posting his original code, sorry.

Did you try to defuse the bomb using the simulation I posted, according to the instructions I advised were necessary?

restarted


Wire 
0 Connected
Wire 
1 Connected
Wire 
2 Connected
Wire 
3 Connected
Wire 
4 Connected
Bomb activated!
Wire 
1 cut
Wire 
2 cut
Wire 
3 cut
Wire 
4 cut
inactive. is that mean dead now until reset?
Bomb defused!

a7

I understand.

Good luck!

a7

Oh my gosh, I am overwhelmed by the amount of suggestions so quickly! I hope one day I will know enough so I can help someone else out and pay it forward :blush:
Before I try out some of your suggestions, let me clarify a few things:

  1. no, I don't expect this puzzle will take 60 minutes, and in fact, it is the last puzzle of the entire escape room game; I just have the counter set to 60 so it starts up when the room starts up.

  2. the wires will not actually be cut, instead they will be disconnected using alligator clips, so they can be reconnected easily

  3. Yes, I would like leniency in this puzzle so if the wrong wire is disconnected, they can reconnect it and still have a chance to solve the puzzle (once the key is found elsewhere in the room). This is something my business partner and I have disagreed on, but I've never been to an escape room where you only get one chance to solve a puzzle.

Ok, I'm off to try out your suggestions. Will update once I get it working.

Make sense, however …


In these forums, we help people that can supply all the information we request, it makes the job easier and others that read this can learn too.

If you have information you are unable to share, you are making people jump thru hoops.


Lots of good ideas have been freely offered, have fun learning.

Holy crap, dudes. Thanks to LarryD for suggesting I Google state machine, I just found this: itemis CREATE – state machines made easy

I haven't downloaded it yet, but it looks like I could spend hours, days, just tinkering around with it! It looks SOOOOOOOOO helpful, but it feels like cheating :pensive:

There are several State Machine tutorials in the tutorial forum which will make you an expert.

Here is one example:

Besides my suspicion that there is a logic flaw that woukd allow a team to "solve" this puzzle without actually doing, the code you posted with, I think, the slightest of changes as I outlined, does what you want.

That said, it isn't too hard a problem to code. It would just be very important to complete and unambiguously specify exactly what should be necessary for a solution, and how you want the progress to that solution to act.

Your patron friend's videos are interesting, he's obvsly experienced at puzzle design. I like the reduction in time left for mistakes made, there's so much that can be put into something like this, I can say you are having more fun than I am just now.

I would seek to tolerate multiple mistakes better, or at all really. Maybe recognize that all wires have been reconnected, and zero out anything tracking progress. But leave the clock ticking down.

A better written sketch using the ideas you are quite correctly excited about would be easier to enhance endlessly, like the time penalty could be not just a jump, what mightn't be noticed, but the clock running fast until the mistake was corrected.

And so forth.

a7

1 Like

  • If this was me doing things, I would have 5 coloured switches to be operated in sequence.

  • I’d add a reset switch and a count down display decrementing at say every 5 seconds.

  • A wrong push speeds the countdown speed, say every 2 seconds etc.

  • Pushing the reset switch allows re-entering the push sequence but the countdown speed stays what it was …

  • Add an annoying beep every time the display decrements.

  • Only allow 5 attempts (20 attempts for people over 60 years of age).