一个简单的扫雷游戏






4.08/5 (9投票s)
一个用 C# 编写的简单扫雷游戏。

引言
这篇文章是一个简单的示例,供刚开始学习 C# 的初学者使用。Windows 的扫雷是一个有趣的游戏,所以我喜欢编写这个游戏来学习以下内容:
- 如何在编程中使用观察者模式
- 如何使用
DataGridView
及其事件 - 如何使用委托
- 如何制作用户控件
Using the Code
主要的类和接口如下所示。
接口
public interface IClick
{
//to set mine flag
bool RightClick(int x, int y, Presenter pr);
//whether trigger mine
bool LeftClick(int x, int y, Presenter pr);
//double click
bool BothClick(int x, int y, Presenter pr);
}
public interface ICoordinate
{
int XCoordinate { get;}
int YCoordinate { get;}
void SetCoordinate(int x, int y);
}
public interface IGetSetValue
{
void Initialization();
int Lines { get;set;}
int Columns { get;set;}
int MineNums { get;set;}
}
public interface IOberverable
{
void Register(IObserver ober);
void UnRegister(IObserver ober);
}
public interface IObserver
{
void Update(EventArgs brgs);
}
主类
SweepingMap
类定义了窗体的宽度、高度和地雷数量,该类的主要方法是 LeftClick
,表示鼠标左键单击 DataGridView
单元格,RightClick
表示鼠标右键单击 DataGridView
单元格,BothClick
表示鼠标双击 DataGridView
单元格。
public class SweepingMap : IClick, IGetSetValue
{
#region Private Field
/// <summary>
/// to store mines
/// </summary>
private readonly ObjectList mines;
/// <summary>
/// to store the mines which are found
/// </summary>
private readonly ObjectList flags;
private readonly ObjectList hasAccessed;
private int lines;
private int columns;
private int mineNums;
private Random ran = new Random();
#endregion
#region Public Attributes
public ObjectList Mines
{
get
{
return this.mines;
}
}
public ObjectList Flags
{
get
{
return this.flags;
}
}
public int Lines
{
get
{
return this.lines;
}
set
{
this.lines = value;
}
}
public int Columns
{
get
{
return this.columns;
}
set
{
this.columns = value;
}
}
public int MineNums
{
get
{
return this.mineNums;
}
set
{
this.mineNums = value;
}
}
#endregion
#region Constructor
public SweepingMap()
{
this.mines = new ObjectList();
this.flags = new ObjectList();
hasAccessed = new ObjectList();
}
#endregion
#region Private Methods
private int CertainCondition(int x, int y, Condition condition)
{
int count = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if ((i >= 0 && i < Lines) && (j >= 0 &&
j < Columns) && !(i == x && j == y))
{
if (condition(i, j))
count++;
}
}
}
return count;
}
private bool CheckSuccess()
{
int count = 0;
foreach (GridObject f in this.Flags.ObjectTable.Keys)
{
foreach (GridObject m in this.Mines.ObjectTable.Keys)
{
if (this.Flags.ObjectTable[f].Equals(this.Mines.ObjectTable[m]))
count++;
}
}
if (count == this.Mines.ObjectTable.Count)
return true;
else
return false;
}
#endregion
#region Public Methods
public void Initialization()
{
this.mines.Clear();
this.flags.Clear();
this.hasAccessed.Clear();
for (int i = 0; i < this.MineNums; i++)
{
int x = ran.Next(this.Lines);
int y = ran.Next(this.Columns);
#if Debug
System.Diagnostics.Debug.Print("Coordinate:({0},{1}) \n", x, y);
#endif
if (!this.mines.ContainsKey(x, y))
this.mines.Add(x, y);
else
i -= 1;
}
}
//to set mine flag
public bool RightClick(int x, int y, Presenter pr)
{
if (this.flags.ContainsKey(x, y))
{
this.flags.Remove(x, y);
pr(x, y, true);
}
else
{
if (!this.hasAccessed.ContainsKey(x, y) &&
!this.flags.ContainsKey(x, y))
{
this.flags.Add(x, y);
pr(x, y, false);
}
}
if (this.Flags.Length == this.Mines.Length)
{
if (CheckSuccess())
{
return true;
}
}
return false;
}
//whether trigger mine
public bool LeftClick(int x, int y, Presenter pr)
{
if (!this.Flags.ContainsKey(x, y))
{
if (this.Mines.ContainsKey(x, y))
{
pr(x, y, "Mine");
return false;
}
else
{
GridTraversing(x, y, pr);
return true;
}
}
return true;
}
private void GridTraversing(int x, int y, Presenter pr)
{
Stack<GridObject> stack = new Stack<GridObject>();
GridObject tmp = new GridObject(x, y);
stack.Push(tmp);
Condition condition = new Condition(this.Mines.ContainsKey);
while (!(stack.Count == 0))
{
tmp = stack.Pop();
int num = CertainCondition(tmp.XCoordinate, tmp.YCoordinate, condition);
hasAccessed.Add(tmp.XCoordinate, tmp.YCoordinate);
if (num == 0)
{
pr(tmp.XCoordinate, tmp.YCoordinate, num);
for (int i = tmp.XCoordinate - 1; i <= tmp.XCoordinate + 1; i++)
{
for (int j = tmp.YCoordinate - 1; j <= tmp.YCoordinate + 1; j++)
{
if (i >= 0 && i < Lines && j >= 0 && j < Columns
&& !hasAccessed.ContainsKey(i, j)
&& !this.Flags.ContainsKey(i, j))
{
pr(i, j, num);
hasAccessed.Add(i,j);
stack.Push(new GridObject(i, j));
}
}
}
}
else
{
pr(tmp.XCoordinate, tmp.YCoordinate, num);
}
}
}
//double click
public bool BothClick(int x, int y, Presenter pr)
{
Condition condition = new Condition(this.Mines.ContainsKey);
int num = CertainCondition(x, y, condition);
condition = new Condition(this.flags.ContainsKey);
int count = CertainCondition(x, y, condition);
if (count == 0)
return true;
if (num == count)
{
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if (i >= 0 && i < Lines && j >= 0 && j < Columns
&& !(i == x && j == y))
{
if (!(this.flags.ContainsKey(i, j)))
{
if (!LeftClick(i, j, pr))
return false;
}
}
}
}
}
return true;
}
public void DisplayAllMines(int x, int y, Presenter pr)
{
int i, j;
foreach (GridObject cur in mines.ObjectTable.Keys)
{
i = mines.ObjectTable[cur].XCoordinate;
j = mines.ObjectTable[cur].YCoordinate;
if (i != x && j != y)
pr(i, j, mines.ObjectTable[cur]);
}
}
#endregion
}
历史
- 2008年9月22日:初始发布