├── 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 | } --------------------------------------------------------------------------------