-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcodes.ino
407 lines (304 loc) · 11 KB
/
codes.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
#define CHOICE_OFF 0 //Used to control LEDs
#define CHOICE_NONE 0 //Used to check buttons
#define CHOICE_RED (1 << 0)
#define CHOICE_GREEN (1 << 1)
#define CHOICE_BLUE (1 << 2)
#define CHOICE_YELLOW (1 << 3)
#define LED_RED 3//d3 // represented by the orange wiring
#define LED_GREEN 5//d5 // represented by the green wiring
#define LED_BLUE 7//d7 // represented by the blue wiring
#define LED_YELLOW 15//d15 // represented by the orange wiring
// Button pin definitions
#define BUTTON_RED 2 //d2
#define BUTTON_GREEN 4//d4
#define BUTTON_BLUE 6//d6
#define BUTTON_YELLOW 14//d14
// Define game parameters
#define ROUNDS_TO_WIN 13 //Number of rounds to succesfully remember before you win. 13 is do-able.
#define ENTRY_TIME_LIMIT 3000 //Amount of time to press a button before game times out. 3000ms = 3 sec
#define MODE_MEMORY 0
#define MODE_BATTLE 1
#define MODE_BEEGEES 2
// Game state variables
byte gameMode = MODE_MEMORY; //By default, let's play the memory game
byte gameBoard[32]; //Contains the combination of buttons as we advance
byte gameRound = 0; //Counts the number of succesful rounds the player has made it through
void setup()
{
//Setup hardware inputs/outputs. These pins are defined in the hardware_versions header file
//Enable pull ups on inputs
pinMode(BUTTON_RED, INPUT_PULLUP);
pinMode(BUTTON_GREEN, INPUT_PULLUP);
pinMode(BUTTON_BLUE, INPUT_PULLUP);
pinMode(BUTTON_YELLOW, INPUT_PULLUP);
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
//Mode checking
gameMode = MODE_MEMORY; // By default, we're going to play the memory game
// Check to see if the lower right button is pressed
if (checkButton() == CHOICE_YELLOW) play_beegees();
// Check to see if upper right button is pressed
if (checkButton() == CHOICE_GREEN)
{
gameMode = MODE_BATTLE; //Put game into battle mode
//Turn on the upper right (green) LED
setLEDs(CHOICE_GREEN);
toner(CHOICE_GREEN, 150);
setLEDs(CHOICE_RED | CHOICE_BLUE | CHOICE_YELLOW); // Turn on the other LEDs until you release button
while(checkButton() != CHOICE_NONE) ; // Wait for user to stop pressing button
//Now do nothing. Battle mode will be serviced in the main routine
}
play_winner(); // After setup is complete, say hello to the world
}
void loop()
{
attractMode(); // Blink lights while waiting for user to press a button
// Indicate the start of game play
setLEDs(CHOICE_RED | CHOICE_GREEN | CHOICE_BLUE | CHOICE_YELLOW); // Turn all LEDs on
delay(1000);
setLEDs(CHOICE_OFF); // Turn off LEDs
delay(250);
if (gameMode == MODE_MEMORY)
{
// Play memory game and handle result
if (play_memory() == true)
play_winner(); // Player won, play winner tones
else
play_loser(); // Player lost, play loser tones
}
if (gameMode == MODE_BATTLE)
{
play_battle(); // Play game until someone loses
play_loser(); // Player lost, play loser tones
}
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//The following functions are related to game play only
// Play the regular memory game
// Returns 0 if player loses, or 1 if player wins
boolean play_memory(void)
{
randomSeed(millis()); // Seed the random generator with random amount of millis()
gameRound = 0; // Reset the game to the beginning
while (gameRound < ROUNDS_TO_WIN)
{
add_to_moves(); // Add a button to the current moves, then play them back
playMoves(); // Play back the current game board
// Then require the player to repeat the sequence.
for (byte currentMove = 0 ; currentMove < gameRound ; currentMove++)
{
byte choice = wait_for_button(); // See what button the user presses
if (choice == 0) return false; // If wait timed out, player loses
if (choice != gameBoard[currentMove]) return false; // If the choice is incorect, player loses
}
delay(1000); // Player was correct, delay before playing moves
}
return true; // Player made it through all the rounds to win!
}
// Play the special 2 player battle mode
// A player begins by pressing a button then handing it to the other player
// That player repeats the button and adds one, then passes back.
// This function returns when someone loses
boolean play_battle(void)
{
gameRound = 0; // Reset the game frame back to one frame
while (1) // Loop until someone fails
{
byte newButton = wait_for_button(); // Wait for user to input next move
gameBoard[gameRound++] = newButton; // Add this new button to the game array
// Then require the player to repeat the sequence.
for (byte currentMove = 0 ; currentMove < gameRound ; currentMove++)
{
byte choice = wait_for_button();
if (choice == 0) return false; // If wait timed out, player loses.
if (choice != gameBoard[currentMove]) return false; // If the choice is incorect, player loses.
}
delay(100); // Give the user an extra 100ms to hand the game to the other player
}
return true; // We should never get here
}
// Plays the current contents of the game moves
void playMoves(void)
{
for (byte currentMove = 0 ; currentMove < gameRound ; currentMove++)
{
toner(gameBoard[currentMove], 150);
// Wait some amount of time between button playback
// Shorten this to make game harder
delay(150); // 150 works well. 75 gets fast.
}
}
// Adds a new random button to the game sequence, by sampling the timer
void add_to_moves(void)
{
byte newButton = random(0, 4); //min (included), max (exluded)
// We have to convert this number, 0 to 3, to CHOICEs
if(newButton == 0) newButton = CHOICE_RED;
else if(newButton == 1) newButton = CHOICE_GREEN;
else if(newButton == 2) newButton = CHOICE_BLUE;
else if(newButton == 3) newButton = CHOICE_YELLOW;
gameBoard[gameRound++] = newButton;
}
void setLEDs(byte leds)
{
if ((leds & CHOICE_RED) != 0)
digitalWrite(LED_RED, HIGH);
else
digitalWrite(LED_RED, LOW);
if ((leds & CHOICE_GREEN) != 0)
digitalWrite(LED_GREEN, HIGH);
else
digitalWrite(LED_GREEN, LOW);
if ((leds & CHOICE_BLUE) != 0)
digitalWrite(LED_BLUE, HIGH);
else
digitalWrite(LED_BLUE, LOW);
if ((leds & CHOICE_YELLOW) != 0)
digitalWrite(LED_YELLOW, HIGH);
else
digitalWrite(LED_YELLOW, LOW);
}
// Wait for a button to be pressed.
// Returns one of LED colors (LED_RED, etc.) if successful, 0 if timed out
byte wait_for_button(void)
{
long startTime = millis(); // Remember the time we started the this loop
while ( (millis() - startTime) < ENTRY_TIME_LIMIT) // Loop until too much time has passed
{
byte button = checkButton();
if (button != CHOICE_NONE)
{
toner(button, 150); // Play the button the user just pressed
while(checkButton() != CHOICE_NONE) ; // Now let's wait for user to release button
delay(10); // This helps with debouncing and accidental double taps
return button;
}
}
return CHOICE_NONE; // If we get here, we've timed out!
}
// Returns a '1' bit in the position corresponding to CHOICE_RED, CHOICE_GREEN, etc.
byte checkButton(void)
{
if (digitalRead(BUTTON_RED) == 0) return(CHOICE_RED);
else if (digitalRead(BUTTON_GREEN) == 0) return(CHOICE_GREEN);
else if (digitalRead(BUTTON_BLUE) == 0) return(CHOICE_BLUE);
else if (digitalRead(BUTTON_YELLOW) == 0) return(CHOICE_YELLOW);
return(CHOICE_NONE); // If no button is pressed, return none
}
void toner(byte which, int buzz_length_ms)
{
setLEDs(which); //Turn on a given LED
//Play the sound associated with the given LED
switch(which)
{
case CHOICE_RED:
buzz_sound(buzz_length_ms, 1136);
break;
case CHOICE_GREEN:
buzz_sound(buzz_length_ms, 568);
break;
case CHOICE_BLUE:
buzz_sound(buzz_length_ms, 851);
break;
case CHOICE_YELLOW:
buzz_sound(buzz_length_ms, 638);
break;
}
setLEDs(CHOICE_OFF); // Turn off all LEDs
}
// Toggle buzzer every buzz_delay_us, for a duration of buzz_length_ms.
void buzz_sound(int buzz_length_ms, int buzz_delay_us)
{
// Convert total play time from milliseconds to microseconds
long buzz_length_us = buzz_length_ms * (long)1000;
// Loop until the remaining play time is less than a single buzz_delay_us
while (buzz_length_us > (buzz_delay_us * 2))
{
buzz_length_us -= buzz_delay_us * 2; //Decrease the remaining play time
delayMicroseconds(buzz_delay_us);
delayMicroseconds(buzz_delay_us);
}
}
// Play the winner sound and lights
void play_winner(void)
{
setLEDs(CHOICE_GREEN | CHOICE_BLUE);
setLEDs(CHOICE_RED | CHOICE_YELLOW);
setLEDs(CHOICE_GREEN | CHOICE_BLUE);
setLEDs(CHOICE_RED | CHOICE_YELLOW);
}
void winner_sound(void)
{
// Toggle the buzzer at various speeds
for (byte x = 250 ; x > 70 ; x--)
{
for (byte y = 0 ; y < 3 ; y++)
{
delayMicroseconds(x);
delayMicroseconds(x);
}
}
}
// Play the loser sound/lights
void play_loser(void)
{
setLEDs(CHOICE_RED | CHOICE_GREEN);
setLEDs(CHOICE_BLUE | CHOICE_YELLOW);
setLEDs(CHOICE_RED | CHOICE_GREEN);
setLEDs(CHOICE_BLUE | CHOICE_YELLOW);
}
// Show an "attract mode" display while waiting for user to press button.
void attractMode(void)
{
while(1)
{
setLEDs(CHOICE_RED);
delay(100);
if (checkButton() != CHOICE_NONE) return;
setLEDs(CHOICE_BLUE);
delay(100);
if (checkButton() != CHOICE_NONE) return;
setLEDs(CHOICE_GREEN);
delay(100);
if (checkButton() != CHOICE_NONE) return;
setLEDs(CHOICE_YELLOW);
delay(100);
if (checkButton() != CHOICE_NONE) return;
}
}
// Notes in the melody. Each note is about an 1/8th note, "0"s are rests.
int noteDuration = 115; // This essentially sets the tempo, 115 is just about right for a disco groove :)
int LEDnumber = 0; // Keeps track of which LED we are on during the beegees loop
// Do nothing but play bad beegees music
// This function is activated when user holds bottom right button during power up
void play_beegees()
{
//Turn on the bottom right (yellow) LED
setLEDs(CHOICE_YELLOW);
toner(CHOICE_YELLOW, 150);
setLEDs(CHOICE_RED | CHOICE_GREEN | CHOICE_BLUE); // Turn on the other LEDs until you release button
while(checkButton() != CHOICE_NONE) ; // Wait for user to stop pressing button
setLEDs(CHOICE_NONE); // Turn off LEDs
delay(1000); // Wait a second before playing song
while(checkButton() == CHOICE_NONE) //Play song until you press a button
{
// iterate over the notes of the melody:
for (int thisNote = 0; thisNote < 32; thisNote++) {
changeLED();
// to distinguish the notes, set a minimum time between them.
// the note's duration + 30% seems to work well:
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
// stop the tone playing:
}
}
}
// Each time this function is called the board moves to the next LED
void changeLED(void)
{
setLEDs(1 << LEDnumber); // Change the LED
LEDnumber++; // Goto the next LED
if(LEDnumber > 3) LEDnumber = 0; // Wrap the counter if needed
}