Game On!
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:
- Begin_Game
- This subroutine will reset all values to zero and display and save the first pseudo-random color. It will be used when the player first starts the game, or whenever he or she loses.
- Check_Response
- This subroutine checks the player’s response and sets the youLose flag equal to 1 when the response doesn’t match the color sequence. If the player’s response is correct, this subroutine will add one to the round number count.
- Next_Round
- This subroutine will display the previously saved color sequence and add another color to the chain.
EEPROM Color Memory
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.
Player Response
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 …
BiColorLedGame.bs2
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