Target Game – Lambda Expressions

TargetsCd

TargetsMainForm

I have to admit that since figuring out how Lambda expressions work I’ve been using them quite a bit. This and becoming a fan of ArrayList and generic collections over arrays. But for this program I wanted concentrate on the usage of lambda expressions being used with event arguments. The goal of the game is to click on each target before the timer runs out. If you get 4 batches of hits total you progress to the next level. In later versions I’ll implement the ability to select the player level manually.


MainForm Class


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Targets
{
    public partial class MainForm : Form
    {
        //Private class variables.
        private TargetGame _targetGame;
        private int _hits = 0;
        private int _level = 1;
        private int _hitsPerLevel = 4;
        private bool _gamePaused = false;       //Using a bool to check the game paused state. DS

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Shown(object sender, EventArgs e)
        {
            //Exit the event event with lamda expressions.
            exitToolStripMenuItem.Click += (from, ea) => this.Close();

            //Create the target game object.
            _targetGame = new TargetGame(boardSize: mainPictureBox.ClientSize);

            //Set the cursor type.
            mainPictureBox.Cursor = Cursors.Cross;

            //Set up the status.
            updateHitsStatus();

            //Lambda statement for the game rest.
            resetToolStripMenuItem.Click += (from, ea) =>
                {
                    if (_gamePaused)
                        resetGame();        //Calling the resetGame() method using the default parameter. DS
                    else
                        resetGame(_gamePaused);     //Calling the resetGame() method changing its default parameter. DS
                };

            //PictureBox Mouseup lambda expression.
            //I changed this lambda expression so when the game was paused the user couldn't select a target
            //until the game was resumed. DS
            mainPictureBox.MouseUp += (from, ea) =>
                {
                    if (_gamePaused)
                        MessageBox.Show("Sorry the game is paused.", this.Text, MessageBoxButtons.OK);
                    else
                        _targetGame.Select(ea.Location);
                };

            //Lambda delegates for Hit
            _targetGame.Hit += (from, ea) => _hits++;
            _targetGame.Hit += (from, ea) =>
                {
                    if (_hits == _hitsPerLevel * _targetGame.NumberOfTargets)
                    {
                        _hits = 0;
                        _level++;
                        _targetGame.NumberOfTargets++;
                        _targetGame.GameLevel = _level;     //Updating what the level of the game is on. DS
                    }
                };
            _targetGame.Hit += (from, ea) => updateHitsStatus();

            //Lambda statement for the timer expired event. DS
            _targetGame.TimerExpired += (from, ea) =>
                {
                    _hits = 0;          //Resetting the hits counter to zero. DS   
                    updateHitsStatus(); //Updating the game status indicator. DS
                };

            //Setup the PictureBox Paint event.
            mainPictureBox.Paint += (from, ea) => _targetGame.Update(ea.Graphics);

            //Setup the Timer Tick event.
            gameTimer.Tick += (from, ea) => mainPictureBox.Invalidate();

            //Pausing or resuming the game. DS
            pauseToolStripMenuItem.Click += (from, ea) =>
                {
                    if (!_gamePaused)
                    {
                        pauseToolStripMenuItem.Text = "Resume";     //Changing the menu text. DS
                        gameTimer.Enabled = false;                  //Stopping the game timer. DS
                        _gamePaused = true;                         //Changing the gamePaused bool. DS
                    }
                    else
                    {
                        pauseToolStripMenuItem.Text = "Pause";      //Changing the menu text. DS
                        gameTimer.Enabled = true;                   //Starting the game timer.
                        _gamePaused = false;                        //Changing the gamePaused bool. DS
                    }
                };
        }

        //Optional parameter method used when resetting the game. DS
        private void resetGame(bool gameReset = true)
        {
            //If the default method parameter hasn't been changed then run this. DS
            if (gameReset)
            {
                pauseToolStripMenuItem.Text = "Pause";
                gameTimer.Enabled = true;
                _gamePaused = false;
                _hits = 0;
                _level = 1;
                _targetGame.NumberOfTargets = 1;
                _targetGame.GameLevel = _level;
                updateHitsStatus();
                _targetGame.Reset();
            }

            //If the default method parameter has changed then run this. DS
            else
            {
                _hits = 0;
                _level = 1;
                _targetGame.NumberOfTargets = 1;
                _targetGame.GameLevel = _level;
                updateHitsStatus();
                _targetGame.Reset();
            }
        }

        private void checkGameLevel()
        {
            Console.WriteLine(_level);
        }

        private void updateHitsStatus()
        {
            //Update the hits status.
            hitsToolStatusStripLabel.Text = "Level: " + _level.ToString() + " - Hits: " + _hits.ToString();
        }
    }
}

 

TargetGame Class


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Drawing;

namespace Targets
{
    public class TargetGame
    {
        //Private delegates.
        private Target.TargetDrawDelegate _targetDraw;

        //Private class variables.
        private ArrayList _targets = new ArrayList();
        private Random _random = new Random();
        private int _defaultTargetSize = 40;

        //Private class backing properties.
        private Size _boardSize;
        private int _numberOfTargets = 0;
        private int _gameLevel = 0;             //Private int to store what level the game is on. DS

        //Public events and delegates.
        public event EventHandler Hit;
        public event EventHandler TimerExpired;     //Timer expired event hander. DS

        //Public properties.
        public Size BoardSize { set { _boardSize = value; } }
        public int GameLevel { set { _gameLevel = value; } }        //Public setter for the game level. DS
        public int NumberOfTargets
        {
            get { return _numberOfTargets; }
            set { _numberOfTargets = value; }
        }

