Sudoku Solver (Easy)

A class that will solve 'Easy' or 'Mild' Sudoku puzzles.


public class SimpleSudokuSolver : Sodoku.ISudokuSolver
{
    private Sudoku __sudoku;

    public SimpleSudokuSolver()
    {
    }

    #region ISudokuSolver Members

        public Sudoku Sudoku
        {
            get { return this.__sudoku; }
            set { this.__sudoku = value; }
        }
        
        public void Solve()
        {
            this.SolveA();
            this.SolveB();
            this.SolveA();
        }

    #endregion

    protected void SolveA()
    {

        //loop through the grid testing for empty cells which can have only one
        //possible value according to the basic rules of the game, fill in these
        //cells and repeat the loop if at least one addition has been made

        bool foundone;

        do
        {
            foundone = false;

            for ( int i = 0; i < __sudoku.Length; i++ )
            {
                for ( int j = 0; j < __sudoku.Length; j++ )
                {
                    if ( Sudoku.NULL_CHAR == __sudoku[ i, j ] )
                    {
                        char[] possibles = __sudoku.GetCellValidCharacters( i, j );

                        if ( possibles.Length == 1 )
                        {
                            __sudoku[ i, j ] = possibles[0];

                            foundone = true;
                        }
                    }
                }
            }
        }
        while ( foundone == true );
    }

    //'cross-hatching'
    //the same idea can be applied to rows and columns
    protected void SolveB()
    {
        //for each block in the grid, find which characters are not present
        //and which cells are empty. then, for each character, try to rule
        //out all but one of the empty cells.  repeat if at least one addition
        //to the grid has been made
        bool foundone;

        do
        {
            foundone = false;
            
            //for each block, eg. a 9x9 grid will have 9 blocks 
            for ( int N = 0; N < __sudoku.Length; N++ )
            {
                int block_length;
                int row_offset;
                int col_offset;
                ArrayList block_possibles;
                ArrayList empty_cells;
                
                block_length = __sudoku.BlockLength;
                row_offset =  block_length * ( N / block_length );
                col_offset = block_length * ( N % block_length );
                block_possibles = new ArrayList( __sudoku.CharacterSet );
                empty_cells = new ArrayList( block_length );

                //first pass through the block removing any characters
                //that are present from the list of possible characters
                //also store empty cells
                for ( int i = 0; i < block_length; i++ )
                {
                    for ( int j = 0; j < block_length; j++ )
                    {
                        int row = row_offset + i;
                        int col = col_offset + j;

                        char ch = __sudoku[ row, col ];

                        if ( Sudoku.NULL_CHAR != ch )
                        {
                            block_possibles.Remove( ch );
                        }
                        else
                        {
                            empty_cells.Add( new int[] { row, col } );
                        }
                    }
                }

                //next, for each possible character, check each empty cell - if the
                //character is not valid in that cell because it already exists in
                //its row or column, then remove that cell from the list of possible
                //cells ( for that character ), if only one empty cell remains then
                //the character must belong in that cell
                foreach ( char possible_char in block_possibles )
                {
                    //initially, assume all empty cells are possible
                    ArrayList possible_cells = new ArrayList( empty_cells );
                    
                    foreach ( int[] cell in empty_cells )
                    {
                        if ( !__sudoku.IsValidInRow( possible_char, cell[0] ) || 
                            !__sudoku.IsValidInColumn( possible_char, cell[1] ) )
                        {
                            possible_cells.Remove( cell );
                        }
                    }

                    if ( possible_cells.Count == 1 )
                    {
                        int[] cell = (int[]) possible_cells[0];

                        __sudoku[ cell[0], cell[1] ] = possible_char;

                        empty_cells.Remove( cell );

                        foundone = true;
                    }

                }
            }
        }
        while ( foundone == true );
    }
}