Toggle Switch puzzle but sequential

LEDs in the Wokwi simulator don't have much dynamic range, The simulated can't switch on-and-off as fast as real life leds with the mapping of one led to several pixels, delivering through html etc....

Calibrating the screen-represented html mediated simulation to real life colors would be fraught with difficulty, so the simulation just goes with "on" at the refresh rate of the page rendering.

(I just copied the color from @alto777's fine script)

Having some days off and not messing with this gave me time to think, I am wanting to make a completely stand alone game separate from what bigger project this is going to be part of and I went back to the random puzzle, or so I thought it was random. At first, I thought I modified something and did it wrong, but it happens in the

puzzle as well. It doesn't seem to go into random mode, just repeating a predetermined list every time you reset the game. Here is my locked project on WOKWI as well as the code, but it happens in the example game you mentioned in post 12 as well. Not truly random.

// from learnSequencePuzzle.ino

// https://forum.arduino.cc/t/toggle-switch-puzzle-but-sequential/1091460
// https://wokwi.com/projects/357012577248058369

# define SEVEN   5    // puzzle size
# include <SPI.h>

# include <Adafruit_NeoPixel.h>

# define LED_PIN    A2
# define LED_COUNT  SEVEN
# define LED_PIN2    A5
# define LED_COUNT2  45

Adafruit_NeoPixel disaply(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip(LED_COUNT2, LED_PIN2, NEO_GRB + NEO_KHZ800);

unsigned char combo[SEVEN];
unsigned char lastSwitch[SEVEN];
enum Runstate {START, RUNNING, FAULT, SUCCESS} runState;

# define redLED   A1    // bad
# define greenLED A0    // good

# define NOKEY  99      // impossible key value as a flag

const byte inputPin[SEVEN] = {9, 10, 11, 12, 13};
// This pin will be driven LOW to release a lock when puzzle is solved
const byte lockPin = A3;
const byte lockPin2 = A4;

void setup() {
  Serial.begin(115200); Serial.println("HE LL O!\n");

  for (unsigned char ii = 0; ii < SEVEN; ii++) {
    pinMode(inputPin[ii], INPUT_PULLUP);

    lastSwitch[ii] = digitalRead(inputPin[ii]);
  }

  pinMode(greenLED, OUTPUT);
  pinMode(redLED, OUTPUT);
      // Set the lock pin as output and secure the lock
  pinMode(lockPin, OUTPUT);
  digitalWrite(lockPin, HIGH);
  pinMode(lockPin2, OUTPUT);
  digitalWrite(lockPin2, LOW);

  disaply.begin();
  disaply.show();
  strip.clear();
  strip.begin();


  reset();
  runState = RUNNING;
  newCombo0();
}

unsigned char nIn = 0;    // number of correct steps taken sofa

void loop() {
  unsigned char pKey = scanKeys();

  switch (runState) {
    case RUNNING:
      if (pKey == NOKEY)
        return;

      // presst a key, all key are valid so

      if (pKey == combo[nIn]) {
        nIn++;
        displayDisplay();
        goodFlash();
        if (nIn == SEVEN) {
          Serial.println("You are in!");
          runState = SUCCESS;
          // Release the lock
          digitalWrite(lockPin, LOW);
          theaterChase(strip.Color(255, 255, 255), 100, 3, 5); // White, full brightness          
        }
      }
      else {
    Serial.print("             BZZZT!");
        runState = FAULT;
        // Sound the Buzzer
        theaterChase(strip.Color(255, 0, 0), 100, 3, 5); // RED CHASE         
        digitalWrite(lockPin2, HIGH);  
        delay (1000);
        digitalWrite(lockPin2, LOW);                  
      }
      break;
    case FAULT:
      badFlash();
      if (keysum() == 0) {
        reset();
        displayDisplay();
        runState = START;
      }
      break;
    case SUCCESS:
      reward();
      if (pKey != NOKEY) {
        newCombo0();
        reset();
        displayDisplay();
        runState = FAULT;
      }
      break;
    case START:
      runState = RUNNING;
      break;
    default:
      runState = START;
      break;
  }

}

void goodFlash()
{
  digitalWrite(A1, HIGH);
  delay(100);
  digitalWrite(A1, LOW);

  displayDisplay();
}

void badFlash()
{
  static uint32_t last = 0;
  const int interval = 50;
  if (millis() - last >= interval) {
    last = millis();
    digitalWrite(A0, !digitalRead(A0));
    displayDisplay();
    digitalWrite(A0, LOW);
  }
}

void reward()
{
  static uint32_t last = 0;
  const int interval = 600;
  static bool off = true;
  if (millis() - last < interval) return;
  last = millis();
  if (off) { // turn on
    nIn = SEVEN; displayDisplay();
    digitalWrite(A1, HIGH);
  } else {
    digitalWrite(A1, LOW);
    nIn = 0; displayDisplay();
    off = true;
  }
}

void reset()
{
  nIn = 0;
  digitalWrite(A1, LOW);
}

void displayDisplay()
{
  for (unsigned char tt = 0; tt < SEVEN; tt++) {
    //    disaply.setPixelColor(tt, tt >= nIn ? 0x001010 : 0x00ff00);
    if (runState != FAULT) {
      disaply.setPixelColor(combo[tt], tt >= nIn ? 0x000000 : 0x00ff00);
      strip.fill(tt >= nIn ? 0x000000 : 0x00ff00, combo[tt] *9, 9);
    } else {
      disaply.setPixelColor(tt, lastSwitch[tt] ? 0xff0000 : 0x000000);
      strip.fill(lastSwitch[tt] ? 0xff0000 : 0x000000, tt *9, 9);
    }
  }

  disaply.show();
  strip.show();
}

// scanKeysO looks for a button going pressed
// scanKeys just looks for a change of state

unsigned char scanKeys()
{
  // printf("scan keys ");

  unsigned char newKeyQ = NOKEY;

  static unsigned long lastTime;

  unsigned char currentKey = NOKEY;
  unsigned long now = millis();

  if (now - lastTime < 40)      // too soon to look at anything
    return currentKey;
  lastTime = now;
  for (unsigned char ii = 0; ii < SEVEN; ii++) {
    unsigned char aSwitch = digitalRead(inputPin[ii]);
    if (aSwitch != lastSwitch[ii]) {
      currentKey = ii;
      lastSwitch[ii] = aSwitch;
    }
  }

  return currentKey;
}

int keysum() {
  int retval = 0;
  for (unsigned char ii = 0 ; ii < SEVEN; ii++) {
    retval += lastSwitch[ii];
  }
  return retval;
}

unsigned char scanKeys0()
{
  // printf("scan keys ");
  unsigned char newKeyQ = NOKEY;

  static unsigned long lastTime;
  static unsigned char wasPressed = 0;

  char isPressed = 0;
  unsigned char currentKey = NOKEY;
  unsigned long now = millis();

  if (now - lastTime < 40)
    return currentKey;

  for (unsigned char ii = 0; ii < SEVEN; ii++) {
    if (!digitalRead(inputPin[ii])) {
      newKeyQ = ii;
      isPressed = 1;
    }
  }

  if (isPressed != wasPressed) {
    lastTime = now;
    if (isPressed)
      currentKey = newKeyQ;
    wasPressed = isPressed;
  }

  return currentKey;
}



void newCombo0()
{
  for (unsigned char ii = 0; ii < SEVEN; ii++)
    combo[ii] = ii;

  for (int ii = 0; ii < 5000; ii++) {
    unsigned char tt = random(SEVEN);
    unsigned char ss = random(SEVEN);
    unsigned char temp;

    temp = combo[tt];
    combo[tt] = combo[ss];
    combo[ss] = temp;
  }

  // return;   // if you do not want to print the combination!

  for (unsigned char ii = 0; ii < SEVEN; ii++) {
    Serial.print(combo[ii]);
    Serial.print("  ");
  }

  Serial.println();
}

/*
* Fills a strip with a specific color, starting at 0 and continuing
* until the entire strip is filled. Takes two arguments:
* 
*     1. the color to use in the fill
*     2. the amount of time to wait after writing each LED
*/
void colorWipe(uint32_t color, unsigned long wait) {
	for (unsigned int i = 0; i < strip.numPixels(); i++) {
		strip.setPixelColor(i, color);
		strip.show();
		delay(wait);
	}
}

/*
* Runs a marquee style "chase" sequence. Takes three arguments:
*
*     1. the color to use in the chase
*     2. the amount of time to wait between frames
*     3. the number of LEDs in each 'chase' group
*     3. the number of chases sequences to perform
*/
void theaterChase(uint32_t color, unsigned long wait, unsigned int groupSize, unsigned int numChases) {
	for (unsigned int chase = 0; chase < numChases; chase++) {
		for (unsigned int pos = 0; pos < groupSize; pos++) {
			strip.clear();  // turn off all LEDs
			for (unsigned int i = pos; i < strip.numPixels(); i += groupSize) {
				strip.setPixelColor(i, color);  // turn on the current group
			}
			strip.show();
			delay(wait);
		}
	}
}

/*
* Simple rainbow animation, iterating through all 8-bit hues. LED color changes
* based on position in the strip. Takes two arguments:
* 
*     1. the amount of time to wait between frames
*     2. the number of rainbows to loop through
*/
void rainbow(unsigned long wait, unsigned int numLoops) {
	for (unsigned int count = 0; count < numLoops; count++) {
		// iterate through all 8-bit hues, using 16-bit values for granularity
		for (unsigned long firstPixelHue = 0; firstPixelHue < 65536; firstPixelHue += 256) {
			for (unsigned int i = 0; i < strip.numPixels(); i++) {
				unsigned long pixelHue = firstPixelHue + (i * 65536UL / strip.numPixels()); // vary LED hue based on position
				strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));  // assign color, using gamma curve for a more natural look
			}
			strip.show();
			delay(wait);
		}
	}  
}