        //Protected event handlers.
        protected virtual void CountExpiredEventHandler(object sender, EventArgs e)
        {
            //Remove the target.
            Target target = sender as Target;

            if (target != null)
                RemoveTarget(target);

            //Raising the TimerExpires event. DS
            if (TimerExpired != null)
                TimerExpired(sender, e);
        }

        protected virtual void OnHit(object sender, EventArgs e)
        {
            if (Hit != null)
                Hit(sender, e);
        }

        //Constructor.
        public TargetGame(Size boardSize, int numberOfTargets = 1)
        {
            //Initialize the game settings.
            _boardSize = boardSize;
            _numberOfTargets = numberOfTargets;
        }

        //Private methods.
        //I decided to utilize the existing AddTarget() method from the lesson. Instead of it creating the target I used it to decide what level the
        //player is on and adjust the target countdown from there. DS
        private void AddTarget()
        {

            //Usign switch/case to determine what level the game should be started with or as the user progresses. DS
            switch (_gameLevel)
            {
                case 2:
                    {
                        AddTargetToGame(9);
                        break;
                    }
                case 3:
                    {
                        AddTargetToGame(8);
                        break;
                    }
                case 4:
                    {
                        AddTargetToGame(7);
                        break;
                    }
                case 5:
                    {
                        AddTargetToGame(6);
                        break;
                    }
                case 6:
                    {
                        AddTargetToGame(5);
                        break;
                    }
                case 7:
                    {
                        AddTargetToGame(4);
                        break;
                    }
                case 8:
                    {
                        AddTargetToGame(3);
                        break;
                    }
                default:
                    {
                        AddTargetToGame();
                        break;
                    }
            }
        }

        //Method to that adds the target to the game based on what level the player is at.
        //If the game is first starting then the default countdown is 10 seconds. DS
        private void AddTargetToGame(int startingCount = 10)
        {
            //Create the target.
            Target target = new Target(location: new Point(_random.Next(_boardSize.Width - _defaultTargetSize),
                _random.Next(_boardSize.Height - _defaultTargetSize)),
                dimensions: new Size(_defaultTargetSize, _defaultTargetSize),
                fillColor: Color.Blue,
                textColor: Color.White,
                startingCountDown: startingCount);

            //Add and event handler for each target.
            target.CountExpired += new EventHandler(CountExpiredEventHandler);

            //Add the target drawing method to our delegate list. This is using shortcut syntax.
            _targetDraw += target.Draw;

            //Add the target to the ArrayList.
            _targets.Add(target);

            //Console.WriteLine(_gameLevel);      //Test code. Can remove later. DS
        }

        private void RemoveTarget(Target target)
        {
            if (target != null)
            {
                //Remove the event delegate handler for CountExpired event for this target.
                target.CountExpired -= this.CountExpiredEventHandler;

                //Remove the draw delegate from out delegate list.
                _targetDraw -= target.Draw;

                //Remove the target from the ArrayList.
                _targets.Remove(target);
            }
        }

        //Public methods.
        public void Update(Graphics graphics)
        {
            //Add the targets.
            if (_targets.Count == 0)
                while (_targets.Count  0)
                RemoveTarget(_targets[0] as Target);
        }
    }
}

 

Target Class


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Targets
{
    public class Target
    {
        //Public events and delegates.
        public event EventHandler CountExpired;
        public delegate void TargetDrawDelegate(Size boardSize, Graphics graphics);

        //Private class variables.
        private Point _location;
        private Size _dimensions;
        private Color _fillColor;
        private Color _textColor;
        private int _countDown;
        private int _lastTickCount;

        //Constructors.
        public Target(Point location, Size dimensions, Color fillColor, Color textColor, int startingCountDown)     //I changed the optional parameter startingCountDown to a required one. DS
        {
            _location = location;
            _dimensions = dimensions;
            _fillColor = fillColor;
            _textColor = textColor;
            _countDown = startingCountDown;
            _lastTickCount = System.Environment.TickCount;
        }

        //Public methods.
        public void Draw(Size boardSize, Graphics graphics)
        {
            //See if the countdown has expired.
            if (_countDown > 0)
            {
                if (System.Environment.TickCount - _lastTickCount > 1000)
                {
                    _countDown--;
                    _lastTickCount = System.Environment.TickCount;
                }

                //Draw the target.
                using (SolidBrush brush = new SolidBrush(_fillColor))
                    graphics.FillEllipse(brush, new Rectangle(_location, _dimensions));

                //Draw the countdown inside each ellipse.
                RectangleF boundingRectangle = new RectangleF(_location.X, _location.Y, _dimensions.Width, _dimensions.Height);
                using (Font font = new Font("Arial", 12, FontStyle.Bold))
                using (StringFormat stringFormat = new StringFormat())
                using (SolidBrush brush = new SolidBrush(_textColor))
                {
                    //Align the text horizontally and vertically.
                    stringFormat.Alignment = StringAlignment.Center;
                    stringFormat.LineAlignment = StringAlignment.Center;
                    graphics.DrawString(_countDown.ToString(), font, brush, boundingRectangle, stringFormat);
                }
            }
            else
                //Raise an event
                OnCountExpired(EventArgs.Empty);
        }

        public bool Hit(Point location)
        {
            return new Rectangle(_location, _dimensions).Contains(location);
        }

        //Protected event handlers.
        protected virtual void OnCountExpired(EventArgs e)
        {
            if (CountExpired != null)
                CountExpired(this, e);
        }
    }
}

 

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s