100行C#代码实现经典扫雷游戏

作者:微小冷 时间:2023-12-05 16:33:30 

布局

布局效果如下,下面每个“网格”都是一个按钮,点击按钮,就会有相应的事件发生。

100行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;
}

右键单击效果如下

100行C#代码实现经典扫雷游戏

翻面功能

在翻面的时候,如果当前按钮为雷,则雷炸了,游戏结束。

如果当前按钮不是雷,那么判断该按钮周围是否有雷。如果有雷,则当前按钮显示周围雷的个数;如果没雷,则将周围的雷全部翻面&mdash;&mdash;需要调用自身。

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 };
}

效果如下

100行C#代码实现经典扫雷游戏

来源:https://tinycool.blog.csdn.net/article/details/120563165

标签:C#,扫雷,游戏
0
投稿

猜你喜欢

  • 带你走进Maven的大门-最全Maven配置及集成idea工具总结

    2022-12-06 08:41:40
  • 鉴权认证+aop+注解+过滤feign请求的实例

    2022-06-05 14:25:34
  • Flutter投票组件使用方法详解

    2022-05-25 19:07:05
  • Java关于桶排序的知识点总结

    2023-12-06 03:18:04
  • 关于SpringGateway调用服务 接受不到参数问题

    2023-08-31 13:05:00
  • Java如何利用状态模式(state pattern)替代if else

    2021-08-15 09:31:08
  • JS+Struts2多文件上传实例详解

    2022-02-18 21:08:02
  • 7种形式的Android Dialog使用实例

    2023-02-08 19:41:02
  • java中匿名内部类详解

    2022-10-06 14:56:56
  • Mybatis基于注解形式的sql语句生成实例代码

    2023-03-07 03:48:11
  • springBoot Junit测试用例出现@Autowired不生效的解决

    2023-01-24 12:57:59
  • Android开发中记一个SwipeMenuListView侧滑删除错乱的Bug

    2021-08-02 04:24:21
  • Mybatis通过数据库表自动生成实体类和xml映射文件

    2022-01-11 07:05:46
  • java实现简单扫雷游戏

    2022-09-15 13:48:11
  • Java堆&优先级队列示例讲解(上)

    2023-04-09 11:09:59
  • 如何用注解的方式实现Mybatis插入数据时返回自增的主键Id

    2022-02-05 09:29:55
  • 浅谈Android手机的抢红包插件

    2021-08-13 01:04:36
  • C#使用Aforge调用摄像头拍照的方法

    2022-02-10 02:34:36
  • 总结C#删除字符串数组中空字符串的几种方法

    2022-04-14 03:26:42
  • Android 自定义gradle property详解及实例代码

    2022-05-08 15:17:49
  • asp之家 软件编程 m.aspxhome.com