patterncsharpModerate
Sudoku Solver - Recursive Solve
Viewed 0 times
solverrecursivesudokusolve
Problem
Any ideas on making my code a bit more clean and efficient? Any criticism is appreciated.
I don't like that I'm calling my lengthy
```
public class Cell
{
public int value { get; private set; }
public bool isBlank { get; set; }
public List possibilities { get; private set; }
public Section row { get; private set; }
public Section column { get; private set; }
public Section region { get; private set; }
public Cell(int v, bool b)
{
value = v;
isBlank = b;
if (!isBlank)
possibilities = new List();
else
possibilities = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}
public void SetSections(Section r, Section c, Section reg)
{
row = r;
column = c;
region = reg;
}
public void AssignCellValue()
{
// Only continue if cell isn't a preset value
if (isBlank)
{
// If answer found, block cell from use and modify peer region possibilities
if (possibilities.Distinct().Count() == 1)
{
value = possibilities.First(); // Set value to only possibility
possibilities.Clear();
isBlank = false;
row.RefactorPossibilities(); // In peer regions change each cell's possibilities
column.RefactorPossibilities(); // to reflect value change
region.RefactorPossibilities();
row.SingleOut();
column.SingleOut();
region.SingleOut();
}
}
}
public void ForceCellValue(int value)
{
if (isBlank)
{
possibilities.Clear();
possibilities.Add(val
I don't like that I'm calling my lengthy
DeepCopy method so many times, but the BinaryFormatter approach is much much slower. I haven't come up with a better approach than doing a deep copy of the entire SudokuBoard due to all of the interwoven pointers, but I'm sure one exists.Cell```
public class Cell
{
public int value { get; private set; }
public bool isBlank { get; set; }
public List possibilities { get; private set; }
public Section row { get; private set; }
public Section column { get; private set; }
public Section region { get; private set; }
public Cell(int v, bool b)
{
value = v;
isBlank = b;
if (!isBlank)
possibilities = new List();
else
possibilities = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}
public void SetSections(Section r, Section c, Section reg)
{
row = r;
column = c;
region = reg;
}
public void AssignCellValue()
{
// Only continue if cell isn't a preset value
if (isBlank)
{
// If answer found, block cell from use and modify peer region possibilities
if (possibilities.Distinct().Count() == 1)
{
value = possibilities.First(); // Set value to only possibility
possibilities.Clear();
isBlank = false;
row.RefactorPossibilities(); // In peer regions change each cell's possibilities
column.RefactorPossibilities(); // to reflect value change
region.RefactorPossibilities();
row.SingleOut();
column.SingleOut();
region.SingleOut();
}
}
}
public void ForceCellValue(int value)
{
if (isBlank)
{
possibilities.Clear();
possibilities.Add(val
Solution
At a really quick glance at the
-
be consitent with the style you use. Here the first
-
your comments here seems pointless, as they aren't commenting on why something is done.
The same method refactored into two methods.
The
As you see I have added
You should consider to add a
Section.SingleOut() method
You can refactor the
You can refactor the
SudokuBoard.Solved() method
Issues
-
If
Naming
Please check the Naming Guidlines.
General
Section.RefactorPossibilities() method I can give you some general suggestions -
be consitent with the style you use. Here the first
for loop encloses the body in braces {} but the second loop doesn't. -
your comments here seems pointless, as they aren't commenting on why something is done.
public void RefactorPossibilities()
{
for (int i = 0; i 1)
{
items[j].possibilities.Remove(items[i].value);
}
if (items[j].possibilities.Distinct().Count() == 1) // if cell has only 1 possibility value left
{
if (!items.Any(x => x.value == items[j].possibilities.First()))
{
items[j].AssignCellValue(); // set cell to that value
}
}
}
}
}
}The same method refactored into two methods.
public void RefactorPossibilities()
{
IList values = GetCurrentValues();
for (int i = 0; i x.value == items[i].possibilities.First()))
{
items[i].AssignCellValue();
}
}
}
}
private IList GetCurrentValues()
{
IList values = new List(item.Count);
for (int i = 0; i < items.Count; i++)
{
if (items[i].isBlank) { continue; }
values.Add(items[i].Value);
}
return values;
}The
GetCurrentValues() iterates over each item and if the item has a value it will be added to the list. The RefactorPossibilities() method takes these values and if a item doens't have a value it will remove each value from the possibilities. As you see I have added
guard clauses to the loops, to improve readability. You should consider to add a
RemovePosibilities(IList posibilities) to the Cell class. This would simplifiy the RefactorPossibilities() method and has the advantage that you could return a ReadOnlyCollection by the possibilities property of the Cell class. Section.SingleOut() method
// Search the section for possibility values occurring once, and if found set it to specified cell
public void SingleOut()
{
for (int i = 0; i ()
.Where(x => x.isBlank)
.Where(x => x.possibilities.Contains(i + 1))
.Count();
if (numSpecificPoss == 1)
{
Cell SingledOut = items.OfType()
.Where(x => x.isBlank)
.Where(x => x.possibilities.Contains(i + 1))
.Single();
//Console.WriteLine("Singled out");
SingledOut.ForceCellValue(i + 1);
}
}
}You can refactor the
linq query to IEnumerable cells = items.OfType()
.Where(x => x.isBlank)
.Where(x => x.possibilities.Contains(i + 1));
if (cells.Count() == 1)
{
cells.Single().ForceCellValue(i + 1);
}You can refactor the
SectionVerified() method in a similiar way public bool SectionVerified()
{
IEnumerable cells = items.OfType()
.Where(x => x.value > 0)
.Select(x => x.value);
return (cells.Count() == cells.Distinct().Count());
}SudokuBoard.Solved() method
public bool Solved()
{
int regionSum = 0;
int columnSum = 0;
int rowSum = 0;
bool legitimateSolution = true;
for (int i = 0; i x.value);
columnSum = columns[i].items.Sum(x => x.value);
rowSum = rows[i].items.Sum(x => x.value);
if ((regionSum != 45) || (columnSum != 45) || (rowSum != 45))
{
legitimateSolution = false;
break;
}
}
if (legitimateSolution)
return true;
else
return false;
}Issues
- You are using magic numbers here. You should hide them behind a const.
-
If
regionSum != 45 you won't need to sum columns and rows. private const int solvedSum = 45;
public bool Solved()
{
for (int i = 0; i x.value) != solvedSum) { return false; }
if (columns[i].items.Sum(x => x.value) != solvedSum) { return false; }
if (rows[i].items.Sum(x => x.value) != solvedSum) { return false; }
}
return true;
}Naming
Please check the Naming Guidlines.
- Properties should use
PascalCasingfor their names.
- Classes should be named using nouns. So
Solveisn't a good name here. A better name would beSudokuSolver
General
- Avoid single letter variable names like
public Cell(int v, bool b). Single letters can be used as loop iterators and are usuallyi,j,k.
- Again, be consitent with the your coding style. At first I thought, ok, for single
ifandelsestatements no braces{}are used but they are used for singleelse ifstatment. But, after reading more of the code I also found singleelse ifstatments without braces and also singleifstatements using braces.
- Use guard cl
Code Snippets
public void RefactorPossibilities()
{
for (int i = 0; i < items.Count; i++)
{
if (!items[i].isBlank) // if cell has a set value
{
for (int j = 0; j < items.Count; j++) // retrace each cell in region and remove set value
if (items[j].isBlank) // from blank cells possibility list
{
if (items[j].possibilities.Count > 1)
{
items[j].possibilities.Remove(items[i].value);
}
if (items[j].possibilities.Distinct().Count() == 1) // if cell has only 1 possibility value left
{
if (!items.Any(x => x.value == items[j].possibilities.First()))
{
items[j].AssignCellValue(); // set cell to that value
}
}
}
}
}
}public void RefactorPossibilities()
{
IList<int> values = GetCurrentValues();
for (int i = 0; i < items.Count; i++)
{
if (!items[i].isBlank) { continue; }
foreach (int value in values)
{
items[i].possibilities.Remove(value);
}
if (items[i].possibilities.Distinct().Count() == 1)
{
if (!items.Any(x => x.value == items[i].possibilities.First()))
{
items[i].AssignCellValue();
}
}
}
}
private IList<int> GetCurrentValues()
{
IList<int> values = new List<int>(item.Count);
for (int i = 0; i < items.Count; i++)
{
if (items[i].isBlank) { continue; }
values.Add(items[i].Value);
}
return values;
}// Search the section for possibility values occurring once, and if found set it to specified cell
public void SingleOut()
{
for (int i = 0; i < items.Count; i++)
{
int numSpecificPoss = items.OfType<Cell>()
.Where(x => x.isBlank)
.Where(x => x.possibilities.Contains(i + 1))
.Count();
if (numSpecificPoss == 1)
{
Cell SingledOut = items.OfType<Cell>()
.Where(x => x.isBlank)
.Where(x => x.possibilities.Contains(i + 1))
.Single();
//Console.WriteLine("Singled out");
SingledOut.ForceCellValue(i + 1);
}
}
}IEnumerable<Cell> cells = items.OfType<Cell>()
.Where(x => x.isBlank)
.Where(x => x.possibilities.Contains(i + 1));
if (cells.Count() == 1)
{
cells.Single().ForceCellValue(i + 1);
}public bool SectionVerified()
{
IEnumerable<Cell> cells = items.OfType<Cell>()
.Where(x => x.value > 0)
.Select(x => x.value);
return (cells.Count() == cells.Distinct().Count());
}Context
StackExchange Code Review Q#69463, answer score: 13
Revisions (0)
No revisions yet.