aboutsummaryrefslogtreecommitdiff
path: root/challenge-086
diff options
context:
space:
mode:
authorMohammad S Anwar <Mohammad.Anwar@yahoo.com>2020-11-15 01:33:03 +0000
committerGitHub <noreply@github.com>2020-11-15 01:33:03 +0000
commit87c1ebe4780e3c123a7f014da09aa465853ae4c8 (patch)
tree51e9007c029af3ca46657d60247f67b92cdb91ba /challenge-086
parent2d6c7910e4df1a7f2881ceb73998dbfc0a6701ee (diff)
parent0250509d3274640c6161d13a75e0b9b68bd3f942 (diff)
downloadperlweeklychallenge-club-87c1ebe4780e3c123a7f014da09aa465853ae4c8.tar.gz
perlweeklychallenge-club-87c1ebe4780e3c123a7f014da09aa465853ae4c8.tar.bz2
perlweeklychallenge-club-87c1ebe4780e3c123a7f014da09aa465853ae4c8.zip
Merge pull request #2762 from Abigail/abigail/week-086
Abigail/week 086
Diffstat (limited to 'challenge-086')
-rw-r--r--challenge-086/abigail/blog1.txt1
-rw-r--r--challenge-086/abigail/perl/ch-2.pl154
2 files changed, 130 insertions, 25 deletions
diff --git a/challenge-086/abigail/blog1.txt b/challenge-086/abigail/blog1.txt
new file mode 100644
index 0000000000..b97c4ee817
--- /dev/null
+++ b/challenge-086/abigail/blog1.txt
@@ -0,0 +1 @@
+https://wp.me/pcxd30-4C
diff --git a/challenge-086/abigail/perl/ch-2.pl b/challenge-086/abigail/perl/ch-2.pl
index 44646e9087..ecafa5924c 100644
--- a/challenge-086/abigail/perl/ch-2.pl
+++ b/challenge-086/abigail/perl/ch-2.pl
@@ -316,16 +316,58 @@ foreach my $x (@INDICES) {
}
-
################################################################################
#
-# Recursively solve the sudoku.
+# Two helper functions:
+# nr_of_elements: Given a bitfield, return how many elements it represents.
+# elements: Given a bitfield, return the elements it represents.
+#
+################################################################################
+
+my sub nr_of_elements ($bitfield) {
+ sprintf ("%b", $bitfield) =~ y/1/1/;
+}
+
+my sub elements ($bitfield) {
+ grep {$bitfield & (1 << ($_ - 1))} @ELEMENTS;
+}
+
+
+
+################################################################################
#
-# Given a set of solved and unsolved cells, pick a cell with the least
-# number of possibilities left. For each possibility, try this one, and
-# recurse. If any leads to a solution, return this. Else, return false,
-# so we can backtrack.
-# If the set of unsolved cells is empty, we have solved the complete sudoku.
+# Recursively solve the sudoku, given a set of solved cells, and a
+# set of unsolved cells.
+#
+# o If the set of unsolved cells is empty, we have solved the complete
+# sudoku, and we can return the set of solved cells.
+#
+# o If we have an unsolved cell with no possibilities left, there is no
+# solution possible, and we return 1.
+#
+# o If we have cells with one possibility left, then create a todo list
+# of all cells with one possibility left. Then, for each cell "c" of
+# the todo list:
+# - for each other unsolved cell "d" which "c" can see, remove "p"
+# from its set of possibilities.
+# + If, afterwards, "d" has no possibility left,
+# there is no solution.
+# + If, afterwards, "d" has one possibility left,
+# add it to the todo list.
+# - add "c" to the set of solved cells, with value "p"
+# - remove "c" from the set of unsolved cells, and from the todo list.
+#
+# o Otherwise, pick an unsolved cell "c" with the least number of possibilities
+# left (this will be at least two possibilities).
+# - for each of its possibilities "p":
+# + for each unsolved cell "d" which "c" can see, remove "p"
+# from its set of possibilites
+# + add "c" to the set of solved cells, with value "p"
+# + remove "c" from the set of unsolved cells.
+# + recurse:
+# = if there is a solution, return the solution
+# = else, try the next possibility
+# - if no possibility leads to a solution, there is no solution.
#
# For larger sudoku's, we can reach the "deep recursion" warning, so we
# silence it.
@@ -340,27 +382,89 @@ sub solve ($solved, $unsolved) {
return $solved unless keys %$unsolved;
#
- # Find the (a) square which the least possibilities; this
- # means finding the set with the least amount of bits set.
+ # Bucketize the set of unsolved cells, by the number
+ # of possibilities left.
+ #
+ my @buckets;
+ while (my ($key, $value) = each %$unsolved) {
+ push @{$buckets [nr_of_elements $value]} => $key;
+ }
+
+ #
+ # No solution possible.
+ #
+ return if $buckets [0];
+
+ if (@{$buckets [1] || []}) {
+ #
+ # We have unsolved cells with just one possibility left.
+ #
+ my %todo = map {$_ => 1} @{$buckets [1]};
+
+ #
+ # Make copies of the solved and unsolved structures.
+ #
+ my $new_solved = {%$solved};
+ my $new_unsolved = {%$unsolved};
+
+ while (keys %todo) {
+ my ($cell) = sort keys %todo;
+ my $mask = $$new_unsolved {$cell};
+ my ($x, $y) = split $; => $cell;
+
+ #
+ # For all unsolved cells which can be seen by this cell
+ # eliminate the value of this cell from its possibilities.
+ # If no possibilities are left, return undef. If one possibility
+ # is left, push onto @todo.
+ #
+ # In any case, move this cell from the set of unsolved cells
+ # to the set of solved cells.
+ #
+ foreach my $can_see (sees ($x, $y)) {
+ my ($x1, $y1) = @$can_see;
+ if ($$new_unsolved {$x1, $y1} &&
+ $$new_unsolved {$x1, $y1} & $mask) {
+ $$new_unsolved {$x1, $y1} &= ~ $mask;
+ my $nr_of_elements =
+ nr_of_elements $$new_unsolved {$x1, $y1};
+ return if $nr_of_elements == 0;
+ $todo {$x1, $y1} = 1 if $nr_of_elements == 1;
+ }
+ }
+
+ #
+ # Move cell to solved structure, and remove it from %todo.
+ #
+ $$new_solved {$cell} = (elements $mask) [0];
+ delete $$new_unsolved {$cell};
+ delete $todo {$cell};
+ }
+
+ #
+ # Recurse with the new sets
+ #
+ return solve ($new_solved, $new_unsolved);
+ }
+
+ #
+ # Now, find a cell with the least number of possibilities left.
+ # That will be a cell in the first non-empty bucket.
#
- my ($key) = map {$$_ [0]}
- sort {$$a [1] <=> $$b [1]}
- map {[$_, sprintf ("%b", $$unsolved {$_}) =~ y/1/1/]}
- keys %$unsolved;
- my ($x, $y) = split $; => $key;
- my $possibilities = $$unsolved {$key};
+ my ($bucket) = grep {$_} @buckets;
+ my $cell = $$bucket [0];
+ my ($x, $y) = split $; => $cell;
#
- # Guess each possibility for this key.
+ # Guess each possibility for this cell.
#
- foreach my $guess (@ELEMENTS) {
+ foreach my $guess (elements $$unsolved {$cell}) {
my $mask = 1 << ($guess - 1);
- next unless $possibilities & $mask;
#
# Create new solved unsolved structures, as copies from the given ones.
#
- my $new_solved = {%$solved};
+ my $new_solved = {%$solved};
my $new_unsolved = {%$unsolved};
#
@@ -371,16 +475,16 @@ sub solve ($solved, $unsolved) {
#
# Remove the guess from the set of unsolved cells.
#
- delete $$new_unsolved {$key};
+ delete $$new_unsolved {$cell};
#
- # Delete our guess as possibility for each square
+ # Delete our guess as possibility for each cell
# which can be seen.
#
foreach my $can_see (sees ($x, $y)) {
- my ($x, $y) = @$can_see;
- if ($$new_unsolved {$x, $y}) {
- $$new_unsolved {$x, $y} &= ~ $mask;
+ my ($x1, $y1) = @$can_see;
+ if ($$new_unsolved {$x1, $y1}) {
+ $$new_unsolved {$x1, $y1} &= ~ $mask;
}
}
@@ -419,7 +523,7 @@ if (my $r = solve ($solved, $unsolved)) {
}
}
else {
- say "No solution found\n";
+ say "No solution found.";
}