Chapter 4
Simon Says

Now that you’re familiar with C’s syntax and how to interface with the Pi’s GPIO pins, let’s use the skills you learned in the last chapter to create our own version of the electronic “Simon Says” game. In case you’re not familiar with the game, the original design has four colored light-up buttons, each of which plays a sound. The machine lights the buttons in a random sequence, and the player has to repeat the sequence exactly. If they match the sequence, the computer adds another random step, and the game continues. Our version will differ in that it won’t play a musical tone, and we’re going to use five LEDs and switches rather than four. When the player gets the sequence wrong, all five LEDs will flash and the game will end.

Building the Game

To begin, you’ll need to construct your game “board.” You’ll need five different colored LEDs—I used red, white, yellow, blue, and green—and five momentary pushbutton switches. Using a prototyping breadboard or something similar, connect your switches to GPIO pins 13, 19, 26, 20, and 21, in that order. Your switch should connect the pin to ground, the way we did in Chapter 3, “Your First Project.”

Then connect the five LEDs to GPIO pins 14, 15, 18, 23, and 24, in that order. The LED anodes (+) should be connected to the GPIO pins, and the cathodes (–) should be connected to a ground pin. Figure 4-1 shows the wiring setup, and your physical design might look something like Figure 4-2.

c04f001.tif

Figure 4-1: Wiring up Simon Says

c04f002.tif

Figure 4-2: Physical Simon Says setup

The Code

When you’ve got it all wired up, go back to your terminal on the Pi and start a new file, simon.c. Start by including the necessary libraries:

#include <stdio.h>
#include <pigpio.h>

Add a new one as well:

#include <stdlib.h>

