C# - OOP Sudoku
Overview
Sudoku is a number puzzle game, played on a nine by nine cell grid. Within that grid, there are nine sub-regions consisting of three cells by three cells. In this version, there are three levels of difficulty, which are Most clues, Medium clues, and Least clues.
Most clues give you five random numbers from each nine cell horizontal row.
Medium clues give you four random numbers from each nine cell horizontal row.
Least clues give you three random numbers from each nine cell horizontal row.
The object of the game is to fill the remaining cells with the integers one to nine. But each horizontal row can only contain one instance of each integer, and each vertical row can only contain one instance of each integer. There is also the further added constraint that each sub-region can also only contain one instance of each integer.
In a new game, the given numbers (clues) are rendered in red text. The numbers that you enter are rendered in black text. There is a Solution button, which when clicked, reveals the correct answer to the puzzle. Any numbers that you have added that are correctly placed, remain rendered with black text. Any cells that were empty or had an incorrect integer will display the correct integer rendered in blue text.
You have won the game if after clicking the Solution button all of the numbers are rendered in either black or red text.
Core Game class
The Game class is used throughout the duration of the application. It is the core of the application, where each new Sudoku puzzle is created.
Communication from the Game class to the GUI is via two custom Events...
It contains two Public methods, the NewGame method, and the showGridSolution method. These are the methods used to communicate with the class from the GUI.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace sudoku_cs
{
public class Game
{
public event ShowCluesEventHandler ShowClues;
public delegate void ShowCluesEventHandler(int[][] grid);
public event ShowSolutionEventHandler ShowSolution;
public delegate void ShowSolutionEventHandler(int[][] grid);
private List<int>[] HRow = new List<int>[9];
private List<int>[] VRow = new List<int>[9];
private List<int>[] ThreeSquare = new List<int>[9];
private int[][] grid = new int[9][];
private Random r;
public void NewGame(Random rn)
{
this .r = rn;
createNewGame();
}
private void initializeLists()
{
for (int x = 0; x <= 8; x++)
{
HRow[x] = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
VRow[x] = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
ThreeSquare[x] = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
int [] row = new int[9];
grid[x] = row;
}
}
private void createNewGame()
{
while (true)
{
break1:
initializeLists();
for (int y = 0; y <= 8; y++)
{
for (int x = 0; x <= 8; x++)
{
int si = (y / 3) * 3 + (x / 3);
int [] useful = HRow[y].Intersect(VRow[x]).Intersect(ThreeSquare[si]).ToArray();
if (useful.Count() == 0)
{
goto break1;
}
int randomNumber = useful[this.r.Next(0, useful.Count())];
HRow[y].Remove(randomNumber);
VRow[x].Remove(randomNumber);
ThreeSquare[si].Remove(randomNumber);
grid[y][x] = randomNumber;
if (y == 8 & x == 8)
{
goto break2;
}
}
}
};
break2:
if (ShowClues != null)
{
ShowClues(grid);
}
}
public void showGridSolution()
{
if (ShowSolution != null)
{
ShowSolution(grid);
}
}
}
}
The GUI Form
The Form handles all of the Events, from the Controls it contains, and also the two custom Events raised by the Game class.
Extra lines are drawn on the DataGridView to separate the sub-regions in the grid.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace sudoku_cs
{
public partial class Form1 : Form
{
private Game game = new Game();
private Random r = new Random();
public Form1()
{
InitializeComponent();
Load += Form1_Load;
btnNew.Click += btnNew_Click;
btnSolution.Click += btnSolution_Click;
DataGridView1.Paint += DataGridView1_Paint;
ComboBox1.SelectedIndexChanged += ComboBox1_SelectedIndexChanged;
game.ShowClues += game_ShowClues;
game.ShowSolution += game_ShowSolution;
}
private void Form1_Load(System.Object sender, System.EventArgs e)
{
DataGridView1.Rows.Add(9);
ComboBox1.SelectedIndex = 0;
btnNew.PerformClick();
}
private void btnNew_Click(System.Object sender, System.EventArgs e)
{
game.NewGame(r);
}
private void DataGridView1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.DrawLine(new Pen(Color.Black, 2), 75, 0, 75, 228);
e.Graphics.DrawLine(new Pen(Color.Black, 2), 150, 0, 150, 228);
e.Graphics.DrawLine(new Pen(Color.Black, 2), 0, 66, 228, 66);
e.Graphics.DrawLine(new Pen(Color.Black, 2), 0, 132, 228, 132);
}
private void btnSolution_Click(System.Object sender, System.EventArgs e)
{
game.showGridSolution();
}
private void ComboBox1_SelectedIndexChanged(System.Object sender, System.EventArgs e)
{
btnNew.PerformClick();
}
public void game_ShowClues(int[][] grid)
{
for (int y = 0; y <= 8; y++)
{
List<int> cells = new List<int>(new int[] {1,2,3,4,5,6,7,8,9});
for (int c = 1; c <= 9 - (5 - ComboBox1.SelectedIndex); c++)
{
int randomNumber = cells[r.Next(0, cells.Count())];
cells.Remove(randomNumber);
}
for (int x = 0; x <= 8; x++)
{
if (cells.Contains(x + 1))
{
DataGridView1.Rows[y].Cells[x].Value = grid[y][x];
DataGridView1.Rows[y].Cells[x].Style.ForeColor = Color.Red;
DataGridView1.Rows[y].Cells[x].ReadOnly = true;
}
else
{
DataGridView1.Rows[y].Cells[x].Value = "";
DataGridView1.Rows[y].Cells[x].Style.ForeColor = Color.Black;
DataGridView1.Rows[y].Cells[x].ReadOnly = false;
}
}
}
}
public void game_ShowSolution(int[][] grid)
{
for (int y = 0; y <= 8; y++)
{
for (int x = 0; x <= 8; x++)
{
if (DataGridView1.Rows[y].Cells[x].Style.ForeColor == Color.Black)
{
if (string.IsNullOrEmpty(DataGridView1.Rows[y].Cells[x].Value.ToString()))
{
DataGridView1.Rows[y].Cells[x].Style.ForeColor = Color.Blue;
DataGridView1.Rows[y].Cells[x].Value = grid[y][x];
}
else
{
if (grid[y][x].ToString() != DataGridView1.Rows[y].Cells[x].Value.ToString())
{
DataGridView1.Rows[y].Cells[x].Style.ForeColor = Color.Blue;
DataGridView1.Rows[y].Cells[x].Value = grid[y][x];
}
}
}
}
}
}
}
}
Conclusion
This example shows how easy it is to create a simple desktop game using either VB.Net or C# (see this VB.Net version). Modern computers are ideal for creating this type of game, with each new game being random and unique. Using OOP techniques and principles in this game results in simple readable and orderly code.
Downloads
See also
VB.Net version
Online JavaScript version
Articles related to game programming
VB.Net - Perspective
VB.Net - Vertex
VB.Net - WordSearch
VB.Net - MasterMind
VB.Net - OOP BlackJack
VB.Net - Numbers Game
VB.Net - HangMan
Console BlackJack - VB.Net | C#
TicTacToe - VB.Net | C#
OOP Conway's Game of Life - VB.Net | C#
OctoWords VB.Net | C#
OOP Buttons Guessing Game VB.Net | C#
OOP Tangram Shapes Game VB.Net | C#
VB.Net - Three-card Monte
VB.Net - Split Decisions
VB.Net - Pascal's Pyramid
VB.Net - Random Maze Games
(Office) Wordsearch Creator
VB.Net - Event Driven Programming - LockWords Game
C# - Crack the Lock
VB.Net - Totris