// Forward checking algorithm static private Tuple <Sudoku, int> ForwardCheckingWithHeuristic(Sudoku sudoku) { int expansionCount = 0; Stack <Sudoku> stack = new Stack <Sudoku>(); stack.Push(sudoku); Sudoku newSudoku = sudoku.Clone(); while (!sudoku.IsSolution()) { newSudoku = sudoku.Clone(); bool alreadyPopped = false; // Update (dynamic) mcv list (inherent) newSudoku.UpdateMCVList(); // Generate and push successor using mcvList // Get new variable, first one in the mcv list Tuple <int, int> newVariable = newSudoku.mcvList[0].Item1; // Remove so that variables get looped through List <int> domain = newSudoku.domains[newVariable.Item1, newVariable.Item2]; newSudoku[newVariable.Item1, newVariable.Item2] = domain[sudoku.domainCounter]; // Check if the new sudoku is a partial solution, if so, push to stack if (newSudoku.IsPartialSolution(newVariable)) { stack.Push(newSudoku); } // Domain and expansion counter sudoku.domainCounter++; expansionCount++; /* * Update constraints where this sudoku is a part of. * Find all constraints that should be updated and update them * check all constraints in constraintset. * If V_i has value, get all constraints C_ji where V_j not instantiated, and make them consistent. */ foreach (Tuple <Tuple <int, int>, Tuple <int, int> > constraint in newSudoku.constraintSet) { if (constraint.Item2.Equals(newVariable)) { // If V_j is not fixed(not instantiated) if (!newSudoku.fixedNumbers.Contains(new Tuple <int, int>(constraint.Item1.Item1, constraint.Item1.Item2))) { // Remove the value of V_i from domains of V_j newSudoku.domains[constraint.Item1.Item1, constraint.Item1.Item2].Remove(domain[newSudoku.domainCounter]); // If new domain of a location is empty, it should step back. // Problem with double backtrack step (if also sudoku.domaincounter >= domain.count), so we created extra check in backtrack step. if (newSudoku.domains[constraint.Item1.Item1, constraint.Item1.Item2].Count == 0) { // Get previous sudoku sudoku = stack.Pop(); Console.WriteLine("empty domain, step back to previous sudoku"); // Update already popped (to make sure we don't pop twice) alreadyPopped = true; break; } } } } // Backtrack step (make sure stack isn't popped yet) if (sudoku.domainCounter >= domain.Count && alreadyPopped == false) { sudoku = stack.Pop(); Console.WriteLine("backtrack-step"); } } return(new Tuple <Sudoku, int>(sudoku, expansionCount)); }
// Forward checking algorithm static private Tuple <Sudoku, int> ForwardChecking(Sudoku sudoku) { int expansionCount = 0; Stack <Sudoku> stack = new Stack <Sudoku>(); stack.Push(sudoku); Sudoku newSudoku = sudoku.Clone(); while (!sudoku.IsSolution()) { newSudoku = sudoku.Clone(); bool alreadyPopped = false; // Generate and push successor Tuple <int, int> newVariable = newSudoku.EmptyVariable(0); List <int> domain = newSudoku.domains[newVariable.Item1, newVariable.Item2]; newSudoku[newVariable.Item1, newVariable.Item2] = domain[sudoku.domainCounter]; if (newSudoku.IsPartialSolution(newVariable)) { stack.Push(newSudoku); } // Domain counter goes up sudoku.domainCounter++; expansionCount++; /* * Update constraints where this sudoku is a part of. * Find all constraints that should be updated and update them * check all constraints in constraintset. * If V_i has value, get all constraints C_ji where V_j not instantiated, and make them consistent. */ foreach (Tuple <Tuple <int, int>, Tuple <int, int> > constraint in newSudoku.constraintSet) { if (constraint.Item2.Equals(newVariable)) { // If V_j is not fixed (not instantiated) if (!newSudoku.fixedNumbers.Contains(new Tuple <int, int>(constraint.Item1.Item1, constraint.Item1.Item2))) { // Remove the value of V_i from domains of V_j newSudoku.domains[constraint.Item1.Item1, constraint.Item1.Item2].Remove(domain[newSudoku.domainCounter]); // If new domain of a location is empty, it should step back. // Problem with double backtrack step (if also sudoku.domaincounter >= domain.count), therefor we added extra check at backtrack step if (newSudoku.domains[constraint.Item1.Item1, constraint.Item1.Item2].Count == 0) { // Get previous sudoku sudoku = stack.Pop(); Console.WriteLine("empty domain, step back to previous sudoku"); // Set alreadyPopped to true, so we don't pop twice. alreadyPopped = true; break; } } } } // Backtrack step (make sure stack isn't popped yet) if (sudoku.domainCounter >= domain.Count && alreadyPopped == false) { sudoku = stack.Pop(); Console.WriteLine("backtrack-step"); } } return(new Tuple <Sudoku, int>(sudoku, expansionCount)); }
static void Main() { while (true) { int[,] input = ReadSudoku(); Sudoku sudoku = new Sudoku(input); Console.WriteLine(); Console.WriteLine("Select algorithm: "); Console.WriteLine(" 1: Chronological Backtracking"); Console.WriteLine(" 2: Chronological Backtracking with MCV heuristic"); Console.WriteLine(" 3: Forward Checking"); Console.WriteLine(" 4: Forward Checking with MCV heuristic"); Console.WriteLine(); // Switch for selecting algorithm. in each case, keep time, select algorithm, extract data, stop time and write answers. switch (int.Parse(Console.ReadLine())) { case 1: { long x = DateTime.Now.Ticks; Tuple <Sudoku, int> answer = ChronologicalBacktracking(sudoku); Sudoku newSudoku = answer.Item1; int expansions = answer.Item2; Console.WriteLine("Time Elapsed (in seconds): " + TimeSpan.FromTicks(DateTime.Now.Ticks - x).TotalSeconds); Console.WriteLine("Expanded: " + expansions + " vertices"); Console.WriteLine("Is Solution? " + newSudoku.IsSolution().ToString()); Console.WriteLine(); newSudoku.WriteSudoku(); break; } case 2: { long x = DateTime.Now.Ticks; Tuple <Sudoku, int> answer = ChronologicalBacktrackingWithHeuristic(sudoku); Sudoku newSudoku = answer.Item1; int expansions = answer.Item2; Console.WriteLine("Time Elapsed (in seconds): " + TimeSpan.FromTicks(DateTime.Now.Ticks - x).TotalSeconds); Console.WriteLine("Expanded: " + expansions + " vertices"); Console.WriteLine("Is Solution? " + newSudoku.IsSolution().ToString()); Console.WriteLine(); newSudoku.WriteSudoku(); break; } case 3: { Console.Write("not implemented"); long x = DateTime.Now.Ticks; Tuple <Sudoku, int> answer = ForwardChecking(sudoku); Sudoku newSudoku = answer.Item1; int expansions = answer.Item2; Console.WriteLine("Time Elapsed (in seconds): " + TimeSpan.FromTicks(DateTime.Now.Ticks - x).TotalSeconds); Console.WriteLine("Expanded: " + expansions + " vertices"); Console.WriteLine("Is Solution? " + newSudoku.IsSolution().ToString()); Console.WriteLine(); newSudoku.WriteSudoku(); break; } case 4: { Console.WriteLine("not implemented"); break; } } Console.WriteLine(); } }