Build Your Own Simon Game with Meadow

Photo of jorgedevs

Made by jorgedevs

About the project

Use the power of Meadow to make a compact size Simon game on a half breadboard with LEDs, push buttons, and a piezo speaker.

Project info

Difficulty: Easy

Platforms: MicrosoftSparkFun

Estimated time: 1 hour

License: Apache License 2.0 (Apache-2.0)

Items used in this project

Hardware components

RedBot Buzzer Buzzer RedBot Buzzer Buzzer x 1
Tiny Breadboard Tiny Breadboard x 1
Momentary Pushbutton Switch - 12mm Square Momentary Pushbutton Switch - 12mm Square x 4
Jumper Wire Kit - 140pcs Jumper Wire Kit - 140pcs x 1
Raspberry Pi 4 Model B 1GB, 2GB, 4GB or 8GB RAM Raspberry Pi 4 Model B 1GB, 2GB, 4GB or 8GB RAM x 1
Meadow F7 Micro Meadow F7 Micro x 1

Software apps and online services

Microsoft Visual Studio 2019 Microsoft Visual Studio 2019

Story

In this project, you'll build a game inspired by the popular logic game Simon using a Meadow F7 Micro, LEDs of different colors, push buttons and a piezo speaker, and write the code using Meadow.Foundation. Everything you need to build this project is included in the Wilderness Labs Meadow F7 w/Hack Kit Pro.

Push buttons (or momentary tactile buttons) are physical buttons that are used to complete a circuit when pressed and break the circuit when released. Push buttons come is a wide range of sizes and configurations, we'll use a common type with four (4) leads that are designed to fit standard prototype boards. When the button is pressed, all four leads are connected. You can read more about push buttons here.

Piezospeakers (or piezoelectric speaker) is a loudspeaker that uses the piezoelectric effect for generating sound. The initial mechanical motion is created by applying a voltage to a piezoelectric material, and this motion is typically converted into audible sound using diaphragms and resonators.

Meadow.Foundation is a platform for quickly and easily building connected things using.NET on Meadow. Created by Wilderness Labs, it's completely open source and maintained by the Wilderness Labs community.

If you're new working with Meadow, I suggest you go to the Getting Started w/ Meadow by Controlling the Onboard RGB LED project to properly set up your development environment.

Step 1 - Assemble the Circuit

Wire the circuit like the following Fritzing diagram:

Circuit assemble of Simon game connected to Meadow

Circuit assemble of Simon game connected to Meadow

Step 2 - Create a Meadow Project

Create a new Meadow Application project in Visual Studio 2019 for Windows or macOS; since our game is a variation of the game Simon, let's call it Simon.

Step 3 - Write the Code for the Simon Project

Add SimonGameClass

First class we're going to add to the project contains the main logic of the Simon game. Add a new class to your project named SimonGame and add the following code:

using System;namespace Simon{    public enum GameState    {        Start,        NextLevel,        Win,        GameOver,    }    public class SimonEventArgs : EventArgs    {        public GameState GameState { get; set; }        public SimonEventArgs(GameState state)        {            GameState = state;        }    }    public class SimonGame    {        static int MAX_LEVELS = 25;        static int NUM_BUTTONS = 4;        public delegate void GameStateChangedDelegate(object sender, SimonEventArgs e);        public event GameStateChangedDelegate OnGameStateChanged = delegate { };        public int Level { get; set; }        int currentStep;        int[] Steps = new int[MAX_LEVELS];        Random rand = new Random((int)DateTime.Now.Ticks);        public void Reset()        {            OnGameStateChanged(this, new SimonEventArgs(GameState.Start));            Level = 1;            currentStep = 0;            NextLevel();        }        public int[] GetStepsForLevel()        {            var steps = new int[Level];            for (int i = 0; i < Level; i++)            steps[i] = Steps[i];            return steps;        }        public void EnterStep(int step)        {            if (Steps[currentStep] == step)            {                currentStep++;            }            else            {                OnGameStateChanged(this, new SimonEventArgs(GameState.GameOver));                Reset();            }            if (currentStep == Level)            {                NextLevel();            }        }        void NextLevel()        {            currentStep = 0;            Level++;            if (Level >= MAX_LEVELS)            {                OnGameStateChanged(this, new SimonEventArgs(GameState.Win));                Reset();                return;            }            var level = string.Empty;            for (int i = 0; i < Level; i++)            {                Steps[i] = rand.Next(NUM_BUTTONS);                level += Steps[i] + ", ";            }            OnGameStateChanged(this, new SimonEventArgs(GameState.NextLevel));        }    }}

