一、概述
最近买了心机NOKIA6300,上面有一款扫雷游戏,闲暇时光总是在玩,玩难度的级别还挺难过的。呵呵
后来就想使用C#写个扫雷程序玩玩,游戏玩多了,游戏的思想就不知不觉的有了,下面所有的代码是没有任何参考的情况下,自己写出来的,有许多瑕疵,程序也没经过什么优化,主要的算法思想其实就那么几行。
先给出程序的运行界面,要添加一些控件,这里就不啰嗦了,程序本来有源码,但是,为了方便,我将所有的代码都写在了Form1中(这个习惯不好,嘿嘿)。
二、编程思路
程序主要的是Bound这个类,继承了表单中的Button类,我感觉就用按钮来模拟类比较快,仅仅是考虑快,也许有其他的方法,主要是因为Button控件本身就有单击事件,扫雷就需要这两个事件,如果自己写,可能要写这两个事件。
雷阵需要有一个数组雷存储其状态,代码中都有说明了,数组中的每个下标都和雷有对应关系,所以,我给所有的雷添加了X,Y的属性,这个做是方便雷的定位,在计算周围雷数时非常有帮助。
添加了雷阵数组后,有两个事情比较重要:一、随机生成雷的分布,这个算法有很多,我是用自己感觉比较简单的算法,将所有的雷随机的放在没有雷的位置上,就这样。二、计算周围雷数;在放置了雷后,要计算这个雷周围的数字,一共是八个位置,其实很简单,只要有雷的位置,它的周围框都加一就好了,但是注意判断条件就好了,只要这些框在0-MAX的范围内就好了。
在初始化了雷阵后,剩下的就简单了,添加雷的左击时间和右击时间,判断是否踩到雷,是否扫到雷等等。
在扫雷中还有一个比较重要的算法是对扫雷的递归,在扫雷游戏中,如果一个雷上的周围雷数是零,就可以将它周围的所有的格子翻开,(因为没有雷嘛),和计算周围雷数的方法一样,只是这里调用的是递归的方法,只要给按钮设置好是否已经翻开的状态就好了,翻开的按钮就不进行递归操作了。
public partial class Form1 : Form { public Form1() { InitializeComponent(); } //炸弹类 public class Bound : System.Windows.Forms.Button { private int _Flag; //标志 0未翻开 1翻开 2标记 private int m_status; //状态 0无炸弹 1有炸弹 private int m_Nabar; //周围的炸弹数量 private int _x; private int _y; public Bound(int x, int y) { _Flag = 0; status = 0; Nabar = 0; this._x = x; this._y = y; } public int Flag { get { return _Flag; } set { _Flag = value; } } public int status { get { return m_status; } set { m_status = value; } } public int Nabar { get { return m_Nabar; } set { m_Nabar = value; } } public int x { get { return _x; } set { _x = value; } } public int y { get { return _y; } set { _y = value; } } } //矩阵大小 private const int quart = 12; private static int[,] bounds = new int[quart, quart]; //存放雷的数组 private static Bound[,] bound; private static int Max_Bound_Count = 15; //炸弹的总量 private static int ClearBounds = 0; //成功清除炸弹数量 private static DateTime startTime; //游戏开始时间 private void Form1_Load(object sender, EventArgs e) { startTime = DateTime.Now; int i, j; int bound_count; //产生雷阵数组 Random rnd = new Random(); for (i = 0; i < quart; i++) for (j = 0; j < quart; j++) { bounds[i, j] = 0; } bound_count = 0; while (bound_count < Max_Bound_Count) { i = rnd.Next(quart); j = rnd.Next(quart); if (bounds[i, j] == 0) { bounds[i, j] = 1; bound_count++; } } bound = new Bound[quart, quart]; //初始化所有雷 for (i = 0; i < quart; i++) for (j = 0; j < quart; j++) { bound[i, j] = new Bound(i, j); bound[i, j].status = bounds[i, j]; //面板的宽度不变,修改每个雷的长和宽 bound[i, j].Width = panel1.Width / quart; bound[i, j].Height = panel1.Height / quart; float currentSize; //currentSize = bound[i,j].Font.Size; currentSize = 9F; bound[i, j].Font = new Font(bound[i, j].Font.Name, currentSize, bound[i, j].Font.Style, bound[i, j].Font.Unit); //分布雷的位置 bound[i, j].Left = i * bound[i, j].Width; bound[i, j].Top = j * bound[i, j].Height; //bound[i, j].Text = i.ToString(); //扫雷事件注册 bound[i, j].Click += new EventHandler(Btn_Click); bound[i, j].MouseUp += new MouseEventHandler(BtnRight_Click); this.panel1.Controls.Add(bound[i, j]); } //计算周围雷的数量 for (i = 0; i < quart; i++) for (j = 0; j < quart; j++) { if (bounds[i, j] == 1) { if (i - 1 >= 0 && j - 1 >= 0) bound[i - 1, j - 1].Nabar++; if (j - 1 >= 0) bound[i, j - 1].Nabar++; if (i + 1 <= quart - 1 & j - 1 >= 0) bound[i + 1, j - 1].Nabar++; if (i - 1 >= 0) bound[i - 1, j].Nabar++; if (i + 1 <= quart - 1) bound[i + 1, j].Nabar++; if (i - 1 >= 0 && j + 1 <= quart - 1) bound[i - 1, j + 1].Nabar++; if (j + 1 <= quart - 1) bound[i, j + 1].Nabar++; if (i + 1 <= quart - 1 && j + 1 <= quart - 1) bound[i + 1, j + 1].Nabar++; } } } private void ReStartGame() { int i, j; int bound_count; //清除的雷数清0 ClearBounds = 0; startTime = DateTime.Now; //产生雷阵数组 Random rnd = new Random(); for (i = 0; i < quart; i++) for (j = 0; j < quart; j++) { bounds[i, j] = 0; } bound_count = 0; while (bound_count < Max_Bound_Count) { i = rnd.Next(quart); j = rnd.Next(quart); if (bounds[i, j] == 0) { bounds[i, j] = 1; bound_count++; } } for (i = 0; i < quart; i++) for (j = 0; j < quart; j++) { bound[i, j].Nabar = 0; bound[i, j].status = bounds[i, j]; bound[i, j].Flag = 0; bound[i, j].FlatStyle = FlatStyle.Standard; bound[i, j].BackColor = System.Drawing.SystemColors.Control; bound[i, j].Text = ""; Image img = null; bound[i, j].BackgroundImage = img; } //计算周围雷的数量 for (i = 0; i < quart; i++) for (j = 0; j < quart; j++) { if (bounds[i, j] == 1) { if (i - 1 >= 0 && j - 1 >= 0) bound[i - 1, j - 1].Nabar++; if (j - 1 >= 0) bound[i, j - 1].Nabar++; if (i + 1 <= quart - 1 & j - 1 >= 0) bound[i + 1, j - 1].Nabar++; if (i - 1 >= 0) bound[i - 1, j].Nabar++; if (i + 1 <= quart - 1) bound[i + 1, j].Nabar++; if (i - 1 >= 0 && j + 1 <= quart - 1) bound[i - 1, j + 1].Nabar++; if (j + 1 <= quart - 1) bound[i, j + 1].Nabar++; if (i + 1 <= quart - 1 && j + 1 <= quart - 1) bound[i + 1, j + 1].Nabar++; } } } //左击排雷 private void Btn_Click(object sender, EventArgs e) { Bound vbound = (Bound)sender; //如果不是未翻开状态,则退出 if (vbound.Flag != 0) return; vbound.Flag = 1; vbound.FlatStyle = FlatStyle.Flat; if (vbound.status == 0) { //bound.BackColor = System.Drawing.Color.Red; //当前的周围没有雷,就进行递归的排雷 if (vbound.Nabar == 0) { vbound.BackColor = System.Drawing.Color.White; int i = vbound.x; int j = vbound.y; if (i - 1 >= 0 && j - 1 >= 0) Btn_Click(bound[i - 1, j - 1], e); if (j - 1 >= 0) Btn_Click(bound[i, j - 1], e); if (i + 1 <= quart - 1 & j - 1 >= 0) Btn_Click(bound[i + 1, j - 1], e); if (i - 1 >= 0) Btn_Click(bound[i - 1, j], e); if (i + 1 <= quart - 1) Btn_Click(bound[i + 1, j], e); if (i - 1 >= 0 && j + 1 <= quart - 1) Btn_Click(bound[i - 1, j + 1], e); if (j + 1 <= quart - 1) Btn_Click(bound[i, j + 1], e); if (i + 1 <= quart - 1 && j + 1 <= quart - 1) Btn_Click(bound[i + 1, j + 1], e); } else { vbound.Text = vbound.Nabar.ToString(); } } else if (vbound.status == 1) { //vbound.BackColor = System.Drawing.Color.Red; vbound.BackgroundImage = imageList1.Images[1]; vbound.BackgroundImageLayout = ImageLayout.Stretch; MessageBox.Show("you lose!"); ReStartGame(); } CheckGame(); } //扫雷事件 private void BtnRight_Click(object sender, MouseEventArgs e) { Bound vbound = (Bound)sender; //右击鼠标 if (e.Button == MouseButtons.Right) { //如果已经翻开过,则返回 if (vbound.Flag == 1) return; //未翻开 if (vbound.Flag == 0) { //vbound.BackColor = System.Drawing.Color.Red; vbound.BackgroundImage = imageList1.Images[0]; vbound.BackgroundImageLayout = ImageLayout.Stretch; if (vbound.status == 1) ClearBounds++; vbound.Flag = 2; } else //标记扫雷 { Image img = null; vbound.BackgroundImage = img; if (vbound.status == 1) ClearBounds--; vbound.Flag = 0; } CheckGame(); } } //判断游戏状态 private void CheckGame() { int i, j; //清除所有雷 if (ClearBounds == Max_Bound_Count) { //所有雷排完 for (i = 0; i < quart; i++) for (j = 0; j < quart; j++) { if (bound[i, j].Flag == 0) { return; } } MessageBox.Show("you win!"); } //toolStripStatusLabel1.Text = ClearBounds.ToString() + "/" + Max_Bound_Count.ToString(); } private void 开始ToolStripMenuItem_Click(object sender, EventArgs e) { startTime = DateTime.Now; ReStartGame(); } private void timer1_Tick(object sender, EventArgs e) { TimeSpan ts = new TimeSpan(); ts = DateTime.Now.Subtract(startTime); toolStripStatusLabel2.Text = DateTime.Parse(ts.ToString()).ToString("HH:mm:ss"); } }
三、小结
游戏设置了游戏的时间,这个是游戏扩展趣味性的问题了,可以根据这个做个排行榜,游戏还可以设置一些难度分级,我看了手机游戏,雷数和阵列数比例为1:9的难度较低,比例为1:4的难度就较大了。游戏要根据这些参数来动态的变化这些数据,增加游戏的可玩性。
自己在C#上写的第一个游戏程序,有点信心了,呵呵,和新手共勉。
文章原创地址:http://blog.csdn.net/much0726/archive/2009/04/20/4093519.aspx