Is it possible to do truly random?

Use randomSeed

1 Like

randomSeed() would certainly shake things up.

Another trick that would also is to just call random() frequently.

In scanKwys() where it has the print statement that is commented out, right after that just call random(), you don't need to do anything with the number, just calling it moves along the sequence.

    random(777);  // any number really

Since scanKeys() is getting called alla time, it's like dealing cards rapidly. When you need a random number for realz, you'll be at some, um, random point in the deck, so to speak.

a7

No. Not Las Vegas Nevada random. But you can avoid repeating the same pseudorandom sequence, if you store the current random value in EEPROM after you have called random() a few times in your program. Then load that value and pass it to randomSeed() at boot time.

You could use the timing of user inputs and ADC noise to inject some randomness into the stream. If you put a call to this:

setSeed(random(RANDOM_MAX)+micros()+analogRead(floatingPin));

...before you need a random() and whenever you notice a keypress, it will break the predictability of the RNG.

Yes, but just calling random() at the loop rate would do as well.

For "real" random numbers some larger amount of trouble must be gone to, including replacing the random() code, which is probably not suitable for, say, cryptographic purposes.

For an escape room toy, I wonder if users would catch on to being fed the same sequences if game play even did feed a batch of participants the same numbers one afternoon.

Or you could take the ultimate dodge and consider it to be a feature… clever participants wou,d gain an advantage.