Notice that the SimonGame class implements the following methods:

  • Reset - Starts the game from the first level
  • int[] GetStepsForLevel - Returns a series of steps of a certain level
  • EnterStep - Verifies that the user's button pressed is the correct option
  • NextLevel - Used to generate a new sequence of steps for a new level

It also contains GameStates and SimonEventArgs for when triggering an event when the game changes to any state. More on that next.

MeadowApp Class

For the main MeadowApp class, copy the following code below:

using Meadow;using Meadow.Devices;using Meadow.Foundation.Audio;using Meadow.Foundation.Leds;using Meadow.Foundation.Sensors.Buttons;using Meadow.Hardware;using System;using System.Threading;namespace Simon{    public class MeadowApp : App<F7Micro, MeadowApp>    {        int ANIMATION_DELAY = 200;        float[] notes = new float[] { 261.63f, 329.63f, 392, 523.25f };        Led[] leds = new Led[4];        PushButton[] pushButtons = new PushButton[4];        PiezoSpeaker speaker;        bool isAnimating = false;        SimonGame game = new SimonGame();        public MeadowApp()        {            leds[0] = new Led(Device.CreateDigitalOutputPort(Device.Pins.D10));            leds[1] = new Led(Device.CreateDigitalOutputPort(Device.Pins.D09));            leds[2] = new Led(Device.CreateDigitalOutputPort(Device.Pins.D08));            leds[3] = new Led(Device.CreateDigitalOutputPort(Device.Pins.D07));            pushButtons[0] = new PushButton(                Device.CreateDigitalInputPort(                    Device.Pins.D01,                     InterruptMode.EdgeBoth,                     ResistorMode.Disabled            ));            pushButtons[0].Clicked += ButtonRedClicked;            pushButtons[1] = new PushButton(                Device.CreateDigitalInputPort(                    Device.Pins.D02,                     InterruptMode.EdgeBoth,                     ResistorMode.Disabled            ));            pushButtons[1].Clicked += ButtonGreenClicked;            pushButtons[2] = new PushButton(                Device.CreateDigitalInputPort(                    Device.Pins.D03,                     InterruptMode.EdgeBoth,                     ResistorMode.Disabled            ));            pushButtons[2].Clicked += ButtonBlueClicked;            pushButtons[3] = new PushButton(                Device.CreateDigitalInputPort(                    Device.Pins.D04,                     InterruptMode.EdgeBoth,                     ResistorMode.Disabled            ));            pushButtons[3].Clicked += ButtonYellowClicked;            speaker = new PiezoSpeaker(Device.CreatePwmPort(Device.Pins.D11));            Console.WriteLine("Welcome to Simon");            SetAllLEDs(true);            game.OnGameStateChanged += OnGameStateChanged;            game.Reset();        }        void ButtonRedClicked(object sender, EventArgs e)        {            OnButton(0);        }        void ButtonGreenClicked(object sender, EventArgs e)        {            OnButton(1);        }        void ButtonBlueClicked(object sender, EventArgs e)        {            OnButton(2);        }        void ButtonYellowClicked(object sender, EventArgs e)        {            OnButton(3);        }        void OnButton(int buttonIndex)        {            Console.WriteLine("Button tapped: " + buttonIndex);            if (isAnimating == false)            {                TurnOnLED(buttonIndex);                game.EnterStep(buttonIndex);            }        }        void OnGameStateChanged(object sender, SimonEventArgs e)        {            var th = new Thread(() =>            {                switch (e.GameState)                {                    case GameState.Start:                        break;                    case GameState.NextLevel:                        ShowStartAnimation();                        ShowNextLevelAnimation(game.Level);                        ShowSequenceAnimation(game.Level);                        break;                    case GameState.GameOver:                        ShowGameOverAnimation();                        game.Reset();                        break;                    case GameState.Win:                        ShowGameWonAnimation();                        break;                }            });            th.Start();        }        void TurnOnLED(int index, int duration = 400)        {            leds[index].IsOn = true;            speaker.PlayTone(notes[index], duration);            leds[index].IsOn = false;        }        void SetAllLEDs(bool isOn)        {            leds[0].IsOn = isOn;            leds[1].IsOn = isOn;            leds[2].IsOn = isOn;            leds[3].IsOn = isOn;        }        void ShowStartAnimation()        {            if (isAnimating)                return;            isAnimating = true;            SetAllLEDs(false);            for (int i = 0; i < 4; i++)            {                leds[i].IsOn = true;                Thread.Sleep(ANIMATION_DELAY);            }            for (int i = 0; i < 4; i++)            {                leds[3 - i].IsOn = false;                Thread.Sleep(ANIMATION_DELAY);            }            isAnimating = false;        }        void ShowNextLevelAnimation(int level)        {            if (isAnimating)                return;            isAnimating = true;            SetAllLEDs(false);            for (int i = 0; i < level; i++)            {                Thread.Sleep(ANIMATION_DELAY);                SetAllLEDs(true);                Thread.Sleep(ANIMATION_DELAY * 3);                SetAllLEDs(false);            }            isAnimating = false;        }        void ShowSequenceAnimation(int level)        {            if (isAnimating)                return;            isAnimating = true;            var steps = game.GetStepsForLevel();            SetAllLEDs(false);            for (int i = 0; i < level; i++)            {                Thread.Sleep(200);                TurnOnLED(steps[i], 400);            }            isAnimating = false;        }        void ShowGameOverAnimation()        {            if (isAnimating)                return;            isAnimating = true;            speaker.PlayTone(123.47f, 750);            for (int i = 0; i < 20; i++)            {                SetAllLEDs(false);                Thread.Sleep(50);                SetAllLEDs(true);                Thread.Sleep(50);            }            isAnimating = false;        }        void ShowGameWonAnimation()        {            ShowStartAnimation();            ShowStartAnimation();            ShowStartAnimation();            ShowStartAnimation();        }    }}

