[C#] [作品] 小游戏之扫雷 (Mine Sweeper) 开发过程及算法分析 扫雷原理
前几天突然想到做一个扫雷游戏来练练手,记得我刚接触程序的时候,就有一个同学做了个扫雷作为期末作品交上去的,当时还觉得这个扫雷还是比较复杂的,但到了现在这个时候就感觉是很简单的一个小游戏。
扫雷游戏大概流程如下:
- 创建地图。可以用控件的方式来做小格子,也可以用GDI+,但是对于绘图方面不太熟,就采用控件的方式。
- 根据地图的大小来决定地雷数,随机生成地雷。如果不好决定地图大小与地雷数之间的关系,也可以固定地雷数目。
- 计算每个地雷周围格子的数字。这个简单,遍历每个地雷,然后在周围8个格子上各加1就好。
- 事件绑定。处理鼠标左右键的点击,右击标记,左击打开格子。还要处理左右键同时按下的情况,自动打开格子。
- 胜负的判断。
还有其它的一些杂项:
- 格子几个状态下的样式处理(按下、踩中的地雷、标记错误的地雷、旗子、关闭、打开,打开状态下又分为地雷,空白和数字,还有不同数字的颜色处理)。
- 打开格子的处理,数字和地雷直接打开,空白的话自动打开周围8个格子,8个格子中有空白的话继续打开,如此反复。
- 计时。
还有这些由于时间关系还没做:
- 第一次按下时一定不是地雷。这个可以在第一次点击之后再渲染地图,并且在生成地雷时避开点击的坐标。
- 自定义地图及地雷数。
- 重置地图。
使用C#面向对象编程:
MineCell.cs
格子对象
属性:格子底下的数字,-1代表地雷;坐标;格子当前状态。
核心方法:
ChangeStatus
改变格子状态
C#
//改变状态(打开,关闭,按下,旗子,问号,错误标记,踩中地雷) public void ChangeStatus(string status) { this.Status = status; if (status == "Open") { FlatAppearance.MouseDownBackColor = Color.LightGray; FlatAppearance.MouseOverBackColor = FlatAppearance.MouseDownBackColor; BackColor = FlatAppearance.MouseDownBackColor; if ( Number>0 ) ForeColor = MineMap.NumColors[Number-1]; if (Number == -1) { Image = boom; } else if (Number == 0) { Text = ""; } else { Text = Number.ToString(); } } else if(status == "Close") { FlatAppearance.MouseDownBackColor = Color.LightGray; FlatAppearance.MouseOverBackColor = Color.FromArgb(250, 250, 250); BackColor = FlatAppearance.MouseOverBackColor; Text = ""; Image = null; } else if(status == "Down") { BackColor = Color.LightGray; } else if(status == "Flag") { FlatAppearance.MouseDownBackColor = Color.FromArgb(250, 250, 250); FlatAppearance.MouseOverBackColor = FlatAppearance.MouseDownBackColor; BackColor = FlatAppearance.MouseDownBackColor; Image = Properties.Resources.flag; } else if(status == "Unknown") { FlatAppearance.MouseDownBackColor = Color.FromArgb(250, 250, 250); FlatAppearance.MouseOverBackColor = FlatAppearance.MouseDownBackColor; BackColor = FlatAppearance.MouseDownBackColor; Image = Properties.Resources.boom; } else if(status == "Error") { FlatAppearance.MouseDownBackColor = Color.LightGray; FlatAppearance.MouseOverBackColor = FlatAppearance.MouseDownBackColor; BackColor = FlatAppearance.MouseDownBackColor; Image = Properties.Resources.error; } else if(status == "Hit") { FlatAppearance.MouseDownBackColor = Color.Red; FlatAppearance.MouseOverBackColor = FlatAppearance.MouseDownBackColor; BackColor = FlatAppearance.MouseDownBackColor; } }
MineMap.cs
地图对象
属性:地图数据,地雷总数,地雷坐标,错误标记坐标,以及一些初始化的数据:地图大小,格子大小,标记总数,未打开格子总数,数字对应的颜色,鼠标状态:左键按下,右键按下,左右键同时按下,左右键曾经同时按下。
核心方法:
计算地雷周围的数字
C#
Point[] aroundPoints = GetAroundPoint(mine); foreach (Point aroundPoint in aroundPoints) { MineCell cell = Map[aroundPoint.X, aroundPoint.Y]; if (cell.Number != -1) cell.Number += 1; }
GetAroundPoint
获取周围8个点
使用Where
过滤边界情况
C#
public Point[] GetAroundPoint(Point p) { Point[] points = new Point[8]; points[0] = new Point(p.X - 1, p.Y - 1); points[1] = new Point(p.X , p.Y - 1); points[2] = new Point(p.X + 1, p.Y - 1); points[3] = new Point(p.X - 1, p.Y ); points[4] = new Point(p.X + 1, p.Y ); points[5] = new Point(p.X - 1, p.Y + 1); points[6] = new Point(p.X , p.Y + 1); points[7] = new Point(p.X + 1, p.Y + 1); points = points.Where(point => point.X >= 0) .Where(point => point.X < mapWidth) .Where(point => point.Y >= 0) .Where(point => point.Y < mapHeight).ToArray(); return points; }
左右键同时点击并释放时自动打开周围格子
C#
if (isBothButtonDowned) { int aroundFlags = 0; Point[] aroundPoints = GetAroundPoint(((MineCell)sender).Point); foreach (Point point in aroundPoints) { MineCell aroundCell = Map[point.X, point.Y]; if (aroundCell.Status == "Down") aroundCell.ChangeStatus("Close"); if (aroundCell.Status == "Flag") aroundFlags++; } if (cell.Number == aroundFlags && cell.Status == "Open") { foreach (Point point in aroundPoints) { OpenCell(point); } } }
胜利的判断
当标记的旗子数加上未打开的格子数刚好等于地雷数时,即可判断为完成。
C#
if(CloseCellTotal+FlagsTotal == MineTotal) { }
程序及源码下载:
版权声明:本文为原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
本文链接:https://www.qwqoffice.com/article.php?mod=view&tid=26
本文链接:https://www.qwqoffice.com/article.php?mod=view&tid=26