While I was testing one or the other version above I did inadvertently memorize the first three combinations that always come out if no care is taken… and months later still knew the first two.

Meanwhile I can't remember my girlfriend's telephone number. :expressionless:

a7

The AVR's code isn't bad:

But loop() based calls to it is still deterministic, unless something injects some external entropy, like depending on user-supplied events.

If you need a seed at setup, you may not be able to wait for user input, and analogRead() of a floating pin or the power supply might get you something non-deterministic.

// RandomSeed.ino https://wokwi.com/projects/358276294916324353
// for https://forum.arduino.cc/t/toggle-switch-puzzle-but-sequential/1091460/47 

const int seedtype = 4;

void setup() {
  Serial.begin(115200);
  switch (seedtype) {
    case 1:
      randomSeed(20230304) ; //
    case 2:
      pinMode(12, INPUT_PULLUP);
      Serial.println("Ground pin 12 for a random number");
      while (digitalRead(12)) random(RANDOM_MAX);
      break;
    case 3:
      randomSeed(random(RANDOM_MAX) + micros() + analogRead(A0));
      break;
    case 4:
      randomSeed(analogRead(A0)); // 10 bits if you're lucky
      break;
    case 5:
      randomSeed(1+analogRead(A0) % 4); // maybe two bits of randomness
      break;
    case 0:
      break;
    default:
      Serial.println("bad seed scheme");
      ;
  }

  Serial.println(random(RANDOM_MAX));

  for (int i = 6; i; --i)
    Serial.println(random(6));
}

