Sudoku Constraint Propagation

From CSE231 Wiki
Revision as of 20:26, 23 September 2021 by Cosgroved (talk | contribs) (Created page with "===Constraint Propagator=== {{CodeToImplement|DefaultConstraintPropagator|createCandidateSetsFromGivens<br>createNextCandidateSets<br>assign<br>eliminate|sudoku.lab}} You wi...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Constraint Propagator

class: DefaultConstraintPropagator.java Java.png
methods: createCandidateSetsFromGivens
createNextCandidateSets
assign
eliminate
package: sudoku.lab
source folder: student/src/main/java

You will need to implement two public methods to satisfy the ConstraintPropagator interface: createCandidateSetsFromGivens and createNextCandidateSets. Each of these two methods will be invoked from a different constructor in the #DefaultImmutableSudokuPuzzle class. It should be relatively obvious which one goes with which based on the parameters.


Attention niels epting.svg Warning:
Resist the temptation to cut corners or lock in the given values too soon.
Load up the entire board with each square associated with all 9 candidates.
Then in a separate phase assign the givens.


createCandidateSetsFromGivens

method: public Map<Square, SortedSet<Integer>> createCandidateSetsFromGivens(String givens) Sequential.svg (sequential implementation only)

This method will be invoked when you have loaded a new puzzle. Whether it is from the newspaper, or your airline magazine, or Dr. Arto Inkala there is a set of givens the puzzle creator has provided. That string of givens is parsed into a Map from Square to Optional<Integer>. If a square is given (that is it has a specified starting value) the Optional will be present and its value will be set. If a specific spot of the board wasn't given in the input, it will be empty.

Circle-information.svg Tip: A good choice for Maps which have enums as their keys is EnumMap. Use new EnumMap<>(Square.class) for an EnumMap of Squares.

A good approach here is to start of by initializing all of the squares to all the options (1 through 9) whether or not the square has a given value.

Circle-information.svg Tip: Square.values() is useful here for iterating over all of the Square enum constants.
Circle-information.svg Tip: [CandidateSet.createAllCandidates() is useful here when looking to start a square's candidates off at [1,2,3,4,5,6,7,8,9].

Once that is complete go through the givens and get your private method(s) to work for you.

createNextCandidateSets

method: public Map<Square, SortedSet<Integer>> createNextCandidateSets(Map<Square, SortedSet<Integer>> otherCandidateSets, Square square, int value) Sequential.svg (sequential implementation only)

This method should be invoked when you are searching for the solution in your solver. Much like when you need to create a new copy of the board every time you make a decision in the n-queens search so too will you need to create a new copy of your sudoku board every time you make a decision in your backtracking search.

Make sure to use the SudokuUtils.deepCopyOf method and refrain from mutating the incoming parameter otherCandidateSets.

As warned above, do not actually do any of the constraint propagation in this method. Simply get the copy of the options set, then leverage your private assign method (which in turn may leverage eliminate) to update the copy to be correct before returning it.

Circle-information.svg Tip:If you have two Lists, a and b, and you set a = b, then any changes you make to a will also be made to b. Both variables reference the same objects; they are not copies.

Assign and Eliminate

Background

Peter Norvig's Essay is very helpful here. We have adopted his terms, but challenge yourself to complete this section without simply translating his pseudocode.

To simplify things a bit, we have elected to not short circuit when a 0 option square is found (relying on the search ordering to take care of that for us).

It is up to you how to utilize these two methods. The goal is to update the map that is passed in to reflect the "solving" of a square. For example, if assign is called on square A1 for the value 1, the map that is returned should link the square A1 to a list containing just the value 1 ([1]). Furthermore, any square that is a peer of A1 (ex. B1), should not have the value 1 in its set in the map.

But in solving Sudoku, there are some strategies that we can rely on to more efficiently complete the puzzle. The first one, and required in order to finish this lab, is Peer Elimination Constraint Propagation (rule 1 in the Norvig essay). The rule is as follows: "If a square has only one possible value, then eliminate that value from the square's peers."

Let's very slowly walk through a example of this propagation in detail to make it more clear what we are looking for. Start by looking at this nearly finished board:

Board Info
PeerConstraint1.PNG We can see that the red square (D1) has only one available option. At this point, let's say we call assign on the red square D1 for the value 1.
PeerConstraint3.PNG As part of the assignment process, all the peers of red square D1 have the value 1 removed from their possibilities. In this example, yellow square, A1, is directly affected! After eliminating the alternative option, there is only one possible value yellow square A1 could be. This is where our constraint propagator needs to take the next step and treat yellow square A1 as assigned.
PeerConstraint4.PNG This step right here (going from a small 7 to a big 7) is not actually something our program will do. In our implementation, having only one option in a square's set is equivalent to that square being "assigned" to that value. Because of this, you actually do not need to call assign again for yellow square A1.
PeerConstraint5.PNG Now that yellow square A1 is known, the final step is to go through all of A1's peers (including green square C3) and remove the value 7. Note that now there is only one possible option left for green square C3. Your code should keep going, looking at all the peers of C3 and eliminating the value 1, and so on. Some puzzles can be completely solved using only Peer Elimination constraint propagation!
Two Paths
All Assign All The Time

In this implementation, you will perform all of your work in the assign method. eliminate will never be invoked.

Mutual Recursion

In this implementation, assign and eliminate will take on their own roles, at times calling each other. This implementation leads nicely into the extra credit portion.

assign(resultOptionSets,square,value)

method: private void assign(Map<Square, SortedSet<Integer>> resultOptionSets, Square square, int value) Sequential.svg (sequential implementation only)

Circle-information.svg Tip:If you are hitting a ConcurrentModificationException here, make sure you are using CandidateSet.
Circle-information.svg Tip:If you are hitting a ConcurrentModificationException here and you are using CandidateSet, think about how a chain reaction might be causing what you ensured to no longer be true.

eliminate(resultOptionSets,square,value)

method: private void eliminate(Map<Square, SortedSet<Integer>> resultOptionSets, Square square, int value) Sequential.svg (sequential implementation only)