/* Below is a complete Console program (single file) that: builds a valid, fully-solved Sudoku by recursive backtracking, removes numbers while keeping the puzzle uniquely solvable, and prints both the puzzle and the solution in the console. You can paste this into a new Console App (Program.cs) and run. */ using System; using System.Collections.Generic; class Program { static readonly int Size = 9; static readonly int Box = 3; static readonly Random Rng = new Random(); static void Main() { // 1) Create an empty grid int[,] solution = new int[Size, Size]; // 2) Fill it with a complete valid Sudoku (backtracking) if (!FillGrid(solution)) { Console.WriteLine("Failed to generate a full Sudoku solution."); return; } // Make a copy to keep the solution int[,] puzzle = CopyGrid(solution); // 3) Ask for a difficulty (how many cells to remove) Console.Write("Choose difficulty (E=Easy, M=Medium, H=Hard) [default M]: "); string diff = (Console.ReadLine() ?? "").Trim().ToUpperInvariant(); int removeTarget = diff switch { "E" => 30, // ~30 blanks (51 clues) "H" => 50, // ~50 blanks (31 clues) _ => 40 // Medium ~40 blanks (41 clues) }; // 4) Carve out cells while keeping uniqueness MakePuzzleWithUniqueSolution(puzzle, removeTarget); // 5) Print results Console.WriteLine(); Console.WriteLine("=== PUZZLE ==="); PrintGrid(puzzle); Console.WriteLine(); Console.WriteLine("=== SOLUTION ==="); PrintGrid(solution); Console.WriteLine(); Console.WriteLine("Press any key to exit..."); Console.ReadKey(); } // -------------------- Sudoku generation -------------------- // Fill the grid fully using backtracking (random order for variety) static bool FillGrid(int[,] grid) { int row, col; if (!FindEmpty(grid, out row, out col)) { // No empty cell: solved return true; } // Try numbers 1..9 in random order foreach (int num in ShuffledNumbers()) { if (IsSafe(grid, row, col, num)) { grid[row, col] = num; if (FillGrid(grid)) return true; grid[row, col] = 0; // backtrack } } return false; // trigger backtracking } // Check if placing num at [row,col] obeys Sudoku rules static bool IsSafe(int[,] grid, int row, int col, int num) { // Row for (int c = 0; c < Size; c++) if (grid[row, c] == num) return false; // Column for (int r = 0; r < Size; r++) if (grid[r, col] == num) return false; // 3x3 box int r0 = (row / Box) * Box; int c0 = (col / Box) * Box; for (int r = 0; r < Box; r++) for (int c = 0; c < Box; c++) if (grid[r0 + r, c0 + c] == num) return false; return true; } // Find the next empty cell (0 = empty). Returns false if none. static bool FindEmpty(int[,] grid, out int row, out int col) { for (int r = 0; r < Size; r++) for (int c = 0; c < Size; c++) if (grid[r, c] == 0) { row = r; col = c; return true; } row = -1; col = -1; return false; } // Return numbers 1..9 in random order static IEnumerable ShuffledNumbers() { List nums = new List(9); for (int i = 1; i <= 9; i++) nums.Add(i); for (int i = nums.Count - 1; i > 0; i--) { int j = Rng.Next(i + 1); (nums[i], nums[j]) = (nums[j], nums[i]); } return nums; } // -------------------- Puzzle carving with uniqueness -------------------- static void MakePuzzleWithUniqueSolution(int[,] puzzle, int removeTarget) { // Create a list of all positions [0..80] and shuffle List positions = new List(Size * Size); for (int i = 0; i < Size * Size; i++) positions.Add(i); // Fisher–Yates shuffle for (int i = positions.Count - 1; i > 0; i--) { int j = Rng.Next(i + 1); (positions[i], positions[j]) = (positions[j], positions[i]); } int removed = 0; foreach (int pos in positions) { if (removed >= removeTarget) break; int row = pos / Size; int col = pos % Size; if (puzzle[row, col] == 0) continue; // already empty int backup = puzzle[row, col]; puzzle[row, col] = 0; // Check uniqueness: count solutions up to 2 int solutions = CountSolutions(CopyGrid(puzzle), 2); if (solutions == 1) { removed++; } else { // not unique — revert puzzle[row, col] = backup; } } } // Backtracking solver that counts how many solutions exist (up to 'limit') static int CountSolutions(int[,] grid, int limit) { int count = 0; SolveAndCount(grid, ref count, limit); return count; } static void SolveAndCount(int[,] grid, ref int count, int limit) { if (count >= limit) return; // early stop if enough found int row, col; if (!FindEmpty(grid, out row, out col)) { // Found one solution count++; return; } // Try 1..9 (ordered or random — ordered is slightly faster here) for (int num = 1; num <= 9; num++) { if (IsSafe(grid, row, col, num)) { grid[row, col] = num; SolveAndCount(grid, ref count, limit); if (count >= limit) return; // early stop grid[row, col] = 0; } } } // -------------------- Utilities -------------------- static int[,] CopyGrid(int[,] src) { int[,] dst = new int[Size, Size]; for (int r = 0; r < Size; r++) for (int c = 0; c < Size; c++) dst[r, c] = src[r, c]; return dst; } static void PrintGrid(int[,] grid) { for (int r = 0; r < Size; r++) { if (r % 3 == 0) Console.WriteLine("+-------+-------+-------+"); for (int c = 0; c < Size; c++) { if (c % 3 == 0) Console.Write("| "); int val = grid[r, c]; Console.Write(val == 0 ? ". " : (val.ToString() + " ")); } Console.WriteLine("|"); } Console.WriteLine("+-------+-------+-------+"); } } /* How the algorithm works (step-by-step, in plain words) 1. Board representation We use a 9x9 int array. 0 means “empty”. 2. Rules as code A helper IsSafe(row, col, num) checks whether num can be placed at [row,col] (no duplicates in row, column, or 3×3 box). 3. Build a full solution (recursion + backtracking) Find the next empty cell (top-to-bottom, left-to-right). Try candidates 1..9 in random order. If a candidate fits (IsSafe), place it and recurse to the next cell. If we get stuck, backtrack: remove the number and try the next one. This eventually fills the board with a valid solution. 4. Make a puzzle by removing numbers Randomize all 81 cell positions. Try to remove one cell (set it to 0). Check uniqueness: run a solution counter (a solver that stops after it finds 2 solutions). If there’s still exactly 1 solution → keep it removed. Otherwise restore the number. Continue until we removed enough numbers for the chosen difficulty (e.g., 40 empties). 5. Print nicely A helper prints the grid with lines every 3 rows/columns. */