奇怪的扫雷炸弹周围的数字行为
Weird Minesweeper bombs-around numbers behaviour
所以我为 UWP 制作了一个扫雷克隆版。当然,我有数字说明周围有多少地雷。所以问题来了(你可以在图片上看到它):
左上角、顶部、右上角和左侧字段只是缺少它们的编号。但是当我第二次点击时(我没有实施第二次点击开放字段的预防措施)他们添加了他们的1(地雷的位置不同,因为我重新启动了应用程序以再次截图):
我尝试通过添加 2 而不是 1 来解决这个问题,但问题仍然存在,而且更加奇怪。
第一次点击:
第二次点击:
所以,最后是代码:
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace Mynesweeper
{
public class Cell
{
public enum CellType
{
Safe = 0,
Bomb = 1,
Flag = 2,
FirstClick = 3
}
public CellType cellState;
public bool covered = true;
}
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MineField : Page
{
Cell[,] cells = new Cell[0, 0];
bool isGenerated = false;
Button[,] fieldBtns = new Button[0, 0];
int maxMines = 0;
int[,] nearbyBombs = new int[0, 0];
public MineField()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string param = e.Parameter.ToString();
if (param == "tutorial")
{
MinesAmount.Text = "4";
GenerateLevel(25, 25, 4);
}
else
{
TextBlock errTxtBlck = new TextBlock() { Name = "ErrorTextBlock", Text = "Error: no or incorrect parameter was specified when navigating to MineField.xaml\r\n\r\nIf you're the player and've downloaded the game through Windows Store, then let me know about this error by contacting me at artyomisflash@mail.ru. If you're not a player then maybe you're me and you have to fix this error.", HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch, Margin = new Windows.UI.Xaml.Thickness(16, 0, 16, 0), TextWrapping = Windows.UI.Xaml.TextWrapping.Wrap, VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Center, FontSize = 24, TextAlignment = Windows.UI.Xaml.TextAlignment.Center };
MainGrid.Children.Add(errTxtBlck);
SmileyFace.Text = "...";
}
}
public static int Clamp(int val, int min, int max)
{
if (val < min)
{
return min;
}
else if (val > max)
{
return max;
}
else
{
return val;
}
}
void GenerateLevel(int x, int y, int mines)
{
cells = new Cell[x, y];
StackPanel[] fieldRows = new StackPanel[y];
fieldBtns = new Button[x, y];
maxMines = mines;
nearbyBombs = new int[x, y];
for (int i = 0; i < y; i++)
{
fieldRows[i] = new StackPanel() { Name = "MineFieldPanelRow" + i, Orientation = Orientation.Horizontal };
MineFieldPanelRows.Children.Add(fieldRows[i]);
for (int j = 0; j < x; j++)
{
fieldBtns[j, i] = new Button() { Name = "MineFieldButton" + j + "_" + i, Width = 64, Height = 64, Margin = new Thickness(4), Padding = Margin = new Thickness(0) };
fieldBtns[j, i].Click += MineFieldButtonClick;
fieldBtns[j, i].Content = new FontIcon() { Glyph = "\uE890", FontSize = 32 };
fieldRows[i].Children.Add(fieldBtns[j, i]);
cells[j, i] = new Cell()
{
covered = true
};
nearbyBombs[j, i] = 0;
}
}
}
void PlaceMines()
{
Random rnd = new Random();
for (int i = 0; i < maxMines; i++)
{
int x = rnd.Next(0, cells.GetLength(0));
int y = rnd.Next(0, cells.GetLength(1));
while (cells[x, y].cellState == Cell.CellType.Bomb || cells[x, y].cellState == Cell.CellType.FirstClick)
{
x = rnd.Next(0, cells.GetLength(0));
y = rnd.Next(0, cells.GetLength(1));
}
cells[x, y].cellState = Cell.CellType.Bomb;
}
}
private void MineFieldButtonClick(object sender, RoutedEventArgs e)
{
if (!isGenerated)
{
for (int i = 0; i < fieldBtns.GetLength(1); i++)
{
for (int j = 0; j < fieldBtns.GetLength(0); j++)
{
if (fieldBtns[j, i] == sender)
{
cells[j, i].cellState = Cell.CellType.FirstClick;
break;
}
}
}
PlaceMines();
isGenerated = true;
}
for (int i = 0; i < cells.GetLength(1); i++)
{
for (int j = 0; j < cells.GetLength(0); j++)
{
if (cells[j, i].cellState == Cell.CellType.Bomb)
{
fieldBtns[j, i].Content = new FontIcon() { Glyph = "\uE783", FontSize = 32 };
nearbyBombs[j, i] = -1;
if (i - 1 > -1)
{
if (j - 1 > -1)
{
nearbyBombs[j - 1, i - 1]+=2;
}
nearbyBombs[j, i - 1]+=2;
if (j + 1 <= nearbyBombs.GetLength(0) - 1)
{
nearbyBombs[j + 1, i - 1]+=2;
}
}
if (j - 1 > -1)
{
nearbyBombs[j - 1, i]+=2;
}
if (j + 1 <= nearbyBombs.GetLength(0) - 1)
{
nearbyBombs[j + 1, i]++;
}
if (i + 1 <= nearbyBombs.GetLength(1) - 1)
{
if (j - 1 >= 0)
{
nearbyBombs[j - 1, i + 1]++;
}
nearbyBombs[j, i + 1]++;
if (j + 1 <= nearbyBombs.GetLength(0) - 1)
{
nearbyBombs[j + 1, i + 1]++;
}
}
}
else if (nearbyBombs[j, i] == 0)
{
fieldBtns[j, i].Content = null;
}
else
{
int bombsNearby = nearbyBombs[j, i];
fieldBtns[j, i].Content = new TextBlock() { Text = bombsNearby.ToString(), FontSize = 32 };
}
}
}
}
}
}
XAML 部分为:
<Page
x:Class="Mynesweeper.MineField"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Mynesweeper"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid x:Name="MainGrid">
<Grid.Transitions>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True"/>
</TransitionCollection>
</Grid.Transitions>
<StackPanel x:Name="Stats" Margin="16,16,16,0" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<TextBlock x:Name="TimeAmount" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" FontSize="32" Width="64" TextAlignment="Right" Margin="0,0,16,0"/>
<Button x:Name="Smiley" Padding="0" Width="64" Height="64" HorizontalAlignment="Center" VerticalAlignment="Stretch" FontSize="32" d:LayoutOverrides="LeftMargin, RightMargin, LeftPosition, RightPosition">
<TextBlock x:Name="SmileyFace" Text=":)" TextLineBounds="Tight" TextAlignment="Center"/>
</Button>
<TextBlock x:Name="MinesAmount" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" FontSize="32" Width="64" Margin="16,0,0,0"/>
</StackPanel>
<StackPanel x:Name="MineFieldPanelRows" Margin="0,16,0,0" VerticalAlignment="Center" HorizontalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<StackPanel.RenderTransform>
<CompositeTransform ScaleX="0.25" ScaleY="0.25"/>
</StackPanel.RenderTransform> <!-- specially downscaled to fit the whole field on the screen -->
</StackPanel>
</Grid>
</Page>
我真的不知道为什么会出现这个问题。也许我在某处打错了字?请帮忙!
以下代码:
if (j - 1 > -1)
{
nearbyBombs[j - 1, i]+=2;
}
在检查以确保 i > 0
的任何块之外。因此,如果你在 i = 0 处有地雷,你的 codez 就会爆炸。这让我相信你的令人费解的 if
陈述完全是错误的,而且它们太令人费解以至于无法尝试找出精确的点你搞砸了你的计数。大海捞针,一发现又错了,简直不值一提。
让我向您推荐一种不同的方法:
当你指的是height
和width
时,请停止使用x
和y
,并且当然要停止使用i
和j
当你指的是 x
和 y
。自 1600 年代中期以来,笛卡尔平面坐标名称的约定一直是水平轴 'x' 和垂直轴 'y',(Wikipedia)你不需要发明一个2017年新公约.
有人告诉您,即使您有一个由单个语句组成的块,也应该始终使用大括号。如果您认为这是明智的建议,那就继续用花括号把自己打倒吧,但至少让我们远离 Whosebug 上的花括号垃圾邮件,简洁很重要。
在您的代码还没有运行之前就停止使用 Random()
。在一组特定的坐标处放置特定数量的炸弹,并确保在引入随机性之前你的东西能正常工作。
退出嵌套 if
语句的复杂且不可读的层叠 zig-zag 面条,做一些更简单的事情。
像这样:
void updateBombCountsAroundBomb( int bombY, int bombX, int height, int width )
{
for( y = bombY - 1; y <= bombY + 1; y++ )
for( x = bombX - 1; x <= bombX + 1; x++ )
if( y != bombY && x != bombX )
if( y >= 0 && y < height && x >= 0 && x < width )
nearbyBombs[y, x]++;
}
一旦你让它工作,你可以尝试优化它,如果你想这样做,知道如果你不小心破坏它,你总是可以恢复到以前的工作版本。 (测试规则z。)
你的问题是这个语句:
else if (nearbyBombs[j, i] == 0)
您正在检查某个单元格的炸弹计数是否为零,因为您仍在遍历网格并设置炸弹计数,因此在进行此检查时,尚未执行该单元格的所有计算.
解决方案是先执行一次计算炸弹数量,完成后,然后再执行一次以创建按钮并在其中显示炸弹数量。
所以我为 UWP 制作了一个扫雷克隆版。当然,我有数字说明周围有多少地雷。所以问题来了(你可以在图片上看到它):
左上角、顶部、右上角和左侧字段只是缺少它们的编号。但是当我第二次点击时(我没有实施第二次点击开放字段的预防措施)他们添加了他们的1(地雷的位置不同,因为我重新启动了应用程序以再次截图):
我尝试通过添加 2 而不是 1 来解决这个问题,但问题仍然存在,而且更加奇怪。
第一次点击:
第二次点击:
所以,最后是代码:
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace Mynesweeper
{
public class Cell
{
public enum CellType
{
Safe = 0,
Bomb = 1,
Flag = 2,
FirstClick = 3
}
public CellType cellState;
public bool covered = true;
}
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MineField : Page
{
Cell[,] cells = new Cell[0, 0];
bool isGenerated = false;
Button[,] fieldBtns = new Button[0, 0];
int maxMines = 0;
int[,] nearbyBombs = new int[0, 0];
public MineField()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string param = e.Parameter.ToString();
if (param == "tutorial")
{
MinesAmount.Text = "4";
GenerateLevel(25, 25, 4);
}
else
{
TextBlock errTxtBlck = new TextBlock() { Name = "ErrorTextBlock", Text = "Error: no or incorrect parameter was specified when navigating to MineField.xaml\r\n\r\nIf you're the player and've downloaded the game through Windows Store, then let me know about this error by contacting me at artyomisflash@mail.ru. If you're not a player then maybe you're me and you have to fix this error.", HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch, Margin = new Windows.UI.Xaml.Thickness(16, 0, 16, 0), TextWrapping = Windows.UI.Xaml.TextWrapping.Wrap, VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Center, FontSize = 24, TextAlignment = Windows.UI.Xaml.TextAlignment.Center };
MainGrid.Children.Add(errTxtBlck);
SmileyFace.Text = "...";
}
}
public static int Clamp(int val, int min, int max)
{
if (val < min)
{
return min;
}
else if (val > max)
{
return max;
}
else
{
return val;
}
}
void GenerateLevel(int x, int y, int mines)
{
cells = new Cell[x, y];
StackPanel[] fieldRows = new StackPanel[y];
fieldBtns = new Button[x, y];
maxMines = mines;
nearbyBombs = new int[x, y];
for (int i = 0; i < y; i++)
{
fieldRows[i] = new StackPanel() { Name = "MineFieldPanelRow" + i, Orientation = Orientation.Horizontal };
MineFieldPanelRows.Children.Add(fieldRows[i]);
for (int j = 0; j < x; j++)
{
fieldBtns[j, i] = new Button() { Name = "MineFieldButton" + j + "_" + i, Width = 64, Height = 64, Margin = new Thickness(4), Padding = Margin = new Thickness(0) };
fieldBtns[j, i].Click += MineFieldButtonClick;
fieldBtns[j, i].Content = new FontIcon() { Glyph = "\uE890", FontSize = 32 };
fieldRows[i].Children.Add(fieldBtns[j, i]);
cells[j, i] = new Cell()
{
covered = true
};
nearbyBombs[j, i] = 0;
}
}
}
void PlaceMines()
{
Random rnd = new Random();
for (int i = 0; i < maxMines; i++)
{
int x = rnd.Next(0, cells.GetLength(0));
int y = rnd.Next(0, cells.GetLength(1));
while (cells[x, y].cellState == Cell.CellType.Bomb || cells[x, y].cellState == Cell.CellType.FirstClick)
{
x = rnd.Next(0, cells.GetLength(0));
y = rnd.Next(0, cells.GetLength(1));
}
cells[x, y].cellState = Cell.CellType.Bomb;
}
}
private void MineFieldButtonClick(object sender, RoutedEventArgs e)
{
if (!isGenerated)
{
for (int i = 0; i < fieldBtns.GetLength(1); i++)
{
for (int j = 0; j < fieldBtns.GetLength(0); j++)
{
if (fieldBtns[j, i] == sender)
{
cells[j, i].cellState = Cell.CellType.FirstClick;
break;
}
}
}
PlaceMines();
isGenerated = true;
}
for (int i = 0; i < cells.GetLength(1); i++)
{
for (int j = 0; j < cells.GetLength(0); j++)
{
if (cells[j, i].cellState == Cell.CellType.Bomb)
{
fieldBtns[j, i].Content = new FontIcon() { Glyph = "\uE783", FontSize = 32 };
nearbyBombs[j, i] = -1;
if (i - 1 > -1)
{
if (j - 1 > -1)
{
nearbyBombs[j - 1, i - 1]+=2;
}
nearbyBombs[j, i - 1]+=2;
if (j + 1 <= nearbyBombs.GetLength(0) - 1)
{
nearbyBombs[j + 1, i - 1]+=2;
}
}
if (j - 1 > -1)
{
nearbyBombs[j - 1, i]+=2;
}
if (j + 1 <= nearbyBombs.GetLength(0) - 1)
{
nearbyBombs[j + 1, i]++;
}
if (i + 1 <= nearbyBombs.GetLength(1) - 1)
{
if (j - 1 >= 0)
{
nearbyBombs[j - 1, i + 1]++;
}
nearbyBombs[j, i + 1]++;
if (j + 1 <= nearbyBombs.GetLength(0) - 1)
{
nearbyBombs[j + 1, i + 1]++;
}
}
}
else if (nearbyBombs[j, i] == 0)
{
fieldBtns[j, i].Content = null;
}
else
{
int bombsNearby = nearbyBombs[j, i];
fieldBtns[j, i].Content = new TextBlock() { Text = bombsNearby.ToString(), FontSize = 32 };
}
}
}
}
}
}
XAML 部分为:
<Page
x:Class="Mynesweeper.MineField"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Mynesweeper"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid x:Name="MainGrid">
<Grid.Transitions>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True"/>
</TransitionCollection>
</Grid.Transitions>
<StackPanel x:Name="Stats" Margin="16,16,16,0" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<TextBlock x:Name="TimeAmount" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" FontSize="32" Width="64" TextAlignment="Right" Margin="0,0,16,0"/>
<Button x:Name="Smiley" Padding="0" Width="64" Height="64" HorizontalAlignment="Center" VerticalAlignment="Stretch" FontSize="32" d:LayoutOverrides="LeftMargin, RightMargin, LeftPosition, RightPosition">
<TextBlock x:Name="SmileyFace" Text=":)" TextLineBounds="Tight" TextAlignment="Center"/>
</Button>
<TextBlock x:Name="MinesAmount" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" FontSize="32" Width="64" Margin="16,0,0,0"/>
</StackPanel>
<StackPanel x:Name="MineFieldPanelRows" Margin="0,16,0,0" VerticalAlignment="Center" HorizontalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<StackPanel.RenderTransform>
<CompositeTransform ScaleX="0.25" ScaleY="0.25"/>
</StackPanel.RenderTransform> <!-- specially downscaled to fit the whole field on the screen -->
</StackPanel>
</Grid>
</Page>
我真的不知道为什么会出现这个问题。也许我在某处打错了字?请帮忙!
以下代码:
if (j - 1 > -1)
{
nearbyBombs[j - 1, i]+=2;
}
在检查以确保 i > 0
的任何块之外。因此,如果你在 i = 0 处有地雷,你的 codez 就会爆炸。这让我相信你的令人费解的 if
陈述完全是错误的,而且它们太令人费解以至于无法尝试找出精确的点你搞砸了你的计数。大海捞针,一发现又错了,简直不值一提。
让我向您推荐一种不同的方法:
当你指的是
height
和width
时,请停止使用x
和y
,并且当然要停止使用i
和j
当你指的是x
和y
。自 1600 年代中期以来,笛卡尔平面坐标名称的约定一直是水平轴 'x' 和垂直轴 'y',(Wikipedia)你不需要发明一个2017年新公约.有人告诉您,即使您有一个由单个语句组成的块,也应该始终使用大括号。如果您认为这是明智的建议,那就继续用花括号把自己打倒吧,但至少让我们远离 Whosebug 上的花括号垃圾邮件,简洁很重要。
在您的代码还没有运行之前就停止使用
Random()
。在一组特定的坐标处放置特定数量的炸弹,并确保在引入随机性之前你的东西能正常工作。退出嵌套
if
语句的复杂且不可读的层叠 zig-zag 面条,做一些更简单的事情。
像这样:
void updateBombCountsAroundBomb( int bombY, int bombX, int height, int width )
{
for( y = bombY - 1; y <= bombY + 1; y++ )
for( x = bombX - 1; x <= bombX + 1; x++ )
if( y != bombY && x != bombX )
if( y >= 0 && y < height && x >= 0 && x < width )
nearbyBombs[y, x]++;
}
一旦你让它工作,你可以尝试优化它,如果你想这样做,知道如果你不小心破坏它,你总是可以恢复到以前的工作版本。 (测试规则z。)
你的问题是这个语句:
else if (nearbyBombs[j, i] == 0)
您正在检查某个单元格的炸弹计数是否为零,因为您仍在遍历网格并设置炸弹计数,因此在进行此检查时,尚未执行该单元格的所有计算.
解决方案是先执行一次计算炸弹数量,完成后,然后再执行一次以创建按钮并在其中显示炸弹数量。