很久没有更新博客了,我都有些想她了
回家时途经朋友那里,在北大青鸟教室里灵感迸发,扫雷 V2.0 由此而生,较V1.0 巨大革新,尤其是UI。新的窗体更好看,更人性化,工作去支持拖动,支持不同级别以及自定义方案扫雷,效能还可以。感觉相当好了!
自己先赞一个!
接下来看代码吧。
//Mines.cs 主文件
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 Mines
{
public partial class Mines : Form
{
//分别用于存储左键单击数,右键单击数,左右键单击数之和,累计时间,剩余雷数,行,列,雷数,方块总数,完成正确判断的方块数
int leftCounter, rightCounter, bothCounter, TimeCounter, RestOfMines, Row, Line, NumOfMines, BlockNumber, tmrCounter;
//雷方块控件数组
Label[,] lblBlock;
//计算并存储数字方块周围的雷数目
int[] MinesCounter;
//存储对雷方块是否揭开,是否为雷,是否标示的判断值
bool[] IsCovered,IsMine,IsJudge;
//用于拖动窗体,前者记录鼠标的相对位置,后者记录鼠标左键的状态
Point MouseOff;
bool LeftFlag;
//用来记录结束游戏的方式
internal enum Result
{
Lose,
Win
}
public Mines()
{
InitializeComponent();
}
//初始化函数,创建并初始化所有手动控件
public void Compose(int numRow, int numLine, int numMines)
{
Row = numRow;
Line = numLine;
BlockNumber = Row * Line;
NumOfMines = numMines;
lblBlock = new Label[Row, Line]; //按需创建方块
MinesCounter = new int[BlockNumber];
IsCovered = new bool[BlockNumber];
IsMine = new bool[BlockNumber];
IsJudge = new bool[BlockNumber];
this.Size = new Size(110 + 23 * Line, 162 + 23 * Row); //窗体适应控件大小
for (int i = 0; i < Row; i++)
for (int j = 0; j < Line; j++)
{
lblBlock[i, j] = new Label();
this.Controls.Add(lblBlock[i, j]);
lblBlock[i, j].Left = 49 + 23 * j;
lblBlock[i, j].Top = 66 + 23 * i;
lblBlock[i, j].Width = 24;
lblBlock[i, j].Height = 24;
lblBlock[i, j].Name = (i * Line + j).ToString();
lblBlock[i, j].BorderStyle = BorderStyle.FixedSingle;
lblBlock[i, j].TextAlign = ContentAlignment.MiddleCenter;
lblBlock[i, j].Font = new Font("微软雅黑", 10.6F, FontStyle.Regular);
lblBlock[i, j].MouseDown += new MouseEventHandler(this.lblBlock_MouseDown); //手动添加事件,下同
lblBlock[i, j].MouseUp += new MouseEventHandler(this.lblBlock_MouseUp);
lblBlock[i, j].BackColor = Color.FromArgb(95, 170, 200);
int index = i * Line + j; //二维索引转换为一维索引
IsCovered[index] = true;
IsMine[index] = false;
IsJudge[index] = false;
MinesCounter[index] = 0;
}
Startup(); //初始化其他变量
}
//轻量初始化其他变量
public void Startup()
{
leftCounter = rightCounter = bothCounter = 0;
TimeCounter = 0;
RestOfMines = NumOfMines;
lblTime.Text = "时间 0 s";
for(int i=0;i<Row;i++)
for(int j=0;j<Line;j++)
{
lblBlock[i,j].Text="";
lblBlock[i, j].BackColor = Color.FromArgb(95,170,200);
int index=i*Line+j;
IsCovered[index]=true;
IsMine[index]=false;
IsJudge[index]=false;
MinesCounter[index]=0;
}
CreateMines(); //生成雷
CountMines(); //计算剩下的方块上的数字
tmrMines.Start(); //开始计算剩余雷数及其他事件
}
//生成雷
public void CreateMines()
{
Random random=new Random();
List<int> listMines = new List<int>(RestOfMines);
int MineOrder;
while (listMines.Count < RestOfMines)
{
MineOrder = random.Next(0, BlockNumber);
if (!listMines.Contains(MineOrder)) //生成不重复的所需量的随机数
{
listMines.Add(MineOrder);
IsMine[MineOrder] = true;
}
}
}
//计算剩余方块上的数字
public void CountMines()
{
for(int index=0;index<BlockNumber;index++)
for(int i=index/Line-1;i<index/Line+2;i++) //一维索引转换为二维索引,下同
for (int j = index % Line-1; j < index % Line + 2; j++)
{
if (i < 0 || i >= Row || j < 0 || j >= Line) //避免数组越界
continue;
if (i * Line + j== index)
continue;
if (IsMine[i * Line + j] == true)
MinesCounter[index]++;
}
}
//顾名思义,推零函数(方法),挖开数字为零的方块时向周围拓展
public void PutZero(int p)
{
int index;
for(int i=p/Line-1;i<p/Line+2;i++)
for (int j = p % Line-1; j < p % Line + 2; j++)
{
index = Line * i + j;
if (i < 0 || i >= Row || j < 0 || j >= Line)
continue;
if (IsMine[index]||IsJudge[index]) //跳过含雷或者挖开的方块(或许判断含雷在这有些多余?)
continue;
if(MinesCounter[index]!=0)
lblBlock[i , j ].Text = MinesCounter[index].ToString();
lblBlock[i , j ].BackColor = Color.FromArgb(190,220,245);
IsCovered[index] = false; //记录方块的状态
}
}
//结束游戏方法
private void GameOver(Result result)
{
tmrMines.Stop();
tmrTime.Stop(); //一轮游戏结束,计时器停止工作
if (result==Result.Lose)
for (int i = 0; i < Row;i++ )
for(int j=0;j < Line;j++ )
{
if (IsMine[i*Line+j])
{
lblBlock[i,j].Text = "雷";
lblBlock[i,j].BackColor = Color.FromArgb(250,65,50);
}
}
ResultBox boxResult = new ResultBox(result,ref TimeCounter);
boxResult.ShowDialog(); //调用结果窗体,显示游戏结果
Startup();
}
//程序启动时自动构建并初始化所有控件
private void Mines_Load(object sender, EventArgs e)
{
Compose(16,16,40);
}
private void lblBlock_MouseDown(Object sender, MouseEventArgs e)
{
switch (e.Button)
{
case MouseButtons.Left:
leftCounter++;
bothCounter++;
break;
case MouseButtons.Right:
rightCounter++;
bothCounter++;
break;
}
}
private void lblBlock_MouseUp(Object sender, MouseEventArgs e)
{
Label bClick = (Label)sender;
int index = int.Parse(bClick.Name);
switch (e.Button)
{
case MouseButtons.Left:
leftCounter++;
bothCounter++;
break;
case MouseButtons.Right:
rightCounter++;
bothCounter++;
break;
}
//推雷事件
if (bothCounter >= 3)
{
int counter = 0;
if (IsCovered[index] == false)
{
for (int i = index / Line-1; i < index / Line + 2; i++)
for (int j = index % Line-1; j < index % Line + 2; j++)
{
if (i < 0 || i >= Line || j < 0 || j >= Row)
continue;
if (IsMine[Line * i + j ] && IsJudge[Line * i + j ])
counter++;
if (!IsMine[Line * i + j] && IsJudge[Line * i + j ])
counter = -9;
}
if (counter == MinesCounter[index])
PutZero(index);
if (counter < 0)
GameOver(Result.Lose);
}
}
//单击方块事件
else if (leftCounter == 2)
{
if (!tmrTime.Enabled)
tmrTime.Start(); //揭开第一个方块时开始计算时间
if (IsCovered[index] == false || IsJudge[index]) ; //跳过揭开了的方块和标注了的方块
else if (IsMine[index])
{
GameOver(Result.Lose); //不幸踩雷
}
else
{
if(MinesCounter[index]!=0 )
lblBlock[index / Line, index % Line].Text = MinesCounter[index].ToString(); //无雷的方块显示对应的数字(0除外)
bClick.BackColor = Color.FromArgb(190, 220, 250);
IsCovered[index] = false;
}
}
//标注雷或取消标注事件
else if (rightCounter == 2)
{
if (IsCovered[index] == false) ; //跳过揭开了的方块
else if (IsJudge[index])
{
bClick.Text = ""; //取消标注
bClick.BackColor = Color.FromArgb(95, 170, 200);
IsJudge[index] = false;
RestOfMines += 1;
}
else
{
bClick.Text = "!"; //标注雷
bClick.BackColor = Color.Orange;
IsJudge[index] = true;
RestOfMines -= 1;
}
}
leftCounter = rightCounter = bothCounter = 0; //初始化状态变量
}
private void tmrMines_Tick(object sender, EventArgs e)
{
tmrCounter = 0; //初始化该变量,用来记录完成正确判断的方块数
lblMines.Text = "还有 " + RestOfMines.ToString() + "个雷"; //实时显示剩余雷数
for (int index = 0; index < BlockNumber; index++)
{
if (IsJudge[index] == true && IsMine[index] == true || IsCovered[index] == false)
tmrCounter++;
if (MinesCounter[index]==0&&IsCovered[index]==false)
PutZero(index); //当有零方块被揭开时,调用推零函数挖开周围一圈
}
if (tmrCounter == BlockNumber)
{ //正确判断并完成了所有扫雷工作
for (int index = 0; index < BlockNumber; index++)
{
IsMine[index] = false;
IsCovered[index] = true;
}
GameOver(Result.Win); //游戏以赢的方式结束并重新开始
Startup();
}
}
private void tmrTime_Tick(object sender, EventArgs e)
{
lblTime.Text = "时间 " + (TimeCounter++/10).ToString() + " s"; //实时显示时间
}
//开局
private void tsmStartup_Click(object sender, EventArgs e)
{
if (tmrTime.Enabled)
tmrTime.Stop();
Startup();
}
//退出
private void tsmExit_Click(object sender, EventArgs e)
{
this.Close();
}
//关于
private void tsmAbout_Click(object sender, EventArgs e)
{
AboutBox About = new AboutBox();
About.ShowDialog();
}
//设置(自定义)
private void tsmCostom_Click(object sender, EventArgs e)
{
InitializeBox Initialize = new InitializeBox();
Initialize.ShowDialog();
if (Initialize.SetRow * Initialize.SetLine * Initialize.SetMines!=0)
{
Row = Initialize.SetRow;
Line = Initialize.SetLine;
NumOfMines = Initialize.SetMines;
foreach(Label Block in lblBlock)
Block.Dispose(); //在重新创建控件前释放控件
Compose(Row, Line, NumOfMines);
}
}
//以下事件用于拖动窗体
private void Mines_MouseDown(object sender, MouseEventArgs e)
{
MouseOff = new Point(- (e.X+5), - (e.Y+27)); //记录鼠标开始时相对窗体的位置,常数为有框窗体的偏差
LeftFlag = true; //记录鼠标左键的状态,true为被按下
}
private void Mines_MouseMove(object sender, MouseEventArgs e)
{
if (LeftFlag)
{
Point MouseSet = Control.MousePosition; //记录鼠标的动态位置(引用)
MouseSet.Offset(MouseOff.X, MouseOff.Y); //计算窗体相对于鼠标的新位置
Location = MouseSet;
}
}
private void Mines_MouseUp(object sender, MouseEventArgs e)
{
if (LeftFlag)
LeftFlag = false; //鼠标左键松开
}
}
}
//InitializeBox.cs 收集扫雷所需方案信息的窗体 重要
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 Mines
{
public partial class InitializeBox : Form
{
internal int SetRow, SetLine, SetMines; //三个变量分别存储扫雷方案的行数,列数,和雷数
Point MouseOff;
bool LeftFlag;
public InitializeBox()
{
SetRow = 0;
SetLine = 0;
SetMines = 0;
InitializeComponent();
}
//低级扫雷方案,9*9网格 10个雷
private void rbLow_CheckedChanged(object sender, EventArgs e)
{
lblLow.ForeColor = Color.Black;
lblMiddle.ForeColor = Color.DimGray;
lblAdvanced.ForeColor = Color.DimGray;
lblRow.ForeColor = Color.DimGray;
lblLine.ForeColor = Color.DimGray;
lblMines.ForeColor = Color.DimGray;
tbRow.Enabled = false ;
tbLine.Enabled = false;
tbMines.Enabled = false;
btnOK.Enabled = true;
}
//中级扫雷方案 16*16网格 40个雷
private void rbMiddle_CheckedChanged(object sender, EventArgs e)
{
lblLow.ForeColor = Color.DimGray;
lblMiddle.ForeColor = Color.Black;
lblAdvanced.ForeColor = Color.DimGray;
lblRow.ForeColor = Color.DimGray;
lblLine.ForeColor = Color.DimGray;
lblMines.ForeColor = Color.DimGray;
tbRow.Enabled = false;
tbLine.Enabled = false;
tbMines.Enabled = false;
btnOK.Enabled = true;
}
//高级扫雷方案 16*30网格 99个雷
private void rbAdvanced_CheckedChanged(object sender, EventArgs e)
{
lblLow.ForeColor = Color.DimGray;
lblMiddle.ForeColor = Color.DimGray;
lblAdvanced.ForeColor = Color.Black;
lblRow.ForeColor = Color.DimGray;
lblLine.ForeColor = Color.DimGray;
lblMines.ForeColor = Color.DimGray;
tbRow.Enabled = false;
tbLine.Enabled = false;
tbMines.Enabled = false;
btnOK.Enabled = true;
}
//自定义方案
private void rbCostom_CheckedChanged(object sender, EventArgs e)
{
lblLow.ForeColor = Color.DimGray;
lblMiddle.ForeColor = Color.DimGray;
lblAdvanced.ForeColor = Color.DimGray;
lblRow.ForeColor = Color.Black;
lblLine.ForeColor = Color.Black;
lblMines.ForeColor = Color.Black;
tbRow.Enabled = true;
tbLine.Enabled = true;
tbMines.Enabled = true;
}
//计时器用于判定所需信息是否集齐
private void tmr_Tick(object sender, EventArgs e)
{
if (tbRow.TextLength > 0 && tbLine.TextLength > 0 && tbMines.TextLength > 0 || !rbCostom.Checked)
btnOK.Enabled = true;
else
btnOK.Enabled = false;
}
//收集扫雷所需方案信息
private void btnOK_Click(object sender, EventArgs e)
{
if (rbLow.Checked)
{
SetRow = 9;
SetLine = 9;
SetMines = 10;
}
else if (rbMiddle.Checked)
{
SetRow = 16;
SetLine = 16;
SetMines = 40;
}
else if (rbAdvanced.Checked)
{
SetRow = 16;
SetLine = 30;
SetMines = 99;
}
else
{
SetRow = int.Parse(tbRow.Text);
SetLine = int.Parse(tbLine.Text);
SetMines = int.Parse(tbMines.Text);
}
if (SetRow * SetLine > SetMines)
this.Close();
else
lblInitialize.Text = "您输入有误,请重新输入!";
}
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
}
private void InitializeBox_MouseDown(object sender, MouseEventArgs e)
{
MouseOff = new Point(-e.X, -e.Y);
LeftFlag = true;
}
private void InitializeBox_MouseMove(object sender, MouseEventArgs e)
{
if (LeftFlag)
{
Point MouseSet = Control.MousePosition;
MouseSet.Offset(MouseOff.X, MouseOff.Y);
Location = MouseSet;
}
}
private void InitializeBox_MouseUp(object sender, MouseEventArgs e)
{
if (LeftFlag)
LeftFlag = false;
}
private void InitializeControl_MouseDown(object sender, MouseEventArgs e)
{
Control conTemp = (Control)sender; //独创当前控件引用
MouseOff = new Point(-(conTemp.Location.X + e.X), -(conTemp.Location.Y + e.Y));
LeftFlag = true;
}
}
}