Compare commits
10 Commits
d74b7a9413
...
21af99f8ae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21af99f8ae | ||
|
|
b3e3b50d64 | ||
|
|
2445155e93 | ||
|
|
548b29ea22 | ||
|
|
b953371536 | ||
|
|
2c477ef637 | ||
|
|
ebd536897c | ||
|
|
542667699e | ||
|
|
4f2b5cd940 | ||
|
|
d565970903 |
1
Cell.cs
1
Cell.cs
@ -5,7 +5,6 @@
|
|||||||
public string Value = " ";
|
public string Value = " ";
|
||||||
public bool IsMine;
|
public bool IsMine;
|
||||||
public bool IsMarked;
|
public bool IsMarked;
|
||||||
|
|
||||||
public Cell(bool mine)
|
public Cell(bool mine)
|
||||||
{
|
{
|
||||||
if (mine)
|
if (mine)
|
||||||
|
|||||||
44
Database.cs
Normal file
44
Database.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace MineSweeper
|
||||||
|
{
|
||||||
|
internal class Database
|
||||||
|
{
|
||||||
|
private readonly string path;
|
||||||
|
private class PlayerData
|
||||||
|
{
|
||||||
|
public string name { get; set; }
|
||||||
|
public int wins { get; set; } = 0;
|
||||||
|
public int loses { get; set; } = 0;
|
||||||
|
}
|
||||||
|
private record Root(List<PlayerData> players);
|
||||||
|
private readonly Root root;
|
||||||
|
public Database()
|
||||||
|
{
|
||||||
|
path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MineSweeper by H1K0");
|
||||||
|
if (!Directory.Exists(path))
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
path = Path.Combine(path, "db.json");
|
||||||
|
if (!File.Exists(path))
|
||||||
|
File.WriteAllText(path, "{\"players\":[]}");
|
||||||
|
root = JsonSerializer.Deserialize<Root>(File.ReadAllText(path));
|
||||||
|
}
|
||||||
|
public int PlayersCount() { return root.players.Count; }
|
||||||
|
public int FindPlayer(string name) { return root.players.FindIndex(p => p.name == name); }
|
||||||
|
public void AddPlayer(string name)
|
||||||
|
{
|
||||||
|
PlayerData newplayer = new();
|
||||||
|
newplayer.name = name;
|
||||||
|
root.players.Add(newplayer);
|
||||||
|
}
|
||||||
|
public int[] Stats(int playerId) { return new int[] { root.players[playerId].wins, root.players[playerId].loses }; }
|
||||||
|
public void AddGame(int playerId, bool win)
|
||||||
|
{
|
||||||
|
if (win)
|
||||||
|
root.players[playerId].wins++;
|
||||||
|
else
|
||||||
|
root.players[playerId].loses++;
|
||||||
|
}
|
||||||
|
public void Update() { File.WriteAllText(path, JsonSerializer.Serialize(root)); }
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Field.cs
37
Field.cs
@ -5,9 +5,10 @@
|
|||||||
private readonly int width = 0;
|
private readonly int width = 0;
|
||||||
private readonly int height = 0;
|
private readonly int height = 0;
|
||||||
private readonly int size = 0;
|
private readonly int size = 0;
|
||||||
|
private int nmines = 0;
|
||||||
private readonly List<Cell> cells = new();
|
private readonly List<Cell> cells = new();
|
||||||
private readonly List<Cell> opened = new();
|
private readonly List<Cell> opened = new();
|
||||||
public Field(int input_width, int input_height, int nmines)
|
public Field(int input_width, int input_height, int input_nmines)
|
||||||
{
|
{
|
||||||
if (input_width < 0 || input_height < 0)
|
if (input_width < 0 || input_height < 0)
|
||||||
throw new ArgumentException("Field dimensions must be natural numbers.");
|
throw new ArgumentException("Field dimensions must be natural numbers.");
|
||||||
@ -19,12 +20,11 @@
|
|||||||
throw new ArgumentException("The number of mines can not be negative.");
|
throw new ArgumentException("The number of mines can not be negative.");
|
||||||
if (nmines > input_width * input_height)
|
if (nmines > input_width * input_height)
|
||||||
throw new ArgumentException("The number of mines can not be greater than the number of all cells.");
|
throw new ArgumentException("The number of mines can not be greater than the number of all cells.");
|
||||||
width = input_width;
|
(width, height, nmines) = (input_width, input_height, input_nmines);
|
||||||
height = input_height;
|
|
||||||
size = width * height;
|
size = width * height;
|
||||||
cells = new List<Cell>();
|
cells = new List<Cell>();
|
||||||
for (int i = 0; i < size; i++)
|
for (int i = 0; i < size; i++)
|
||||||
cells.Add(new Cell(nmines-- > 0));
|
cells.Add(new Cell(input_nmines-- > 0));
|
||||||
Random rnd = new();
|
Random rnd = new();
|
||||||
for (int i = 0; i < size; i++)
|
for (int i = 0; i < size; i++)
|
||||||
{
|
{
|
||||||
@ -78,7 +78,7 @@
|
|||||||
{
|
{
|
||||||
Console.Write(" " + ((opened.Contains(cells[y * width + x]) || cells[y * width + x].IsMarked) ? cells[y * width + x].Show() : "■"));
|
Console.Write(" " + ((opened.Contains(cells[y * width + x]) || cells[y * width + x].IsMarked) ? cells[y * width + x].Show() : "■"));
|
||||||
}
|
}
|
||||||
Console.WriteLine(" ║");
|
Console.WriteLine(" ║ " + (char)('A' + y));
|
||||||
}
|
}
|
||||||
Console.Write(" ╚");
|
Console.Write(" ╚");
|
||||||
for (int i = 0; i < 2 * width + 1; i++)
|
for (int i = 0; i < 2 * width + 1; i++)
|
||||||
@ -86,15 +86,21 @@
|
|||||||
Console.Write("═");
|
Console.Write("═");
|
||||||
}
|
}
|
||||||
Console.WriteLine("╝");
|
Console.WriteLine("╝");
|
||||||
|
Console.Write(" ");
|
||||||
|
for (int i = 0; i < width;)
|
||||||
|
{
|
||||||
|
Console.Write(" " + ++i);
|
||||||
|
}
|
||||||
|
Console.WriteLine($"\n\nRemaining mines: {nmines}.");
|
||||||
}
|
}
|
||||||
public bool Open(int y, int x)
|
public bool Open(int y, int x)
|
||||||
{
|
{
|
||||||
if (y < 0 || y >= height || x < 0 || x >= width)
|
if (y < 0 || y >= height || x < 0 || x >= width)
|
||||||
throw new Exception("Coordinates out of the field!");
|
throw new Exception("Coordinates out of the field!");
|
||||||
if (cells[y * width + x].IsMine)
|
|
||||||
return true;
|
|
||||||
if (cells[y * width + x].IsMarked)
|
if (cells[y * width + x].IsMarked)
|
||||||
return false;
|
return false;
|
||||||
|
if (cells[y * width + x].IsMine)
|
||||||
|
return true;
|
||||||
if (!opened.Contains(cells[y * width + x]))
|
if (!opened.Contains(cells[y * width + x]))
|
||||||
{
|
{
|
||||||
opened.Add(cells[y * width + x]);
|
opened.Add(cells[y * width + x]);
|
||||||
@ -150,7 +156,7 @@
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
public void OpenAll()
|
public void OpenAll(bool automark)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < size; i++)
|
for (int i = 0; i < size; i++)
|
||||||
{
|
{
|
||||||
@ -158,14 +164,27 @@
|
|||||||
opened.Add(cells[i]);
|
opened.Add(cells[i]);
|
||||||
if (cells[i].IsMarked && !cells[i].IsMine)
|
if (cells[i].IsMarked && !cells[i].IsMine)
|
||||||
cells[i].SetWrong();
|
cells[i].SetWrong();
|
||||||
|
if (automark && cells[i].IsMine)
|
||||||
|
{
|
||||||
|
cells[i].Mark();
|
||||||
|
nmines = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
this.Draw();
|
||||||
}
|
}
|
||||||
public void Mark(int y, int x)
|
public void Mark(int y, int x)
|
||||||
{
|
{
|
||||||
if (!opened.Contains(cells[y * width + x]))
|
if (!opened.Contains(cells[y * width + x]))
|
||||||
|
{
|
||||||
cells[y * width + x].Mark();
|
cells[y * width + x].Mark();
|
||||||
|
nmines--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void Unmark(int y, int x)
|
||||||
|
{
|
||||||
|
cells[y * width + x].Unmark();
|
||||||
|
nmines++;
|
||||||
}
|
}
|
||||||
public void Unmark(int y, int x) { cells[y * width + x].Unmark(); }
|
|
||||||
public bool Check()
|
public bool Check()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < size; i++)
|
for (int i = 0; i < size; i++)
|
||||||
|
|||||||
31
Game.cs
31
Game.cs
@ -14,35 +14,40 @@
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Console.Clear();
|
Console.Clear();
|
||||||
field.Draw();
|
|
||||||
if (field.Check())
|
if (field.Check())
|
||||||
{
|
{
|
||||||
|
field.OpenAll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
field.Draw();
|
||||||
Console.Write("Enter your command: ");
|
Console.Write("Enter your command: ");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
command = Console.ReadLine().ToUpper().Split();
|
command = Console.ReadLine().ToUpper().Split();
|
||||||
if (command.Length == 1)
|
if (command.Length == 1)
|
||||||
{
|
{
|
||||||
int x = Convert.ToInt16(command[0].Substring(1)) - 1;
|
int x = Convert.ToInt16(command[0][1..]) - 1;
|
||||||
int y = command[0].First() - 'A';
|
int y = command[0].First() - 'A';
|
||||||
if (field.Open(y, x))
|
if (field.Open(y, x))
|
||||||
{
|
{
|
||||||
this.Finish();
|
Console.Clear();
|
||||||
|
field.OpenAll(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (command.Length == 2)
|
else if (command.Length > 1)
|
||||||
{
|
{
|
||||||
if (command[0] != "M" && command[0] != "U")
|
if (command[0] != "M" && command[0] != "U")
|
||||||
throw new Exception("Invalid command.");
|
throw new Exception("Invalid command.");
|
||||||
int x = Convert.ToInt16(command[1].Substring(1)) - 1;
|
for (int i = 1; i < command.Length; i++)
|
||||||
int y = command[1].First() - 'A';
|
{
|
||||||
if (command[0] == "M")
|
int x = Convert.ToInt16(command[i][1..]) - 1;
|
||||||
field.Mark(y, x);
|
int y = command[i].First() - 'A';
|
||||||
else
|
if (command[0] == "M")
|
||||||
field.Unmark(y, x);
|
field.Mark(y, x);
|
||||||
|
else
|
||||||
|
field.Unmark(y, x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -52,11 +57,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void Finish()
|
|
||||||
{
|
|
||||||
field.OpenAll();
|
|
||||||
Console.Clear();
|
|
||||||
field.Draw();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
46
Main.cs
46
Main.cs
@ -5,17 +5,41 @@
|
|||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Game game;
|
Game game;
|
||||||
|
Database database = new();
|
||||||
|
int PlayerId;
|
||||||
|
string PlayerName = "";
|
||||||
|
Console.Title = "MineSweeper by H1K0";
|
||||||
|
Console.WriteLine("(C) Masahiko AMANO a.k.a. H1K0, 2022\n\n" +
|
||||||
|
"Hey! Let's play the MineSweeper game!\nPress any button for help or Enter to log in.");
|
||||||
|
while (Console.ReadKey(true).Key != ConsoleKey.Enter)
|
||||||
|
Console.WriteLine("\nThe game follows the classic rules.\n" +
|
||||||
|
"Target cell coordinates are represented this way: A1, F5, I8. Case insensitive.\n" +
|
||||||
|
"Type coordinates of the cell to open it or use prefixes: \"M\" to mark or \"U\" to unmark the cell.\n" +
|
||||||
|
"For example: \"B9\", \"M D7\".\n" +
|
||||||
|
"Press any button to see help again or just press Enter to start the game.");
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Console.WriteLine("(C) Masahiko AMANO a.k.a. H1K0, 2022\n\n" +
|
Console.Write("\nYour name: ");
|
||||||
"Hey! Let's play the MineSweeper game!\nType anything for help or just press Enter to start the game.");
|
PlayerName = Console.ReadLine();
|
||||||
while (Console.ReadLine().Length != 0)
|
PlayerId = database.FindPlayer(PlayerName);
|
||||||
Console.WriteLine("\nThe game follows the classic rules.\n" +
|
if (PlayerId == -1 || PlayerName == "")
|
||||||
"Target cell coordinates are represented this way: A1, F5, I8. Case insensitive.\n" +
|
{
|
||||||
"Type coordinates of the cell to open it or use prefixes: \"M\" to mark or \"U\" to unmark the cell.\n" +
|
Console.WriteLine("Could not find a player with the name \"" + PlayerName + "\". Press Enter to add new or any other button to re-enter.");
|
||||||
"For example: \"B9\", \"M D7\".\n" +
|
if (Console.ReadKey(true).Key == ConsoleKey.Enter)
|
||||||
"Type anything to see help again or just press Enter to start the game.\n");
|
{
|
||||||
Console.Write("Okay, let's go!\nEnter field width, height and number of mines separated with a space: ");
|
database.AddPlayer(PlayerName);
|
||||||
|
PlayerId = database.PlayersCount() - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Console.WriteLine("You logged in as \"" + PlayerName + "\".\nYour stats:");
|
||||||
|
Console.WriteLine($"Wins: {database.Stats(PlayerId)[0]}\nLoses: {database.Stats(PlayerId)[1]}");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Console.Write("\nOkay, let's play!\nEnter field width, height and number of mines separated with a space: ");
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -35,12 +59,14 @@
|
|||||||
Console.WriteLine("You win! Congratulations!");
|
Console.WriteLine("You win! Congratulations!");
|
||||||
else
|
else
|
||||||
Console.WriteLine("You lose!");
|
Console.WriteLine("You lose!");
|
||||||
|
database.AddGame(PlayerId, result);
|
||||||
|
database.Update();
|
||||||
Console.WriteLine("Press Enter to play again or Escape to exit.");
|
Console.WriteLine("Press Enter to play again or Escape to exit.");
|
||||||
ConsoleKey key;
|
ConsoleKey key;
|
||||||
bool exit = false;
|
bool exit = false;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
key = Console.ReadKey().Key;
|
key = Console.ReadKey(true).Key;
|
||||||
if (key == ConsoleKey.Enter)
|
if (key == ConsoleKey.Enter)
|
||||||
{
|
{
|
||||||
Console.Clear();
|
Console.Clear();
|
||||||
|
|||||||
@ -1,10 +1,29 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<RepositoryUrl>https://github.com/H1K0/MineSweeper.git</RepositoryUrl>
|
||||||
|
<Copyright>Masahiko AMANO a.k.a. H1K0</Copyright>
|
||||||
|
<Authors>H1K0</Authors>
|
||||||
|
<StartupObject>MineSweeper.Program</StartupObject>
|
||||||
|
<PackageProjectUrl>https://github.com/H1K0/MineSweeper</PackageProjectUrl>
|
||||||
|
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||||
|
<Description>Console MineSweeper game on C#</Description>
|
||||||
|
<PackageIcon>favicon.png</PackageIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="favicon.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="favicon.png">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
favicon.png
Normal file
BIN
favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Reference in New Issue
Block a user