15,743,179 members
Articles / Programming Languages / C#
Article
Posted 17 Dec 2005

314.9K views
95 bookmarked

Sudoku Game in C#

Rate me:
The sudoku game in C#.

Introduction

I came across the Sudoku game a few months ago. I commute by train daily to my office; this puzzle, in everyday newspaper, was one of my favorite ways to pass time in the train. I thought why not develop this game in C#? This article discusses the implementation of this game in C#. The game provides three complexity levels: simple, medium and complex. The user can view the answer for a few seconds to get a clue. I hope you will enjoy playing this game. You require .NET Framework installed on your PC or laptop to run this program.

Rules of Sudoku

The game has simple rules. The game presented in this article arranges numbers in 9X9 matrix, which is the most common form of this game. The rules are as follows:

1. The number, in each row and column, should be between 1 and 9 and should appear only once.
2. The 9X9 matrix is made of nine 3X3 matrices. So, the number in each of these subsets should also be between 1 and 9 and should appear only once.

Now that we know the rules, let us see the implementation.

Implementation

The main class implementing this game is `Sudoku` and is implemented in Sudoku.cs file. The view is implemented using a `DataGrid` and the main form for implementing the view is in SudokuMainForm.cs file. The basic design idea is to first generate the solution and then unmask certain spots based on the complexity level. Initially, I spent some time to create a solution by populating sets which were independent using random numbers between 1 and 9. Then, I tried to fill other sets. This was a bit complex and was taking time. Hence, I decided to take one unique solution as the base and then derive other unique solutions by swapping rows, columns, sets and reversing numbers. This way, I could generate 1000s of combinations. `GenerateGame()` method of the `Sudoku` class uses this technique. The other part implements the view. I have used `DataGrid` which is a very useful control to represent data in tabular form. I have used `DataSet` which is easier to bind to the grid. As I am using two dimensional array to keep the problem and answer sets, I have exposed two properties `ProblemSet` and `AnswerSet` which return the data in two dimensional array as a `DataSet`.

Another important part of the game is to provide data validations. The `ColumnChanging` event of the `DataTable` is quite useful and I have used this event to handle data validations such as "valid number", "answer position not changed" and "number is not a duplicate". The event arguments of this event enable us to set the appropriate error message for the affected column. I have provided a `button` control to show the answer, which displays the answer for a few seconds and then brings back the problem. You can use this if the problem is complex and for getting some clues. The next section describes the code. I will only explain the important methods. You can see the full implementation by downloading the source code using the links given above.

Using the Code

`Sudoku` class is the main class implementing the game. `GenerateGame(GameLevel level)` method generates the new game. The code for this is given below:

C#
```// Method: GenerateGame
// Purpose: Generates game based on complexity level.
public void GenerateGame(GameLevel level)
{
// InitialiseSet
// This first creates answer set by using Game combinations
InitialiseSet();
int minPos,maxPos,noOfSets;

// Now unmask positions and create problem set.
switch(level)
{
case GameLevel.SIMPLE:
minPos=4;
maxPos=6;
noOfSets=8;
break;
case GameLevel.MEDIUM:
minPos=3;
maxPos=5;
noOfSets= 7;
break;
case GameLevel.COMPLEX:
minPos=3;
maxPos=5;
noOfSets = 6;
break;
default:
break;
}```

In the above code, `GameLevel` is of type `enum` and it defines the complexity levels. The `InitialiseSet()` method generates the unique solution. Then, based on the complexity level, the `Unmask()` method will keep some answer spots and create the `ProblemSet` by clearing other positions. Here, the important property is the `GameSet` that returns the `DataSet` which is used to bind to the `DataGrid`. The `DataSet` returns three sets of data: `ProblemSet`, a copy of `ProblemSet` and `AnswerSet`. This retrieval of all sets of data in a single `DataSet` simplifies the load and save functionality:

C#
```// Property:GameSet
// Return Problem Set as DataSet

public DataSet ProblemSet
{
get{ return FormDataSet();}
}```

The above property invokes the `private` method, `FormDataSet()`. This method then generates the `Dataset` from the array and returns to the caller. Data validations are handled using the `columnchanging` event of the `DataTable`. The following code snippet shows the event handler code:

