I got this thing to work, and am very pleased!
Pictures are here.
It's basically a Simple Simon game. The LEDs blink in random order, then the player has to press the buttons in the same order. If you get it right, the green light blinks (and in future, the game gets harder). If wrong, the red light blinks (and game gets easier).
The hardware was straightforward - just a bunch of LEDs and a row of switches salvaged from a busted answering machine in the junk pile. (Potential pitfall of stuff from the junk pile - it was thrown away for a reason, maybe stuff won't work.) I wrote a simple program to test the switches by lighting the corresponding LED.
Hardest part was getting to grips with C syntax, which is new to me. My structured programming techniques from 20 years ago worked well. In fact, I think structured programming techniques apply to hardware design as well. I tried to be tidy in order to keep things organized and easy to manage.
It still has a couple of bugs to be worked out - however, I am thinking of leaving it as-is. Why? Because the stories people create to account for the game's malfunction are far more interesting than the reactions when it works correctly. When it works fine, people can play for a bit, see how it works, and then get tired and walk away. They don't need to really engage. Howerver, a sporadic malfunction is a puzzle: the players have to think hard about what might account for a strange behavior, and this is a much richer interaction than simply playing the game.
I still need to:
* implement debouncing code for the switches, which are sometimes giving double responses
* define constants for the number of LEDs and switches - currently hard-coded in two loops
* implement makeHarder and makeEasier routines - I will work with the amount of error and the time taken to respond
* implement a time-out routine when waiting for input
* implement a blink routine when waiting for input
* make a fancier song & dance for success
Keep reading to see the complete source code for the project.
// Simple Simon game for Physical Computing
//
// Gian Pablo Villamil (gian.pablo@gmail.com)
// September 15, 2006
//
// Arduino blinks LEDs in a randomly chosen sequence. Player must press
// matching switches in same sequence.
//
// If correct, green LED lights and game is made harder (shorter delay, longer sequence)
// If wrong, red LED lights and game is made easier (longer delay)
//
// Required parts
// Game sequence - red, green, blue, yellow LEDs
// Scoring - red, green LED
// Input - four momentary switches
// Define the pin assignments for the output LEDs
#define GameLEDStartPin 2
#define GameLEDEndPin 5
#define LoseLEDPin 6
#define WinLEDPin 7
// Define the pin assignments for the answer switches
#define AnsSwitchStartPin 8
#define AnsSwitchEndPin 11
// Define the arrays to hold the "challenge" sequence and
// the user response.
//
// In the GameArray and AnswerArray, the values are:
// 0 = yellow
// 1 = blue
// 2 = green
// 3 = red
int maxGameLength = 10;
int GameArray[10];
int AnswerArray[10];
// Define starting game state
int curGameLength = 4; // four blinks
int curGameDelay = 1000; // 1 second
// Define turn score and total score
int turnScore = 0 ;
int totalScore = 0 ;
int rightAnswer ;
void setup () {
pinMode(LoseLEDPin, OUTPUT);
pinMode(WinLEDPin, OUTPUT);
for (int i = GameLEDStartPin; i <= GameLEDEndPin; i++) {
pinMode(i, OUTPUT);
}
for (int i = AnsSwitchStartPin; i <= AnsSwitchEndPin; i++) {
pinMode(i, INPUT);
}
}
// Main loop - should be easy to follow
void loop () {
randomizeGame () ;
showGame () ;
getAnswer () ;
checkAnswer () ;
if (rightAnswer) {
Success();
MakeHarder();
}
else
{
Failure();
MakeEasier();
}
}
// Fill the game array with random values between 0 and 3
void randomizeGame () {
for (int i = 1; i <= curGameLength; i++) {
GameArray[i] = random(0,4); // what's up with this? 0 to 3 returns max of 2
}
}
// Blink the LEDs according to the values in the game array
void showGame () {
for (int i = 1; i <= curGameLength; i++) {
digitalWrite(GameArray[i]+GameLEDStartPin, HIGH);
delay(curGameDelay); // long pause to show game
digitalWrite(GameArray[i]+GameLEDStartPin, LOW);
delay(curGameDelay/2); // short pause to ensure separation is clear
}
}
// Get the user's answer and store it in the answer array
// Must check debounce routine
void getAnswer () {
int finishedAnswer = false ;
for (int i = 1; i <= curGameLength; i++) {
finishedAnswer = false;
while (!finishedAnswer ) {
for (int j = 0; j <= 3; j++) { // poll the switches
if (digitalRead(AnsSwitchStartPin+j)) {
while (digitalRead(AnsSwitchStartPin+j)) {} // wait for switch to be released
AnswerArray[i] = j;
finishedAnswer = true;
}
}
}
}
}
// Compare each element of the game and answer arrays
// If they match, increase the turn score
// If the turnscore equals length of the game, then it's right
void checkAnswer () {
turnScore = 0;
rightAnswer = false;
for (int i = 1; i<= curGameLength; i++) {
if (GameArray[i] == AnswerArray[i]) {
turnScore = turnScore + 1;
}
}
if (turnScore == curGameLength) {rightAnswer = true;}
}
// Reward the user - flash the green LED
void Success () {
digitalWrite(WinLEDPin, HIGH);
delay(1000);
digitalWrite(WinLEDPin, LOW);
totalScore = totalScore + 1;
}
// Punish the user - flash the red LED
void Failure () {
digitalWrite(LoseLEDPin, HIGH);
delay(1000);
digitalWrite(LoseLEDPin, LOW);
}
// Make the game harder - reduce delay and/or increase length
void MakeHarder () {
}
// Make the game easier - increase delay and/or decrease length
// Maybe make it smart by looking at turnscore/gamelength
void MakeEasier () {
}