一、概述
最近買了心機NOKIA6300,上面有一款掃雷遊戲,閑暇時光總是在玩,玩難度的級别還挺難過的。呵呵
後來就想使用C#寫個掃雷程式玩玩,遊戲玩多了,遊戲的思想就不知不覺的有了,下面所有的代碼是沒有任何參考的情況下,自己寫出來的,有許多瑕疵,程式也沒經過什麼優化,主要的算法思想其實就那麼幾行。
先給出程式的運作界面,要添加一些控件,這裡就不啰嗦了,程式本來有源碼,但是,為了友善,我将所有的代碼都寫在了Form1中(這個習慣不好,嘿嘿)。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicGcq5yck5WdvJ2LcBjM0ATOwAjMvw1cldWYtlUeyRnbF9CX2IzNwg2Y112LcRXZu9lbkN3Yfd2bsJ2Xw9CXzV2Zh1WavwFdl5mLuR2cj5yZvxmYtA3Lc9CX6MHc0RHaiojIsJye.jpg)
二、程式設計思路
程式主要的是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