void loop() {
}

Of those schemes, the user input one has the most randomness/entropy.

In non-cryptographic cases, usually setting seed just once with a good value is enough, because it will start you at one of the RANDOM_MAX positions in the random code's cycle, but if you need more entropy, injecting some when you can get it (i.e. from each UI interaction, analogRead() network delay,... etc.. ) is a good scheme.

Which we have here. loop() will run some hundreds or thousands of times between keystrokes, random() will be however further along in its sequence.

I've seen the analogRead() trick, but never looked to see it working well or not.

Meanwhile, I've changed the way the combination is made to a "just in time" algorithm, because coffee and waiting on my beach buddy, she who must never be kept waiting. Irony defined.

This

static unsigned char doleFrom[PSIZE];
static unsigned char nLeft;

void resetComboMachine()
{
  nLeft = PSIZE;

  for (unsigned char ii = 0; ii < PSIZE; ii++) {
    combo[ii] = 0xff;
    doleFrom[ii] = ii;
  }
}

unsigned char doleNext()
{
  unsigned char next;
  unsigned char rx;

  if (nLeft == 0) errorStop("no keys left to dole");

  rx = random(0, nLeft);
  next = doleFrom[rx];
  nLeft--;
  doleFrom[rx] = doleFrom[nLeft];

  return next;
}

lets us not even have the next piece of the combination until it is needed. Only when you have entered N digits correctly and are trying for N + 1 is that number placed into the combination.

See it in context here.


I did some testing, can't give it a stamp of approval just now, but it seems to work.

The next level of fiendish puzzle making is to never let the next digit guessed be correct. An exercise for the reader.

a7

Now, to clarify, when I say random,I still only want to use each switch only once leaving just 120 different combinations, that could be easy to manage. If one introduces multiple use of the switches, it could get really off track.

The general idea is to have this as a waiting room puzzle. Add a countdown timer and a counter to determine how many puzzles solved in an amount of time. Also add some better feedback to show how on track one is. This could even be moved to a Simon game as an offshoot. Going to fit it into a small nanuk 910 case and theme it.

This is a test of mental strength. How systematically you can work through an unknown pattern and how many you can successfully do. Possibly adding a "wrong" counter as well.

This is similar to the "crack the code" laser cut safe with 8 LEDs and a four digit combo. I'll try to find the link.

Here is the link to the YouTube video. I am in the process of laser cutting the items to assemble this. It looks quite fun.

Nothing anyone has suggested changes that behaviour.

We've been trying to address what we thought was your issue, that the combinations offered were the same every time your restart the puzzle.

No, that's different kind of puzzle, now popular with words and letters in wordle and quordle.

bulls and cows
bagels and donuts
Mastermind

a7

Forgive me, it sounded like it was going along the lines of constantly calling up the next "digit" or "switch" meaning it could be used twice. My mistake.

It WOULD be similar to a wordle game though, in all actuality. More or less just a battery powered pass along game that can be handed off to multiple people.

So, yes, random is still the goal.

With 120 combinations, you only need to consume about log(120)/log(2)=7 bits of randomness per run,

The plain AVR random() function has a cycle length of 2^31, so it would be fine for millions of consecutive runs without falling into the same cycle. As long as you can make sure you don't start with at the same default, it will be fine.

