**[Source Code](Program.cs) * *
Sudoku is a randomly generated number puzzle game.The goal is to fill in the entire board with the numbers 1 - 9.However, you cannot duplicate the values within the same row, column or 3x3 square.
╔═══════╦═══════╦═══════╗
║ 7 6 2 ║ 9 1 5 ║ 8 4 3 ║
║ 5 4 3 ║ 7 8 6 ║ 2 1 9 ║
║ 9 1 8 ║ 2 3 4 ║ 5 7 6 ║
╠═══════╬═══════╬═══════╣
║ 4 3 1 ║ 5 9 8 ║ 7 6 2 ║
║ 6 2 5 ║ 4 7 3 ║ 1 9 8 ║
║ 8 7 9 ║ 1 6 2 ║ 3 5 4 ║
╠═══════╬═══════╬═══════╣
║ 3 9 6 ║ 8 5 1 ║ 4 2 7 ║
║ 1 8 4 ║ 6 2 7 ║ 9 3 5 ║
║ 2 5 7 ║ 3 4 9 ║ 6 8 1 ║
╚═══════╩═══════╩═══════╝
The * *arrow keys(↑, ↓, ←, →) * *are used to change the selected cell.The * *1 - 9 number keys * *are used to insert a value into the current cell(if the move is valid). The * *\[delete\] and \[backspace\] keys** will remove values from the board. The **\[end\] key** will generate a new puzzle if you get stuck. The * *\[enter\] key** will generate a new puzzle after you win.The **\[escape\] key** will exit the game.
public static void Main()
bool closeRequested = false;
int?[,] generatedBoard = Sudoku.Generate();
int?[,] activeBoard = (int?[,])generatedBoard.Clone();
while (!closeRequested && ContainsNulls(activeBoard))
Console.SetCursorPosition(0, 0);
Console.WriteLine("Sudoku");
ConsoleWrite(activeBoard, generatedBoard);
Console.WriteLine("Press arrow keys to select a cell.");
Console.WriteLine("Press 1-9 to insert values.");
Console.WriteLine("Press [delete] or [backspace] to remove.");
Console.WriteLine("Press [escape] to exit.");
Console.WriteLine("Press [end] to generate a new sudoku.");
Console.SetCursorPosition(y * 2 + 2 + (y / 3 * 2), x + 3 + +(x / 3));
switch (Console.ReadKey(true).Key)
case ConsoleKey.UpArrow: x = x <= 0 ? 8 : x - 1; break;
case ConsoleKey.DownArrow: x = x >= 8 ? 0 : x + 1; break;
case ConsoleKey.LeftArrow: y = y <= 0 ? 8 : y - 1; break;
case ConsoleKey.RightArrow: y = y >= 8 ? 0 : y + 1; break;
case ConsoleKey.D1: case ConsoleKey.NumPad1: activeBoard[x, y] = IsValidMove(activeBoard, generatedBoard, 1, x, y) ? 1 : activeBoard[x, y]; break;
case ConsoleKey.D2: case ConsoleKey.NumPad2: activeBoard[x, y] = IsValidMove(activeBoard, generatedBoard, 2, x, y) ? 2 : activeBoard[x, y]; break;
case ConsoleKey.D3: case ConsoleKey.NumPad3: activeBoard[x, y] = IsValidMove(activeBoard, generatedBoard, 3, x, y) ? 3 : activeBoard[x, y]; break;
case ConsoleKey.D4: case ConsoleKey.NumPad4: activeBoard[x, y] = IsValidMove(activeBoard, generatedBoard, 4, x, y) ? 4 : activeBoard[x, y]; break;
case ConsoleKey.D5: case ConsoleKey.NumPad5: activeBoard[x, y] = IsValidMove(activeBoard, generatedBoard, 5, x, y) ? 5 : activeBoard[x, y]; break;
case ConsoleKey.D6: case ConsoleKey.NumPad6: activeBoard[x, y] = IsValidMove(activeBoard, generatedBoard, 6, x, y) ? 6 : activeBoard[x, y]; break;
case ConsoleKey.D7: case ConsoleKey.NumPad7: activeBoard[x, y] = IsValidMove(activeBoard, generatedBoard, 7, x, y) ? 7 : activeBoard[x, y]; break;
case ConsoleKey.D8: case ConsoleKey.NumPad8: activeBoard[x, y] = IsValidMove(activeBoard, generatedBoard, 8, x, y) ? 8 : activeBoard[x, y]; break;
case ConsoleKey.D9: case ConsoleKey.NumPad9: activeBoard[x, y] = IsValidMove(activeBoard, generatedBoard, 9, x, y) ? 9 : activeBoard[x, y]; break;
case ConsoleKey.End: goto NewPuzzle;
case ConsoleKey.Backspace: case ConsoleKey.Delete: activeBoard[x, y] = generatedBoard[x, y] ?? null; break;
case ConsoleKey.Escape: closeRequested = true; break;
Console.WriteLine("Sudoku");
ConsoleWrite(activeBoard, generatedBoard);
Console.WriteLine("You Win!");
Console.WriteLine("Play Again [enter], or quit [escape]?");
switch (Console.ReadKey(true).Key)
case ConsoleKey.Enter: break;
Console.Write("Sudoku was closed.");
public static bool IsValidMove(int?[,] board, int?[,] lockedBoard, int value, int x, int y)
if (!(lockedBoard[x, y] is null))
for (int i = x - x % 3; i <= x - x % 3 + 2; i++)
for (int j = y - y % 3; j <= y - y % 3 + 2; j++)
if (board[i, j] == value)
for (int i = 0; i < 9; i++)
if (board[x, i] == value)
for (int i = 0; i < x; i++)
if (board[i, y] == value)
public static bool ContainsNulls(int?[,] board)
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
public static void ConsoleWrite(int?[,] board, int?[,] lockedBoard)
ConsoleColor consoleColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine("╔═══════╦═══════╦═══════╗");
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
if (!(lockedBoard is null) && !(lockedBoard[i, j] is null))
Console.Write((lockedBoard[i, j].HasValue ? lockedBoard[i, j].ToString() : "■") + " ");
Console.ForegroundColor = ConsoleColor.White;
Console.Write((board[i, j].HasValue ? board[i, j].ToString() : "■") + " ");
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine("╠═══════╬═══════╬═══════╣");
Console.WriteLine("╚═══════╩═══════╩═══════╝");
Console.ForegroundColor = consoleColor;
public static class Sudoku
public static int?[,] Generate(
if (blanks.HasValue && blanks < 0 || 81 < blanks)
throw new ArgumentOutOfRangeException(nameof(blanks), blanks.Value, $"{nameof(blanks)} < 0 || 81 < {nameof(blanks)}");
else if (!blanks.HasValue)
blanks = random.Next(0, 82);
int?[,] board = new int?[9, 9];
(int[] Values, int Count)[,] valids = new (int[] Values, int Count)[9, 9];
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
valids[i, j] = (new int[9], -1);
void GetValidValues(int row, int column)
bool SquareValid(int value, int row, int column)
for (int i = row - row % 3; i <= row; i++)
for (int j = column - column % 3; j <= column - column % 3 + 2 && !(i == row && j == column); j++)
if (board[i, j] == value)
bool RowValid(int value, int row, int column)
for (int i = 0; i < column; i++)
if (board[row, i] == value)
bool ColumnValid(int value, int row, int column)
for (int i = 0; i < row; i++)
if (board[i, column] == value)
valids[row, column].Count = 0;
for (int i = 1; i <= 9; i++)
if (SquareValid(i, row, column) &&
RowValid(i, row, column) &&
ColumnValid(i, row, column))
valids[row, column].Values[valids[row, column].Count++] = i;
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
while (valids[i, j].Count == 0)
Console.SetCursorPosition(0, 0);
Program.ConsoleWrite(board, null);
Console.WriteLine("Press [Enter] To Continue...");
int index = random.Next(0, valids[i, j].Count);
int value = valids[i, j].Values[index];
valids[i, j].Values[index] = valids[i, j].Values[valids[i, j].Count - 1];
Console.SetCursorPosition(0, 0);
Program.ConsoleWrite(board, null);
Console.WriteLine("Press [Enter] To Continue...");
foreach (int i in random.NextUnique(blanks.Value, 0, 81))
board[row, column] = null;