C#版扫雷游戏

知识点:

  1.  枚举类型的使用
  2. 当某个方法暂时不想去实现的时候,最好在方法体中写上:throw new NotImplementedException()
  3. 如何将图片资源引入到项目的属性中去
  4. 设置控件的大小:pane.Size = new Size(paneWidth,paneHeight);
  5. 设置控件的位置:pane.Location = new Point(paneLeft,paneTop);
  6. 事件的动态绑定:pane.MouseDown += pane_MouseDown;
  7. 如何定义事件并引发事

          public delegate void MineSweepedSuccessEventHandler(object sender,EventArgs e);
          public event MineSweepedSuccessEventHandler MineSweepedSucces;

Form1(主窗体)代码: 

View Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 扫雷游戏
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //注意:只有将自定义控件MineField拖到Form1中,才可以使用
            this.mineField1.Init(10,8);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.mineField1.DisplayAll();
        }

    }
}

 

Pane.cs(继承自Button控件): 

View Code
/***************************************************************
 * 当前类用于封装方格,该方格继承自button控件(考虑到雷区中方格类似按钮)
 * 共有三个属性,三个方法
 * 注意事项:
 * 1.当某个方法暂时没去实现的时候,可以在方法体中写上:throw new NotImplementedException();
 * 2.表示方格状态的时候,使用到了枚举类型
 * *************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace 扫雷游戏
{
    /// <summary>
    /// 封装方格
    /// </summary>
    public class Pane:Button
    {
        /// <summary>
        /// 设置或获取当前方格是否埋有地雷
        /// </summary>
        public bool hasMine{get;set;}
        /// <summary>
        /// 设置或获取当前方格周围的地雷数
        /// </summary>
        public int aroundMineNums { get; set; }

        /// <summary>
        /// 获取或设置当前方格的状态
        /// </summary>
        public PaneState State{get;set;}

        public Pane()
        {
            this.BackgroundImageLayout = ImageLayout.Stretch;//设置方格的背景图以平铺的方式展现
        }
        /// <summary>
        /// 打开方格
        /// </summary>
        public void Open()
        {
            if (this.hasMine)//踩雷
            {
                this.BackgroundImage = Properties.Resources.MineBomp;
                this.Enabled=false;
            }
            else 
            {
                switch (this.aroundMineNums)
                {
                    case 0:
                        this.BackgroundImage=null;                       
                        break;
                    case 1:
                        this.BackgroundImage=Properties.Resources.Num1;
                        break;
                    case 2:
                        this.BackgroundImage=Properties.Resources.Num2;
                        break;
                    case 3:
                        this.BackgroundImage=Properties.Resources.Num3;
                        break;
                    case 4:
                        this.BackgroundImage=Properties.Resources.Num4;
                        break;
                    case 5:
                        this.BackgroundImage=Properties.Resources.Num5;
                        break;
                    case 6:
                        this.BackgroundImage=Properties.Resources.Num6;
                        break;
                    case 7:
                        this.BackgroundImage=Properties.Resources.Num7;
                        break;
                    case 8:
                        this.BackgroundImage=Properties.Resources.Num8;
                        break;
                }
                this.Enabled=false;
            }
        }
        /// <summary>
        /// 标记方格
        /// </summary>
        public void Mark()
        {
            this.BackgroundImage = Properties.Resources.Marked;
            this.State = PaneState.Marked;
        }
        /// <summary>
        /// 重置标记的方格
        /// </summary>
        public void Reset()
        {
            this.BackgroundImage = null;
            this.State = PaneState.Closed;
        }
    }
    /// <summary>
    /// 枚举类型
    /// 方格状态
    /// </summary>
    public enum PaneState 
    {
        /// <summary>
        /// 关闭状态
        /// </summary>
        Closed,
        /// <summary>
        /// 打开状态
        /// </summary>
        Opened,
        /// <summary>
        /// 标记状态
        /// </summary>
        Marked,
    }
}

 

用户自定义控件:MineField类(雷区) 

View Code
/***************************************************************
 * 自定义用户控件
 * 设计的时候,并没有往“雷区”中添加控件(即:小方格),控件主要是在运行的时候动态加载进来的
 * 要点:
 * 1.如何定义事件并引发事件
 * 2.综合考虑代码的可读性、可重用性,可将LayoutPane()方法独立出来
 * 3.设置方格的大小:pane.Size = new Size(paneWidth,paneHeight);
 * 4.设置方格的位置:pane.Location = new Point(paneLeft,paneTop);
 * 5.考虑到之后可能会对随机算法进行改变,故将随机布雷的方法抽象出来
 * 6.动态的事件绑定
 * *************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 扫雷游戏
{
    public partial class MineField : UserControl
    {
        /// <summary>
        /// 定义一个扫雷成功引发的事件
        /// </summary>
        /// <param name="sender">事件的发起者</param>
        /// <param name="e">事件向外部传递的参数</param>
        public delegate void MineSweepedSuccessEventHandler(object sender,EventArgs e);
        public event MineSweepedSuccessEventHandler MineSweepedSucces;

        /// <summary>
        /// 定义一个扫雷失败引发的事件
        /// </summary>
        /// <param name="sender">事件的发起者</param>
        /// <param name="e">事件向外部传递的参数</param>
        public delegate void MineSweepedFailEventHandler(object sender,EventArgs e);
        public event MineSweepedFailEventHandler MineSweepedFail;

        public MineField()
        {
            InitializeComponent();
        }
        
        /// <summary>
        /// 初始化雷区(即:将小方格动态加载到雷区中)
        /// </summary>
        /// <param name="paneLineCount">一个方向过去方格的数量</param>
        /// <param name="mineCount">整个雷区中地雷的数量</param>
        public void Init(int paneLineCount,int mineCount)
        {
            //根据指定的数量构建雷区中的方格
            for (int i = 0; i < paneLineCount * paneLineCount;i++ )
            {
                Pane pane = new Pane();
                this.Controls.Add(pane);//将自定义用户控件(方格)添加到"雷区"中
            }
            //布局雷区中方格的位置
            this.LayoutPane();
            //随机布雷
            this.LayoutMines(mineCount);
            //设置每个方格四周的地雷数
            foreach(Pane pane in this.Controls)
            {
                pane.aroundMineNums = this.GetAroundMineCount(pane);
            }
        }

        /// <summary>
        /// 布局雷区中的所有方格的位置
        /// </summary>
        public void LayoutPane()
        {
            //初始化时,也会触发MineField_SizeChanged事件,而此时,雷区中的方格数为0
            if(this.Controls.Count==0)
            {
                return;
            }
            int paneLineCount=(int)Math.Sqrt(this.Controls.Count);//获取一行或一列下来方格的数量
            int paneWidth=this.Width/paneLineCount;//方格的宽度
            int paneHeight=this.Height/paneLineCount;//方格的高度
            int paneIndex = 0;//方格的索引
            int paneTop = 0;
            int paneLeft = 0;
            for(int row=0;row<paneLineCount;row++)
            {
                paneTop = row * paneHeight;//方格的TOP
                for (int col = 0; col < paneLineCount; col++)
                {
                    paneLeft = col * paneWidth;
                    Pane pane = this.Controls[paneIndex] as Pane;
                    //为每个方格动态的绑定MouseDown事件,当触发该事件时,会调用pane_MouseDown方法
                    //这其实是一个委托,将MouseDonw事件委托给pane_MouseDown方法
                    pane.MouseDown += pane_MouseDown;
                    //设置方格的大小                    
                    pane.Size = new Size(paneWidth,paneHeight);
                    //设置方格的位置
                    pane.Location = new Point(paneLeft,paneTop);
                    //索引递增
                    paneIndex++;
                }
            }
        }
        /// <summary>
        /// 响应pane被鼠标点击时的操作
        /// </summary>
        /// <param name="sender">事件的发起对象</param>
        /// <param name="e"></param>
        private void pane_MouseDown(object sender, MouseEventArgs e)
        {
            Pane pane = sender as Pane;
            this.DisplayAround(pane);    
        }

        private void DisplayAround(Pane currentPane)
        {
            if(currentPane.State==PaneState.Opened||currentPane.hasMine)
            {
                return;
            }
            currentPane.Open();
            List<Pane> panes = this.GetAroundPanes(currentPane);//得到当前pane四周的所有pane
            foreach(Pane p in panes)
            {
                if (this.GetAroundMineCount(p) == 0)
                {
                    DisplayAround(p);
                }
                else
                {
                    if(p.State!=PaneState.Opened && !currentPane.hasMine)
                    {
                        p.Open();
                    }
                }
            }
        }

        public void DisplayAll()
        { 
            foreach(Pane pane in this.Controls)
            {
                if(pane.State!=PaneState.Opened)
                {
                    pane.Open();
                }
            }
        }
        /// <summary>
        /// 获取指定的方格四周的地雷数
        /// </summary>
        private int GetAroundMineCount(Pane pane)
        {
            List<Pane> panes = this.GetAroundPanes(pane);
            int mineCount = 0;
            foreach(Pane p in panes)
            {
                if(p.hasMine)
                {
                    mineCount++;
                }
            }
            return mineCount;
        }
        /// <summary>
        /// 获取指定的方格四周的所有方格
        /// </summary>
        /// <returns></returns>
        private List<Pane> GetAroundPanes(Pane pane)
        { 
            List<Pane> result=new List<Pane>();
            foreach(Pane p in this.Controls)
            {
                //左右方向
                if(p.Top==pane.Top&&Math.Abs(p.Left-pane.Left)==p.Width)
                {
                    result.Add(p);
                }
                //上下方向
                if(p.Left==pane.Left&&Math.Abs(p.Top-pane.Top)==p.Height)
                {
                    result.Add(p);
                }
                //45度斜角方向
                if(Math.Abs(p.Top-pane.Top)==p.Height&&Math.Abs(p.Left-pane.Left)==p.Width)
                {
                    result.Add(p);
                }
            }
            return result;
        }
        /// <summary>
        /// 随机布雷:将HasMine的属性改为true即可
        /// </summary>
        /// <param name="mineCOunt">地雷的数量</param>
        private void LayoutMines(int mineCount)
        {
            Random rand = new Random();
            for (int i = 0; i < mineCount;i++ )
            {
                int mineIndex=rand.Next(0,this.Controls.Count);
                Pane pane=this.Controls[mineIndex] as Pane;
                pane.hasMine = true;
            }
        }
        /// <summary>
        /// 当雷区的区域大小改变时,重新布置雷区中方格的大小和位置
        /// </summary>
        /// <param name="sender">事件的发起者</param>
        /// <param name="e"></param>
        private void MineField_SizeChanged(object sender, EventArgs e)
        {
            this.LayoutPane();
        }
    }
}

 

posted @ 2012-12-17 17:54  曾是土木人  阅读(2444)  评论(0编辑  收藏  举报