The constraint creation functions now take an IntoIterator allowing
you to use generic collections, and even perform maps and filters.
This is useful, for example, when the variables are in a 2d array.
This is a very simple all different constraint, e.g.
- It does not know that if there are more variables than candidates,
it has reached a contradiction.
- It does not know that, if there are as many variables as candidates,
if only one variable can take on a candidate value, then all other
candidates for that variable can be eliminated.
Each constraint is a little sub-program that attempts to reduce the
search space. They should only be run as required, i.e. when the
candidates of the variables they use were updated, though this has not
yet been implemented.
Constraints are traits so that puzzles may implement their own
specialised contraints as required. We have split the trait into an
"on_assigned" call and an "on_updated" call for clarity. The provided
methods simply return true, indicating no contradiction.
The puzzle search type is a puzzle with some variables assigned. It
will be passed (mutably) to constraints for them to do their thing.
All changes to a variables' candidates must go through this object so
that we can wake up affected constraints.
When a valid solution has been found, we will create a Solution
structure. The values assigned to the puzzle variables can be
accessed using the indexing notation: solution[var].
Candidates are always an i32 (rather than a generic integer type) to
more easily perform arithmetic. The puzzles we will deal with should
always be small enough such that we do not exceed the limits of i32.
Candidate groups are separated into an enum. This is to help us keep
it general, as we would like to include a range option in the future.
We have chosen to return a variable token for every variable created,
instead of referencing puzzle variables by string or something else.
Puzzle variables should only be used in the puzzle that created it,
though this is neither checked nor enforced.
Eventually, we would also like to overload the standard arithmetic
symbols to ergonomically build constraint expressions.