/// <summary> /// Standard constructor /// </summary> /// <param name = "size">Length of one side of the square</param> public SudokuModel(int size) { // Initialize our variables _size = size; _sizeSqrt = Convert.ToInt32(Math.Sqrt(size)); _sizeSquared = size * size; _sizeCubed = _sizeSquared * size; _possibilitySetModel = (1 << _size) - 1; _possibilitySetCell = new int[Size, Size]; _remainingPossibilityCount = new int[Size, Size]; _value = new int[Size, Size]; _lastChangedIterCell = new int[Size, Size]; // Initialize the Cell objects, and the cache values for (var col = 0; col < size; ++col) { for (var row = 0; row < size; ++row) { _possibilitySetCell[col, row] = PossibilitySetModel; _remainingPossibilityCount[col, row] = Size; _value[col, row] = -1; _lastChangedIterCell[col, row] = -1; CellChanged = null; } } CellChanged += HandleCellChanged; _solved = new int[3, _size]; _lastChangedIterRegion = new int[3, _size]; _cellsRegion = new Cell[3, _size][]; _intersectingRegions = new Region[Size, Size][]; for (var i = 0; i < _size; ++i) { _lastChangedIterRegion[0, i] = -1; _lastChangedIterRegion[1, i] = -1; _lastChangedIterRegion[2, i] = -1; var colCells = new Cell[_size]; for (var j = 0; j < _size; ++j) { colCells[j] = new Cell(i, j); } _cellsRegion[(int)RegionType.Column, i] = colCells; var rowCells = new Cell[_size]; for (var j = 0; j < _size; ++j) { rowCells[j] = new Cell(j, i); } _cellsRegion[(int)RegionType.Row, i] = rowCells; var sqCells = new Cell[_size]; var sqrCol = i % _sizeSqrt; var sqrRow = i / _sizeSqrt; var startCol = sqrCol * _sizeSqrt; var startRow = sqrRow * _sizeSqrt; for (var c = 0; c < _sizeSqrt; ++c) { for (var r = 0; r < _sizeSqrt; ++r) { sqCells[r * _sizeSqrt + c] = new Cell(c + startCol, r + startRow); } } _cellsRegion[(int)RegionType.Square, i] = sqCells; } }
public Cell[][] GetCellsMatrix() { Cell[][] matrix = new Cell[rows][]; int row = rows; while (row-- > 0) { matrix[row] = new Cell[columns]; int column = columns; while (column-- > 0) matrix[row][column] = cells[row][column]; } return matrix; }
public SubGrid(int columns, int rows, int column, int row) { this.columns = rows; // Swop columns and rows values this.rows = columns; Column = column; Row = row; cells = new Cell[this.rows][]; for (row = 0; row < this.rows; row++) { cells[row] = new Cell[this.columns]; for (column = 0; column < this.columns; column++) cells[row][column] = new Cell(columns, rows, column, row); } }
private Cell[][] GetTransposedCellsMatrix() // Get state of current grid - returned as a transposed n*m matrix (not separated by sub grids) { int subGridColumn = columns; int matrixColumn = subGridColumn * rows; // Use SubGrid's number of rows i.e. swopped columns Cell[][] matrix = new Cell[matrixColumn][]; int subGridRows = rows; int matrixRows = subGridRows * columns; while (matrixColumn-- > 0) matrix[matrixColumn] = new Cell[matrixRows]; while (subGridColumn-- > 0) { int subGridRow = subGridRows; while (subGridRow-- > 0) { var subMatrix = subGrids[subGridRow][subGridColumn].GetCellsMatrix(); int cellRow = columns; while (cellRow-- > 0) { int cellColumn = rows; while (cellColumn-- > 0) matrix[subGridColumn * rows + cellColumn][subGridRow * columns + cellRow] = subMatrix[cellRow][cellColumn]; } } } return matrix; }
private List<LimitedOption> FindOptionsLimitedToMatrix(Cell[][] cells) { List<LimitedOption> limitedOptions = new List<LimitedOption>(); for (int index = 0; index < columns * rows; index++) { IEnumerable<Cell> unsetCells = cells[index].Where(x => !x.IsSet); // Get cells that are still to be set int totalUnsetCells = unsetCells.Count(); bool found = false; int pick = 1; int maxRemainingOptions = unsetCells.Where(x => x.TotalOptionsRemaining < totalUnsetCells).Max(x => (int?)x.TotalOptionsRemaining) ?? 0; // Max < totalUnsetCells while (!found && pick++ < maxRemainingOptions) { IEnumerable<Cell> options = unsetCells.Where(x => x.TotalOptionsRemaining <= pick); // Get options with at least the number of bits to pick set var enumerator = combinations.Select(options, pick).GetEnumerator(); // Get enumerator to each combination of options to select, based on the number of items to pick while (!found && enumerator.MoveNext()) { int removeOptions = BitUtilities.BitwiseOR(enumerator.Current.Select(x => x.Options)); if (found = BitUtilities.NumberOfBitsSet(removeOptions) <= pick) limitedOptions.Add(new LimitedOption { Column = index, Row = index, Options = removeOptions }); } } } return limitedOptions; }
private Cell[] GetCellsArray() { Cell[] array = new Cell[columns * rows * columns * rows]; int subGridRow = rows; while (subGridRow-- > 0) { int subGridColumn = columns; while (subGridColumn-- > 0) { var subMatrix = subGrids[subGridRow][subGridColumn].GetCellsMatrix(); int cellColumn = rows; while (cellColumn-- > 0) { int cellRow = columns; while (cellRow-- > 0) array[(subGridRow * columns + cellRow) * columns * rows + subGridColumn * rows + cellColumn] = subMatrix[cellRow][cellColumn]; } } } return array; }
public Cell[] Save() { int size = columns * rows; Cell[] cells = new Cell[size * size]; for (int subGridRow = 0; subGridRow < rows; subGridRow++) for (int subGridColumn = 0; subGridColumn < columns; subGridColumn++) { var subMatrix = subGrids[subGridRow][subGridColumn].GetCellsMatrix(); for (int cellRow = 0; cellRow < columns; cellRow++) for (int cellColumn = 0; cellColumn < rows; cellColumn++) cells[subGridRow * size * columns + subGridColumn * rows + cellRow * size + cellColumn] = new Cell(subMatrix[cellRow][cellColumn]); } return cells; }
private void Load(Cell[] cells) { Cell[][] subGrid = new Cell[columns][]; for (int row = 0; row < columns; row++) // Use SubGrid's number of rows i.e. swopped columns subGrid[row] = new Cell[rows]; int size = columns * rows; for (int subGridRow = 0; subGridRow < rows; subGridRow++) for (int subGridColumn = 0; subGridColumn < columns; subGridColumn++) { for (int cellRow = 0; cellRow < columns; cellRow++) for (int cellColumn = 0; cellColumn < rows; cellColumn++) subGrid[cellRow][cellColumn] = new Cell(cells[subGridRow * size * columns + subGridColumn * rows + cellRow * size + cellColumn]); subGrids[subGridRow][subGridColumn].SetCells(subGrid); } }
private bool MatrixValid(Cell[][] matrix) { bool valid = true; int size = columns * rows; int index = size; while (valid && index-- > 0) { var setOptions = matrix[index].Where(x => x.IsSet).GroupBy(x => x.Options).Where(x => x.Count() == 1).Select(x => x.Key); // Get unique set cells var unsetOptions = matrix[index].Where(x => !x.IsSet).Select(x => x.Options); valid = setOptions.Count() + unsetOptions.Count() == size && // Ensures setOptions did not contain duplicates (BitUtilities.BitwiseOR(setOptions) | BitUtilities.BitwiseOR(unsetOptions)) == (1 << size) - 1; // totalSetOptions | totalUnsetOptions must contain all the options } return valid; }
public void SetCells(Cell[][] subGrid) { for (int row = 0; row < rows; row++) for (int column = 0; column < columns; column++) cells[row][column] = subGrid[row][column]; }
/// <summary> /// We update our cache variables and bubble up the event. /// </summary> void HandleCellChanged(int col, int row, RegionType? foundInType, int foundInI) { EliminatedCount += 1; LastChangedCell = new Cell(col, row); var value = _value[col, row]; foreach (var region in GetIntersectingRegions(col, row)) { SetLastChangedIterRegion(region.Type, region.I, ChangeCount); if (value != -1) { SetValueSolved(region.Type, region.I, value); } } ++ChangeCount; if (value != -1) { ++SolvedCount; if (foundInType.HasValue) { DuplicateElimination.EliminateDuplicates(this, col, row, foundInType.Value, foundInI); } else { DuplicateElimination.EliminateDuplicates(this, col, row); } } if (ModelChanged != null) { ModelChanged(col, row); } }
public bool Contains(RegionType type, int i, Cell cell) { switch (type) { case RegionType.Column: return cell.Column == i; case RegionType.Row: return cell.Row == i; case RegionType.Square: var sqrCol = i % _sizeSqrt; var sqrRow = i / _sizeSqrt; var startCol = sqrCol * _sizeSqrt; var startRow = sqrRow * _sizeSqrt; return (cell.Column >= startCol && cell.Column < startCol + _sizeSqrt && cell.Row >= startRow && cell.Row < startRow + _sizeSqrt); } throw new InvalidOperationException(); }