├── README.md
├── ASCII Minesweeper.csproj
├── Core
├── Plot.cs
└── Grid.cs
├── LICENSE
├── .gitignore
└── Program.cs
/README.md:
--------------------------------------------------------------------------------
1 | # CSharp-Minesweeper
2 |
3 | Minesweeper game within terminal with ASCII characters
4 |
5 | You play by inputting with m and then the coordinates of the square you wish to click to mark it with a flag or just type the coordinates of the square to click it.
6 |
7 | Run `dotnet run` in your terminal to get started.
8 |
--------------------------------------------------------------------------------
/ASCII Minesweeper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | ASCII_Minesweeper
7 | enable
8 | enable
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Core/Plot.cs:
--------------------------------------------------------------------------------
1 | namespace ASCII_Minesweeper.Core {
2 | ///
3 | /// Initializes a new instance of the Plot class with specified row and column.
4 | ///
5 | /// The row index of the plot.
6 | /// The column index of the plot.
7 | public class Plot(int row, int col)
8 | {
9 | public bool isVisible = false;
10 | public bool isFlagged = false;
11 | public int row = row, col = col;
12 | public string content = "0";
13 |
14 | ///
15 | /// Sets the content of the plot to a mine.
16 | ///
17 | public string GetValue() {
18 | if (this.isFlagged)
19 | return "⚐";
20 | else if (!this.isVisible)
21 | return " ";
22 | else if (this.content == "0")
23 | return ".";
24 | else
25 | return this.content;
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Valerius Petrini
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 |
11 | [Dd]ebug/
12 | [Rr]elease/
13 | x64/
14 | [Bb]in/
15 | [Oo]bj/
16 |
17 | # MSTest test Results
18 | [Tt]est[Rr]esult*/
19 | [Bb]uild[Ll]og.*
20 |
21 | *_i.c
22 | *_p.c
23 | *_i.h
24 | *.ilk
25 | *.meta
26 | *.obj
27 | *.pch
28 | *.pdb
29 | *.pgc
30 | *.pgd
31 | *.rsp
32 | *.sbr
33 | *.tlb
34 | *.tli
35 | *.tlh
36 | *.tmp
37 | *.tmp_proj
38 | *.log
39 | *.vspscc
40 | *.vssscc
41 | .builds
42 | *.pidb
43 | *.log
44 | *.svclog
45 | *.scc
46 |
47 | # Visual C++ cache files
48 | ipch/
49 | *.aps
50 | *.ncb
51 | *.opensdf
52 | *.sdf
53 | *.cachefile
54 |
55 | # Visual Studio profiler
56 | *.psess
57 | *.vsp
58 | *.vspx
59 |
60 | # Guidance Automation Toolkit
61 | *.gpState
62 |
63 | # ReSharper is a .NET coding add-in
64 | _ReSharper*/
65 | *.[Rr]e[Ss]harper
66 | *.DotSettings.user
67 |
68 | # Click-Once directory
69 | publish/
70 |
71 | # Publish Web Output
72 | *.Publish.xml
73 | *.pubxml
74 | *.azurePubxml
75 |
76 | # NuGet Packages Directory
77 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
78 | packages/
79 | ## TODO: If the tool you use requires repositories.config, also uncomment the next line
80 | !packages/repositories.config
81 |
82 | # Windows Azure Build Output
83 | csx/
84 | *.build.csdef
85 |
86 | # Windows Store app package directory
87 | AppPackages/
88 |
89 | # Others
90 | sql/
91 | *.Cache
92 | ClientBin/
93 | [Ss]tyle[Cc]op.*
94 | ![Ss]tyle[Cc]op.targets
95 | ~$*
96 | *~
97 | *.dbmdl
98 | *.[Pp]ublish.xml
99 |
100 | *.publishsettings
101 |
102 | # RIA/Silverlight projects
103 | Generated_Code/
104 |
105 | # Backup & report files from converting an old project file to a newer
106 | # Visual Studio version. Backup files are not needed, because we have git ;-)
107 | _UpgradeReport_Files/
108 | Backup*/
109 | UpgradeLog*.XML
110 | UpgradeLog*.htm
111 |
112 | # SQL Server files
113 | App_Data/*.mdf
114 | App_Data/*.ldf
115 |
116 | # =========================
117 | # Windows detritus
118 | # =========================
119 |
120 | # Windows image file caches
121 | Thumbs.db
122 | ehthumbs.db
123 |
124 | # Folder config file
125 | Desktop.ini
126 |
127 | # Recycle Bin used on file shares
128 | $RECYCLE.BIN/
129 |
130 | # Mac desktop service store files
131 | .DS_Store
132 |
133 | _NCrunch*
134 |
135 | .vscode/
136 | *.sln
137 |
--------------------------------------------------------------------------------
/Core/Grid.cs:
--------------------------------------------------------------------------------
1 | namespace ASCII_Minesweeper.Core {
2 | public class Grid {
3 | public List> plots = new();
4 | public int rows = 9;
5 | public int columns = 9;
6 | public int bombs = 10;
7 |
8 | ///
9 | /// Initializes a new instance of the Grid class with specified rows, columns, and bombs.
10 | ///
11 | public void CreateGrid() {
12 | for (int i = 0; i < this.rows; i++) {
13 | this.plots.Add(new List());
14 | for (int j = 0; j < this.columns; j++) {
15 | this.plots[i].Add(new Plot(i, j));
16 | }
17 | }
18 | for (int i = 0; i < this.bombs; i++)
19 | this.PlaceBomb();
20 | for (int i = 0; i < this.rows; i++) {
21 | for (int j = 0; j < this.columns; j++) {
22 | Plot value = this.plots[i][j];
23 | if (value.content == "*")
24 | UpdateValues(i, j);
25 | }
26 | }
27 |
28 | }
29 |
30 | ///
31 | /// Places a bomb at a random location on the grid.
32 | /// If the randomly selected plot already contains a bomb, it recursively calls itself to find a
33 | /// new location.
34 | ///
35 | public void PlaceBomb() {
36 | int row = new Random().Next(0, this.rows);
37 | int col = new Random().Next(0, this.columns);
38 | Plot plot = this.plots[row][col];
39 | if (!(plot.content == "*"))
40 | plot.content = "*";
41 | else
42 | this.PlaceBomb();
43 | }
44 |
45 | ///
46 | /// Parses a string value to an integer and returns it as a string.
47 | ///
48 | /// The string value to parse.
49 | /// The parsed integer value as a string.
50 | public static string ParseToValue(string value) {
51 | return int.Parse(value).ToString();
52 | }
53 |
54 | ///
55 | /// Updates the values of the plots in the top and bottom rows relative to the current plot
56 | /// based on the column index.
57 | ///
58 | /// The current row of plots.
59 | /// The column index of the current plot.
60 | public void UpdateTopAndBottomRowValues(List currentRow, int col) {
61 | if (col - 1 > -1)
62 | currentRow[col - 1].content = UpdateValue(currentRow[col - 1].content);
63 | currentRow[col].content = UpdateValue(currentRow[col].content);
64 | if (9 > col + 1)
65 | currentRow[col + 1].content = UpdateValue(currentRow[col + 1].content);
66 | }
67 |
68 | ///
69 | /// Updates the value of a plot based on its current value.
70 | /// If the value is "*", it remains unchanged; otherwise, it increments the value by 1.
71 | ///
72 | /// The current value of the plot.
73 | /// The updated value as a string.
74 | public string UpdateValue(string value) {
75 | if (!(value == "*"))
76 | return (int.Parse(value) + 1).ToString();
77 | return "*";
78 | }
79 |
80 | ///
81 | /// Updates the values of the plots in the grid based on the specified row and column indices
82 | /// and updates the values of the plots in the top and bottom rows relative to the current plot.
83 | ///
84 | /// The row index of the current plot.
85 | /// The column index of the current plot.
86 | public void UpdateValues(int row, int col) {
87 | List currentRow;
88 | if (row - 1 > -1) {
89 | currentRow = this.plots[row - 1];
90 | this.UpdateTopAndBottomRowValues(currentRow, col);
91 | }
92 | currentRow = this.plots[row];
93 | if (col - 1 > -1)
94 | currentRow[col - 1].content = UpdateValue(currentRow[col - 1].content);
95 | if (9 > col + 1)
96 | currentRow[col + 1].content = UpdateValue(currentRow[col + 1].content);
97 | if (9 > row + 1) {
98 | currentRow = this.plots[row + 1];
99 | this.UpdateTopAndBottomRowValues(currentRow, col);
100 | }
101 | }
102 |
103 | ///
104 | /// Prints the current state of the game board to the console.
105 | ///
106 | public void PrintBoard() {
107 | Console.Clear();
108 | string leadingSpaces = " ".PadRight(this.rows.ToString().Length + 1, ' ');
109 | Console.WriteLine(leadingSpaces + " A B C D E F G H I");
110 | Console.WriteLine(leadingSpaces + "╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗");
111 | for (int i = 0; i < this.rows; i++) {
112 | Console.Write(i.ToString().PadRight(this.rows.ToString().Length + 1, ' ') + "║");
113 | for (int j = 0; j < this.columns; j++) {
114 | Console.Write(" " + this.plots[i][j].GetValue() + " ║");
115 | }
116 | if (!(i == this.columns - 1))
117 | Console.Write("\n" + leadingSpaces + "╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n");
118 | }
119 | Console.WriteLine("\n" + leadingSpaces + "╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝");
120 | }
121 | }
122 | }
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using ASCII_Minesweeper.Core;
2 |
3 | namespace ASCII_Minesweeper {
4 | ///
5 | /// Main class for the ASCII Minesweeper game.
6 | ///
7 | class Program {
8 | public Grid grid = new();
9 |
10 | ///
11 | /// Starts the game and handles the game loop.
12 | ///
13 | private void Play() {
14 | if (this.Victory()) {
15 | Console.WriteLine("You win!");
16 | return;
17 | }
18 | // Get value
19 | int[] input = this.Choose();
20 | int row = input[1];
21 | int col = input[0];
22 | Plot value = this.grid.plots[row][col];
23 | // If it is a mine then you lose
24 | if (value.content == "*" && !value.isFlagged) {
25 | this.grid.PrintBoard();
26 | Console.WriteLine("You lose!");
27 | return;
28 | }
29 | // If value is 0, not visible, and not flagged, set it to visible and
30 | // do the same for all adjacent squares until all the 0's are visible
31 | if (!value.isVisible && !value.isFlagged)
32 | this.CheckZeros(row, col, new HashSet());
33 | this.grid.PrintBoard();
34 | this.Play();
35 | }
36 |
37 | ///
38 | /// Checks if the player has won the game.
39 | /// A player wins if all non-mine plots are visible.
40 | ///
41 | private bool Victory() {
42 | int count = 0;
43 | foreach (List row in this.grid.plots)
44 | foreach (Plot col in row)
45 | if (col.content != "*" && col.isVisible)
46 | count++;
47 | if (count == 71)
48 | return true;
49 | return false;
50 | }
51 |
52 | ///
53 | /// Recursively checks all adjacent plots for zeros and sets them to visible.
54 | /// This method uses depth-first search to explore all connected zeros.
55 | ///
56 | /// The row index of the current plot.
57 | /// The column index of the current plot.
58 | /// A set of visited plots to avoid cycles.
59 | private void CheckZeros(int row, int col, HashSet visited) {
60 | // Check if row or column is outside of boundries
61 | if (row < 0 || row >= this.grid.rows || col < 0 || col >= this.grid.columns)
62 | return;
63 | if (visited.Contains(this.grid.plots[row][col]))
64 | return;
65 | // Set plot visible
66 | this.grid.plots[row][col].isVisible = true;
67 | // If the value is not 0 then return
68 | if (this.grid.plots[row][col].content != "0")
69 | return;
70 |
71 | visited.Add(this.grid.plots[row][col]);
72 | // Do the same for all 8 adjacent plots
73 | CheckZeros(row - 1, col, visited);
74 | CheckZeros(row + 1, col, visited);
75 | CheckZeros(row - 1, col + 1, visited);
76 | CheckZeros(row - 1, col - 1, visited);
77 | CheckZeros(row + 1, col + 1, visited);
78 | CheckZeros(row + 1, col - 1, visited);
79 | CheckZeros(row, col + 1, visited);
80 | CheckZeros(row, col - 1, visited);
81 | }
82 |
83 | ///
84 | /// Prompts the user to choose a square or place a marker.
85 | ///
86 | private int[] Choose() {
87 | List letters = new() { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' };
88 | List numbers = new() { '0', '1', '2', '3', '4', '5', '6', '7', '8' };
89 | while (true) {
90 | Console.Write("Choose a square (eg. E4) or place a marker (eg. mE4): ");
91 | string choice = Console.ReadLine()!.ToLower();
92 | if (
93 | choice.Length == 3 &&
94 | choice[0] == 'm' &&
95 | letters.Contains(choice[1]) &&
96 | numbers.Contains(choice[2])
97 | ) {
98 | int col = char.ToUpper(choice[1]) - 65;
99 | int row = int.Parse(choice[2].ToString());
100 | this.Marker(row, col);
101 | this.Play();
102 | }
103 | else if (
104 | choice.Length == 2 &&
105 | letters.Contains(choice[0]) &&
106 | numbers.Contains(choice[1])
107 | ) {
108 | return new int[] {
109 | char.ToUpper(choice[0]) - 65,
110 | int.Parse(choice[1].ToString())
111 | };
112 | }
113 | else {
114 | this.Choose();
115 | }
116 | }
117 | }
118 |
119 | ///
120 | /// Marks a plot as flagged or unflagged.
121 | ///
122 | /// The row index of the plot.
123 | /// The column index of the plot.
124 | private void Marker(int row, int col){
125 | if (!this.grid.plots[row][col].isVisible)
126 | this.grid.plots[row][col].isFlagged = !this.grid.plots[row][col].isFlagged;
127 | this.grid.PrintBoard();
128 | }
129 |
130 | ///
131 | /// The main entry point for the ASCII Minesweeper game.
132 | ///
133 | static void Main(string[] args) {
134 | Console.OutputEncoding = System.Text.Encoding.UTF8;
135 | Console.WriteLine("Minesweeper");
136 | Program program = new();
137 | program.grid.CreateGrid();
138 | program.grid.PrintBoard();
139 | program.Play();
140 | }
141 | }
142 | }
--------------------------------------------------------------------------------