Click here to Skip to main content
15,891,184 members
Articles / Programming Languages / C#
Article

A Simple Mine Sweeping Game

Rate me:
Please Sign up or sign in to vote.
4.08/5 (10 votes)
22 Sep 2008CPOL 53.5K   2.4K   38   11
A simple mine sweeping game using C#
Image 1

Introduction

This article is a simple example for beginners who are just learning C#. Windows's winmine is an interesting game, so I like to write this game to learn the following things:

  1. How to use observer pattern in your programming
  2. How to use DataGridView and its event
  3. How to use delegate
  4. How to make usercontrol

Using the Code

The main classes and interface are given below.

Interfaces

C#
public interface IClick
{
    //to set mine flag 
    bool RightClick(int x, int y, Presenter pr);
    //whether trigger mine
    bool LeftClick(int x, int y, Presenter pr);

    //double click
    bool BothClick(int x, int y, Presenter pr);
}
public interface ICoordinate
{
    int XCoordinate { get;}
    int YCoordinate { get;}
    void SetCoordinate(int x, int y);
}

public interface IGetSetValue
{
    void Initialization();
    int Lines { get;set;}
    int Columns { get;set;}
    int MineNums { get;set;}
}
public interface IOberverable
{
    void Register(IObserver ober);
    void UnRegister(IObserver ober);
}
public interface IObserver
{
    void Update(EventArgs brgs);
}

Main Class

The SweepingMap class defines form's width, height and number of mines, the main methods of the class are LeftClick which means mouse's left click on DataGridView cells, RightClick which means mouse's right click on DataGridView cells, BothClick means mouse's double click on DataGridView cells.

