01 March 2026

Scaffolding: Mapping - Hide and Seek

 Our game is an interactive hide-and-seek experience built using the Circuit Playground Express, NeoPixels, a potentiometer, a direction switch, a freeze button, and an external photoresistor. The NeoPixel ring represents a circular playing field with ten positions. At the start of each round, the system randomly selects a hidden target position that the player must find.

The player continuously moves around the circle using a direction switch, which determines clockwise or counterclockwise movement. A potentiometer controls movement speed, allowing for adjustable difficulty. When the player believes they are close to the hidden target, they press the freeze button to stop moving. To check their position, they cover the photoresistor, triggering a “hot or cold” color response. The NeoPixel changes color based on how close the player is to the target, ranging from blue (far away) to red (very close). 

The goal is to land exactly on the hidden target. If successful, the player wins, and the NeoPixel turns green before a new round begins.


#include <Adafruit_CircuitPlayground.h>

const int NUM_PIXELS = 10;
const int FREEZE_BUTTON_PIN = A3;
const int PHOTO_PIN = A2;
const int PHOTO_THRESHOLD = 40;

//game state
int playerPosition = 0;
int targetPosition = 0;
bool frozen = false;

//timing
unsigned long lastMoveTime = 0;
int moveDelay = 300;

//button states (edge detection)
bool lastD4 = HIGH;

// Light detection
bool photoTriggered = false;

void drawBackground() {
  for (int i = 0; i < NUM_PIXELS; i++) {
    CircuitPlayground.setPixelColor(i, 25, 25, 25); //dim white, was very bright for me
  }
}

void drawPlayer(uint8_t r, uint8_t g, uint8_t b) {
  CircuitPlayground.setPixelColor(playerPosition, r, g, b);
}

void resetGame() {
  drawBackground();
  playerPosition = 0;
  targetPosition = random(NUM_PIXELS);
  frozen = false;
}

int circularDistance(int a, int b) {
  int d = abs(a - b);
  return min(d, NUM_PIXELS - d);
}

void setup() {
  Serial.begin(9600);

  CircuitPlayground.begin();
  CircuitPlayground.setBrightness(20);

  pinMode(FREEZE_BUTTON_PIN, INPUT_PULLUP);
  pinMode(A4, INPUT_PULLUP);

  randomSeed(analogRead(A0));
  resetGame();
}

void loop() {
  Serial.println(digitalRead(FREEZE_BUTTON_PIN));
  delay(100);
  unsigned long now = millis();

  int potValue = analogRead(A1);

  //map pot to speed (slow → fast)
  moveDelay = map(potValue, 0, 1023, 800, 80);

  int cw  = (targetPosition - playerPosition + 10) % 10;
  int ccw = (playerPosition - targetPosition + 10) % 10;
  int distance = min(cw, ccw);

  // --------- BUTTON (freeze) ----------
  bool freezeButton = digitalRead(FREEZE_BUTTON_PIN);

  if (freezeButton == LOW && lastD4 == HIGH) {
    frozen = !frozen;
    photoTriggered = false;
  }

  lastD4 = freezeButton;
  //press button again to unfreeze player, cover photoresistor while frozen to check your position relative to the target

  // --------- PLAYER MOVEMENT ----------
  if (!frozen && now - lastMoveTime > moveDelay) {
  lastMoveTime = now;

  drawBackground();

    bool directionCW = (digitalRead(A4) == HIGH);

    if (directionCW) {
      //clockwise
      playerPosition = (playerPosition + 1) % NUM_PIXELS;
    } else {
      //counterclockwise
      playerPosition = (playerPosition - 1 + NUM_PIXELS) % NUM_PIXELS;
    }
  }

  bool directionCW = (digitalRead(A4) == HIGH);

  // --------- PHOTORESISTOR (check position) ----------
  int lightValue = analogRead(PHOTO_PIN);

  //debug to check photoresistor value needed
  Serial.print("Light: ");
  Serial.println(lightValue);

  //trigger once when light drops below threshold
  if (frozen && lightValue < PHOTO_THRESHOLD && !photoTriggered) {
  photoTriggered = true;

    drawBackground();

  if (distance == 0) {
    //exact hit = win, show green
    CircuitPlayground.setPixelColor(playerPosition, 0, 255, 0);
    delay(3000);

    //new round
    targetPosition = random(NUM_PIXELS);
    frozen = false;
    drawBackground();
    return;

  } else {
  int r = 0;
  int g = 0;
  int b = 0;

  switch (distance) {
  case 1:
    r = 255; g = 0; b = 0;   // very hot
    break;
  case 2:
    r = 200; g = 40; b = 0;   // warm
    break;
  case 3:
    r = 80; g = 0; b = 120;   // cool
    break;
  case 4:
    r = 0; g = 120; b = 200;   // colder
    break;
  case 5:
  default:
    r = 0; g = 0; b = 255;   // coldest
    break;
}

CircuitPlayground.setPixelColor(playerPosition, r, g, b);
  }

      delay(3000);
      drawBackground();
    }
  //reset trigger when light comes back
  if (lightValue > PHOTO_THRESHOLD + 50) {
  photoTriggered = false;
  }
  // --------- DRAW PLAYER ----------
  drawPlayer(255, 0, 255); //purple
}




No comments:

Post a Comment

Note: Only a member of this blog may post a comment.