My take on the popular game. category 'game', language C#, created 12-Feb-2022, version 1.0, by Luc Pattyn |
License: The author hereby grants you a worldwide, non-exclusive license to use and redistribute the files and the source code in the article in any way you see fit, provided you keep the copyright notice in place; when code modifications are applied, the notice must reflect that. The author retains copyright to the article, you may not republish or otherwise make available the article, in whole or in part, without the prior written consent of the author. Disclaimer: This work is provided “as is”, without any express or implied warranties or conditions or guarantees. You, the user, assume all risk in its use. In no event will the author be liable to you on any legal theory for any special, incidental, consequential, punitive or exemplary damages arising out of this license or the use of the work or otherwise. |
Everyone knows the MineSweeper game that has been part of Windows since the early days. Implementing such a game in C# isn't hard at all, and this article describes my take on it. LP_MineSweeper has been implemented in WinForms (i.e. using the System.Windows.Forms namespace), using Visual Studio 2022 and .NET Framework 4.5 The program is available in executable and in source form.
DesignMy design is based on three mayor classes:
|
When a game is on, there is one GameForm instance, one Game instance, and as many Cells as are required to describe the game (the player can choose any of three built-in game sizes).
The GameForm has a menu structure, a "Start" button, and a container that will be used to display the game board. Interaction is handled by the typical "click handlers". When requested to do so, the GameForm will create a Game instance and then can interact with it, e.g. abort it when a player gives up.
A Game instance basically creates a two-dimensional array of Cell instances, and a Timer.
The game does not know the form, it can not directly interact with the player. All it has is a few delegates that it should invoke when it
wants to signal something to the player; examples include ShowStatus
, ShowTime
and PlaySound
.
In this way one could reuse the Game class unmodified with a very different front-end.
As it inherits from Button, each Cell has a button_Click handler to deal with the player clicking a square.
The handler first discerns left and right mouse clicks, then adjusts the
cell state (mostly properties IsRevealed
and IsFlagged
), modifies its appearance (by changing button properties such as BackColor
and Text), and finally calls Game.UpdateGame to signal the player did something to a square.
That is where the game performs a floodfill when required, recalculates the game state, and informs the outside world as appropriate.
When the player clicks a square that has no neighboring mines (a "safe" cell), the game is supposed to automatically reveal the adjacent cells, and to keep doing that for all safe cells it can reach from the current position. That is typically called a floodfill. Where there exist highly optimized (and complex) floodfill algorithms, we have chosen a very simple one: two nested loops scan the board from top to bottom, and from left to right, and just propagate the information they discover. In order to also support upwards and leftwards information flow, the double loop has been nested inside a while loop, which runs as long as new information is discovered.
It is important to note that the floodfill does not simulate a player really clicking a cell, as that would cause a chain of cell clicks and floodfills that would be nesting inside one another, causing a waste of memory and time, and possibly also avalanche problems. Instead of all that, the flooffill is launched at most once per actual cell click.
GameForm.setupTablePanel
). This should have been made a lot easier!GameForm.resizeForm
holds the code to resize the form
and to center it, taking into account the working area (i.e. the screen size excluding the task bar).
On a multi-monitor system, this code should work as well, and it should keep the form on the same screen while doing so. This has not been tested lacking
such hardware.Cell.button_Paint
has been
added to give unrevealed cells a true 3D effect; note: the base painting is still handled by the original Button, only the 3D stuff has been added.System.Media.SoundPlayer
class.Properties.Settings
class
(ref);
the data is stored in an XML file in the C:\Users\username\AppData\Local\.... path.The C# language has been extended over the years. Some of the additions are just "syntactic sugar", meaning they don't offer new functionality, but just an easier way to code what is intended. I have used some of these, making my code need Visual Studio 2015 at least. Two examples:
game?.Stop();
is equivalent to if (game!=null) game.Stop();
game.ShowTime = (time) => ssTime.Value = time;
is the single-line equivalent to
game.ShowTime = setTime;
assuming the following function were also present:
void setTime(string time) { ssTime.Value = time; }
This has been fun.
Perceler |
Copyright © 2012, Luc Pattyn |
Last Modified 02-Sep-2013 |