@alto77's code defers selection until the user hits a button, so it's stepping along the 2^31 cycle well enough to cover the 7 bits of randomess the problem needs. However, if you knew the timing to 6.25ns, or could count loops(), you'd could then know how many times random() had been called and you could know the next value. A motivated hacker could program a machine to push the buttons at the right times to win with the code 0,1,2,3,4,5,6 every time.

I'll work through the code when I'm back in the office on Monday. For now, some down and dirty prototype pictures for your enjoyment.


So, Just by adding Random ()

void loop() {
  unsigned char pKey = scanKeys();

    random();

in the program, it does randomize the codes being required, but it only does it after the first code. The first one is always the same though. weird, and OK as it does lull a player into a sense of false security I guess, then panic sets in when they memorize the second and third and so on thinking they are good and then failure.

// from learnSequencePuzzle.ino

// https://forum.arduino.cc/t/toggle-switch-puzzle-but-sequential/1091460
// https://wokwi.com/projects/357012577248058369

# define SEVEN   5    // puzzle size
# include <SPI.h>

# include <Adafruit_NeoPixel.h>

# define LED_PIN    A2
# define LED_COUNT  SEVEN
# define LED_PIN2    A5
# define LED_COUNT2  45

Adafruit_NeoPixel disaply(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip(LED_COUNT2, LED_PIN2, NEO_GRB + NEO_KHZ800);

unsigned char combo[SEVEN];
unsigned char lastSwitch[SEVEN];
enum Runstate {START, RUNNING, FAULT, SUCCESS} runState;

# define redLED   A1    // bad
# define greenLED A0    // good

# define NOKEY  99      // impossible key value as a flag

const byte inputPin[SEVEN] = {9, 10, 11, 12, 13};
// This pin will be driven LOW to release a lock when puzzle is solved
const byte lockPin = A3;
const byte lockPin2 = A4;

void setup() {
  Serial.begin(115200); Serial.println("HE LL O!\n");

  for (unsigned char ii = 0; ii < SEVEN; ii++) {
    pinMode(inputPin[ii], INPUT_PULLUP);

    lastSwitch[ii] = digitalRead(inputPin[ii]);
  }

  pinMode(greenLED, OUTPUT);
  pinMode(redLED, OUTPUT);
      // Set the lock pin as output and secure the lock
  pinMode(lockPin, OUTPUT);
  digitalWrite(lockPin, HIGH);
  pinMode(lockPin2, OUTPUT);
  digitalWrite(lockPin2, LOW);

  disaply.begin();
  disaply.show();
  strip.clear();
  strip.begin();


  reset();
  runState = RUNNING;
  newCombo0();
}

unsigned char nIn = 0;    // number of correct steps taken sofa

void loop() {
  unsigned char pKey = scanKeys();

    random();

  switch (runState) {
    case RUNNING:
      if (pKey == NOKEY)
        return;

      // presst a key, all key are valid so

      if (pKey == combo[nIn]) {
        nIn++;
        displayDisplay();
        goodFlash();
        if (nIn == SEVEN) {
          Serial.println("You are in!");
          runState = SUCCESS;
          // Release the lock
          digitalWrite(lockPin, LOW);
          theaterChase(strip.Color(255, 255, 255), 100, 3, 5); // White, full brightness          
        }
      }
      else {
    Serial.print("             BZZZT!");
        runState = FAULT;
        // Sound the Buzzer
        theaterChase(strip.Color(255, 0, 0), 100, 3, 5); // RED CHASE         
        digitalWrite(lockPin2, HIGH);  
        delay (500);
        digitalWrite(lockPin2, LOW);                  
      }
      break;
    case FAULT:
      badFlash();
      if (keysum() == 0) {
        reset();
        displayDisplay();
        runState = START;
      }
      break;
    case SUCCESS:
      reward();
      if (pKey != NOKEY) {
        newCombo0();
        reset();
        displayDisplay();
        runState = FAULT;
      }
      break;
    case START:
      runState = RUNNING;
      break;
    default:
      runState = START;
      break;
  }

}

void goodFlash()
{
  digitalWrite(A1, HIGH);
  delay(100);
  digitalWrite(A1, LOW);

  displayDisplay();
}

void badFlash()
{
  static uint32_t last = 0;
  const int interval = 50;
  if (millis() - last >= interval) {
    last = millis();
    digitalWrite(A0, !digitalRead(A0));
    displayDisplay();
    digitalWrite(A0, LOW);
  }
}

void reward()
{
  static uint32_t last = 0;
  const int interval = 600;
  static bool off = true;
  if (millis() - last < interval) return;
  last = millis();
  if (off) { // turn on
    nIn = SEVEN; displayDisplay();
    digitalWrite(A1, HIGH);
  } else {
    digitalWrite(A1, LOW);
    nIn = 0; displayDisplay();
    off = true;
  }
}

void reset()
{
  nIn = 0;
  digitalWrite(A1, LOW);
}

void displayDisplay()
{
  for (unsigned char tt = 0; tt < SEVEN; tt++) {
    //    disaply.setPixelColor(tt, tt >= nIn ? 0x001010 : 0x00ff00);
    if (runState != FAULT) {
      disaply.setPixelColor(combo[tt], tt >= nIn ? 0x000000 : 0x00ff00);
      strip.fill(tt >= nIn ? 0x000000 : 0x00ff00, combo[tt] *9, 9);
    } else {
      disaply.setPixelColor(tt, lastSwitch[tt] ? 0xff0000 : 0x000000);
      strip.fill(lastSwitch[tt] ? 0xff0000 : 0x000000, tt *9, 9);
    }
  }

  disaply.show();
  strip.show();
}

// scanKeysO looks for a button going pressed
// scanKeys just looks for a change of state

unsigned char scanKeys()
{
  // printf("scan keys ");

  unsigned char newKeyQ = NOKEY;

  static unsigned long lastTime;

  unsigned char currentKey = NOKEY;
  unsigned long now = millis();

  if (now - lastTime < 40)      // too soon to look at anything
    return currentKey;
  lastTime = now;
  for (unsigned char ii = 0; ii < SEVEN; ii++) {
    unsigned char aSwitch = digitalRead(inputPin[ii]);
    if (aSwitch != lastSwitch[ii]) {
      currentKey = ii;
      lastSwitch[ii] = aSwitch;
    }
  }

  return currentKey;
}

int keysum() {
  int retval = 0;
  for (unsigned char ii = 0 ; ii < SEVEN; ii++) {
    retval += lastSwitch[ii];
  }
  return retval;
}

unsigned char scanKeys0()
{
  // printf("scan keys ");
  unsigned char newKeyQ = NOKEY;

  static unsigned long lastTime;
  static unsigned char wasPressed = 0;

  char isPressed = 0;
  unsigned char currentKey = NOKEY;
  unsigned long now = millis();

  if (now - lastTime < 40)
    return currentKey;

  for (unsigned char ii = 0; ii < SEVEN; ii++) {
    if (!digitalRead(inputPin[ii])) {
      newKeyQ = ii;
      isPressed = 1;
    }
  }

  if (isPressed != wasPressed) {
    lastTime = now;
    if (isPressed)
      currentKey = newKeyQ;
    wasPressed = isPressed;
  }

  return currentKey;
}



void newCombo0()
{
  for (unsigned char ii = 0; ii < SEVEN; ii++)
    combo[ii] = ii;

  for (int ii = 0; ii < 5000; ii++) {
    unsigned char tt = random(SEVEN);
    unsigned char ss = random(SEVEN);
    unsigned char temp;

    temp = combo[tt];
    combo[tt] = combo[ss];
    combo[ss] = temp;
  }

  // return;   // if you do not want to print the combination!

  for (unsigned char ii = 0; ii < SEVEN; ii++) {
    Serial.print(combo[ii]);
    Serial.print("  ");
  }

  Serial.println();
}

/*
* Fills a strip with a specific color, starting at 0 and continuing
* until the entire strip is filled. Takes two arguments:
* 
*     1. the color to use in the fill
*     2. the amount of time to wait after writing each LED
*/
void colorWipe(uint32_t color, unsigned long wait) {
	for (unsigned int i = 0; i < strip.numPixels(); i++) {
		strip.setPixelColor(i, color);
		strip.show();
		delay(wait);
	}
}

/*
* Runs a marquee style "chase" sequence. Takes three arguments:
*
*     1. the color to use in the chase
*     2. the amount of time to wait between frames
*     3. the number of LEDs in each 'chase' group
*     3. the number of chases sequences to perform
*/
void theaterChase(uint32_t color, unsigned long wait, unsigned int groupSize, unsigned int numChases) {
	for (unsigned int chase = 0; chase < numChases; chase++) {
		for (unsigned int pos = 0; pos < groupSize; pos++) {
			strip.clear();  // turn off all LEDs
			for (unsigned int i = pos; i < strip.numPixels(); i += groupSize) {
				strip.setPixelColor(i, color);  // turn on the current group
			}
			strip.show();
			delay(wait);
		}
	}
}

/*
* Simple rainbow animation, iterating through all 8-bit hues. LED color changes
* based on position in the strip. Takes two arguments:
* 
*     1. the amount of time to wait between frames
*     2. the number of rainbows to loop through
*/
void rainbow(unsigned long wait, unsigned int numLoops) {
	for (unsigned int count = 0; count < numLoops; count++) {
		// iterate through all 8-bit hues, using 16-bit values for granularity
		for (unsigned long firstPixelHue = 0; firstPixelHue < 65536; firstPixelHue += 256) {
			for (unsigned int i = 0; i < strip.numPixels(); i++) {
				unsigned long pixelHue = firstPixelHue + (i * 65536UL / strip.numPixels()); // vary LED hue based on position
				strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));  // assign color, using gamma curve for a more natural look
			}
			strip.show();
			delay(wait);
		}
	}  
}

