一个简单的扫雷
自己写下来,感觉一下思路过程。
1)画出整个雷区,每个小雷区都有一个横纵坐标(X,Y)
2)随机生成地雷点,同时根据是否是地雷,标记小雷区ISBomb属性
3)点击任一个小雷区的时候,首先判断该点是否是地雷。
如果是给出提示,遍历所有小雷区,将地雷标红
如果不是则判断周围(八个方向)是否有地雷
如果有则显示周围地雷个数
如果没有,则递归遍历周围(八个方向)所有小雷区,直接连接的没有挨着地雷的小雷区标记为不可用,一直到挨着地雷的小雷区为止(这里递归遍历的时候需要将已经遍历过的标记为不可用,不然会堆栈溢出)
检查整个雷区所有仍然可用并且没有显示挨着几个雷的小雷区的个数,如果同雷数相同,则提示闯关成功。
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 MineClearance { public partial class MineClearance : Form { public List<Mine> mines = new List<Mine>(); public List<Mine> Allmines = new List<Mine>(); public int X_mine; public int Y_mine; public int mineNum; public int GameTime; public MineClearance() { InitializeComponent(); } private void MineClearance_Load(object sender, EventArgs e) { X_mine = Convert.ToInt16(this.cbHorizontal.Text); Y_mine = Convert.ToInt16(cbVertical.Text); mineNum = Convert.ToInt16(cbMine.Text); InitializeGame(); timer.Start(); } public void ReLoadGame() { X_mine = Convert.ToInt16(this.cbHorizontal.Text); Y_mine = Convert.ToInt16(cbVertical.Text); mineNum = Convert.ToInt16(cbMine.Text); panelMine.Controls.Clear(); InitializeGame(); GameTime = 0; timer.Start(); } public void InitializeGame() { Allmines.Clear(); for (int v = 0; v < Y_mine; v++) { for (int h = 0; h < X_mine; h++) { Mine mine = CreateMine(h,v); mine.tag = h + "," + v; panelMine.Controls.Add(mine); Allmines.Add(mine); } } CreatBomb(X_mine, Y_mine, mineNum); } /// <summary> /// 创建所有的雷阵,将点击和触雷处理方法 绑定到事件。 /// </summary> /// <param name="x">横坐标</param> /// <param name="y">纵坐标</param> /// <returns></returns> public Mine CreateMine(int x, int y) { Mine mine = new Mine(x,y); mine.Location = new System.Drawing.Point(5 + x * 30, 5 + y * 30); mine.Size = new System.Drawing.Size(28, 28); mine.ButtonClickEvent += new Mine.ButtonClickEventHander(MineClick); mine.TripwireBombEvent += new Mine.TripwireBombEventHander(TripwireBomb); mine.ComplateEvent+=new Mine.ComplateEventHander(ComplateEvent); return mine; } /// <summary> /// 创建一个地雷集合 /// </summary> /// <param name="num">地雷总数</param> /// <returns>返回一个地雷集合</returns> public void CreatBomb(int maxXValue, int maxYValue, int num) { mines.Clear(); bool ifExist = false; for (int i = 0; i < num; i++) { ifExist = false; int x = new Random(GetRandomSeed()).Next(1, maxXValue); int y = new Random(GetRandomSeed()).Next(1, maxYValue); Mine m = new Mine(x, y); m.IsBomb = true; m.tag = x + "," + y; foreach (Mine mine in mines) { if (m.tag == mine.tag) { i--; ifExist = true; break; } } if (ifExist != true) { mines.Add(m); int number = Convert.ToInt16(cbHorizontal.Text) * y + x; //先求得当前炸弹,在所有雷阵中的位置,然后将其属性设置为雷。 Allmines[number].IsBomb = true; } } } /// <summary> /// 产生随机数 /// 因为为随机数产生,在很短时间内会得到同样的值 /// 另一种解决方法,是延时一段时间 /// </summary> /// <returns></returns> static int GetRandomSeed() { byte[] bytes = new byte[4]; System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); rng.GetBytes(bytes); return BitConverter.ToInt32(bytes, 0); } /// <summary> /// 得到按钮 m 周围所有炸弹个数 /// </summary> /// <param name="m">所要求得的按钮</param> /// <returns>按钮个数</returns> public int NearHaveBomb(Mine m) { int num = 0; if (IsBomb(m.X, m.Y - 1)) num++; if (IsBomb(m.X, m.Y + 1)) num++; if (IsBomb(m.X - 1, m.Y)) num++; if (IsBomb(m.X + 1, m.Y)) num++; if (IsBomb(m.X - 1, m.Y - 1)) num++; if (IsBomb(m.X - 1, m.Y + 1)) num++; if (IsBomb(m.X + 1, m.Y - 1)) num++; if (IsBomb(m.X + 1, m.Y + 1)) num++; return num; } /// <summary> /// 通过横纵坐标确定是否是炸弹 /// </summary> /// <param name="X"></param> /// <param name="Y"></param> /// <returns></returns> public bool IsBomb(int X,int Y) { if (X < 0 || Y < 0 || X >= X_mine || Y >= Y_mine ) return false; int number = Convert.ToInt16(cbHorizontal.Text) * Y + X; if (Allmines[number].IsBomb == true) { return true; } return false; } /// <summary> /// 获得参数提供按钮周围炸弹数 /// 1)炸弹数不为0,则显示炸弹数 /// 2)炸弹数为0 ,则递归清空所临的空白处。并且在边界上显示所临近的炸弹数 /// </summary> /// <param name="m">要求去检测的炸弹</param> public void MineClick(Mine m) { //获得临近炸弹数 int number = NearHaveBomb(m); if (number != 0) { m.btnMine.Text = number.ToString(); return; } else { m.Enabled = false; //需要及时设置enable属性为false,不然会造成堆栈溢出 int X = m.X; int Y = m.Y; ValidityCheck(Y - 1,X); ValidityCheck(Y + 1,X); ValidityCheck(Y,X-1); ValidityCheck(Y,X+1); ValidityCheck(Y-1,X+1); ValidityCheck(Y+1,X+1); ValidityCheck(Y-1,X-1); ValidityCheck(Y+1,X-1); } } /// <summary> /// 去检测上下左右,左上,左下,右上,右下八个方向是否真实有效 /// </summary> /// <param name="Y">上下边界</param> /// <param name="X">左右边界</param> void ValidityCheck(int Y,int X) { if(Y >= 0 && Y< Y_mine && X >= 0 && X <X_mine) { int num = X_mine * Y + X; if (Allmines[num].Enabled == true) this.MineClick(Allmines[num]); } } /// <summary> /// 如果猜中地雷,则遍历所有的地雷,将地雷标红。 /// </summary> public void TripwireBomb() { int ItemNum = Allmines.Count; for(int i = 0;i<ItemNum;i++) { if(Allmines[i].Enabled != false) { if (Allmines[i].IsBomb == true) { Allmines[i].btnMine.BackColor = Color.Red; } Allmines[i].Enabled = false; } } timer.Stop(); Application.DoEvents(); if (DialogResult.Yes == MessageBox.Show("是否重新开始?", "提示信息", MessageBoxButtons.YesNo)) { ReLoadGame(); } } /// <summary> /// 重新加载游戏 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnRetry_Click(object sender, EventArgs e) { ReLoadGame(); } public void ComplateEvent() { int n = 0; for (int i = 0; i < Allmines.Count; i++) { if (Allmines[i].Enabled == true && Allmines[i].btnMine.Text == "") { n++; } } if (n == mineNum) { for (int i = 0; i < Allmines.Count; i++) { if (Allmines[i].Enabled == true && Allmines[i].btnMine.Text == "") { Allmines[i].ForeColor = Color.Red; Allmines[i].BackColor = Color.Red; } else { Allmines[i].BackColor = Color.Green; } } timer.Stop(); GameTime = 0; if (MessageBox.Show("真棒!在开一局?", "提示信息", MessageBoxButtons.YesNo) == DialogResult.Yes) { ReLoadGame(); } } } private void timer_Tick(object sender, EventArgs e) { GameTime++; this.lblTime.Text = GameTime.ToString(); } } }
雷定义
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; namespace MineClearance { public partial class Mine : UserControl { //监视按键点击 public delegate void ButtonClickEventHander(Mine m); public event ButtonClickEventHander ButtonClickEvent; //监视触发炸弹 public delegate void TripwireBombEventHander(); public event TripwireBombEventHander TripwireBombEvent; //完成扫雷检测 public delegate void ComplateEventHander(); public event ComplateEventHander ComplateEvent; /// <summary> /// 定义雷的坐标 /// </summary> private int _X; private int _Y; private string _tag; private bool _IsBomb; public int X { get { return _X; } set { this._X = value; } } public int Y { get { return _Y; } set { this._Y = value; } } public string tag { get { return _tag; } set { this._tag = value; } } public bool IsBomb { get { return _IsBomb; } set { _IsBomb = value; } } public Mine(int x, int y) { InitializeComponent(); this.X = x; this.Y = y; } /// <summary> /// 用户点击后事件 /// 1)确认点击按钮是否是炸弹 /// 2)如果是炸弹,给出提示信息 /// 3)如果不是炸弹得到所临炸弹数,并且显示出来,锁定按钮 /// 4)如果所临位置没有没有炸弹,清空周围没有挨着炸弹的按钮锁定。 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnMine_Click(object sender, EventArgs e) { if (this.IsBomb == true) { MessageBox.Show("嘣,很不幸你中弹了!"); this.btnMine.ForeColor = Color.Red; this.btnMine.BackColor = Color.Red; this.Enabled = false; TripwireBombEvent(); return; } else { ButtonClickEvent(this); ComplateEvent(); } } } }