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

Download here (VB.Net / C#)


See also

VB.Net version
Online JavaScript version


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