C#
```// Handler method for data validations.
private void CurrentSet_ColumnChanging(object sender,
System.Data.DataColumnChangeEventArgs e)
{
try
{
lblStatus.Text="";
int rowPos = dataGrid1.CurrentCell.RowNumber;
string currentNumber = e.ProposedValue as string;

int number =Int32.Parse(currentNumber);
if((number < 1)||(number >9))
{
string errorMessage=
"Number should be between 1 and 9";
e.Row.SetColumnError(e.Column,errorMessage);
}
else
{
int col =e.Column.Ordinal;

{
e.ProposedValue = e.Row[e.Column];

}else if(_newGame.CheckForDuplicate(rowPos,col,number)){
e.Row.SetColumnError(e.Column,"Number is Duplicate");

}else
{
e.Row.ClearErrors();
{
lblStatus.Text= "Great!!! You have done it";
}
}
}
}
catch(Exception ex)
{
e.Row.SetColumnError(e.Column,
"Enter valid Number between 1 & 9");
}
}```

The above code first reads the current row position of the cell using the `datagrid1.CurrentCell.RowNumber` property. The user entered value for the cell is available in the event argument property `e.ProposedValue`. This property is converted to a number and then all data validations are performed by calling `CheckForAnswerChangd()` and `CheckForDuplicate()` methods of the `Sudoku` class. The handler also checks whether the solution is complete by invoking `IsSolutionComplete()` method. The `label` control is used to show the status of validations and an appropriate message is shown if the solution is complete. The error message can be set by setting the `e.Row.SetcolumnError` property.

I have used the `timer` control to show the answer for a few seconds and then to redisplay the problem.

New Enhancements

Based on the suggestions from readers, I have amended the program with the following enhancements:

1. Border for each mini 3X3 matrix, to distinguish it clearly from other mini-sets.
3. Spinner text control when you edit a particular cell to enter a number between 1 and 9.
4. Load and Save facility for the game. This will enable the user to continue the game at a later time.

Border for Mini 3x3 Matrix

Since a `DataGrid` is used for the view, either the grid style of each cell can be customized or the border of the `Datagrid` can be changed. To provide border for each mini-set, we need to override the `paint` method of `DataGrid`. The following code snippet shows the code in overloaded method:

C#
```/// DataGrid Paint overridden to paint border
private void DataGrid1_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
// Override this handler to do custom painting.
Point currentPoint = new Point(0,0);
Size size = new Size(PREFERRED_COLUMN_WIDTH*3,
PREFERRED_ROW_HEIGHT*3);
Pen myPen = new Pen(Color.Red,3);

for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
currentPoint.X = i*PREFERRED_ROW_HEIGHT*3;
currentPoint.Y = j*PREFERRED_ROW_HEIGHT*3;
Rectangle rect = new Rectangle(currentPoint,size);
e.Graphics.DrawRectangle(myPen,rect);
}
}
}```

DataGridSpinnerColumn

Features 2 and 3 mentioned above are provided by implementing a custom `DataGrid` column. A custom `DataGrid` column needs to be derived from `DataGridColumnStyle`, which is an `abstract` class. Microsoft .NET Framework provides two types of custom columns which are derived from this class. One is `DataGridTextBoxColumn` and the other is `DataGridBoolColumn`. I have implemented a `DataGridSpinnerColumn` class for `DataGridSpinnerColumn` and it is derived from `DataGridTextBoxColumn`. This is done to reuse the basic properties and methods implemented by this class and to enhance some of its methods required for this custom column. I have used vertical scrollbar control which will act as spinner control.

The main requirement is that when we edit the `cell`, it should display the custom spinner column so that the user can use scroll buttons to change the `cell` value and when the focus goes to the other `cell`, then the value should be displayed in the `cell`. Also, the answer spots should be read only. Because of this feature, the custom column is not general purpose and cannot be used as is in other applications. This is because showing the spinner control is done by overriding the edit method which in turn checks whether the `cell` under edit is the answer spot. If the cell does not contain the answer spot, then only the spinner control is displayed. The code snippet for the edit method is given below.

For more information on `DataGridColumnStyles`, refer to the MSDN documentation.

