Skip to content

Commit

Permalink
BugSweeper. (#446)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbritch authored Feb 27, 2024
1 parent 1a473ae commit 9f63470
Show file tree
Hide file tree
Showing 42 changed files with 1,613 additions and 0 deletions.
27 changes: 27 additions & 0 deletions 8.0/Apps/BugSweeper/BugSweeper.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34616.47
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BugSweeper", "BugSweeper\BugSweeper.csproj", "{655EB510-5342-4DF5-A6DE-818292B79206}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{655EB510-5342-4DF5-A6DE-818292B79206}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{655EB510-5342-4DF5-A6DE-818292B79206}.Debug|Any CPU.Build.0 = Debug|Any CPU
{655EB510-5342-4DF5-A6DE-818292B79206}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{655EB510-5342-4DF5-A6DE-818292B79206}.Release|Any CPU.ActiveCfg = Release|Any CPU
{655EB510-5342-4DF5-A6DE-818292B79206}.Release|Any CPU.Build.0 = Release|Any CPU
{655EB510-5342-4DF5-A6DE-818292B79206}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A82E33F4-3803-433C-873B-015A4AEC813A}
EndGlobalSection
EndGlobal
14 changes: 14 additions & 0 deletions 8.0/Apps/BugSweeper/BugSweeper/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BugSweeper"
x:Class="BugSweeper.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
12 changes: 12 additions & 0 deletions 8.0/Apps/BugSweeper/BugSweeper/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace BugSweeper
{
public partial class App : Application
{
public App()
{
InitializeComponent();

MainPage = new AppShell();
}
}
}
15 changes: 15 additions & 0 deletions 8.0/Apps/BugSweeper/BugSweeper/AppShell.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="BugSweeper.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BugSweeper"
Shell.FlyoutBehavior="Disabled"
Title="BugSweeper">

<ShellContent
Title="Home"
ContentTemplate="{DataTemplate local:MainPage}"
Route="MainPage" />

