using System;
using System.Collections.Generic;
using System.Linq;
public class MinesweeperBoard
{
public int Rows, Cols, NMines;
public HashSet<(int r, int c)> Mines = new();
public int[,] Numbers; // nombre de mines voisines
public HashSet<(int r, int c)> Revealed = new();
public HashSet<(int r, int c)> Flagged = new();
public bool GameOver, Won;
private static readonly (int r, int c)[] Offsets = {
(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)
};
public MinesweeperBoard(int rows, int cols, int nMines, (int r,int c)? safeCell, Random rng)
{
Rows = rows; Cols = cols; NMines = nMines;
Numbers = new int[rows, cols];
// Cellules candidates (exclure safe_cell + ses voisins pour un premier clic sûr)
var excluded = new HashSet<(int r, int c)>();
if (safeCell is { } sc) { excluded.Add(sc); foreach (var n in Neighbors(sc.r, sc.c)) excluded.Add(n); }
var candidates = new List<(int r, int c)>();
for (int r = 0; r < rows; r++) for (int c = 0; c < cols; c++)
if (!excluded.Contains((r,c))) candidates.Add((r,c));
// Echantillonner nMines mines (Fisher-Yates partiel)
for (int i = 0; i < Math.Min(nMines, candidates.Count); i++) {
int j = rng.Next(i, candidates.Count);
(candidates[i], candidates[j]) = (candidates[j], candidates[i]);
Mines.Add(candidates[i]);
}
// Calculer les indices
for (int r = 0; r < rows; r++) for (int c = 0; c < cols; c++)
if (!Mines.Contains((r,c))) {
int cnt = 0; foreach (var (nr,nc) in Neighbors(r,c)) if (Mines.Contains((nr,nc))) cnt++;
Numbers[r,c] = cnt;
}
}
public List<(int r, int c)> Neighbors(int r, int c) {
var res = new List<(int r, int c)>(8);
foreach (var (dr,dc) in Offsets) {
int nr = r+dr, nc = c+dc;
if (nr >= 0 && nr < Rows && nc >= 0 && nc < Cols) res.Add((nr,nc));
}
return res;
}
public bool Reveal(int r, int c) {
if (Revealed.Contains((r,c)) || Flagged.Contains((r,c))) return true;
if (Mines.Contains((r,c))) { GameOver = true; return false; }
Revealed.Add((r,c));
if (Numbers[r,c] == 0)
foreach (var (nr,nc) in Neighbors(r,c))
if (!Revealed.Contains((nr,nc))) Reveal(nr,nc);
int safeCells = Rows*Cols - Mines.Count;
if (Revealed.Count == safeCells) Won = true;
return true;
}
public void Flag(int r, int c) {
if (Revealed.Contains((r,c))) return;
if (!Flagged.Add((r,c))) Flagged.Remove((r,c)); // toggle
}
public List<(int r,int c)> UnknownNeighbors(int r, int c) =>
Neighbors(r,c).Where(p => !Revealed.Contains(p) && !Flagged.Contains(p)).ToList();
public List<(int r,int c)> FlaggedNeighbors(int r, int c) =>
Neighbors(r,c).Where(p => Flagged.Contains(p)).ToList();
public int RemainingMines => NMines - Flagged.Count;
public MinesweeperBoard Copy() {
var b = new MinesweeperBoard(Rows, Cols, 0, null, null) {
Rows = Rows, Cols = Cols, NMines = NMines, Numbers = (int[,])Numbers.Clone(),
Mines = new HashSet<(int r, int c)>(Mines), Revealed = new HashSet<(int r, int c)>(Revealed),
Flagged = new HashSet<(int r, int c)>(Flagged), GameOver = GameOver, Won = Won
};
return b;
}
}
// Plateau de demonstration : 8x8, 10 mines, premier clic (4,4) garantit sur.
var rng0 = new Random(1);
var board0 = new MinesweeperBoard(8, 8, 10, safeCell: (4,4), rng0);
board0.Reveal(4, 4);
Console.WriteLine($"Plateau : {board0.Rows}x{board0.Cols}, {board0.NMines} mines");
Console.WriteLine($"Cellules revelees apres le premier clic : {board0.Revealed.Count}");
Console.WriteLine($"Cellules restantes (inconnues) : {board0.Rows*board0.Cols - board0.Revealed.Count}");
Console.WriteLine($"Mines a trouver : {board0.RemainingMines}");