모두 아시는 고전 '사이먼 세이즈' 게임입니다. 불이 켜지는 순서를 보고 해당 버튼을 눌러 따라하는 게임이죠. 해크스터 라이브에서 '업버터(Upverter)'라는 PCB 설계 툴을 시연했는데, 이 프로젝트는 PCB로 전환하기에 재미있고 간단한 예제입니다. 아래는 하드웨어와 코드를 포함한 기본 게임 제작 과정입니다.
스파크펀의 인벤터 키트 예제를 참고해 약간 수정했습니다. 아래 프리칭 다이어그램을 확인하세요.
코드
이 게임의 재미는 다양한 변형이 가능하다는 점입니다. 위에서 언급했듯 스파크펀 예제를 하드웨어 기초로 삼고 코드는 직접 작성했습니다. 직접 코딩이 부담스럽다면 아래 제 코드를 쓰거나, 스파크펀 예제 코드를 약간 수정해 쓰거나, 하드웨어 설정과 호환되는 다른 사이먼 세이즈 코드를 찾아보셔도 됩니다.
코드에 상세 주석과 디버깅용 시리얼 프린트를 넣어 게임 실행 중 시리얼 모니터에서 상황을 확인할 수 있습니다. 아래 영상에서 실제 작동 모습을 확인하세요!
사이먼 세이즈 게임 영상
참고: 영상에서는 연속 5회 성공 시 승리하도록 설정했습니다. 난이도 조절을 위해 이 숫자를 변경할 수 있지만, 영상 길이를 위해 5로 설정했습니다.
이제 브레드보드를 PCB로 전환할 준비가 됐습니다! 브레드보드에서 PCB로 2부 - 업버터로 PCB 설계하기에서 방법을 배워보세요.
코드
사이먼 세이즈 아두이노 코드 (Arduino)
int ledArray[] = {12, 9, 5, 2}; // ledArray[0]=녹색, [1]=빨강, [2]=노랑, [3]=파랑
int buttonArray[] = {13, 10, 6, 3}; // buttonArray[0]=녹색, [1]=빨강, [2]=노랑, [3]=파랑
int pinCount = 4;
int buzzer = 8;
int ledState = 0;
int gameState = 0;
#define NUMBERTOWIN 5 // 승리 조건: 연속 성공 횟수 (변경 가능)
int gameValues[NUMBERTOWIN];
int roundNum = 0;
unsigned long previousMillis = 0;
unsigned long interval1 = 500;
void setLed(int ledNum);
void displayClue(int roundNum);
boolean waitForInput(int buttonNum);
void setup() {
Serial.begin(9600);
Serial.println("setup");
for (int Pin = 0; Pin < pinCount; Pin++) {
pinMode(ledArray[Pin], OUTPUT);
}
for (int Pin = 0; Pin < pinCount; Pin++) {
pinMode(buttonArray[Pin], INPUT_PULLUP); // 내부 풀업 사용 (코드 작성 후 10k 외부 저항 추가함, 코드 수정 불필요)
}
randomSeed(analogRead(0)); // 아날로그 노이즈로 랜덤 시드 생성
}
void loop() {
if (gameState == 0) {
int button0 = digitalRead(buttonArray[0]);
int button1 = digitalRead(buttonArray[1]);
int button2 = digitalRead(buttonArray[2]);
int button3 = digitalRead(buttonArray[3]);
// 버튼 미눌림 시(풀업이므로 모두 HIGH) 프리게임 모드
if (button0 && button1 && button2 && button3) {
pre_game();
} else { // 버튼 눌림 시 게임 시작
Serial.println("button Pressed");
setLed(-1); // 모든 LED 끄기
roundNum = 0;
delay(1000);
gameState = 1;
}
}
if (gameState == 1) gamePlay();
if (gameState == 2) { // 승리
delay(500);
winning_sound();
winDisplay(); winDisplay(); winDisplay();
loseDisplay();
Serial.println("winDisplay");
delay(1000);
gameState = 0;
}
if (gameState == 3) { // 패배
delay(500);
losing_sound();
loseDisplay(); loseDisplay(); loseDisplay();
Serial.println("loseDisplay");
delay(1000);
gameState = 0;
}
}
void pre_game() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis > interval1) {
if (ledState == 0) { Serial.println("pre green"); setLed(0); ledState = 1; }
else if (ledState == 1) { Serial.println("pre red"); setLed(1); ledState = 2; }
else if (ledState == 2) { Serial.println("pre yellow"); setLed(2); ledState = 3; }
else if (ledState == 3) { Serial.println("pre blue"); setLed(3); ledState = 0; }
previousMillis = currentMillis;
}
}
void gamePlay() {
Serial.println("gamePlay");
for (int i = 0; i < NUMBERTOWIN; i++) {
gameValues[i] = floor(random(0, 4));
Serial.println(gameValues[i], DEC);
}
Serial.print("Starting Round "); Serial.println(roundNum, DEC);
for (int r = 0; r < NUMBERTOWIN; r++) {
displayClue(roundNum);
for (int i = 0; i <= roundNum; i++) {
Serial.print("Waiting for input "); Serial.print(i+1, DEC);
Serial.print(" of "); Serial.print(roundNum+1, DEC); Serial.println("");
if (waitForInput(gameValues[i])) {
if (i == roundNum) {
Serial.println("Correct");
if (roundNum == NUMBERTOWIN - 1) {
Serial.println("You Win!");
gameState = 2;
return;
} else {
Serial.println("Continue");
}
}
} else {
Serial.println("You Lose!");
gameState = 3;
return;
}
}
roundNum++;
delay(1000);
}
}
void setLed(int ledNum) {
if (ledNum < 0) {
for (int i = 0; i < pinCount; i++) digitalWrite(ledArray[i], LOW);
} else {
for (int i = 0; i < pinCount; i++) {
digitalWrite(ledArray[i], (i == ledNum) ? HIGH : LOW);
}
}
}
void displayClue(int roundNum) {
setLed(-1);
for (int i = 0; i <= roundNum; i++) {
setLed(gameValues[i]);
delay(750);
setLed(-1);
delay(250);
}
}
boolean waitForInput(int buttonNum) {
while (true) {
int button0 = digitalRead(buttonArray[0]);
int button1 = digitalRead(buttonArray[1]);
int button2 = digitalRead(buttonArray[2]);
int button3 = digitalRead(buttonArray[3]);
if (!button0 || !button1 || !button2 || !button3) { // 버튼 눌림 감지 (풀업이므로 LOW)
delay(250); // 디바운싱
if (buttonNum == 0) { digitalWrite(ledArray[0], HIGH); delay(250); digitalWrite(ledArray[0], LOW); }
else if (buttonNum == 1) { digitalWrite(ledArray[1], HIGH); delay(250); digitalWrite(ledArray[1], LOW); }
else if (buttonNum == 2) { digitalWrite(ledArray[2], HIGH); delay(250); digitalWrite(ledArray[2], LOW); }
else if (buttonNum == 3) { digitalWrite(ledArray[3], HIGH); delay(250); digitalWrite(ledArray[3], LOW); }
if (buttonNum == 0 && button0 == 0) { Serial.println("0 Pressed"); return true; }
else if (buttonNum == 1 && button1 == 0) { Serial.println("1 Pressed"); return true; }
else if (buttonNum == 2 && button2 == 0) { Serial.println("2 Pressed"); return true; }
else if (buttonNum == 3 && button3 == 0) { Serial.println("3 Pressed"); return true; }
else { return false; }
}
}
}
void winDisplay() {
digitalWrite(ledArray[0], HIGH); delay(75); digitalWrite(ledArray[0], LOW); delay(75);
digitalWrite(ledArray[1], HIGH); delay(75); digitalWrite(ledArray[1], LOW); delay(75);
digitalWrite(ledArray[2], HIGH); delay(75); digitalWrite(ledArray[2], LOW); delay(75);
digitalWrite(ledArray[3], HIGH); delay(75); digitalWrite(ledArray[3], LOW); delay(75);
digitalWrite(ledArray[2], HIGH); delay(75); digitalWrite(ledArray[2], LOW); delay(75);
digitalWrite(ledArray[1], HIGH); delay(75); digitalWrite(ledArray[1], LOW); delay(75);
digitalWrite(ledArray[0], HIGH); delay(75); digitalWrite(ledArray[0], LOW); delay(75);
}
void winning_sound() {
Serial.println("buzzer1");
tone(buzzer, 294, 250); delay(200);
tone(buzzer, 294, 250); delay(200);
tone(buzzer, 294, 250); delay(200);
tone(buzzer, 392, 500); delay(500);
Serial.println("second 392");
tone(buzzer, 392, 250); delay(200);
tone(buzzer, 440, 250); delay(200);
tone(buzzer, 392, 250); delay(200);
tone(buzzer, 440, 250); delay(200);
tone(buzzer, 494, 500);
}
void loseDisplay() {
digitalWrite(ledArray[0], HIGH); digitalWrite(ledArray[1], HIGH);
digitalWrite(ledArray[2], HIGH); digitalWrite(ledArray[3], HIGH);
delay(200);
digitalWrite(ledArray[0], LOW); digitalWrite(ledArray[1], LOW);
digitalWrite(ledArray[2], LOW); digitalWrite(ledArray[3], LOW);
delay(200);
}
void losing_sound() {
Serial.println("losing_sound");
tone(buzzer, 98, 250); delay(250);
tone(buzzer, 93, 250); delay(250);
tone(buzzer, 87, 250); delay(250);
}