Visual C# 2005 (Windows) Guide
Sudoku: Basic Application

The Sudoku application is a utility to help people solve Sudoku puzzles. The screenshot shows the basic application. Numbers are entered into the grid by clicking with the right mouse button on one the the text boxes that make up the grid. The user can only choose to add values which are valid (that is, ones that are allowed in those locations under the rules of Sudoku). Each text box displays either the value that has been chosen for that cell or the list of possible values for that cell. Possible values are displayed in a small font, chosen values are displayed in a large font.

sudoku application screenshot

The instructions on this page show how to set up the application so that numbers can entered and candidates displayed for each cell. There are lots of aspects to the setting up of the application which will be used later on when we have added some methods for getting the computer to solve the puzzle. One thing you will notice is that most of the arrays in this application (apart from the array of 3x3 minigrids) have 10 elements in each dimension. The first element is usually ignored. This just makes the programming a whole lot easier when we're dealing with the numbers from 1-9 in the puzzle.

The Sudoku application is a Windows application. Start a new project and save it with a suitable name.

The GridSquare Class

We will create a separate code file for this class. Go to the Project menu and choose to add a new class called GridSquare.cs.

The following fields are added to this class,

private int GridRow;
private int GridCol;
private int SquareValue = 0;
private int[] SquareCandidates = new int[10] { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private int GridMiniSquare;

There will be an instance of this class for each of the 81 cells in the grid. These fields represent the following pieces of information,

  • Row of the cell
  • Column of the cell
  • The value that has been assigned to the cell (0 means no value assigned)
  • An array of possible values (the 0 item is ignored)
  • A value representing the 3x3 square to which the cell belongs

The constructor method for this class accepts 2 parameters (the row and column of the cell). This information is used to work out the minisquare that the cell belongs to.

public GridSquare(int r, int c)
{
   GridRow = r;
   GridCol = c;
   GridMiniSquare = (int)((c-1) / 3) + (3 * (int)((r-1)/3));
}

You will have noticed that all of the fields in the class are private. So that they can be accessed from outside of the class, we define a series of properties,

public int MiniSquare
{
   get { return GridMiniSquare; }
}

public int Value
{
   get { return SquareValue; }
   set { SquareValue = value; }
}

public int Row
{
   get { return GridRow; }
   set { GridRow = value; }
}

public int Col
{
   get { return GridCol; }
   set { GridCol = value; }
}

The array of candidates also needs an accessor. The following is an example of an indexer. This will allow us to manipulate candidates from outside of the class.

public int this[int i]
{
   get { return SquareCandidates[i]; }
   set { SquareCandidates[i] = value; }
}

There are 2 methods for this class. The first is to calculate the number of candidates for the cell. This will be useful later on since any cell with a single candidate must be that value. We also need a method to display the candidates for the cell.

public int NumCandidates()
{
   int sumCands = 0;
   for (int i = 1; i <= 9; i++)
   {
      sumCands += SquareCandidates[i];
   }
   sumCands = 9 - sumCands;
   return sumCands;
}

public string DisplayCandidates()
{
   string cands = "";
   for (int i = 1; i <= 9; i++)
   {
      if (SquareCandidates[i] == 0)
      {
         cands += i + " ";
      }
      else
      {
         cands += " ";
      }
   }
   return cands;
}

For a complete listing of this class, click on GridSquare Listing.

The SudokuGrid Class

The SudokuGrid class is used to represent an entire puzzle. It will contain all of the solving logic when we come to program this later on. There are 2 fields, an array of GridSquares and an array containing information that we will use to iterate through the cells in a 3x3 minisquare.

private GridSquare[,] GridCell = new GridSquare[10,10];
private int[,] GridRefs = { { 1, 1 }, { 1, 4 }, { 1, 7 }, { 4, 1 }, { 4, 4 }, { 4, 7 }, { 7, 1 }, { 7, 4 }, { 7, 7 } };

The constructor method for the class sets up the array of GridSquares.

public SudokuGrid()
{
   for (int i = 1; i <= 9; i++)
   {
      for (int j = 1; j <= 9; j++)
      {
         GridCell[i, j] = new GridSquare(i, j);
      }
   }
}

We need an accessor for the GridCell field. This is as follows,

public GridSquare this[int i, int j]
{
   get { return GridCell[i, j]; }
}

Finally, we need a method for entering values into the grid. This requires some thought because, when we add a value to the grid, it changes which values are possible in the row, column or minisquare where the value is entered.

public void EnterItem(int row, int col, int cellvalue)
{
   GridCell[row, col].Value = cellvalue;
   //eliminate from columns and rows
   for (int i = 1; i <= 9; i++)
   {
      if (i != col)
      {
         GridCell[row, i][cellvalue] = 1;
      }
      if (i != row)
      {
         GridCell[i,col][cellvalue] = 1;
      }
   }
   //eliminate from minisquares
   for (int i = GridRefs[GridCell[row, col].MiniSquare, 0]; i <= GridRefs[GridCell[row, col].MiniSquare, 0] + 2; i++)
   {
      for (int j = GridRefs[GridCell[row, col].MiniSquare, 1]; j <= GridRefs[GridCell[row, col].MiniSquare, 1] + 2; j++)
      {
         GridCell[i, j][cellvalue] = 1;
      }
   }
}

For a complete listing of this class, click on SudokuGrid Listing.

Nearly There - Setting Up The Form

The application is based around the form. Our last job is to program some logic in the form to use our 2 classes. First make your form about 550x550 in size and add a ContextMenuStrip to the form, calling it mnuInput. Then add the following variable declarations to the top of the form.

private TextBox[,] txtSquare = new TextBox[10,10];
private SudokuGrid grid;
private int tempR=0;
private int tempC=0;

The array of text boxes is used to make up the grid, the grid is the name we will give to our instance of the puzzle class. The other two variables will be used to temporarily store the row and column of the text box that the user selects.

The form_load event is used to set up the grid on the form. There are a lot of properties to set for the text boxes. Fortunately we can do this wih a loop.

private void frmMain_Load(object sender, EventArgs e)
{
   for (int i = 1; i <= 9; i++)
   {
      for (int j = 1; j <= 9; j++)
      {
      txtSquare[i, j] = new TextBox();
      txtSquare[i, j].Name = i + "" + j;
      txtSquare[i, j].Text = "";
      txtSquare[i, j].Multiline = true;
      txtSquare[i, j].Size = new Size(50, 50);
      txtSquare[i, j].Location = new Point(10 + ((j - 1) * 55), 30 + ((i - 1) * 55));
      txtSquare[i, j].ReadOnly = true;
      txtSquare[i, j].TextAlign = HorizontalAlignment.Center;
      txtSquare[i, j].TabStop = false;
      txtSquare[i, j].Font = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Regular);
      txtSquare[i, j].Cursor = Cursors.Default;
      txtSquare[i, j].ContextMenuStrip = mnuInput;
      if ((i + j) % 2 == 0)
      {
         txtSquare[i, j].BackColor = Color.LightBlue;
      }
      this.Controls.Add(txtSquare[i, j]);
      txtSquare[i, j].MouseDown += new MouseEventHandler(HandleInput);
      }
   }
   grid = new SudokuGrid();
   UpdateDisplay();
}

