wingman007

Sudoku

Oct 30th, 2025
2,478
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.16 KB | Software | 0 0
  1. /*
  2. Below is a complete Console program (single file) that:
  3.  
  4. builds a valid, fully-solved Sudoku by recursive backtracking,
  5.  
  6. removes numbers while keeping the puzzle uniquely solvable, and
  7.  
  8. prints both the puzzle and the solution in the console.
  9.  
  10. You can paste this into a new Console App (Program.cs) and run.
  11. */
  12.  
  13. using System;
  14. using System.Collections.Generic;
  15.  
  16. class Program
  17. {
  18.     static readonly int Size = 9;
  19.     static readonly int Box = 3;
  20.     static readonly Random Rng = new Random();
  21.  
  22.     static void Main()
  23.     {
  24.         // 1) Create an empty grid
  25.         int[,] solution = new int[Size, Size];
  26.  
  27.         // 2) Fill it with a complete valid Sudoku (backtracking)
  28.         if (!FillGrid(solution))
  29.         {
  30.             Console.WriteLine("Failed to generate a full Sudoku solution.");
  31.             return;
  32.         }
  33.  
  34.         // Make a copy to keep the solution
  35.         int[,] puzzle = CopyGrid(solution);
  36.  
  37.         // 3) Ask for a difficulty (how many cells to remove)
  38.         Console.Write("Choose difficulty (E=Easy, M=Medium, H=Hard) [default M]: ");
  39.         string diff = (Console.ReadLine() ?? "").Trim().ToUpperInvariant();
  40.         int removeTarget = diff switch
  41.         {
  42.             "E" => 30, // ~30 blanks (51 clues)
  43.             "H" => 50, // ~50 blanks (31 clues)
  44.             _   => 40  // Medium ~40 blanks (41 clues)
  45.         };
  46.  
  47.         // 4) Carve out cells while keeping uniqueness
  48.         MakePuzzleWithUniqueSolution(puzzle, removeTarget);
  49.  
  50.         // 5) Print results
  51.         Console.WriteLine();
  52.         Console.WriteLine("=== PUZZLE ===");
  53.         PrintGrid(puzzle);
  54.  
  55.         Console.WriteLine();
  56.         Console.WriteLine("=== SOLUTION ===");
  57.         PrintGrid(solution);
  58.  
  59.         Console.WriteLine();
  60.         Console.WriteLine("Press any key to exit...");
  61.         Console.ReadKey();
  62.     }
  63.  
  64.     // -------------------- Sudoku generation --------------------
  65.  
  66.     // Fill the grid fully using backtracking (random order for variety)
  67.     static bool FillGrid(int[,] grid)
  68.     {
  69.         int row, col;
  70.         if (!FindEmpty(grid, out row, out col))
  71.         {
  72.             // No empty cell: solved
  73.             return true;
  74.         }
  75.  
  76.         // Try numbers 1..9 in random order
  77.         foreach (int num in ShuffledNumbers())
  78.         {
  79.             if (IsSafe(grid, row, col, num))
  80.             {
  81.                 grid[row, col] = num;
  82.                 if (FillGrid(grid)) return true;
  83.                 grid[row, col] = 0; // backtrack
  84.             }
  85.         }
  86.  
  87.         return false; // trigger backtracking
  88.     }
  89.  
  90.     // Check if placing num at [row,col] obeys Sudoku rules
  91.     static bool IsSafe(int[,] grid, int row, int col, int num)
  92.     {
  93.         // Row
  94.         for (int c = 0; c < Size; c++)
  95.             if (grid[row, c] == num) return false;
  96.  
  97.         // Column
  98.         for (int r = 0; r < Size; r++)
  99.             if (grid[r, col] == num) return false;
  100.  
  101.         // 3x3 box
  102.         int r0 = (row / Box) * Box;
  103.         int c0 = (col / Box) * Box;
  104.         for (int r = 0; r < Box; r++)
  105.             for (int c = 0; c < Box; c++)
  106.                 if (grid[r0 + r, c0 + c] == num) return false;
  107.  
  108.         return true;
  109.     }
  110.  
  111.     // Find the next empty cell (0 = empty). Returns false if none.
  112.     static bool FindEmpty(int[,] grid, out int row, out int col)
  113.     {
  114.         for (int r = 0; r < Size; r++)
  115.             for (int c = 0; c < Size; c++)
  116.                 if (grid[r, c] == 0)
  117.                 {
  118.                     row = r;
  119.                     col = c;
  120.                     return true;
  121.                 }
  122.  
  123.         row = -1;
  124.         col = -1;
  125.         return false;
  126.     }
  127.  
  128.     // Return numbers 1..9 in random order
  129.     static IEnumerable<int> ShuffledNumbers()
  130.     {
  131.         List<int> nums = new List<int>(9);
  132.         for (int i = 1; i <= 9; i++) nums.Add(i);
  133.         for (int i = nums.Count - 1; i > 0; i--)
  134.         {
  135.             int j = Rng.Next(i + 1);
  136.             (nums[i], nums[j]) = (nums[j], nums[i]);
  137.         }
  138.         return nums;
  139.     }
  140.  
  141.     // -------------------- Puzzle carving with uniqueness --------------------
  142.  
  143.     static void MakePuzzleWithUniqueSolution(int[,] puzzle, int removeTarget)
  144.     {
  145.         // Create a list of all positions [0..80] and shuffle
  146.         List<int> positions = new List<int>(Size * Size);
  147.         for (int i = 0; i < Size * Size; i++) positions.Add(i);
  148.  
  149.         // Fisher–Yates shuffle
  150.         for (int i = positions.Count - 1; i > 0; i--)
  151.         {
  152.             int j = Rng.Next(i + 1);
  153.             (positions[i], positions[j]) = (positions[j], positions[i]);
  154.         }
  155.  
  156.         int removed = 0;
  157.  
  158.         foreach (int pos in positions)
  159.         {
  160.             if (removed >= removeTarget) break;
  161.  
  162.             int row = pos / Size;
  163.             int col = pos % Size;
  164.  
  165.             if (puzzle[row, col] == 0) continue; // already empty
  166.  
  167.             int backup = puzzle[row, col];
  168.             puzzle[row, col] = 0;
  169.  
  170.             // Check uniqueness: count solutions up to 2
  171.             int solutions = CountSolutions(CopyGrid(puzzle), 2);
  172.             if (solutions == 1)
  173.             {
  174.                 removed++;
  175.             }
  176.             else
  177.             {
  178.                 // not unique — revert
  179.                 puzzle[row, col] = backup;
  180.             }
  181.         }
  182.     }
  183.  
  184.     // Backtracking solver that counts how many solutions exist (up to 'limit')
  185.     static int CountSolutions(int[,] grid, int limit)
  186.     {
  187.         int count = 0;
  188.         SolveAndCount(grid, ref count, limit);
  189.         return count;
  190.     }
  191.  
  192.     static void SolveAndCount(int[,] grid, ref int count, int limit)
  193.     {
  194.         if (count >= limit) return; // early stop if enough found
  195.  
  196.         int row, col;
  197.         if (!FindEmpty(grid, out row, out col))
  198.         {
  199.             // Found one solution
  200.             count++;
  201.             return;
  202.         }
  203.  
  204.         // Try 1..9 (ordered or random — ordered is slightly faster here)
  205.         for (int num = 1; num <= 9; num++)
  206.         {
  207.             if (IsSafe(grid, row, col, num))
  208.             {
  209.                 grid[row, col] = num;
  210.                 SolveAndCount(grid, ref count, limit);
  211.                 if (count >= limit) return; // early stop
  212.                 grid[row, col] = 0;
  213.             }
  214.         }
  215.     }
  216.  
  217.     // -------------------- Utilities --------------------
  218.  
  219.     static int[,] CopyGrid(int[,] src)
  220.     {
  221.         int[,] dst = new int[Size, Size];
  222.         for (int r = 0; r < Size; r++)
  223.             for (int c = 0; c < Size; c++)
  224.                 dst[r, c] = src[r, c];
  225.         return dst;
  226.     }
  227.  
  228.     static void PrintGrid(int[,] grid)
  229.     {
  230.         for (int r = 0; r < Size; r++)
  231.         {
  232.             if (r % 3 == 0)
  233.                 Console.WriteLine("+-------+-------+-------+");
  234.  
  235.             for (int c = 0; c < Size; c++)
  236.             {
  237.                 if (c % 3 == 0) Console.Write("| ");
  238.  
  239.                 int val = grid[r, c];
  240.                 Console.Write(val == 0 ? ". " : (val.ToString() + " "));
  241.             }
  242.             Console.WriteLine("|");
  243.         }
  244.         Console.WriteLine("+-------+-------+-------+");
  245.     }
  246. }
  247. /*
  248. How the algorithm works (step-by-step, in plain words)
  249.  
  250. 1. Board representation
  251. We use a 9x9 int array. 0 means “empty”.
  252.  
  253. 2. Rules as code
  254. A helper IsSafe(row, col, num) checks whether num can be placed at [row,col] (no duplicates in row, column, or 3×3 box).
  255.  
  256. 3. Build a full solution (recursion + backtracking)
  257.  
  258. Find the next empty cell (top-to-bottom, left-to-right).
  259.  
  260. Try candidates 1..9 in random order.
  261.  
  262. If a candidate fits (IsSafe), place it and recurse to the next cell.
  263.  
  264. If we get stuck, backtrack: remove the number and try the next one.
  265. This eventually fills the board with a valid solution.
  266.  
  267. 4. Make a puzzle by removing numbers
  268.  
  269. Randomize all 81 cell positions.
  270.  
  271. Try to remove one cell (set it to 0).
  272.  
  273. Check uniqueness: run a solution counter (a solver that stops after it finds 2 solutions).
  274.  
  275. If there’s still exactly 1 solution → keep it removed.
  276.  
  277. Otherwise restore the number.
  278.  
  279. Continue until we removed enough numbers for the chosen difficulty (e.g., 40 empties).
  280.  
  281. 5. Print nicely
  282. A helper prints the grid with lines every 3 rows/columns.
  283. */
Tags: C# Sudoku .NET
Advertisement