//allows for copies of the uncert object to be made, so that the copy wont modify the original public unCert deepCopy() { unCert other = new unCert(); other.prev = this.prev.deepCopy(); gSquare = this.gSquare.deepCopy(); other.guessed = new List <int>(); foreach (int x in this.guessed) { other.guessed.Add(x); } return(other); }
//used to check if user input would create a final board that is consistent. Uses a priority queue to implement minimal remaining value algorithm //on the list of squares that make up the board. Guesses when it comes to a square that cant be reduced to only one possible value. If a square has no possible //values and the guessStack is empty then the state is consistent and an error that corresponds to the type of consistency error is returned. Otherwise if the board //can be completed in a valid state then null is returned. public Error checkConsistency(Square temp) { FastPriorityQueue <Square> testQueue = new FastPriorityQueue <Square>(81); //used for mrv algorithm Stack <unCert> guessStack = new Stack <unCert>(); //stack that keeps track of guessed states foreach (Square sq in this.Board) //populate priority queue with squares from the board { sq.genPossibles(this.Board); testQueue.Enqueue(sq, sq.Heur); } while (testQueue.Any()) //loop until the queue is empty { if (testQueue.First.Given || temp == testQueue.First || testQueue.First.Solved) //if the square has been solved, is a given, or is user entered remove the sqaure from the queue { testQueue.Dequeue(); } else if (testQueue.First.Heur == 1)//if the square only has one possible value set it to that value and declare it as solved, remove it from the queue and update the board, and the domain of the squares that that square constrains { testQueue.First.Number = testQueue.First.PossibleVals[0]; testQueue.First.Solved = true; updateBoard(testQueue.First); testQueue.Dequeue(); for (int i = 0; i < testQueue.Count; i++)//update the priority of the square in the queue based on the new number in the board { testQueue.UpdatePriority(testQueue.ElementAt(i), testQueue.ElementAt(i).Heur); } } else if (testQueue.First.Heur == 0) //a square has no possible values so the current state is inconsistent( as proposed by ac3 algorithm) { if (guessStack.Count == 0) //no guesses have been made so the board is incosistent due to the user input so display an error { Error err = new Error(Error.errType.single, temp); return(err); } else//otherwise a guess caused an inconsistency so a correction needs to be made { foreach (int x in guessStack.First().Guessed)//restrict the domain based on the guesses that have already been made, since those are wrong as they caused an inconsistency { guessStack.First().Gsquare.PossibleVals.Remove(x); } if (guessStack.First().Gsquare.PossibleVals.Count == 0)//if there are no more values left in the guessed squares domain that means the bad guess occured on a previous square, pop this state and check the lower guess in the stack { guessStack.Pop(); guessStack.First().Prev.board[guessStack.First().Gsquare.Y * 9 + guessStack.First().Gsquare.X].Solved = false; guessStack.First().Prev.board[guessStack.First().Gsquare.Y * 9 + guessStack.First().Gsquare.X].Heur = -1; continue; } if (guessStack.Count != 0)//as long as the stack isnt empty update the guessed square to be false again and set its heur value at -1 so it will be at the front of the queue, then reset the board to the backed up version { guessStack.First().Prev.board[guessStack.First().Gsquare.Y * 9 + guessStack.First().Gsquare.X].Solved = false; guessStack.First().Prev.board[guessStack.First().Gsquare.Y * 9 + guessStack.First().Gsquare.X].Heur = -1; foreach (Square sq in guessStack.First().Prev.board) { this.modBoard(sq); } this.Placed = guessStack.First().Prev.Placed; testQueue.Clear(); foreach (Square sq in this.board) { testQueue.Enqueue(sq, sq.Heur); } } } } else if (testQueue.First.Heur > 1 || testQueue.First.Heur == -1) //a guess needs to be made as it has multiple possible values { if (guessStack.Count == 0 || guessStack.First().Gsquare != testQueue.First()) //if the guess stack is empty or the guess at the top of the stack doesnt correspond to the current square make a new guess object { unCert tempCert = new unCert(); int guessing = 0; tempCert.Gsquare = testQueue.First.deepCopy(); tempCert.Prev = this.deepCopy(); guessing = testQueue.First.PossibleVals[0]; testQueue.First.PossibleVals.Remove(0); testQueue.First.Solved = true; testQueue.First.Number = guessing; tempCert.Guessed.Add(guessing); guessStack.Push(tempCert); updateBoard(testQueue.First); testQueue.Dequeue(); for (int i = 0; i < testQueue.Count; i++) { testQueue.UpdatePriority(testQueue.ElementAt(i), testQueue.ElementAt(i).Heur); } } else//otherwise modify the guess object at the top of the guess stack and make a new guess, and update the board based on the new guess. { guessStack.First().Gsquare = testQueue.First().deepCopy(); guessStack.First().Prev = this.deepCopy(); foreach (int x in guessStack.First().Guessed) { testQueue.First.PossibleVals.Remove(x); } if (testQueue.First.PossibleVals.Count == 0) { continue; } testQueue.First.Solved = true; int guessing = testQueue.First.PossibleVals[0]; testQueue.First.Number = guessing; guessStack.First().Guessed.Add(guessing); updateBoard(testQueue.First); testQueue.Dequeue(); for (int i = 0; i < testQueue.Count; i++) { testQueue.UpdatePriority(testQueue.ElementAt(i), testQueue.ElementAt(i).Heur); } } } } return(null);//no errors encountered, state is consistent }