aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Pankoff <ccntrq@screenri.de>2020-11-09 16:18:10 +0100
committerAlexander Pankoff <ccntrq@screenri.de>2020-11-15 16:42:43 +0100
commita148af844121fad7b3b10aad44f976964d092682 (patch)
treeaae6b4d5926f412f642323e44a7c0b6740fa19d3
parentb831e749d5bf341fe9ecc225c3676808918ab7ea (diff)
downloadperlweeklychallenge-club-a148af844121fad7b3b10aad44f976964d092682.tar.gz
perlweeklychallenge-club-a148af844121fad7b3b10aad44f976964d092682.tar.bz2
perlweeklychallenge-club-a148af844121fad7b3b10aad44f976964d092682.zip
add perl solution for wk-086 ch-2
-rwxr-xr-xchallenge-086/alexander-pankoff/perl/ch-2.pl155
1 files changed, 155 insertions, 0 deletions
diff --git a/challenge-086/alexander-pankoff/perl/ch-2.pl b/challenge-086/alexander-pankoff/perl/ch-2.pl
new file mode 100755
index 0000000000..1314a2c1b1
--- /dev/null
+++ b/challenge-086/alexander-pankoff/perl/ch-2.pl
@@ -0,0 +1,155 @@
+#!/usr/bin/env perl
+use v5.20;
+use utf8;
+use strict;
+use warnings;
+use feature qw(say signatures);
+no warnings 'experimental::signatures';
+
+use List::Util qw(all any);
+use Storable qw(dclone);
+
+use Getopt::Long qw(:config auto_help);
+use Pod::Usage;
+
+{
+
+ my $INPUT = slurp(*STDIN);
+
+ my @SUDOKU = map {
+ [ map { $_ eq '_' ? undef : $_ } grep { /[0-9_]/ } split( /\s*/, $_ ) ]
+ } split( /\n/, $INPUT );
+
+ pod2usage( -message => "Invalid input", -exitval => 1 )
+ unless all { @$_ == 9 } @SUDOKU;
+
+ my $solved = solve( \@SUDOKU );
+
+ die "no solution found\n" unless $solved;
+ say render_sudoku($solved);
+}
+
+sub render_sudoku($sudoku) {
+ join(
+ "\n",
+ map {
+ join( ' ', '[', map { $_ // '_' } @$_, ']' )
+ } @$sudoku
+ );
+}
+
+sub solve($sudoku) {
+ return $sudoku if solved($sudoku);
+
+ my %cache;
+ while ( my %next = next_free_position( $sudoku, \%cache ) ) {
+ my $row = $next{row};
+ my $col = $next{col};
+ $cache{ $row . $col } = 1;
+
+ for my $candidate ( @{ $next{domain} } ) {
+ my $test = [@$sudoku];
+ $test->[$row] = [ @{ $test->[$row] } ];
+ $test->[$row][$col] = $candidate;
+ my $solved = solve($test);
+ return $solved if $solved;
+ }
+ }
+}
+
+sub next_free_position ( $sudoku, $cache ) {
+ my %min;
+ for my $row ( 0 .. 8 ) {
+ for my $col ( 0 .. 8 ) {
+ next if defined $sudoku->[$row][$col] || $cache->{ $row . $col };
+ my @domain = get_candidates( $row, $col, $sudoku );
+ return if !@domain;
+
+ if ( !%min || @domain < @{ $min{domain} } ) {
+ %min = (
+ row => $row,
+ col => $col,
+ domain => \@domain,
+ );
+ }
+ }
+ }
+
+ return %min;
+}
+
+sub get_candidates ( $row, $col, $sudoku ) {
+ intersection(
+ [ col_candidates( $col, $sudoku ) ],
+ [ row_candidates( $row, $sudoku ) ],
+ [ box_candidates( $row, $col, $sudoku ) ]
+ );
+}
+
+sub solved($sudoku) {
+ all { !row_candidates( $_, $sudoku ) } 0 ... 8;
+}
+
+sub row_candidates ( $row, $sudoku ) {
+ _missing( @{ $sudoku->[$row] } );
+}
+
+sub col_candidates ( $col, $sudoku ) {
+ _missing( map { $_->[$col] } @{$sudoku} );
+}
+
+sub box_candidates ( $row, $col, $sudoku ) {
+
+ my $box_start_row = int( $row / 3 ) * 3;
+ my $box_start_col = int( $col / 3 ) * 3;
+
+ my @elems =
+ map { @{$_}[ $box_start_col .. $box_start_col + 2 ] }
+ ( @{$sudoku}[ $box_start_row .. $box_start_row + 2 ] );
+ return _missing(@elems);
+}
+
+sub _missing(@elems) {
+ state $domain = [ 1 .. 9 ];
+ difference( $domain, \@elems );
+}
+
+sub difference ( $a, $b ) {
+ my %b_lookup = map { $_ ? ( $_ => 1 ) : () } @$b;
+ grep { !$b_lookup{$_} } @$a;
+}
+
+sub intersection ( $a, $b, @more ) {
+ my %a_lookup = map { $_ => 1 } @$a;
+ my @res =
+ grep {
+ my $b_elem = $_;
+ $a_lookup{$b_elem};
+ } @$b;
+
+ return @res if !@more;
+ return intersection( \@res, @more );
+}
+
+sub slurp($fh) {
+ local $/ = undef;
+ my $out = <$fh>;
+ return $out;
+}
+
+=pod
+
+=head1 NAME
+
+wk-086 ch-2 - Sudoku Puzzle
+
+=head1 SYNOPSIS
+
+ch-2.pl [options]
+
+ This programm will complete the given Sudoku Puzzle
+
+ Options:
+ --help print this help text
+
+=cut