C#
public class SweepingMap : IClick, IGetSetValue
    {
        #region Private Field
        /// <summary>
        /// to store mines
        /// </summary>
        private readonly ObjectList mines;
        /// <summary>
        /// to store the mines which are found
        /// </summary>
        private readonly ObjectList flags;
        private readonly ObjectList hasAccessed;
        private int lines;
        private int columns;
        private int mineNums;
        private Random ran = new Random();
        #endregion
        #region Public Attributes
        public ObjectList Mines
        {
            get
            {
                return this.mines;
            }
        }
        public ObjectList Flags
        {
            get
            {
                return this.flags;
            }
        }
        public int Lines
        {
            get
            {
                return this.lines;
            }
            set
            {
                this.lines = value;
            }
        }
        public int Columns
        {
            get
            {
                return this.columns;
            }
            set
            {
                this.columns = value;
            }
        }
        public int MineNums
        {
            get
            {
                return this.mineNums;
            }
            set
            {
                this.mineNums = value;
            }
        }
        #endregion
        #region Constructor
        public SweepingMap()
        {
            this.mines = new ObjectList();
            this.flags = new ObjectList();
            hasAccessed = new ObjectList();
        }
        #endregion
        #region Private Methods
        private int CertainCondition(int x, int y, Condition condition)
        {
            int count = 0;
            for (int i = x - 1; i <= x + 1; i++)
            {
                for (int j = y - 1; j <= y + 1; j++)
                {
                    if ((i >= 0 && i < Lines) && (j >= 0 && 
			j < Columns) && !(i == x && j == y))
                    {
                        if (condition(i, j))
                            count++;
                    }
                }
            }        
            return count;
        }
        private bool CheckSuccess()
        {
            int count = 0;
            foreach (GridObject f in this.Flags.ObjectTable.Keys)
            {
                foreach (GridObject m in this.Mines.ObjectTable.Keys)
                {
                    if (this.Flags.ObjectTable[f].Equals(this.Mines.ObjectTable[m]))
                        count++;
                }
            }
            if (count == this.Mines.ObjectTable.Count)
                return true;
            else
                return false;
        }
        #endregion
        #region Public Methods
        public void Initialization()
        {
            this.mines.Clear();
            this.flags.Clear();
            this.hasAccessed.Clear();
            for (int i = 0; i < this.MineNums; i++)
            {
                int x = ran.Next(this.Lines);
                int y = ran.Next(this.Columns);
#if Debug
                System.Diagnostics.Debug.Print("Coordinate:({0},{1}) \n", x, y);
#endif
                if (!this.mines.ContainsKey(x, y))
                    this.mines.Add(x, y);
                else
                    i -= 1;
            }
        }
        //to set mine flag 
        public bool RightClick(int x, int y, Presenter pr)
        {
            if (this.flags.ContainsKey(x, y))
            {
                this.flags.Remove(x, y);
                pr(x, y, true);
            }
            else
            {
                if (!this.hasAccessed.ContainsKey(x, y) && 
				!this.flags.ContainsKey(x, y))
                {
                    this.flags.Add(x, y);
                    pr(x, y, false);
                }
            }
            if (this.Flags.Length == this.Mines.Length)
            {
                if (CheckSuccess())
                {
                    return true;
                }
            }
            return false;
        }
        //whether trigger mine
        public bool LeftClick(int x, int y, Presenter pr)
        {
            if (!this.Flags.ContainsKey(x, y))
            {
                if (this.Mines.ContainsKey(x, y))
                {
                    pr(x, y, "Mine");
                    return false;
                }
                else
                {
                    GridTraversing(x, y, pr);
                    return true;
                }
               
            }
            return true;
        }
        private void GridTraversing(int x, int y, Presenter pr)
        {
            Stack<GridObject> stack = new Stack<GridObject>();
            GridObject tmp = new GridObject(x, y);
            stack.Push(tmp);
            Condition condition = new Condition(this.Mines.ContainsKey);
            while (!(stack.Count == 0))
            {
                tmp = stack.Pop();
                int num = CertainCondition(tmp.XCoordinate, tmp.YCoordinate, condition);
                hasAccessed.Add(tmp.XCoordinate, tmp.YCoordinate);
                if (num == 0)
                {
                    pr(tmp.XCoordinate, tmp.YCoordinate, num);
                    for (int i = tmp.XCoordinate - 1; i <= tmp.XCoordinate + 1; i++)
                    {
                        for (int j = tmp.YCoordinate - 1; j <= tmp.YCoordinate + 1; j++)
                        {
                            if (i >= 0 && i < Lines && j >= 0 && j < Columns 
                                && !hasAccessed.ContainsKey(i, j) 
                                && !this.Flags.ContainsKey(i, j))
                            {
                                pr(i, j, num);
                                hasAccessed.Add(i,j);
                                stack.Push(new GridObject(i, j));
                            }
                        }
                    }
                }
                else
                {
                    pr(tmp.XCoordinate, tmp.YCoordinate, num);
                }
            }
        }
        //double click
        public bool BothClick(int x, int y, Presenter pr)
        {
            Condition condition = new Condition(this.Mines.ContainsKey);
            int num = CertainCondition(x, y, condition);
            condition = new Condition(this.flags.ContainsKey);
            int count = CertainCondition(x, y, condition);
            if (count == 0)
                return true;
            if (num == count)
            {
                for (int i = x - 1; i <= x + 1; i++)
                {
                    for (int j = y - 1; j <= y + 1; j++)
                    {
                        if (i >= 0 && i < Lines && j >= 0 && j < Columns 
                           && !(i == x && j == y))
                        {
                            if (!(this.flags.ContainsKey(i, j))) 
                            {
                                if (!LeftClick(i, j, pr))
                                    return false;
                            }
                        }
                    }
                }
            }
            return true;
        }
        public void DisplayAllMines(int x, int y, Presenter pr)
        {
            int i, j;
            foreach (GridObject cur in mines.ObjectTable.Keys)
            {
                i = mines.ObjectTable[cur].XCoordinate;
                j = mines.ObjectTable[cur].YCoordinate;
                if (i != x && j != y)
                    pr(i, j, mines.ObjectTable[cur]);
            }
        }
        #endregion
    }