There are two methods that don't yet exist - UpdateDisplay and HandleInput. Don't be worried by the errors at this stage - they should disappear when you have programmed the methods.

We can use the form's paint event to draw on some gridlines to make the puzzle easier on the eye.

private void frmMain_Paint(object sender, PaintEventArgs e)
{
   Graphics g = e.Graphics;
   g.DrawLine(Pens.Black, 172, 30, 172, 520);
   g.DrawLine(Pens.Black, 337, 30, 337, 520);
   g.DrawLine(Pens.Black, 10, 192, 500, 192);
   g.DrawLine(Pens.Black, 10, 357, 500, 357);
}

The UpdateDisplay procedure checks to see if the cell has a value. If it does, the value is displayed in large text, if not the candidates are displayed in small text.

private void UpdateDisplay()
{
   Font smallFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Regular);
   Font largeFont = new Font(FontFamily.GenericSansSerif, 20, FontStyle.Bold);
   for (int i = 1; i <= 9; i++)
   {
      for (int j = 1; j <= 9; j++)
      {
         if (grid[i, j].Value == 0)
         {
            txtSquare[i, j].Font = smallFont;
            txtSquare[i, j].Text = grid[i, j].DisplayCandidates();
         }
         else
         {
            txtSquare[i, j].Font = largeFont;
            txtSquare[i, j].Text = System.Convert.ToString( grid[i, j].Value);
         }
      }
   }
}

The following 3 procedures are used to manage the input. When the user opens up the contextmenustrip, the items in the menu are set to be the values that are allowed in that cell. If the cell has a value, we will need a method to remove that value. That isn't explained here - it has been left for you to do yourself.

private void HandleInput(object sender, MouseEventArgs e)
{
   TextBox myTemp = (TextBox)sender;
   string name = myTemp.Name;
   tempR = System.Convert.ToInt32(name.Substring(0,1));
   tempC = System.Convert.ToInt32(name.Substring(1));
}

private void mnuInput_Opening(object sender, CancelEventArgs e)
{
   mnuInput.Items.Clear();
   if (grid[tempR, tempC].Value == 0)
   {
      for (int i = 1; i <= 9; i++)
      {
         if (grid[tempR, tempC][i] == 0)
         {
            mnuInput.Items.Add("Make " + i);
         }
      }
   }
   else
   {
      mnuInput.Items.Add("Remove Number");
   }
}

private void mnuInput_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
   if (grid[tempR, tempC].Value == 0)
   {
      int valToEnter = System.Convert.ToInt32(e.ClickedItem.Text.Substring(5));
      mnuInput.Close();
      grid.EnterItem(tempR, tempC, valToEnter);
   }
   else
   {
      //to add - remove the number
   }
   UpdateDisplay();
}

The first procedure sets our two global variables to the row and column of the text box clicked. The second procedure is executed as the menu opens - it fills the menu with the items that can be entered in the cell. The third method inserts the chosen item into the grid.

Now that the form has been designed, we need to test and debug the application. Sort out any errors you have come across and make sure that the application works before moving on to the next page.

There aren't many comments used in this code. Since you haven't written it yourself, it might be a good idea to work back through the code and comment as many lines as you have patience for - it will help you later.