If you weren't using all your analog pins, you could use a floating analog input to inject some randomness before the first code with something like this:

void setup(){
    ...
    randomSeed(analogRead(A5)); // inject floating pin noise into RNG
    ...
}

Not weird. 100 percent predictable. I knew it would play out exactly as it has.

a7

And I don't like kludges, no matter how you spell kluge, but a first-time flag can fix you up as well:

void loop() {
  static unsigned char firstCombo = true;

  unsigned char pKey = scanKeys();

  random();  // spin the RNG

  switch (runState) {
    case RUNNING:
      if (pKey == NOKEY)
        return;

      // now that random has run ahead a bit

      if (firstCombo) {
        newCombo0();
        firstCombo = false;
      }

      // presst a key, all key are valid so

      if (pKey == combo[nIn]) {...
 

Just holds off forming the first combination until the loop has run for awhile. Everything else operates as it always did. The kluge is only across a few lines of code here, so can be trusted.

Yes, @DaveX - if I time the very first button press with precision, I will be able to game this. :expressionless:

That's the trouble with algorithmic approches. I have just spent time looking at true random number generators, lotsa nice ideas like using radioactivity or noise from transistor junctions...

Sry, tried to test it but I can yet make no sense of the new game. Confidence is high, however. :wink:

a7

@projectbox24 !

Edit the diagram.json file so you only have 5 pixels

    {
      "type": "wokwi-neopixel-matrix",
      "id": "neopixels1",
      "top": -286.99,
      "left": 10.37,
      "rotate": 180,
      "attrs": { "pixleate": "1", "rows": "1", "cols": "5" }
    },

Edit the *.ino file to reflect the proper link to the wokwi

 https://wokwi.com/projects/358460632962894849

If you make a new wokwi, keep that comment up to date.

Use the global search and replace facility in the wokwi to fix my stupid SEVEN, maybe replace it with PSIZE for "puzzle size"

I tried with the switches preset. I did see some green sectors I think, and some red sectors and red LEDs. But I tired of guessing - I never did solve it. If that's your aim, you have made a challenging puzzle!

And please explain what we're supposed to be seeing with the slide switches replacing the pushbuttons. I just don't get how you are expecting them to be manipulated.

TIA

a7