History

  • 22nd September, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer acca
China China
I love to communicate with eachother.

Comments and Discussions

 
GeneralMy vote of 1 Pin
geniaz128-Jul-09 1:36
geniaz128-Jul-09 1:36 
GeneralDownload Invalid Pin
Bill Warner24-Sep-08 6:46
Bill Warner24-Sep-08 6:46 
GeneralRe: Download Invalid Pin
vivounicorn24-Sep-08 14:48
vivounicorn24-Sep-08 14:48 
Generalsuggestions Pin
RednaxelaFX23-Sep-08 6:40
RednaxelaFX23-Sep-08 6:40 
Hey there. It's good to see yet another mine sweep implementaion. It sure runs Poke tongue | ;-P
Haven't digged deep into your code yet, but I guess there's a good space for improvements.

First of all, there are quite a few typos in your code, like "IOberverable" (IObserverable), and it's pretty irritating. What's a "BothClick" with a comment "double click" anyway?

In .NET, a property is one with a getter and/or a setter, while an Attribute is what you use to mark/modify different parts of the code. I know it's easy to mix them up because the same word is used for both of them in Chinese.

There are a few places that does nothing useful at all.

GridObject.cs:
public override int GetHashCode( )
{
    return base.GetHashCode( );
}

It's okay to remove methods like this altogether.

You're using GridObjects in Dictionary<,> as keys, to make your life easier, GridObject should implement IEqualityComparer<GridObject>, so that you don't have to cast them into object whenever you're searching in the Dictionary<,>. When you're searching like this:

ObjectList.cs:
foreach ( GridObject cur in this.objectTable.Keys )
{
    if ( tmp.Equals( cur as object ) )
        return objectTable[ cur ];
}

the search becomes linear, and you loose the point of using a Dictionary<,> (which is a generic hash map).

Implementing GridObject.Equals() via reflection is not really a good idea, since you're not trying to write a generic equality comparer. There are only 3 fields in GridObject, comparing them takes 3 lines of code, while your implementation via reflection takes more code with worse perfoemance. And even with reflection, this cast could have been done away:
thisResult = ( ( FieldInfo ) thisFields[ i ] ).GetValue( thisObj );
and turned into this:
thisResult = thisFields[ i ].GetValue( thisObj );

You don't have to explictly call a delegate's constructor when using a method as a delegate, the compiler will infer that for you. Which makes:
getSetControl.BothClick( x, y, new Presenter( SetGridImage ) )
turn into:
getSetControl.BothClick( x, y, SetGridImage )

The code feels a bit Java-ish. I'm not sure why, maybe it just came from the first impressions. Or maybe it's because the way you're using delegates...for some of them, you could have used custom events instead.

Well, I'll just stop here. Good luck programming Smile | :)

- RednaxelaFX
GeneralRe: suggestions Pin
vivounicorn23-Sep-08 15:20
vivounicorn23-Sep-08 15:20 
QuestionLIAR? -- Nope! Just mis-labled downloads [modified] Pin
Brad Bruce23-Sep-08 5:53
Brad Bruce23-Sep-08 5:53 
AnswerRe: LIAR? Pin
f r i s c h23-Sep-08 5:58
f r i s c h23-Sep-08 5:58 
GeneralRe: LIAR? Pin
RednaxelaFX23-Sep-08 6:42
RednaxelaFX23-Sep-08 6:42 
GeneralRe: LIAR? Pin
Brad Bruce23-Sep-08 7:52
Brad Bruce23-Sep-08 7:52 
GeneralRe: LIAR? Pin
Humble Programmer23-Sep-08 8:03
Humble Programmer23-Sep-08 8:03 
GeneralRe: LIAR? Pin
Brad Bruce23-Sep-08 8:53
Brad Bruce23-Sep-08 8:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.