I wondered if anyone has any ideas to make this cleaner or more efficient. Mostly this was an exercise to generate the data once (So speed really isn't too important, but I'd love to see what techniques you could think of) This is an algorithm I derived and implemented without external guidance of any kind, so I'm asking for guidance now.
This does give the output I am expecting, I validated it with a recursive implementation that I made first.
I'm thinking this might be helpful in validating solutions, or in generating new boards.
For the Sudoku table:
--1 --- ---
--- 1-- ---
--- --- --1
--- -1- ---
1-- --- ---
--- --- 1--
--- --- -1-
--- --1 ---
-1- --- ---
1 would be represented by the array: 238316751. In the first 9 cell box, the 1 is at position 2 in the array of that box.
///This outputs the valid pointers
///for example 327742830 is valid, a number could be in pos 3 of cell 0, 2 in cell 1, etc
/// So arrays are compatable when no columns have the same number in them
/// A starting board with a high number of compatable arrays would be very difficult i believe
public IEnumerable<string> OutputArrayPoints()
{
return OutputArrayPoints(new int[9]);
}
//Will contain the cell indexes that are not excluded by other cells already
int[][] ToCheck = new int[9][];
//Pointers to the current cell indexes in use
int[] ToCheckPointers = new int[9];
//arrayPoints will contain the current value to return
//Really it should be equal to loop through ToCheck[c][ToCheckPointers[c]]
public IEnumerable<string> OutputArrayPoints(int[] arrayPoints)
{
//This loop is basically a depth first search
//ToCheck gets filled with possible values for each cell,
// and the lowest pointer gets incremented until it runs out,
// then moves up a cell to go down a different path
for (int c = 0; c >= 0; /*++c*/)
{
if (c == 8 && ToCheck[c] != null && ToCheckPointers[c] < ToCheck[c].Length)
{
//Should be a valid solution, yield it
yield return GetResult(arrayPoints);
++ToCheckPointers[c];
}
else if (c < 9 && ToCheck[c] != null && ToCheckPointers[c] < ToCheck[c].Length)
{
//Fill this cell of arrayPointers, and move to the next
arrayPoints[c] = ToCheck[c][ToCheckPointers[c]];
++c;
}
else if (ToCheck[c] != null && ToCheckPointers[c] == ToCheck[c].Length)
{
//ToCheck at this level has been fully processed, clear it out
ToCheck[c] = null;
//Step up a cell to start processing the next
--c;
//if c is less than 0, we have gone through every option
if (c == -1) yield break;
//Increase the pointer I'm pointing at
++ToCheckPointers[c];
}
else if (c < 9 && ToCheck[c] == null)
{
//Fill any empty ones
ToCheck[c] = CellsToCheck(arrayPoints, c).ToArray();
ToCheckPointers[c] = 0;
//lazy? (could this be in a cleaner spot? or do I need to store arrayPoints at all?):
arrayPoints[c] = ToCheck[c][ToCheckPointers[c]];
if (c < 8) ++c;
}
}
}
//Return the cells that are possible given the other positions taken
IEnumerable<int> CellsToCheck(int[] arrayPoints, int depth)
{
int rowSkip1 = (depth % 3) > 0 ? arrayPoints[depth - 1] / 3 : -1;
int rowSkip2 = (depth % 3) > 1 ? arrayPoints[depth - 2] / 3 : -1;
int colSkip1 = depth > 2 ? arrayPoints[depth - 3] % 3 : -1;
int colSkip2 = depth > 5 ? arrayPoints[depth - 6] % 3 : -1;
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
if ((rowSkip1 != i && rowSkip2 != i)
&& (colSkip1 != j && colSkip2 != j)) yield return j + i*3;
}
}
}
//Return a string representation
string GetResult(IEnumerable<int> cells)
{
return string.Join("", cells.Select(i => i.ToString()).ToArray());
}