There are several things happening in this class. The most important thing to note here is that Simon works as a state machine, meaning that when starting a game, passing to a next level, winning or losing the game are all game states and when changing states, an event is triggered and OnGameStateChanged event handler will run the appropriate routine depending on the state of the game.

Input - Push Buttons

The only way the user interacts with the Simon game is through four push buttons. In the code, these push buttons are declared on the top, named buttons as an array of four items, and in the MeadowApp's Constructor, we instantiate each PushButton and after we declare the handlers for each one, which all of them invoke the OnButton method that ultimately checks if the button pressed is the correct step of the game's sequence.

OutPut - LEDs and PiezoSpeaker

The peripherals used for output in this project are the LEDs and the PiezoSpeaker. Using Meadow.Fundation we can simply instanciate four(4) LED and PiezoSpeaker object in the InitializerPeripherals method, where we specify in which pins these components are connected to the Netduino.

Animations

To give the user feedback on which state the game is in, we wrote the following animation methods:

  • void ShowStartAnimation()
  • void ShowNextLevelAnimation(int level)
  • void ShowSequenceAnimation(int level)
  • void ShowGameOverAnimation()
  • void ShowGameWonAnimation()

These animation methods are explicit scripts of turning LEDs on and off in a certain sequence to express different states of the game.

Program Class

using Meadow;using System.Threading;namespace Simon{    class Program    {        static IApp app;        public static void Main(string[] args)        {            if (args.Length > 0 && args[0] == "--exitOnDebug") return;            // instantiate and run new meadow app            app = new MeadowApp();            Thread.Sleep(Timeout.Infinite);        }    }}

Step 5 - Run the Project

Click the run button in Visual Studio to see your Simon game in action! Notice the start animation, and all the LEDs flashing a certain number of times to indicate in which level you currently are!

Running Simon game

Running Simon game

Check out Meadow.Foundation

This project is only the tip of the iceberg in terms of the extensive exciting things you can do with Meadow.Foundation.

  • It comes with a huge peripheral driver library with drivers for the most common sensors and peripherals.
  • The peripheral drivers encapsulate the core logic and expose a simple, clean, modern API.
  • This project is backed by a growing community that is constantly working on building cool connected things and are always excited to help new-comers and discuss new projects.

References

Schematics, diagrams and documents

Simon circuit diagram

Code

Simon game complete project

You can check the complete project in our Meadow_Project_Samples repo

Credits

Leave your feedback...