//Returns the minimal puzzle corresponding to this one
        public Puzzle findMinimalPuzzle()
        {
            Boolean isSolutionUnique = this.isSolutionUnique();

            //If this puzzle is already non-unique, just exit
            if (!isSolutionUnique)
            {
                System.Console.WriteLine("This isn't unique!");
                return(null);
            }

            //Tracks a list of potential candidates. i.e. the list of nodes the current node connects to
            List <Puzzle> listOfCandidates = new List <Puzzle>();

            //Represents the current node
            Puzzle initialPuzzle = new Puzzle();

            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    initialPuzzle.values[i, j] = this.values[i, j];
                }
            }

            //Initially, the only node we see is the beginning puzzle
            listOfCandidates.Add(initialPuzzle);

            do
            {
                //Main logic loop

                int len = listOfCandidates.Count;
                for (int i = 0; i < len; i++)
                {
                    //listOfCandidates.Count is constantly decreasing in length, so accessing listOfCandidates.ElementAt(i) is dangerous
                    if (i >= listOfCandidates.Count)
                    {
                        break;
                    }

                    Puzzle candidate = listOfCandidates.ElementAt(i);

                    for (int y = 0; y < 9; y++)
                    {
                        for (int x = 0; x < 9; x++)
                        {
                            //Check every location on the puzzle we're looking at to see if we can reduce it without making it non-unique
                            Puzzle nextPuzzle = candidate.findReducedPuzzle(x, y);

                            if (nextPuzzle != null)
                            {
                                if (!nextPuzzle.isContained(listOfCandidates))
                                {
                                    //If we found a reduced non-unique non-duplicate version of the current puzzle, then add it to the list of candidates
                                    listOfCandidates.Add(nextPuzzle);
                                }
                            }
                        }
                    }

                    if (listOfCandidates.Count > 1)
                    {
                        //Now we've added every node that this node connects to that is farther from the root
                        //(i.e. has less numbers given),so we don't need this node anymore
                        listOfCandidates.Remove(candidate);
                    }
                    //We're done with that candidate, move on to the next one in listOfCandidates
                }

                //We went through all the elements in listOfCandidates from before, but now that list has changed.
                // -If there's more than one candidate puzzle left, then re-execute the for loop with the remaining candidates
                // -If there's only one candidate puzzle left, exit and return that. It lasted through the most removals
                //  of numbers without being non-unique, so it must be the minimal puzzle
            } while (!(listOfCandidates.Count == 1 && listOfCandidates.ElementAt(0).isSolutionUnique()));

            return(listOfCandidates.ElementAt(0));
        }