C#
```// On edit, add scroll event handler, and display combobox
protected override void Edit(System.Windows.Forms.CurrencyManager
source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly,
string instantText, bool cellIsVisible)
{
//Call base method which is important else
//edit will not function properly
instantText, cellIsVisible);

// Check if cell is not empty
if(this.TextBox.Text.TrimEnd()!="")
{
// Get Column position

int dataValue = Int32.Parse(this.TextBox.Text);
int pos = this.MappingName.LastIndexOf("col");
if(pos > -1)
{
string colIndex = this.MappingName.Substring(pos+3);
int colPos = Int32.Parse(colIndex);
// Check whether it is answer spot.
colPos,dataValue);
}
}
else
{
}
{
// Save current row in the DataGrid and currency manager
// associated with the data source for the DataGrid
this._currentRow = rowNum;
this.cm = source;

{
// Make parent of scrollbar same as parent.
this.vsBar.Parent = this.TextBox.Parent;
Rectangle rect =
this.DataGridTableStyle.DataGrid.GetCurrentCellBounds();
//Place this control to right.
this.vsBar.Location =
new Point(rect.Right-this.SpinnerWidth,rect.Top);
this.vsBar.Size =
new Size(this.SpinnerWidth,this.TextBox.Height);

// Make the scrollbar visible and place on top textbox control
this.vsBar.Show();
// As textbox control also there let us bring this to front.
this.vsBar.BringToFront();

this.vsBar.Show();
//    this.TextBox.Text= this.vsBar.Value.ToString();
//Set text color properties different as we are editing cell.
this.TextBox.BackColor = Color.Blue;
this.TextBox.ForeColor=Color.White;
}
else
{
this.TextBox.BackColor=Color.White;
this.TextBox.ForeColor =Color.Black;
}
}
}```

Please refer to the source code for full implementation of this class.

I have re-factored the `sudoku` class to provide this facility. The properties `ProblemSet` and `AnswerSet` are now replaced by the `GameSet` property.

This will enable us to get all sets of data in a single `DataSet`. I have used `ReadXml()` and `WritXml()` methods of the `DataSet` class for implementing the load and save functionalities. Also, I have exposed the `InitialiseGameSet()` method to initialize all the data members of `sudoku` class once the `DataSet` is loaded from the XML file. This code is easy and hence not described here.

Conclusion

You have seen that how easy it is to implement this game in C# using the power of Microsoft .NET. You can download the full source code using the link provided at the top. l hope you will enjoy this article and the new enhancements to this game. If you have any suggestions or difficulties, then please leave a note in the comments section below. Good luck !!!

History

• 25th December, 2005: Initial version

A list of licenses authors might use can be found here.

Written By
Software Developer (Senior)
United Kingdom
I am Solution Architect with 20+ years of IT experience in the field of real time,embedded,client/server and web based applications and Business Intelligence . I am currently working as Senior Consultant for Infor.

 First Prev Next
 Sorry, excellent game otherwise. epolgar27-Jul-23 6:56 epolgar 27-Jul-23 6:56
 Unhandled Exception epolgar26-Jul-23 13:56 epolgar 26-Jul-23 13:56
 Valid dataset? jeremycrouse29-Mar-12 15:06 jeremycrouse 29-Mar-12 15:06
 Re: Valid dataset? Gokuldas29-Mar-12 22:15 Gokuldas 29-Mar-12 22:15
 Re: Valid dataset? jeremycrouse30-Mar-12 8:40 jeremycrouse 30-Mar-12 8:40
 Grid jeremycrouse27-Mar-12 7:16 jeremycrouse 27-Mar-12 7:16
 Project Orielzie9-Feb-12 3:17 Orielzie 9-Feb-12 3:17
 Help with developing Sudoku Member 851752322-Jan-12 10:49 Member 8517523 22-Jan-12 10:49
 License regd AlwaysACreator3-Jul-09 23:34 AlwaysACreator 3-Jul-09 23:34
 More solutions for one game zolino315-Apr-08 11:59 zolino3 15-Apr-08 11:59
 Good, but... J. Bakovsky26-Dec-05 0:24 J. Bakovsky 26-Dec-05 0:24
 Re: Good, but... jaaka3-Jan-06 11:44 jaaka 3-Jan-06 11:44
 [WORKAROUND] Does not work on PocketPC (Datetime.now) , but ........ belliv21-Dec-05 23:36 belliv 21-Dec-05 23:36
 Strange behaviour cryptxl19-Dec-05 21:14 cryptxl 19-Dec-05 21:14
 Logic Matthew Hazlett19-Dec-05 14:44 Matthew Hazlett 19-Dec-05 14:44
 Re: Logic Gokuldas19-Dec-05 21:33 Gokuldas 19-Dec-05 21:33
 Hi Matthew, Thanks for your comments and I suggestions. I will try to incorporate these features in next version of this game. Regards, Gokuldas
 Nice game DotDue18-Dec-05 20:35 DotDue 18-Dec-05 20:35
 Re: Nice game Gokuldas19-Dec-05 1:38 Gokuldas 19-Dec-05 1:38
 Last Visit: 31-Dec-99 18:00     Last Update: 25-Sep-23 3:34 Refresh 1