// Constructor // filename - file from which to read the puzzle public Sudoku(string filename) { // TODO: Add the following exception handling // - Validate that the length of the first row is a perfect square value // - Validate that each line contains the same number of elements // - Validate that each element is a numeric value between 1 and gridSize or the unknown value using (TextFieldParser parser = new TextFieldParser(filename)) { int row = 0; parser.Delimiters = new string[] { "," }; while (!parser.EndOfData) { // Read in a line of the puzzle string[] parts = parser.ReadFields(); if (row == 0) { //Initialize the grid gridSize = parts.Length; subGridSize = Convert.ToInt32(Math.Sqrt(gridSize)); sudokuGrid = new NumberBlock[gridSize, gridSize]; // Initialize the range of possible block values rangeOfValues = new List<int>(Enumerable.Range(1, gridSize).ToList()); } // Populate the puzzle for (var col = 0; col < gridSize; col++) { sudokuGrid[row, col] = new NumberBlock(row, col, subGridSize, Convert.ToInt32(parts[col])); } row++; } } }
// Constructor // puzzle - the sudoku puzzle to be solved public Sudoku(int[,] puzzle) { // TODO: Error checking - check puzzle is actually a grid //Initialize the grid gridSize = puzzle.GetLength(0); subGridSize = Convert.ToInt32(Math.Sqrt(gridSize)); sudokuGrid = new NumberBlock[gridSize, gridSize]; // Initialize the range of possible block values rangeOfValues = new List<int>(Enumerable.Range(1, gridSize).ToList()); // Populate the grid with the data passed in for (var row = 0; row < gridSize; row++) for (var col = 0; col < gridSize; col++) { try { sudokuGrid[row, col] = new NumberBlock(row, col, subGridSize, puzzle[row, col]); } catch (Exception e) { // TODO: Do something more intelligent with the exception... return; } } }
public void NumberBlockConstructorSuccessful() { try { NumberBlock nb = new NumberBlock(1, 2, 3); Assert.IsFalse(nb.ValueSet); Assert.AreEqual(nb.PossibleValues.Count, 9); Assert.AreEqual(nb.Row, 1); Assert.AreEqual(nb.Column, 2); Assert.AreEqual(nb.Grid, 0); Assert.AreEqual(nb.RangeOfValues.Count, 9); } catch (NumberBlock.PositionException e) { Assert.Fail("Constructor should not have thrown the PositionExcpetion"); } catch (NumberBlock.ValueException e) { Assert.Fail("Constructor should not have thrown the ValueException"); } try { NumberBlock nb = new NumberBlock(4, 3, 3, 8); Assert.IsTrue(nb.ValueSet); Assert.AreEqual(nb.Value, 8); Assert.AreEqual(nb.Row, 4); Assert.AreEqual(nb.Column, 3); Assert.AreEqual(nb.Grid, 4); Assert.AreEqual(nb.PossibleValues.Count, 0); Assert.AreEqual(nb.RangeOfValues.Count, 9); } catch (NumberBlock.PositionException e) { Assert.Fail("Constructor should not have thrown the PositionExcpetion"); } catch (NumberBlock.ValueException e) { Assert.Fail("Constructor should not have thrown the ValueException"); } }
// This method handles updating a specific block's possible values private void UpdateBlockPossibleValues(NumberBlock block, int value) { var possibleVals = block.PossibleValues; if (possibleVals.Contains(value)) { possibleVals.Remove(value); block.PossibleValues = possibleVals; // If the block is now set, recurively call MarkValueAsUsed if (block.ValueSet) { MarkValueAsUsed(block); } } }
// This method iterated through the row, column and grid for a block that has been set // and removes the block value from the possibleValues list of each block. This may result // in another block being set. This method is called recursively to set subsequent blocks. private void MarkValueAsUsed(NumberBlock block) { for (var col = 0; col < gridSize; col++) { UpdateBlockPossibleValues(sudokuGrid[block.Row, col], block.Value); } for (var row = 0; row < gridSize; row++) { UpdateBlockPossibleValues(sudokuGrid[row, block.Column], block.Value); } int startingRow = (block.Grid / subGridSize) * subGridSize; int startingCol = (block.Grid % subGridSize) * subGridSize; for (int row = startingRow; row < startingRow + subGridSize; row++) for (int col = startingCol; col < startingCol + subGridSize; col++) { UpdateBlockPossibleValues(sudokuGrid[row, col], block.Value); } }
public void UpdatePossibleValues() { List<int> possibleValues = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; NumberBlock nb = null; try { nb = new NumberBlock(0, 0, 3); } catch (NumberBlock.PositionException e) { Assert.Fail("No exception should have been raised, PositionException raised"); } catch (NumberBlock.ValueException e) { Assert.Fail("No exception should have been raised, ValueException raised"); } Assert.IsFalse(nb.ValueSet); Assert.AreEqual(possibleValues.Count, nb.PossibleValues.Count); foreach (var value in nb.PossibleValues) { Assert.IsTrue(possibleValues.Contains(value)); } // Only even numbers possible possibleValues = new List<int>() { 2, 4, 6, 8 }; nb.PossibleValues = possibleValues; Assert.AreEqual(possibleValues.Count, nb.PossibleValues.Count); Assert.IsFalse(nb.ValueSet); foreach (var value in nb.PossibleValues) { Assert.IsTrue(possibleValues.Contains(value)); } // Only the value 6 left possibleValues = new List<int> { 6 }; nb.PossibleValues = possibleValues; Assert.AreEqual(nb.PossibleValues.Count, 0); Assert.IsTrue(nb.ValueSet); Assert.AreEqual(6, nb.Value); }
public void NumbeBlockConstructorThrowsPositionException() { // Row number < 0 try { NumberBlock nb = new NumberBlock(-1, 0, 3, 1); Assert.Fail("Constructor should have thown a PositionException, no exception thrown"); } catch (NumberBlock.PositionException e) { // success } catch (NumberBlock.ValueException e) { Assert.Fail("Constructor should have thrown a PositionException, ValueException thrown"); } // Row number >= 9 try { NumberBlock nb = new NumberBlock(9, 0, 3, 1); Assert.Fail("Constructor should have thown a PositionException, no exception thrown"); } catch (NumberBlock.PositionException e) { // success } catch (NumberBlock.ValueException e) { Assert.Fail("Constructor should have thrown a PositionException, ValueException thrown"); } // Column number < 0 try { NumberBlock nb = new NumberBlock(0, -1, 3, 1); Assert.Fail("Constructor should have thown a PositionException, no exception thrown"); } catch (NumberBlock.PositionException e) { // success } catch (NumberBlock.ValueException e) { Assert.Fail("Constructor should have thrown a PositionException, ValueException thrown"); } // Column number >= 9 try { NumberBlock nb = new NumberBlock(0, 9, 3, 1); Assert.Fail("Constructor should have thown a PositionException, no exception thrown"); } catch (NumberBlock.PositionException e) { // success } catch (NumberBlock.ValueException e) { Assert.Fail("Constructor should have thrown a PositionException, ValueException thrown"); } }
public void NumberBlockConstructorThrowsValueException() { // Value < -1 try { NumberBlock nb = new NumberBlock(0, 0, 2, -3); } catch (NumberBlock.ValueException e) { // success } catch (NumberBlock.PositionException e) { Assert.Fail("Constructor should have thrown a ValueException, PositionException thrown"); } // Value > 9 try { NumberBlock nb = new NumberBlock(0, 0, 4, 10); } catch (NumberBlock.ValueException e) { // success } catch (NumberBlock.PositionException e) { Assert.Fail("Constructor should have thrown a ValueException, PositionException thrown"); } }