Memory games have become a big hit in recent years. They flood video games, phone applications, and even social networking sites. Studies have shown that by playing games that require a certain level of memory recall, people can actually improve their fluid intelligence (the ability to reason and solve new problems). This activity will help improve your memory by displaying a series of random LED colors through a Bi-Color LED. You will then have to repeat the pattern shown using pushbuttons. But be careful! In each round the colors will appear faster and faster, making it harder to remember.
Check out the video below for an overview before you start.
If you are new to the BASIC Stamp microcontroller or to programming, it would be a good idea to review the following before beginning:
This activity can be completed solely with the parts included in the BASIC Stamp Activity Kit (#90005). Below is a listing of all parts required for this activity:
Source code for the Bi-Color LED Memory Game [1]
Figure 1 shows the schematic for the game board. Build the circuit shown below, paying close attention to the polarity of your LEDs. Before continuing, check that your circuit meets the following criteria:
Figure 1 - Game Board Schematic
Using Electrical Connections to Control LEDs
The red and green LEDs are added to the circuit so the player can keep track of which button corresponds to what color. They also light up when a button is pressed so the player can imitate the pattern shown by the bi-color LED. Instead of writing additional code to monitor the pushbutton state and then turn the LED on or off depending on that state, we can connect the LED so that it only emits light when the connection has been closed between the pushbutton terminals. For more information, see What’s a Microcontroller? Chapter 3, Activity #1.
You might want to trim the LED leads so they can be mounted closer to the pushbuttons. If you decide to do this for your application and accidentally cut the leads so you’re not sure which one is shortest anymore, all is not lost, and the cathode can still be determined. If you look at the top of the LED, you will notice that one edge is either flattened or has a small notch in it. Whichever side has this marking is the cathode.
Before continuing, it’s always a good idea to check your wiring for any errors. The easiest way is to run a program that tests each aspect of your circuit.
' HardwareTest.bs2 ' Tests the functionality of each piece of your circuit. ' {$STAMP BS2} ' {$PBASIC 2.5} displayRed PIN 15 displayGreen PIN 14 DO HIGH displayRed ' Red LOW displayGreen PAUSE 200 HIGH displayGreen ' Green LOW displayRed PAUSE 200 DEBUG HOME, ? IN0 ' Display PB States DEBUG ? IN1 PAUSE 100 LOOP
If your code worked as directed, you’re free move on to the next section! However, if your circuit did not work as described above, here are some quick troubleshooting tips:
One of the most important parts of the game is the bi-color LED emitting random light patterns. This can be accomplished using the BASIC Stamp’s RANDOM command. This command generates a series of pseudo-random numbers ranging from 0 to 65535. By looking at a single bit of the pseudo-random number generated, we can program the bi‑color LED to blink green or red depending on if the value of that bit is 0 or 1.
Unfortunately, things aren’t quite that simple. You may have noticed that the term “pseudo-random” is used. This is because the BASIC Stamp can only generate what’s known as pseudo-random numbers. This means that even though the number generated appears random, it is really generated based on a logical operation. This operation uses the initial value (also known as the “seed”) to tap into a sequence of 65535 seemingly random numbers. At the start of our program, this value will always be zero, which in turn will generate the same sequence of numbers—and the LED will blink the same red-green pattern. This wouldn’t be very challenging for our game, so we’ll have to apply other coding techniques to generate a more desirable set of random numbers.
In RandomTest.bs2, we will accomplish this by using the BUTTON command. This command monitors the state of a button, and does not allow the code to move on until the state changes. Since the user can’t accurately time when they push a button, the variable generateColor will keep tapping into the sequence of pseudo-random numbers until the player presses the bottom pushbutton.
Run RandomTest.bs2 and:
' RandomTest.bs2 ' Tests that the bi-color LED blinks random red-green patterns. ' {$STAMP BS2} ' {$PBASIC 2.5} displayRed PIN 15 ' Red Pin displayGreen PIN 14 ' Green Pin btnWrk VAR BYTE ' Var for BUTTON command generateColor VAR WORD ' Random number variable displayColor VAR generateColor.BIT0 ' Bit 0 of random number Begin_Game: RANDOM generateColor ' Create a random number BUTTON 0, 1, 100, 250, btnWrk, 0, Begin_Game ' Do not continue until ' button is pressed DO IF (displayColor = 1) THEN ' If bit0 of the random HIGH displayRed ' number generated is 1, LOW displayGreen ' display the color red. PAUSE 500 ELSEIF (displayColor = 0) THEN ' If bit0 of the random HIGH displayGreen ' number generated is 0, LOW displayRed ' display the color green. PAUSE 500 ENDIF LOW displayRed ' Turn LED off before next LOW displayGreen ' color PAUSE 500 RANDOM generateColor ' Generate new random number LOOP ' Loop forever
Figure 2 - Game Circuit
As well as being fun, this game also ties in a lot of useful programming techniques. Essentially, there are three main “things” we want our program to do: display the color sequence, save the colors shown, and check user response. Since we want the program to keep executing these main tasks indefinitely, the use of subroutines is perfect here. Our main loop will check if the youLose flag has been set, and depending on if the flag has been set or not, will cycle through three subroutines:
In order to save the color sequence, we’ll have to make extensive use of the READ and WRITE commands. Since the EEPROM address always increments by 1 each time we call the WRITE command, we’ll also have to keep a running count of the current EEPROM address in order to read the color sequence later.
The first thing we’ll want to specify, however, is what gets written to EEPROM if the color is red or green. In order to keep things simple, we’ll write the number 1 to EEPROM if the color red is displayed and 0 to EEPROM if the color green is displayed:
IF (displayColor = 1) THEN ... WRITE eepromAddress, 1 ELSEIF (displayColor = 0) THEN ... WRITE eepromAddress, 0
After each color is written to EEPROM, we’ll add 1 to the EEPROM address counter. This makes displaying the color sequence a simple FOR…NEXT loop:
FOR eepromAddress = 0 TO currentAddress READ eepromAddress, savedColor IF (savedColor = 1) THEN ... ELSEIF (savedColor = 0) THEN ... ENDIF ... NEXT
Confused?
For more information on reading and writing data to and from EEPROM, check out the READ and WRITE commands in the PBASIC Language Webhelp.
Once the color pattern is displayed, we want to check and be sure that the sequence of pushbutton states matches the sequence of colors shown. The first thing we want to do is read the color sequence from EEPROM. This can be achieved in the same way we display the color sequence, with a FOR…NEXT loop:
FOR eepromAddress = 0 TO currentAddress READ eepromAddress, savedColor
Next, we’ll want to keep track of the player pushbutton input, and this can get a bit tricky. The code executes very quickly, and we have to be sure that it will wait for the player to enter the entire color sequence. If we’re not careful, the code will read the first pushbutton state as the entire color sequence, which would be very frustrating for the player!
The simplest way to tackle this problem is to make sure the code waits for the player to press and release a pushbutton. This will make the code wait for a press and release each time through the loop. Since the pushbutton states will be 0 before we check the player response against the saved color, we’ll simply use two sets of variables to keep track of everything. The first set (PbRed and PbGreen) will hold the states of the red and green pushbuttons. Since we then want to save the states of these pushbuttons, the second set of variables (Red and Green) will be set to the states of PbRed and PbGreen before the pushbutton is released and their values are reset to 0.
DO PbGreen = IN1 PbRed = IN0 LOOP UNTIL (PbGreen = 1) OR (PbRed = 1) Green = PbGreen Red = PbRed DO PbGreen = IN1 PbRed = IN0 LOOP UNTIL (PbGreen = 0) AND (PbRed = 0)
Next we’ll want to compare the player input against the color displayed. When the user is repeating the color pattern displayed by the bi-color LED, there are four conditions that can occur, with only two results:
Color Displayed |
Player Response |
Result |
Red |
Red (Red = 1) |
Next Round |
Red (savedColor = 1) |
Green (Green = 1) |
Lose |
Green (savedColor = 0) |
Red (Red = 1) |
Lose |
Green (savedColor = 0) |
Green (Green = 1) |
Next Round |
Since all of our values are either 0 or 1, we can use conditional IF…THEN statements to compare the value in savedColor to the state of the pushbutton pressed. If the button pressed does not match the color shown, the youLose flag is set and the program returns to the main loop. Otherwise, the program continues through the subroutine and adds 1 to the variable roundNumber if the player inputs the correct sequence:
IF savedColor <> Green THEN PbGreen = 0 PAUSE 100 ELSEIF savedColor <> Red THEN youLose = 1 RETURN ELSEIF savedColor = Red THEN PbRed = 0 PAUSE 100 ELSEIF savedColor = Green THEN youLose = 1 RETURN ENDIF
The <> Operator:
When writing code, we always want things to be as clear as possible. In the example above, the <> operator was used. This is a comparative operator which means “not equal.” So instead of writing:
IF (savedColor = 0) AND (Green = 1) THEN …
We can replace this with much simpler code using the not-equal operator:
IF savedColor <> Green THEN …
Enter and run BiColorLedGame.bs2 to start improving your fluid memory! Since the highest round number is displayed whenever the player loses, challenge your family and friends and see who can reach the highest level!
' BiColorLedGame.bs2 ' This program is for a memory game using a bi-color LED and two pushbuttons. ' The bi-color LED will emit random red-green patterns, which the player will ' have to imitate using the two pushbuttons wired to the board. ' {$STAMP BS2} ' {$PBASIC 2.5} ' -----[ EEPROM Definitions ]------------------------------------------------ eepromAddress VAR Word ' Current address in EEPROM currentAddress VAR Word ' Address counter variable for ' reading from EEPROM ' -----[ I/O Definitions ]--------------------------------------------------- displayRed PIN 15 ' Red Pin displayGreen PIN 14 ' Green Pin ' -----[ Variables ]--------------------------------------------------------- PbGreen VAR Byte ' Pushbutton pressed = green PbRed VAR Byte ' Pushbutton pressed = red Green VAR Byte ' Green pushbutton select Red VAR Byte ' Red pushbutton select btnWrk VAR Byte ' BUTTON command workspace youLose VAR Byte ' Lose flag roundNumber VAR Word ' Current level delayTime VAR Word ' Current light delay time generateColor VAR Word ' Random color generated displayColor VAR generateColor.BIT0 ' Bit 0 of random color gen. savedColor VAR Word ' Save colors displayed GOSUB Begin_Game ' At startup, go to the ' Begin_Game subroutine ' -----[ Main Routine ]------------------------------------------------------ Main: DO GOSUB Check_Response ' Check player response IF (youLose = 1) THEN ' If the youLose flag has been DEBUG "You Lose! Game Over.", CR, ' set, inform the player & show "Level Reached: ", ' the highest level reached DEC roundNumber PAUSE 2000 DEBUG CLS GOSUB Begin_Game ' Reset - go to Begin_Game ELSEIF (youLose = 0) THEN ' If the youLose flag was not GOSUB Next_Round ' set, go to the Next_Round ENDIF ' subroutine LOOP ' -----[ Subroutine - Begin_Game ]------------------------------------------- ' Only used at beginning or after the player loses. Resets all variables to ' their initial values, and displays the first color. Begin_Game: ' Reset all variables to their initial values. eepromAddress = 0 currentAddress = 0 roundNumber = 0 delayTime = 750 ' Tell user to press the bottom button to begin and randomize the variable ' generateColor until the button is pressed. DEBUG HOME, "Press the bottom button to begin.", CR RANDOM generateColor BUTTON 0, 1, 100, 250, btnWrk, 0, Begin_Game DEBUG CLS ' Clear the Screen IF (displayColor = 1) THEN ' If bit0 of the random number HIGH displayRed ' generated is 1, then display LOW displayGreen ' the color red. PAUSE delayTime ' Wait for the value in delayTime WRITE eepromAddress, 1 ' Write a 1 to EEPROM to save ' the color displayed. ELSEIF (displayColor = 0) THEN ' If bit0 of the random number HIGH displayGreen ' generated is 0, then display LOW displayRed ' the color green. PAUSE delayTime ' Wait for the value in delayTime WRITE eepromAddress, 0 ' Write a 0 to EEPROM to save ENDIF ' the color displayed LOW displayRed ' Turn the LED off LOW displayGreen RETURN ' Return to Main ' -----[ Subroutine - Check_Response ]--------------------------------------- ' Checks the player's response to see if the pattern they've entered matches ' the pattern displayed by the LEDs. Check_Response: youLose = 0 ' You lose flag is cleared ' Reads values from EEPROM until it reaches the number of colors that have ' been displayed. FOR eepromAddress = 0 TO currentAddress READ eepromAddress, savedColor ' Monitor pushbutton states until one is pressed DO PbGreen = IN1 ' State of pin 1 (green button) PbRed = IN0 ' State of pin 0 (red button) LOOP UNTIL (PbGreen = 1) OR (PbRed = 1) Green = PbGreen ' Save the state of each PB Red = PbRed ' Save the state of each PB ' Wait until the player releases the pushbutton DO PbGreen = IN1 ' State of pin 1 (green button) PbRed = IN0 ' State of pin 0 (red button) LOOP UNTIL (PbGreen = 0) AND (PbRed = 0) IF savedColor <> Green THEN ' If the values of savedColor PbGreen = 0 ' and Green are different, PAUSE 100 ' keep looping ELSEIF savedColor <> Red THEN ' If the values of savedColor youLose = 1 ' and Red are different, the RETURN ' player lost, return to main ELSEIF savedColor = Red THEN ' If the values of savedColor PbRed = 0 ' and Red are equal, keep PAUSE 100 ' looping ELSEIF savedColor = Green THEN ' If the values of savedColor youLose = 1 ' and Red are equal, the RETURN ' player lost, return to main ENDIF NEXT ' If the code got here, the player did not lose, add 1 to the round number roundNumber = roundNumber + 1 RETURN ' Return to main ' -----[ Subroutine - Next_Round ]-------------------------------------------- ' Displays the last colors shown and adds a new random color to the sequence Next_Round: PAUSE 750 ' Wait so player can get ready ' Subtract 50 from the delayTime so the sequence displays faster, but set a ' limit so the colors are still viewable delayTime = (delayTime - 50) MIN 200 ' Read the last colors from EEPROM and display them FOR eepromAddress = 0 TO currentAddress READ eepromAddress, savedColor IF (savedColor = 1) THEN HIGH displayRed LOW displayGreen PAUSE delayTime ELSEIF (savedColor = 0) THEN HIGH displayGreen LOW displayRed PAUSE delayTime ENDIF LOW displayRed LOW displayGreen PAUSE delayTime NEXT RANDOM generateColor ' Generate a new random number IF (displayColor = 1) THEN ' If bit0 of the random number HIGH displayRed ' generated is 1, then display LOW displayGreen ' the color red. PAUSE delayTime ' Wait for value in delayTime WRITE eepromAddress, 1 ' Write a 1 to EEPROM to save ' the color displayed ELSEIF (displayColor = 0) THEN ' If bit0 of the random number HIGH displayGreen ' generated is 0, then display LOW displayRed ' the color green. PAUSE delayTime ' Wait for value in delayTime WRITE eepromAddress, 0 ' Write a 0 to EEPROM to save ENDIF ' the color displayed. LOW displayRed ' Turn LED off LOW displayGreen currentAddress = currentAddress + 1 ' Add 1 to the current EEPROM ' address counter RETURN ' Return to Main
Links
[1] https://learn.parallax.com/sites/default/files/Files/Docs/Projects/LEDGame/LED-Memory-Game.zip