How the Game Works

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
(savedColor = 1)

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