100行C#代码实现经典扫雷游戏
作者:微小冷 发布时间:2023-12-05 16:33:30
标签:C#,扫雷,游戏
布局
布局效果如下,下面每个“网格”都是一个按钮,点击按钮,就会有相应的事件发生。
由于UniformGrid
中每个Grid的尺寸相等,所以作为雷区的容器。
<DockPanel>
<DockPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="5"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Margin" Value="5"/>
<Setter Property="InputMethod.IsInputMethodEnabled" Value="False"/>
</Style>
</DockPanel.Resources>
<ToolBar DockPanel.Dock="Top">
<TextBlock Text="雷区尺寸"/>
<TextBox Width="40" Text="20" x:Name="txtNumX"/>
<TextBlock Text="×"/>
<TextBox Width="40" Text="20" x:Name="txtNumY"/>
<TextBlock Text="雷数"/>
<TextBox Width="40" Text="20" x:Name="txtNumMine"/>
<Button Content="🔄" Click="btnNewGame_Click"/>
</ToolBar>
<UniformGrid Name="ugMine">
</UniformGrid>
生成雷区
值得一提的是,由于随机数可能在生成过程中产生重复的值,所以这里通过概率的方式来生成雷。
假设按钮数为N,雷数为n,那么在][0,N]之间随机生成一个数x,如果x<n,则判定当前按钮为雷。按钮是否为雷的标志作为布尔型存放在btn.tag中。
由于通过遍历的方法生成雷,所以一旦剩余雷的个数和剩余按钮的个数相等,就说明剩余的按钮全都是雷。这种情况发生,则不必进行随机数的判定。
private void newGame()
{
x = int.Parse(txtNumX.Text);
y = int.Parse(txtNumY.Text);
var nBtns = x * y;
nMark = 0;
nRes = int.Parse(txtNumMine.Text);
if (nRes > nBtns)
nRes = nBtns;
pMine = new List<int>();
ugMine.Rows = y;
ugMine.Columns = x;
ugMine.Children.Clear();
Random rd = new Random();
int numSetMine = 0; //已经布置的雷的个数
for (int i = 0; i < nBtns; i++)
{
var btn = new Button();
ugMine.Children.Add(btn);
btn.Click += Btn_Click;
btn.MouseRightButtonDown += Btn_MouseRightButtonDown;
btn.Content = "";
btn.Tag = false;
if ((nRes - numSetMine) == (nBtns - i) || //如果剩余的雷数刚好等于剩余的按钮数,则剩下的按钮都是雷
(numSetMine < nRes && rd.Next(0, nBtns) < nRes))
{
btn.Tag = true;
numSetMine += 1;
pMine.Add(i);
}
}
}
左键扫雷和右键标记
左键点击,则类似于一个翻面的动作;右键点击,则相当于是标记,而且在标记之后,不能再通过左键进行翻面。
//左键单击
private void Btn_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
int index = ugMine.Children.IndexOf(btn);
flipButton(index);
if(nMark == pMine.Count || nRes == pMine.Count)
MessageBox.Show("您赢了!");
}
//右键单击
private void Btn_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var btn = sender as Button;
var flag = btn.Content.ToString() != "🚩";
if (flag)
btn.Click -= Btn_Click;//如果已经标记,则卸载左键的功能
else
btn.Click += Btn_Click;//如果取消标记,则重新挂载左键的功能
btn.Content = flag ? "🚩" : "";
btn.Foreground = flag ? Brushes.Red : Brushes.Gray;
nMark += flag ? 1 : -1;
}
右键单击效果如下
翻面功能
在翻面的时候,如果当前按钮为雷,则雷炸了,游戏结束。
如果当前按钮不是雷,那么判断该按钮周围是否有雷。如果有雷,则当前按钮显示周围雷的个数;如果没雷,则将周围的雷全部翻面——需要调用自身。
private void flipButton(int index)
{
var btn = ugMine.Children[index] as Button;
if (!btn.IsEnabled)
return;
if ((bool)btn.Tag)
{
foreach (var i in pMine)
{
var mine = ugMine.Children[i] as Button;
mine.Content = "💥";
mine.Foreground = Brushes.Red;
}
MessageBox.Show("您输了");
return;
}
nRes -= 1;
btn.IsEnabled = false;
int numMines = 0;
var nears = setNear(index);
foreach (var i in nears)
{
var near = ugMine.Children[i] as Button;
if ((bool)near.Tag)
numMines += 1;
}
if (numMines != 0)
btn.Content = numMines;
else
foreach (var i in nears)
flipButton(i);
}
其中setNear
是用于获取当前按周围按钮的序号,这里分别需要考虑四个角、四个边以及中间区域。
private int[] setNear(int index)
{
if (index == 0)
return new int[3] { 1, x, x + 1 };
if (index == x * y - 1)
return new int[3] { index - 1, index - x, index - x - 1 };
if (index == x - 1)
return new int[3] { x - 2, 2 * x - 1, 2 * x - 2 };
if (index == x * y - x)
return new int[3] { index + 1, index - x, index - x + 1 };
if (index % x == 0)
return new int[5] { index - x, index - x + 1, index + 1, index + x, index + x + 1 };
if (index % x == (x - 1))
return new int[5] { index - x - 1, index - x, index - 1, index + x - 1, index + x };
if (index < x)
return new int[5] { index - 1, index + 1, index + x - 1, index + x, index + x + 1 };
if (index > x * (y - 1))
return new int[5] { index - x - 1, index - x, index - x + 1, index - 1, index + 1 };
return new int[8] { index - 1, index + 1, index - x, index-x-1,
index-x+1,index + x,index+x-1,index+x+1 };
}
效果如下
来源:https://tinycool.blog.csdn.net/article/details/120563165