</Shell>
10 changes: 10 additions & 0 deletions 8.0/Apps/BugSweeper/BugSweeper/AppShell.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace BugSweeper
{
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
}
}
220 changes: 220 additions & 0 deletions 8.0/Apps/BugSweeper/BugSweeper/Board.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
namespace BugSweeper
{
class Board : AbsoluteLayout
{
// Alternative sizes make the tiles a tad small.
const int COLS = 9; // 16
const int ROWS = 9; // 16
const int BUGS = 10; // 40

Tile[,] tiles = new Tile[ROWS, COLS];
int flaggedTileCount;
bool isGameInProgress; // on first tap
bool isGameInitialized; // on first double-tap
bool isGameEnded;

// Events to notify page.
public event EventHandler GameStarted;
public event EventHandler<bool> GameEnded;

public int BugCount => BUGS;

public int FlaggedTileCount
{
get => flaggedTileCount;
set
{
if (flaggedTileCount != value)
{
flaggedTileCount = value;
OnPropertyChanged();
}
}
}

public Board()
{
for (int row = 0; row < ROWS; row++)
{
for (int col = 0; col < COLS; col++)
{
Tile tile = new Tile(row, col);
tile.TileStatusChanged += OnTileStatusChanged;
this.Children.Add(tile);
tiles[row, col] = tile;
}
}

SizeChanged += (sender, args) =>
{
double tileWidth = this.Width / COLS;
double tileHeight = this.Height / ROWS;
foreach (Tile tile in tiles)
{
Rect bounds = new Rect(tile.Col * tileWidth, tile.Row * tileHeight, tileWidth, tileHeight);
AbsoluteLayout.SetLayoutBounds(tile, bounds);
}
};

NewGameInitialize();
}

public void NewGameInitialize()
{
// Clear all the tiles.
foreach (Tile tile in tiles)
tile.Initialize();

isGameInProgress = false;
isGameInitialized = false;
isGameEnded = false;
this.FlaggedTileCount = 0;
}


// Not called until the first tile is double-tapped.
void DefineNewBoard(int tappedRow, int tappedCol)
{
// Begin the assignment of bugs.
Random random = new Random();
int bugCount = 0;

while (bugCount < BUGS)
{
// Get random row and column.
int row = random.Next(ROWS);
int col = random.Next(COLS);

// Skip it if it's already a bug.
if (tiles[row, col].IsBug)
continue;

// Avoid the tappedRow & Col & surrounding ones.
if (row >= tappedRow - 1 &&
row <= tappedRow + 1 &&
col >= tappedCol - 1 &&
col <= tappedCol + 1)
continue;

// It's a bug!
tiles[row, col].IsBug = true;

// Calculate the surrounding bug count.
CycleThroughNeighbors(row, col,
(neighborRow, neighborCol) =>
{
++tiles[neighborRow, neighborCol].SurroundingBugCount;
});

bugCount++;
}
}

void CycleThroughNeighbors(int row, int col, Action<int, int> callback)
{
int minRow = Math.Max(0, row - 1);
int maxRow = Math.Min(ROWS - 1, row + 1);
int minCol = Math.Max(0, col - 1);
int maxCol = Math.Min(COLS - 1, col + 1);

for (int neighborRow = minRow; neighborRow <= maxRow; neighborRow++)
{
for (int neighborCol = minCol; neighborCol <= maxCol; neighborCol++)
{
if (neighborRow != row || neighborCol != col)
callback(neighborRow, neighborCol);
}
}
}

void OnTileStatusChanged(object sender, TileStatus tileStatus)
{
if (isGameEnded)
return;

// With a first tile tapped, the game is now in progress.
if (!isGameInProgress)
{
isGameInProgress = true;

// Fire the GameStarted event.
if (GameStarted != null)
GameStarted(this, EventArgs.Empty);
}

// Update the "flagged" bug count before checking for a loss.
int flaggedCount = 0;

foreach (Tile tile in tiles)
{
if (tile.Status == TileStatus.Flagged)
flaggedCount++;
}

this.FlaggedTileCount = flaggedCount;

// Get the tile whose status has changed.
Tile changedTile = (Tile)sender;

// If it's exposed, some actions are required.
if (tileStatus == TileStatus.Exposed)
{
if (!isGameInitialized)
{
DefineNewBoard(changedTile.Row, changedTile.Col);
isGameInitialized = true;
}

if (changedTile.IsBug)
{
isGameInProgress = false;
isGameEnded = true;

// Fire the GameEnded event!
if (GameEnded != null)
GameEnded(this, false);

return;
}

// Auto expose for zero surrounding bugs.
if (changedTile.SurroundingBugCount == 0)
{
CycleThroughNeighbors(changedTile.Row, changedTile.Col,
(neighborRow, neighborCol) =>
{
// Expose all the neighbors.
tiles[neighborRow, neighborCol].Status = TileStatus.Exposed;
});
}
}

// Check for a win.
bool hasWon = true;

foreach (Tile til in tiles)
{
if (til.IsBug && til.Status != TileStatus.Flagged)
hasWon = false;

if (!til.IsBug && til.Status != TileStatus.Exposed)
hasWon = false;
}

// If there's a win, celebrate!
if (hasWon)
{
isGameInProgress = false;
isGameEnded = true;

// Fire the GameEnded event!
if (GameEnded != null)
GameEnded(this, true);

return;
}
}
}
}

71 changes: 71 additions & 0 deletions 8.0/Apps/BugSweeper/BugSweeper/BugSweeper.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->

<!-- Note for MacCatalyst:
The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
When specifying both architectures, use the plural <RuntimeIdentifiers> instead of the singular <RuntimeIdentifier>.
The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated;
either BOTH runtimes must be indicated or ONLY macatalyst-x64. -->
<!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> -->

<OutputType>Exe</OutputType>
<RootNamespace>BugSweeper</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<MauiVersion>8.0.7</MauiVersion>

<!-- Display name -->
<ApplicationTitle>BugSweeper</ApplicationTitle>

<!-- App Identifier -->
<ApplicationId>com.companyname.bugsweeper</ApplicationId>

<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>

<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>

<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />

<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />

<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185" />

<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />

<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

<ItemGroup>
<None Remove="Resources\Images\dotnet_logo.png" />
<None Remove="Resources\Images\redbug.png" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
</ItemGroup>

</Project>
Loading

0 comments on commit 9f63470

Please sign in to comment.