// WI: ? make virtual calls /// <summary> /// Converts data in the specified binary file to the instance of grid. /// </summary> /// <param name="binaryFile">Binary file containing /// compressed grid.</param> /// <returns>Instance of a particular grid.</returns> public static SudokuGrid FromBinary(FileStream binaryFile) { SudokuGridConstraints constraints; SudokuGridMetrics metrics; byte[] numbersKinds; string content = SudokuConverter.ToText(binaryFile, out constraints, out numbersKinds, out metrics); //string content = SudokuConverter.ToText(binaryFile, out constraints, out metrics); switch (metrics.MaximumNumber) { case 9: var grid = new SudokuGrid9x3(constraints); grid.IterateLinesXY((x, y) => { var cell = new SudokuGridCell(grid, new SudokuGridPosition(x, y, false), byte.Parse(content[metrics.MaximumNumber * y + x].ToString())); //cell.NumberChanged += (s, e) => grid.OnCellNumberChanged(new CellNumberChangedEventArgs((SudokuGridCell)s)); //grid.cells[y, x] = cell; AssignNewCell(grid, cell); }); return grid; default: throw new SudokuGridNotSupportedException(); } }
// TODO: rename private void RegisterCellAsClue(SudokuGridCell cell) { rowsOfNumbers[cell.Position.Y].Add(cell.Number); columnsOfNumbers[cell.Position.X].Add(cell.Number); byte x, y; FindContainingBlock(cell, out x, out y); blocksOfNumbers[y, x].Add(cell.Number); --emptyCellsCount; }
private SudokuGridCell FindFirstCellWithMinimumCandidates(SudokuGrid grid) { byte minCandidates = grid.Metrics.MaximumNumber; SudokuGridCell minCandidatesCell = null; grid.IterateLinesXY((x, y) => { var currCell = grid.Cells[y, x]; if (currCell.Candidates != null && currCell.Candidates.Count < minCandidates) { minCandidates = (byte)currCell.Candidates.Count; minCandidatesCell = currCell; } }); return(minCandidatesCell); }
// TODO: implement the remaining functionality to find all possible solutions // TODO: translate in English // Алгоритм: последовательный проход по клеткам слева направо и вниз // и усечение мн-ва вариантов цифр, // которые могут находиться в данной клетке; единственный // оставшийся вариант подставляется в клетку. Каждый раз, // как совершается повторный обход доски, должна заполняться // хотя бы одна клетка. Иначе алгоритм зацикливается, // и запускается другой алгоритм: // в первую найденную клетку, имеющую наименьшее кол-во вариантов, // подставляется наименьший из них (несущественно) и поиск продолжается // на специальной доске assumptionsGrid по исходному // алгоритму: если полученное решение непротиворечиво, то // оно становится окончательным. Иначе: а) если получаем // противоречие, то исходная подстановка неверна и, // вернувшись к точке осущствления подстановки, // подставляем другую цифру, вновь применяя исохдный алгоритм; // б) если получаем зацикливание, то снова находим эл-т // с наименьшим кол-вом вариантов, подставляем один из них // и применяем исходный алгоритм. // TODO: protected override List<SudokuGridCell[,]> SearchSolutions() protected override bool SearchSolutionDebug() { forbiddenCandidates.Clear(); Grid.IterateLinesXY((x, y) => { SudokuGridCell cell = Grid.Cells[y, x]; if (cell.IsClue) { RegisterCellAsClue(cell); } }); var resultCells = _SearchSolutionDebug(); if (resultCells == null) { return(false); } Grid.Cells = resultCells; Reset(); return(true); }
private SudokuGridCell[,] MakeAssumptionInCell(ref SudokuGridPosition? minCandidatesCellPos) { if (!minCandidatesCellPos.HasValue) { // TODO: optimize: lookup that kind of cell repetitively when its number of candidates changes (decreases): compare against the minimum value from a variable (where to put it?) minCandidatesCellPos = FindFirstCellWithMinimumCandidates(Grid).Position; } var minCandidatesCell = Grid.Cells[minCandidatesCellPos.Value.Y, minCandidatesCellPos.Value.X]; byte assumedNumber = minCandidatesCell.Candidates.ToArray()[0]; assumptionsGrid = (SudokuGrid)Grid.GetType() .GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, CallingConventions.HasThis, new[] { typeof(SudokuGridConstraints) }, null) .Invoke(new [] { (object)Grid.Constraints }); // BUG: !!! as we use the standard grid here, which was not initially intended to be used anywhere except for decode-and-construct scenarios, by the time we reach this code the assumptionsGrid's cells are not initialized yet -- consider introducing some sort of temporary 'grid' (most probably, a kind of 'cells-only layer'). For that reason one of the ctor's code sections was moved into the Reset() method var assumptionsGridSolver = new SudokuSolvingIterationAssumptionTechnique(assumptionsGrid); // WI: cell-to-cell iteration is a very intensive process -- use cells array cloning instead with targeted minCandidatesCell... altering var minCandidatesCellPosCopy = minCandidatesCellPos.Value; assumptionsGrid.IterateLinesXY((x, y) => { SudokuGridCell currCell; var currPos = new SudokuGridPosition(x, y, false); if (currPos.Equals(minCandidatesCellPosCopy)) { currCell = new SudokuGridCell(assumptionsGrid, currPos, assumedNumber); assumptionsGridSolver.lastFilledPos = currPos; assumptionsGridSolver.currCheckingPos = currPos; assumptionsGridSolver.currCheckingPos.Shift(1, assumptionsGrid.Metrics); } else { currCell = new SudokuGridCell(assumptionsGrid, currPos, Grid.Cells[y, x].Number); if (currCell.Candidates != null) { // just to bring into accord with the latest value from the 'parent' grid currCell.Candidates.IntersectWith(Grid.Cells[y, x].Candidates); } } SudokuGrid.AssignNewCell(assumptionsGrid, currCell); //assumptionsGrid.Cells[y, x] = currCell; }); //List<SudokuGridCell[,]> assumptionGridSolutuons = assumptionGridSolver.SearchSolutions(); //if (assumptionGridSolutuons.Count > 0) // correct assumption was made //{ // return assumptionsGrid.Cells; //} //SudokuGridCell[,] solution; if (assumptionsGridSolver.SearchSolutionDebug()) // correct assumption was made { //return solution; return(assumptionsGrid.Cells); } // incorrect assumption was made (executes on contradiction only) minCandidatesCell.Candidates.Remove(assumedNumber); // if there were only two candidates, with assumedNumber being one of them, then the remaining one is a clue if (minCandidatesCell.IsClue) { minCandidatesCellPos = null; var newPos = new SudokuGridPosition(minCandidatesCell.Position.X, minCandidatesCell.Position.Y, false); lastFilledPos = newPos; currCheckingPos = newPos; currCheckingPos.Shift(1, Grid.Metrics); } return(null); }
// WI: consider moving to cell class private void FindContainingBlock(SudokuGridCell cell, out byte x, out byte y) { x = (byte)(cell.Position.X / cell.ParentGrid.Metrics.BlockWidth); y = (byte)(cell.Position.Y / cell.ParentGrid.Metrics.BlockHeight); }
// BUG: used to avoid buggy duplicate-code decision in solver classes -- get rid of as soon as possible internal static void AssignNewCell(SudokuGrid grid, SudokuGridCell cell) { cell.NumberChanged += (s, e) => grid.OnCellNumberChanged(new CellNumberChangedEventArgs((SudokuGridCell)s)); grid.cells[cell.Position.Y, cell.Position.X] = cell; }
public CellNumberChangedEventArgs(SudokuGridCell cell) { this.cell = cell; }
private SudokuGridCell[,] MakeAssumptionInCell(ref SudokuGridPosition? minCandidatesCellPos) { if (!minCandidatesCellPos.HasValue) { // TODO: optimize: lookup that kind of cell repetitively when its number of candidates changes (decreases): compare against the minimum value from a variable (where to put it?) minCandidatesCellPos = FindFirstCellWithMinimumCandidates(Grid).Position; } var minCandidatesCell = Grid.Cells[minCandidatesCellPos.Value.Y, minCandidatesCellPos.Value.X]; byte assumedNumber = minCandidatesCell.Candidates.ToArray()[0]; assumptionsGrid = (SudokuGrid)Grid.GetType() .GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, CallingConventions.HasThis, new[] { typeof(SudokuGridConstraints) }, null) .Invoke(new [] { (object)Grid.Constraints }); // BUG: !!! as we use the standard grid here, which was not initially intended to be used anywhere except for decode-and-construct scenarios, by the time we reach this code the assumptionsGrid's cells are not initialized yet -- consider introducing some sort of temporary 'grid' (most probably, a kind of 'cells-only layer'). For that reason one of the ctor's code sections was moved into the Reset() method var assumptionsGridSolver = new SudokuSolvingIterationAssumptionTechnique(assumptionsGrid); // WI: cell-to-cell iteration is a very intensive process -- use cells array cloning instead with targeted minCandidatesCell... altering var minCandidatesCellPosCopy = minCandidatesCellPos.Value; assumptionsGrid.IterateLinesXY((x, y) => { SudokuGridCell currCell; var currPos = new SudokuGridPosition(x, y, false); if (currPos.Equals(minCandidatesCellPosCopy)) { currCell = new SudokuGridCell(assumptionsGrid, currPos, assumedNumber); assumptionsGridSolver.lastFilledPos = currPos; assumptionsGridSolver.currCheckingPos = currPos; assumptionsGridSolver.currCheckingPos.Shift(1, assumptionsGrid.Metrics); } else { currCell = new SudokuGridCell(assumptionsGrid, currPos, Grid.Cells[y, x].Number); if (currCell.Candidates != null) { // just to bring into accord with the latest value from the 'parent' grid currCell.Candidates.IntersectWith(Grid.Cells[y, x].Candidates); } } SudokuGrid.AssignNewCell(assumptionsGrid, currCell); //assumptionsGrid.Cells[y, x] = currCell; }); //List<SudokuGridCell[,]> assumptionGridSolutuons = assumptionGridSolver.SearchSolutions(); //if (assumptionGridSolutuons.Count > 0) // correct assumption was made //{ // return assumptionsGrid.Cells; //} //SudokuGridCell[,] solution; if (assumptionsGridSolver.SearchSolutionDebug()) // correct assumption was made { //return solution; return assumptionsGrid.Cells; } // incorrect assumption was made (executes on contradiction only) minCandidatesCell.Candidates.Remove(assumedNumber); // if there were only two candidates, with assumedNumber being one of them, then the remaining one is a clue if (minCandidatesCell.IsClue) { minCandidatesCellPos = null; var newPos = new SudokuGridPosition(minCandidatesCell.Position.X, minCandidatesCell.Position.Y, false); lastFilledPos = newPos; currCheckingPos = newPos; currCheckingPos.Shift(1, Grid.Metrics); } return null; }