This library is the one we’ll use to generate a random number when the computer (“Simon”) is generat`ing the sequence of lights.

Before we write the main() function, which is where the actual game play will take place, let’s write the external functions to take care of things like lighting LEDs, choosing numbers, and so on. As you write these, just remember that we’ll be initializing the variables and setting up the LEDs and buttons as soon as we start the main() function.

To start with, we should be able to turn off all of the LEDs if we need to, and we need to be able to flash them all three times when the player loses. For these abilities, we’ll create functions that take the LED array as a parameter and don’t return any values. In your simon.c program, before the main() function, add the following functions:

void allOff(int leds[])
{
    int LED;
    for (LED=0; LED<5; LED++)
    {
        gpioWrite(leds[LED], 0);
    }
    return;
}
void allFlash(int leds[])
{
    int LED, i;
    for (i=0; i<3; i++)
    {
        for (LED=0; LED<5; LED++)
        {
            gpioWrite(leds[LED], 1);
        }
        time_sleep(0.2);
        for (LED=0; LED<5; LED++);
        {
            gpioWrite(leds[LED], 0);
        }
        time_sleep(0.2);
    }
}

allOff() simply cycles through the array and writes a 0 to each member, and allFlash() briefly turns each LED on and off.

Of course, since we have a function to do something when you lose, we should add a function to do something special if you win. Let’s have the LEDs light sequentially a few times, from one end to the other.

void youWin(int leds[])
{
    int LED, i;
    for (i=0; i<3; i++)
    {
        for (LED=0; LED<5; LED++)
        {
            gpioWrite(leds[LED], 1);
            time_sleep(0.1);
            gpioWrite(leds[LED], 0);
            time_sleep(0.1);
        }
    }
}

Our next external function is going to be very simple. This is how Simon chooses an LED to light, based on a random number.

int computerTurn()
{
    return rand() %5;
}

Like I said, it’s very simple. We’ll seed the random number generator in the main() function when we start the program, so now we’re just choosing one between 0 and 4, inclusive, and returning it.

The next function is the one that will play the sequence of LEDs. This sequence will be stored as an array of numbers corresponding to the numbers of the LEDs. This function will take that array (as leds[]) and the length of that array as parameters.

void playPattern(int leds[], int numTurns)
{
    int LED;
    for (LED=0; LED<numTurns; LED++)
    {
        gpioWrite(leds[LED], 1);
        time_sleep(0.5);
        gpioWrite(leds[LED], 0);
        time_sleep(0.5);
    }
    return;
}

playPattern() goes through the sequence, one by one, lighting each LED for a half second, turning it off again, waiting a half second, and then moving to the next LED.

The final external function we need is the one that reads the player’s button press, returning the number of the corresponding LED. This is very similar to the button-pressing program we created earlier; the computer waits, using a while() loop, until it detects a button press on one of the five input pins. When that happens, it returns the number of that pin. main() will then check that number against the number of the corresponding LED.

int getPlayerTurn()
{
    while(1)
    {
        if (gpioRead(13) == 0) return 13;
        else if (gpioRead(19) == 0) return 19;
        else if (gpioRead(26) == 0) return 26;
        else if (gpioRead(20) == 0) return 20;
        else if (gpioRead(21) == 0) return 21;
    }
}

That takes care of all of the external functions. Now let’s write the main() function and the mechanics of the game play.

Here it can sometimes be useful to stop a moment and figure out the sequence of events of the game. If you’re familiar with flowcharts, they can be extremely helpful, especially as the intricacy of your program grows. This game is pretty straightforward, however:

  • The computer will start the game by turning off all LEDs.
  • It will choose a random LED and add it to a sequence in its memory.
  • It will play that sequence.
  • It will then wait for the player to press a button (corresponding to an LED).
  • It will compare that LED to the array in its memory. If it matches, it will wait for the next button press and compare it, and so on. If it doesn’t match, the game will end.
  • If the player successfully imitates the sequence, the process will start over, with the computer choosing another random LED and adding it to the sequence.

Now that we have a plan of action, let’s write main().

We’ll start by initializing the pigpio library as we did before, seeding the random number generator, and then we’ll set up the buttons and LEDs.

int main()
{
    if (gpioInitialise() < 0)
    {
        printf("pigpio initialisation failed
");
        return 1;
    }
    // Initialize random number generator
    time_t t;
    srand((unsigned) time(&t));
    // Declare buttons
    int butOne = 13;
    int butTwo = 19;
    int butThr = 26;
    int butFor = 20;
    int butFiv = 21;
    // Declare LED pins
    int ledOne = 14;
    int ledTwo = 15;
    int ledThr = 18;
    int ledFor = 23;
    int ledFiv = 24;
    int ledArray[5] = {ledOne, ledTwo, ledThr, ledFor, ledFiv};
    // Set up buttons as inputs
    gpioSetMode(butOne, PI_INPUT);
    gpioSetMode(butTwo, PI_INPUT);
    gpioSetMode(butThr, PI_INPUT);
    gpioSetMode(butFor, PI_INPUT);
    gpioSetMode(butFiv, PI_INPUT);
    gpioSetPullUpDown(butOne, PI_PUD_UP);
    gpioSetPullUpDown(butTwo, PI_PUD_UP);
    gpioSetPullUpDown(butThr, PI_PUD_UP);
    gpioSetPullUpDown(butFor, PI_PUD_UP);
    gpioSetPullUpDown(butFiv, PI_PUD_UP);
    //Set up LEDs as outputs
    gpioSetMode(ledOne, PI_OUTPUT);
    gpioSetMode(ledTwo, PI_OUTPUT);
    gpioSetMode(ledThr, PI_OUTPUT);
    gpioSetMode(ledFor, PI_OUTPUT);
    gpioSetMode(ledFiv, PI_OUTPUT);

Now let’s declare the variables we’re going to need:

    // Declare computer array and turn counter
    int simon[50];
    int turns;
    
    // Counter variables
    int i, x, compareSteps;

The simon array is where the computer will store the sequence of numbers. For simplicity’s sake, the game code as shown will end if the player is able to repeat 50 steps successfully. If you’d like to make the game a bit easier (or shorter), change the length of the simon array from 50 to your chosen number.

Now that we’ve set up all the LEDs, buttons, and variables, we can start the actual game play.

    // Turn off all LEDs to start
    allOff(ledArray);
    // Start game (50 turns)
    for (turns = 0; turns<50; turns++)
    {
        // Computer adds random number
        x = computerTurn();
        switch(x)
        {
        case 0:
            simon[turns] = ledOne;
            break;
        case 1:
            simon[turns] = ledTwo;
            break;
        case 2:
            simon[turns] = ledThr;
            break;
        case 3:
            simon[turns] = ledFor;
            break;
        case 4:
            simon[turns] = ledFiv;
            break;
        }
    // Computer plays pattern
    playPattern(simon, turns);
    time_sleep(1);

Again, if you’re setting up a shorter game, change the value of turns here from 50 to your chosen number.

At this point in the for(turns) loop, the simon array now contains a list of LEDs that has as many members as there have been turns. Now we need to cycle through that array, one by one, getting the player’s input, lighting that LED, and comparing it to the current array index. If they match, we’ll move on to the next LED, and if they don’t we’ll end the game. As you might imagine, the easiest way to do this is with another switch() statement:

        for (compareSteps=0; compareSteps<turns; compareSteps++)
        {
            i = getPlayerTurn();
            switch(i)
            {
            case 13:
                gpioWrite(ledOne, 1);
                time_sleep(0.5);
                gpioWrite(ledOne, 0);
                if (simon[compareSteps] != ledOne)
                {
                    allFlash(ledArray);
                    printf("You lose!
");
                    return 0;
                }
                break;
            case 19:
                gpioWrite(ledTwo, 1);
                time_sleep(0.5);
                gpioWrite(ledTwo, 0);
                if (simon[compareSteps] != ledTwo)
                {
                    allFlash(ledArray);
                    printf("You lose!
");
                    return 0;
                }
                break;
            case 26:
                gpioWrite(ledThr, 1);
                time_sleep(0.5);
                gpioWrite(ledThr, 0);
                if (simon[compareSteps] != ledThr)
                {
                    allFlash(ledArray);
                    printf("You lose!
");
                    return 0;
                }
                break;
            case 20:
                gpioWrite(ledFor, 1);
                time_sleep(0.5);
                gpioWrite(ledFor, 0);
                if (simon[compareSteps] != ledFor)
                {
                    allFlash(ledArray);
                    printf("You lose!
");
                    return 0;
                }
                break;
            case 21:
                gpioWrite(ledFiv, 1);
                time_sleep(0.5);
                gpioWrite(ledFiv, 0);
                if (simon[compareSteps] != ledFiv)
                {
                    allFlash(ledArray);
                    printf("You lose!
");
                    return 0;
                }
                break;
            }
        } // end for (y) loop
        time_sleep(1);
    } // end for(turns) loop

Finally, if you’ve successfully imitated the sequence through all of the turns, you’ve won the game!

    // If you've reached this far, you've won
    printf("You won!
");
    youWin(ledArray); 
    gpioTerminate();
} // end main 

There are a few things I’d like to point out here as you look over this program. The first has to do with legibility. As you learn C, you’ll find that there are two schools of thought regarding the placement of brackets ({ }) for functions and loops. Some programmers prefer

for (x=0; x<5; x++) {
    printf("Spam!");
}

and others prefer

for (x=0; x<5; x++)
{
    printf("Spam!");
}

with the opening bracket on its own new line. Either way is correct, and it’s entirely up to you which one you use.

The second thing I’d like to mention is in closing brackets. If you’re not using an IDE that automatically adds the closing bracket for you when you type an opening bracket, get in the habit of typing an opening bracket, hitting Enter/Return, typing the matching closing bracket, and then filling code in between them.

void main() 
{
}

This will ensure that all of your blocks of code are bracketed correctly; if they’re not, the compiler errors can sometimes be extremely obtuse. Unfortunately, gcc has no error message:

Error: opening bracket (line 13) has no corresponding close

And finally, if you have a lot of loops and long functions in your code, it can be helpful to add a comment after the closing bracket that says what exactly it’s closing (as I did with the two for loops and the main() function). It helps if you need to add code within a loop, and you’re not sure which loop or function you’re actually in.

    } // end for (x)
    printf("Avocados!
");
} // end main

That is the end of the Simon Says program. If you need to see it in its entirety, it’s available to inspect or download at https://github.com/wdonat/jumpstarting_c. To compile it, use the flags we discussed earlier:

gcc -Wall -pthread -o simon simon.c -lpigpio -lrt

and run it with

sudo ./simon

(Remember that you have to run it using sudo, since you’re accessing the GPIO pins.)

Running the Program

The program will initialize all the LEDs and buttons and turn them all off. The computer then flashes an LED, and waits for you to press the appropriate button corresponding to that LED. If you get it right, the game then continues, adding LEDs to the sequence, and you have to press the appropriate buttons matching each LED in the sequence. If you make a mistake, all the LEDs flash to indicate that you’ve lost the game. If you win, the LEDs will light sequentially in a sort of celebration loop.

Things to Try

This is obviously a very basic implementation of Simon Says, and there are many things you could try to make the game more difficult or fun to play.

  • The button placement should correspond to the LED placement—the leftmost button controls the leftmost LED, for instance. What if you programmed the buttons backward so that the left button controlled the right LED, and vice versa? Can you make that into an “expert” level?
  • Try adjusting the speed of the game, as found in the time_sleep() function, so that the game subtly speeds up with each turn.
  • The original game had a musical note associated with each LED. How would you attach a speaker and program the Raspberry Pi to play a sound for each LED?

Conclusion

I hope this little book has been helpful, introducing you to this powerful language and showing you that it’s not exceedingly difficult to learn. Of course, this is just an introduction, and you can spend years learning about all that C can do. That learning process can definitely be worth it, however. There are environments and projects where a low-level language such as C is the only thing that will work (small embedded systems, for example, often use C or a subset of the language). Good luck in your future projects, and I look forward to seeing what you come up with!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.43.26