猜你喜欢
- 1.System类System系统类,主要用于获取系统的属性数据和其他操作,因其构造方法是私有的并且类中的成员方法都是静态的,所以在使用的时
- 一、手机版本问题,大多数文章没有涉及这个点,导致他们的代码并无法正常使用M版本以上需要使用的Type--> TYPE_APPLICAT
- android studio版本:2021.2.1例程名称:pravicydialog功能:1、启动app后弹窗隐私协议2、屏蔽返回键3、再
- 一、前言二、案例需求1.编写login.html登录页面,username&password两个输入框2.使用Druid数据库连接池
- 一、获取企业微信群机器人 Webhook 地址业务需要在企业微信推送告警监控或者定时提醒业务,就可以使用企业微信自带的机器人工具Webhoo
- 目录算术操作符移位操作符位操作符赋值操作符 单目操作符(类型) 强制类型转换 &n
- 一、前言近些年,随着微服务框架在越来越多的公司产品中实践落地,以Spring Cloud Alibaba为导向的一站式微服务解决方案也成为微
- 本文实例为大家分享了C#控制台实现飞行棋小游戏的具体代码,供大家参考,具体内容如下游戏标题static void ShowTitle() &
- 本文汇总了常用的DateTime日期类型格式化显示方法,方便读者在使用的时候参考借鉴一下。具体如下所示:1.绑定时格式化日期方法:<A
- 概要设计模式是一门艺术,如果真正了解这门艺术,你会发现,世界都将变得更加优美。定义定义一个用于创建对象的接口,让其子类去决定实例化那个类使用
- 参考了一下网上别人写的,再使用的时候是放在新开的线程中来播放音乐的,后来发现每次进入Activity后就会重复开始一个音乐播放的声音。为了避
- 本文实例讲述了Android游戏开发学习①弹跳小球实现方法。分享给大家供大家参考。具体如下:在学习了一点点Android之后,觉得有必要记录
- 废话不多说了,先给大家上左右无限滑动的代码了。1.左右无限滑动public class MainActivity extends AppCo
- 本文实例讲述了Java实现读取及生成Excel文件的方法。分享给大家供大家参考,具体如下:一、读取Excel文件需要先下载poi-3.0.1
- C# 多态性多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。多态性可以是静态的或动
- 引入所谓泛型,就是创建一个函数,对所有数据类型都生效。最常见的例子就是运算符,毕竟1+1=2,1.0+1.0=2.0,足以看出+是对多种数据
- 一、简介Bottom Sheet是Design Support Library23.2 版本引入的一个类似于对话框的控件。 Bottom S
- 结构:安装NuGet包:using SAP.Middleware.Connector;using System.Data;namespace
- Ping pingSender = new Ping(); PingReply reply = pingSender.Send("
- 写在前面为什么会突然想说说委托?原因吗,起于一个同事的想法,昨天下班的路上一直在想这个问题,如果给委托注册多个方法,会不会都执行呢?为了一探