aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad S Anwar <Mohammad.Anwar@yahoo.com>2020-11-16 03:52:09 +0000
committerGitHub <noreply@github.com>2020-11-16 03:52:09 +0000
commitf3cd56eb42c9297271df954f30f8b2ab00f777ea (patch)
tree6931f16babd74d3fe3665ed095816f59852a7817
parent25c7b36444c96dd911fd780ee5bc4d15fcaedb21 (diff)
parentaa9377a2dbd61a383a1fa7e4dfd2109abd721ef1 (diff)
downloadperlweeklychallenge-club-f3cd56eb42c9297271df954f30f8b2ab00f777ea.tar.gz
perlweeklychallenge-club-f3cd56eb42c9297271df954f30f8b2ab00f777ea.tar.bz2
perlweeklychallenge-club-f3cd56eb42c9297271df954f30f8b2ab00f777ea.zip
Merge pull request #2769 from PerlMonk-Athanasius/branch-for-challenge-086
Perl & Raku solutions to Tasks 1 & 2 of the Perl Weekly Challenge #086
-rw-r--r--challenge-086/athanasius/Sudoku-Data/Empty.dat9
-rw-r--r--challenge-086/athanasius/Sudoku-Data/Example-completed.dat9
-rw-r--r--challenge-086/athanasius/Sudoku-Data/Example.dat9
-rw-r--r--challenge-086/athanasius/Sudoku-Data/Hard.dat11
-rw-r--r--challenge-086/athanasius/Sudoku-Data/Invalid-box.dat9
-rw-r--r--challenge-086/athanasius/Sudoku-Data/Invalid-col.dat9
-rw-r--r--challenge-086/athanasius/Sudoku-Data/Invalid-row.dat9
-rw-r--r--challenge-086/athanasius/Sudoku-Data/Unsolvable.dat12
-rw-r--r--challenge-086/athanasius/Sudoku-Data/Wikipedia.dat11
-rw-r--r--challenge-086/athanasius/perl/Sudoku.pm327
-rw-r--r--challenge-086/athanasius/perl/ch-1.pl170
-rw-r--r--challenge-086/athanasius/perl/ch-2.pl210
-rw-r--r--challenge-086/athanasius/raku/Sudoku.rakumod266
-rw-r--r--challenge-086/athanasius/raku/ch-1.raku143
-rw-r--r--challenge-086/athanasius/raku/ch-2.raku181
15 files changed, 1385 insertions, 0 deletions
diff --git a/challenge-086/athanasius/Sudoku-Data/Empty.dat b/challenge-086/athanasius/Sudoku-Data/Empty.dat
new file mode 100644
index 0000000000..153cd87502
--- /dev/null
+++ b/challenge-086/athanasius/Sudoku-Data/Empty.dat
@@ -0,0 +1,9 @@
+_ _ _ _ _ _ _ _ _
+_ _ _ _ _ _ _ _ _
+_ _ _ _ _ _ _ _ _
+_ _ _ _ _ _ _ _ _
+_ _ _ _ _ _ _ _ _
+_ _ _ _ _ _ _ _ _
+_ _ _ _ _ _ _ _ _
+_ _ _ _ _ _ _ _ _
+_ _ _ _ _ _ _ _ _
diff --git a/challenge-086/athanasius/Sudoku-Data/Example-completed.dat b/challenge-086/athanasius/Sudoku-Data/Example-completed.dat
new file mode 100644
index 0000000000..95d42fbea2
--- /dev/null
+++ b/challenge-086/athanasius/Sudoku-Data/Example-completed.dat
@@ -0,0 +1,9 @@
+4 3 5 2 6 9 7 8 1
+6 8 2 5 7 1 4 9 3
+1 9 7 8 3 4 5 6 2
+8 2 6 1 9 5 3 4 7
+3 7 4 6 8 2 9 1 5
+9 5 1 7 4 3 6 2 8
+5 1 9 3 2 6 8 7 4
+2 4 8 9 5 7 1 3 6
+7 6 3 4 1 8 2 5 9
diff --git a/challenge-086/athanasius/Sudoku-Data/Example.dat b/challenge-086/athanasius/Sudoku-Data/Example.dat
new file mode 100644
index 0000000000..eba378a89f
--- /dev/null
+++ b/challenge-086/athanasius/Sudoku-Data/Example.dat
@@ -0,0 +1,9 @@
+_ _ _ 2 6 _ 7 _ 1
+6 8 _ _ 7 _ _ 9 _
+1 9 _ _ _ 4 5 _ _
+8 2 _ 1 _ _ _ 4 _
+_ _ 4 6 _ 2 9 _ _
+_ 5 _ _ _ 3 _ 2 8
+_ _ 9 3 _ _ _ 7 4
+_ 4 _ _ 5 _ _ 3 6
+7 _ 3 _ 1 8 _ _ _
diff --git a/challenge-086/athanasius/Sudoku-Data/Hard.dat b/challenge-086/athanasius/Sudoku-Data/Hard.dat
new file mode 100644
index 0000000000..59d99d6611
--- /dev/null
+++ b/challenge-086/athanasius/Sudoku-Data/Hard.dat
@@ -0,0 +1,11 @@
+_ 9 _ 3 _ _ _ _ _
+_ _ 7 _ _ _ 6 _ _
+_ _ _ _ 2 4 _ 3 _
+9 1 _ _ _ _ _ _ 8
+_ _ _ _ _ _ _ _ _
+4 _ _ _ _ 5 _ 2 7
+_ 5 _ 8 7 _ _ 6 _
+_ _ 1 _ _ _ 5 _ _
+_ _ _ 5 _ 6 _ 9 _
+
+https://sudoku.com.au/Unsolvable-Sudoku-16.aspx
diff --git a/challenge-086/athanasius/Sudoku-Data/Invalid-box.dat b/challenge-086/athanasius/Sudoku-Data/Invalid-box.dat
new file mode 100644
index 0000000000..f1a12c9a5a
--- /dev/null
+++ b/challenge-086/athanasius/Sudoku-Data/Invalid-box.dat
@@ -0,0 +1,9 @@
+_ _ _ 2 6 _ 7 _ 1
+6 8 _ _ 7 _ _ 9 _
+1 9 _ _ _ 4 5 _ _
+8 2 _ 1 _ _ _ 4 _
+_ _ 4 6 _ 2 9 _ _
+_ 5 _ _ _ 1 _ 2 8
+_ _ 9 3 _ _ _ 7 4
+_ 4 _ _ 5 _ _ 3 6
+7 _ 3 _ 1 8 _ _ _
diff --git a/challenge-086/athanasius/Sudoku-Data/Invalid-col.dat b/challenge-086/athanasius/Sudoku-Data/Invalid-col.dat
new file mode 100644
index 0000000000..bdf3d11c2a
--- /dev/null
+++ b/challenge-086/athanasius/Sudoku-Data/Invalid-col.dat
@@ -0,0 +1,9 @@
+_ _ _ 2 6 _ 7 _ 1
+6 8 _ _ 7 _ _ 9 _
+1 9 _ _ _ 4 5 _ _
+8 2 _ 1 _ _ _ 4 _
+_ _ 4 6 _ 2 9 _ _
+_ 5 _ _ _ 3 _ 2 8
+_ _ 9 3 _ _ _ 7 4
+_ 4 _ 2 5 _ _ 3 6
+7 _ 3 _ 1 8 _ _ _
diff --git a/challenge-086/athanasius/Sudoku-Data/Invalid-row.dat b/challenge-086/athanasius/Sudoku-Data/Invalid-row.dat
new file mode 100644
index 0000000000..a3db0dd27f
--- /dev/null
+++ b/challenge-086/athanasius/Sudoku-Data/Invalid-row.dat
@@ -0,0 +1,9 @@
+2 _ _ 2 6 _ 7 _ 1
+6 8 _ _ 7 _ _ 9 _
+1 9 _ _ _ 4 5 _ _
+8 2 _ 1 _ _ _ 4 _
+_ _ 4 6 _ 2 9 _ _
+_ 5 _ _ _ 3 _ 2 8
+_ _ 9 3 _ _ _ 7 4
+_ 4 _ _ 5 _ _ 3 6
+7 _ 3 _ 1 8 _ _ _
diff --git a/challenge-086/athanasius/Sudoku-Data/Unsolvable.dat b/challenge-086/athanasius/Sudoku-Data/Unsolvable.dat
new file mode 100644
index 0000000000..a091212851
--- /dev/null
+++ b/challenge-086/athanasius/Sudoku-Data/Unsolvable.dat
@@ -0,0 +1,12 @@
+2 _ _ 9 _ _ _ _ _
+_ _ _ _ _ _ _ 6 _
+_ _ _ _ _ 1 _ _ _
+5 _ 2 6 _ _ 4 _ 7
+_ _ _ _ _ 4 1 _ _
+_ _ _ _ 9 8 _ 2 3
+_ _ _ _ _ 3 _ 8 _
+_ _ 5 _ 1 _ _ _ _
+_ _ 7 _ _ _ _ _ _
+
+
+https://www.reddit.com/r/sudoku/comments/7q76ay/friend_tells_me_that_this_is_unsolvable_sudoku/
diff --git a/challenge-086/athanasius/Sudoku-Data/Wikipedia.dat b/challenge-086/athanasius/Sudoku-Data/Wikipedia.dat
new file mode 100644
index 0000000000..b775373a10
--- /dev/null
+++ b/challenge-086/athanasius/Sudoku-Data/Wikipedia.dat
@@ -0,0 +1,11 @@
+5 3 _ _ 7 _ _ _ _
+6 _ _ 1 9 5 _ _ _
+_ 9 8 _ _ _ _ 6 _
+8 _ _ _ 6 _ _ _ 3
+4 _ _ 8 _ 3 _ _ 1
+7 _ _ _ 2 _ _ _ 6
+_ 6 _ _ _ _ 2 8 _
+_ _ _ 4 1 9 _ _ 5
+_ _ _ _ 8 _ _ 7 9
+
+https://en.wikipedia.org/wiki/Sudoku
diff --git a/challenge-086/athanasius/perl/Sudoku.pm b/challenge-086/athanasius/perl/Sudoku.pm
new file mode 100644
index 0000000000..2006ab6e10
--- /dev/null
+++ b/challenge-086/athanasius/perl/Sudoku.pm
@@ -0,0 +1,327 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 086
+=========================
+
+Task #2
+-------
+*Sudoku Puzzle*
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2020 PerlMonk Athanasius #
+#--------------------------------------#
+
+###############################################################################
+package Sudoku;
+###############################################################################
+
+use strict;
+use warnings;
+use Class::Tiny qw( grid );
+use Const::Fast;
+use Set::Scalar;
+use constant DEBUG => 0;
+
+const my $EMPTY_CELL => '_';
+
+#==============================================================================
+# Public methods
+#==============================================================================
+
+#------------------------------------------------------------------------------
+sub BUILD
+#------------------------------------------------------------------------------
+{
+ my ($self, $args) = @_;
+
+ if (defined(my $orig = $args->{clone}))
+ {
+ $self->clone($orig);
+ }
+ elsif (defined(my $file = $args->{file}))
+ {
+ $self->_read_from_file($file);
+ }
+ else
+ {
+ die 'ERROR: Invalid constructor call, stopped';
+ }
+}
+
+#------------------------------------------------------------------------------
+sub clone
+#------------------------------------------------------------------------------
+{
+ my ($self, $orig) = @_;
+
+ for my $row (0 .. 8)
+ {
+ for my $col (0 .. 8)
+ {
+ $self->{grid}[$row][$col] = $orig->{grid}[$row][$col];
+ }
+ }
+}
+
+#------------------------------------------------------------------------------
+sub insert
+#------------------------------------------------------------------------------
+{
+ my ($self, $row, $col, $digit) = @_;
+
+ if ($digit !~ /^[1-9]$/)
+ {
+ die qq[ERROR: Invalid digit "$digit", stopped];
+ }
+
+ my $target = \$self->{grid}[$row][$col];
+
+ if ($$target ne $EMPTY_CELL)
+ {
+ die qq[ERROR: Cannot add "$digit" at position ($row, $col) which ] .
+ qq[already contains "$$target", stopped];
+ }
+
+ $$target = $digit;
+}
+
+#------------------------------------------------------------------------------
+sub display
+#------------------------------------------------------------------------------
+{
+ my ($self) = @_;
+ my $display = '';
+
+ for my $row (0 .. 8)
+ {
+ $display .= sprintf " [ %s ]\n", join ' ', $self->{grid}[$row]->@*;
+ }
+
+ return $display;
+}
+
+#------------------------------------------------------------------------------
+sub is_valid
+#------------------------------------------------------------------------------
+{
+ my ($self) = @_;
+
+ return $self->_rows_are_valid &&
+ $self->_cols_are_valid &&
+ $self->_boxes_are_valid;
+}
+
+#------------------------------------------------------------------------------
+sub count_empty_cells
+#------------------------------------------------------------------------------
+{
+ my ($self) = @_;
+ my $count = 0;
+
+ for my $row (0 .. 8)
+ {
+ for my $col (0 .. 8)
+ {
+ ++$count if $self->{grid}[$row][$col] eq $EMPTY_CELL;
+ }
+ }
+
+ return $count;
+}
+
+#------------------------------------------------------------------------------
+sub select_empty_cell
+#------------------------------------------------------------------------------
+{
+ my ($self) = @_;
+ my %cells;
+
+ for my $row (0 .. 8)
+ {
+ for my $col (0 .. 8)
+ {
+ if ($self->{grid}[$row][$col] eq $EMPTY_CELL)
+ {
+ my @possibles = $self->_find_possible_digits($row, $col);
+
+ my %cell = (row => $row, col => $col, digits => \@possibles);
+
+ push $cells{scalar @possibles}->@*, \%cell;
+ }
+ }
+ }
+
+ my $least = (sort { $a <=> $b } keys %cells)[0];
+
+ return $cells{$least}->[0];
+}
+
+
+#==============================================================================
+# Private methods
+#==============================================================================
+
+#------------------------------------------------------------------------------
+sub _read_from_file
+#------------------------------------------------------------------------------
+{
+ my ($self, $file) = @_;
+
+ open(my $fh, '<', $file)
+ or die qq[ERROR: Cannot open file "$file" for reading, stopped];
+
+ for my $row (0 .. 8)
+ {
+ my $line = <$fh>;
+ chomp $line;
+ my @digits = split /\s+/, $line;
+
+ for my $col (0 .. 8)
+ {
+ my $digit = shift @digits;
+
+ defined $digit
+ or die qq[ERROR: Missing character in file "$file", stopped];
+
+ $digit =~ /[1-9$EMPTY_CELL]/
+ or die qq[ERROR: Invalid character "$digit" in file ] .
+ qq["$file", stopped];
+
+ $self->{grid}[$row][$col] = $digit;
+ }
+ }
+
+ close $fh
+ or die qq[ERROR: Cannot close file "$file", stopped];
+}
+
+#------------------------------------------------------------------------------
+sub _rows_are_valid
+#------------------------------------------------------------------------------
+{
+ my ($self) = @_;
+
+ for my $row (0 .. 8)
+ {
+ my %digits;
+
+ for my $col (0 .. 8)
+ {
+ my $digit = $self->{grid}[$row][$col];
+ my $count = ++$digits{$digit};
+
+ if ($digit ne $EMPTY_CELL && $count > 1)
+ {
+ warn qq[Rule a): Digit "$digit" occurs more than once in ] .
+ qq[row $row\n] if DEBUG;
+
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+#------------------------------------------------------------------------------
+sub _cols_are_valid
+#------------------------------------------------------------------------------
+{
+ my ($self) = @_;
+
+ for my $col (0 .. 8)
+ {
+ my %digits;
+
+ for my $row (0 .. 8)
+ {
+ my $digit = $self->{grid}[$row][$col];
+ my $count = ++$digits{$digit};
+
+ if ($digit ne $EMPTY_CELL && $count > 1)
+ {
+ warn qq[Rule b): Digit "$digit" occurs more than once in ] .
+ qq[column $col\n] if DEBUG;
+
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+#------------------------------------------------------------------------------
+sub _boxes_are_valid
+#------------------------------------------------------------------------------
+{
+ my ($self) = @_;
+
+ for my $row (0, 3, 6)
+ {
+ for my $col (0, 3, 6)
+ {
+ my %digits;
+
+ for my $r ($row .. $row + 2)
+ {
+ for my $c ($col .. $col + 2)
+ {
+ my $digit = $self->{grid}[$r][$c];
+ my $count = ++$digits{$digit};
+
+ if ($digit ne $EMPTY_CELL && $count > 1)
+ {
+ warn qq[Rule c): Digit "$digit" occurs more than ] .
+ qq[once in box ($row, $col)\n] if DEBUG;
+
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+#------------------------------------------------------------------------------
+sub _find_possible_digits
+#------------------------------------------------------------------------------
+{
+ my ($self, $row, $col) = @_;
+ my $possibles = Set::Scalar->new(1 .. 9);
+
+ for my $c (0 .. 8)
+ {
+ $possibles->delete($self->{grid}[$row][$c]);
+ }
+
+ for my $r (0 .. 8)
+ {
+ $possibles->delete($self->{grid}[$r][$col]);
+ }
+
+ my $box_row = $row - ($row % 3);
+ my $box_col = $col - ($col % 3);
+
+ for my $r ($box_row .. $box_row + 2)
+ {
+ for my $c ($box_col .. $box_col + 2)
+ {
+ $possibles->delete($self->{grid}[$r][$c]);
+ }
+ }
+
+ return sort { $a <=> $b } $possibles->members;
+}
+
+###############################################################################
+1;
+###############################################################################
diff --git a/challenge-086/athanasius/perl/ch-1.pl b/challenge-086/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..91c61dbc8c
--- /dev/null
+++ b/challenge-086/athanasius/perl/ch-1.pl
@@ -0,0 +1,170 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 086
+=========================
+
+Task #1
+-------
+*Pair Difference*
+
+Submitted by: Mohammad S Anwar
+
+You are given an array of integers @N and an integer $A.
+
+Write a script to find if there exists a pair of elements in the array whose
+difference is $A.
+
+Print 1 if exists otherwise 0.
+
+Example 1:
+
+ Input: @N = (10, 8, 12, 15, 5) and $A = 7
+ Output: 1 as 15 - 8 = 7
+
+Example 2:
+
+ Input: @N = (1, 5, 2, 9, 7) and $A = 6
+ Output: 1 as 7 - 1 = 6
+
+Example 3:
+
+ Input: @N = (10, 30, 20, 50, 40) and $A = 15
+ Output: 0
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2020 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=comment
+
+Assumption: The pair must be different (but possibly equal) elements in the
+ array
+
+Algorithm: Exhaustive comparison of each pair difference, together with its
+ negation, until a solution is found or all pairs have been examined
+
+=cut
+#==============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use Getopt::Long;
+use Regexp::Common qw( number );
+
+const my $EXPLAIN => 1;
+const my $USAGE =>
+"Usage:
+ perl $0 -A=<Int> -- [<N> ...]
+
+ -A=<Int> A possible difference between two elements in \@N
+ [<N> ...] An array of two or more integers\n";
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 086, Task #1: Pair Difference (Perl)\n\n";
+}
+
+#==============================================================================
+MAIN:
+#==============================================================================
+{
+ my ($N, $A) = parse_command_line();
+
+ printf "Input: \@N = (%s) and \$A = %d\n", join(', ', @$N), $A;
+
+ my ($minuend, $subtrahend) = find_pair($N, $A);
+
+ if (defined $minuend && defined $subtrahend)
+ {
+ if ($EXPLAIN)
+ {
+ print "Output: 1 as $minuend - $subtrahend = $A\n";
+ }
+ else
+ {
+ print "Output: 1\n";
+ }
+ }
+ else
+ {
+ print "Output: 0\n";
+ }
+}
+
+#------------------------------------------------------------------------------
+sub find_pair
+#------------------------------------------------------------------------------
+{
+ my ($N, $A) = @_;
+ my ($minuend, $subtrahend);
+
+ OUTER:
+ for my $i (0 .. $#$N - 1)
+ {
+ for my $j ($i + 1 .. $#$N)
+ {
+ my $difference = $N->[$i] - $N->[$j];
+
+ if ($difference == $A)
+ {
+ $minuend = $N->[$i];
+ $subtrahend = $N->[$j];
+ last OUTER;
+ }
+ elsif ($difference == -$A)
+ {
+ $minuend = $N->[$j];
+ $subtrahend = $N->[$i];
+ last OUTER;
+ }
+ }
+ }
+
+ return ($minuend, $subtrahend);
+}
+
+#------------------------------------------------------------------------------
+sub parse_command_line
+#------------------------------------------------------------------------------
+{
+ my $A;
+
+ GetOptions('A=i' => \$A) or error('Incorrect command line argument');
+
+ defined $A or error('Missing $A');
+
+ $A =~ /\A$RE{num}{int}\z/ or error("\$A ($A) is not an integer");
+
+ my @N = @ARGV;
+
+ scalar @N >= 2 or error('Too few elements in @N');
+
+ for my $n (@N)
+ {
+ $n =~ /\A$RE{num}{int}\z/ or error("\"$n\" is not an integer");
+ }
+
+ return (\@N, $A);
+}
+
+#------------------------------------------------------------------------------
+sub error
+#------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+###############################################################################
diff --git a/challenge-086/athanasius/perl/ch-2.pl b/challenge-086/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..448d1f0b7b
--- /dev/null
+++ b/challenge-086/athanasius/perl/ch-2.pl
@@ -0,0 +1,210 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 086
+=========================
+
+Task #2
+-------
+*Sudoku Puzzle*
+
+Submitted by: Mohammad S Anwar
+
+You are given Sudoku puzzle (9x9).
+
+Write a script to complete the puzzle and must respect the following rules:
+
+a) Each row must have the numbers 1-9 occuring just once.
+b) Each column must have the numbers 1-9 occuring just once.
+c) The numbers 1-9 must occur just once in each of the 9 sub-boxes (3x3) of the
+ grid.
+
+Example:
+
+ [ _ _ _ 2 6 _ 7 _ 1 ]
+ [ 6 8 _ _ 7 _ _ 9 _ ]
+ [ 1 9 _ _ _ 4 5 _ _ ]
+ [ 8 2 _ 1 _ _ _ 4 _ ]
+ [ _ _ 4 6 _ 2 9 _ _ ]
+ [ _ 5 _ _ _ 3 _ 2 8 ]
+ [ _ _ 9 3 _ _ _ 7 4 ]
+ [ _ 4 _ _ 5 _ _ 3 6 ]
+ [ 7 _ 3 _ 1 8 _ _ _ ]
+
+Output:
+
+ [ 4 3 5 2 6 9 7 8 1 ]
+ [ 6 8 2 5 7 1 4 9 3 ]
+ [ 1 9 7 8 3 4 5 6 2 ]
+ [ 8 2 6 1 9 5 3 4 7 ]
+ [ 3 7 4 6 8 2 9 1 5 ]
+ [ 9 5 1 7 4 3 6 2 8 ]
+ [ 5 1 9 3 2 6 8 7 4 ]
+ [ 2 4 8 9 5 7 1 3 6 ]
+ [ 7 6 3 4 1 8 2 5 9 ]
+
+As the above puzzle respect the 3 rules including 9-sub-boxes as shown below:
+
+ [ 4 3 5 ] [ 2 6 9 ] [ 7 8 1 ]
+ [ 6 8 2 ] [ 5 7 1 ] [ 4 9 3 ]
+ [ 1 9 7 ] [ 8 3 4 ] [ 5 6 2 ]
+
+ [ 8 2 6 ] [ 1 9 5 ] [ 3 4 7 ]
+ [ 3 7 4 ] [ 6 8 2 ] [ 9 1 5 ]
+ [ 9 5 1 ] [ 7 4 3 ] [ 6 2 8 ]
+
+ [ 5 1 9 ] [ 3 2 6 ] [ 8 7 4 ]
+ [ 2 4 8 ] [ 9 5 7 ] [ 1 3 6 ]
+ [ 7 6 3 ] [ 4 1 8 ] [ 2 5 9 ]
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2020 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=comment
+
+The module "Sudoku.pm" provides class Sudoku. An instance of this class repre-
+sents a Sudoku grid, which may be empty, partially filled, or complete. The
+following methods are provided:
+
+ Name Description
+ ----------------- -------------------------------------------------------
+ new Reads in a Sudoku puzzle from file
+ clone Creates a deep copy of an existing grid
+ insert Inserts a digit into an empty cell
+ display Returns a string displaying the grid as in the Example
+ is_valid Returns true if the grid complies with rules a), b),
+ and c), false otherwise
+ count_empty_cells Returns the number of empty cells remaining in the grid
+ (an integer in the range 0 to 81)
+ select_empty_cell Returns the coordinates (row and column) of an empty
+ cell for which the number of possible digits is a
+ minimum, together with a list of those digits
+
+The algorithm, implemented in sub solve(), employs backtracking (i.e., a depth-
+first search of the potential search tree) via recursion.
+
+Note: The algorithm stops as soon as a valid solution is found. If no solution
+is possible, an appropriate message is displayed. However, if more than one
+solution is possible, this will not be discovered. (A "well-posed" puzzle has
+only a single solution.)
+
+=cut
+#==============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use File::Basename;
+use Regexp::Common qw( number );
+use lib qw( . );
+use Sudoku;
+
+const my $DEFAULT => '../Sudoku-Data/Example.dat';
+const my @SUFFIXES => qw( .dat .txt );
+const my $USAGE =>
+"Usage:
+ perl $0 [<file_path>] -- The name of a file containing a Sudoku puzzle\n";
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 086, Task #2: Sudoku Puzzle (Perl)\n\n";
+}
+
+#==============================================================================
+MAIN:
+#==============================================================================
+{
+ my $file_path = parse_command_line();
+ my $original = Sudoku->new(file => $file_path);
+ my $filename = fileparse($file_path, @SUFFIXES);
+
+ printf "$filename:\n\n%s\n", $original->display;
+
+ if ($original->is_valid)
+ {
+ if (my $solution = solve($original))
+ {
+ printf "Solution:\n\n%s", $solution->display;
+ }
+ else
+ {
+ print "No solution found\n";
+ }
+ }
+ else
+ {
+ print "This puzzle violates the rules: no solution is possible\n";
+ }
+}
+
+#------------------------------------------------------------------------------
+sub solve # Recursive backtracking routine
+#------------------------------------------------------------------------------
+{
+ my ($current) = @_;
+
+ return if !$current->is_valid; # Failure
+ return $current if $current->count_empty_cells == 0; # Success
+
+ my $cell = $current->select_empty_cell;
+ my @digits = $cell->{digits}->@*;
+
+ return if scalar @digits == 0; # Failure
+
+ for my $digit (@digits)
+ {
+ my $next = Sudoku->new(clone => $current);
+
+ $next->insert($cell->{row}, $cell->{col}, $digit);
+
+ my $result = solve($next); # Recursive call
+
+ return $result if $result; # Success
+ }
+
+ return; # Failure
+}
+
+#------------------------------------------------------------------------------
+sub parse_command_line
+#------------------------------------------------------------------------------
+{
+ my $path = $DEFAULT;
+ my $args = scalar @ARGV;
+
+ if ($args == 1)
+ {
+ $path = $ARGV[0];
+ }
+ elsif ($args > 1)
+ {
+ error("Expected a single command-line argument, found $args");
+ }
+
+ -e $path or error(qq[The file "$path" does not exist]);
+ -s $path or error(qq[The file "$path" is empty]);
+ -f $path or error(qq[The file "$path" is not a plain file]);
+
+ return $path;
+}
+
+#------------------------------------------------------------------------------
+sub error
+#------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+###############################################################################
diff --git a/challenge-086/athanasius/raku/Sudoku.rakumod b/challenge-086/athanasius/raku/Sudoku.rakumod
new file mode 100644
index 0000000000..15e3a5d044
--- /dev/null
+++ b/challenge-086/athanasius/raku/Sudoku.rakumod
@@ -0,0 +1,266 @@
+use v6d;
+
+###############################################################################
+=begin comment
+
+Perl Weekly Challenge 086
+=========================
+
+Task #2
+-------
+*Sudoku Puzzle*
+
+=end comment
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2020 PerlMonk Athanasius #
+#--------------------------------------#
+
+###############################################################################
+unit class Sudoku;
+###############################################################################
+
+my Str constant $EMPTY-CELL = '_';
+my constant @DIGITS = Array[Str].new: '1' .. '9';
+
+subset Cell of Str where { .all ⊂ @DIGITS ∪ $EMPTY-CELL };
+subset Digit of Str where { .all ⊂ @DIGITS };
+
+has Cell @.grid[9; 9];
+has Str $!file;
+
+##=============================================================================
+# Public methods
+##=============================================================================
+
+#------------------------------------------------------------------------------
+submethod BUILD( Str:D :$!file = '' )
+#------------------------------------------------------------------------------
+{
+ if $!file
+ {
+ my Str @lines = $!file.IO.lines;
+
+ for 0 .. 8 -> UInt $row
+ {
+ my Cell @digits = @lines[$row].split: /\s+/;
+
+ for 0 .. 8 -> UInt $col
+ {
+ @!grid[$row; $col] = @digits.shift;
+ }
+ }
+ }
+}
+
+#------------------------------------------------------------------------------
+method clone( Sudoku:D $orig --> Sudoku:D )
+#------------------------------------------------------------------------------
+{
+ my $new-self = Sudoku.new;
+
+ for 0 .. 8 -> UInt $row
+ {
+ for 0 .. 8 -> UInt $col
+ {
+ $new-self.grid[$row; $col] = $orig.grid[$row; $col];
+ }
+ }
+
+ return $new-self;
+}
+
+#------------------------------------------------------------------------------
+method insert( UInt:D $row, UInt:D $col, Digit:D $digit )
+#------------------------------------------------------------------------------
+{
+ my Cell $target = @!grid[$row; $col];
+
+ $target eq $EMPTY-CELL
+ or die qq[ERROR: Cannot add "$digit" at position ($row, $col) which ] ~
+ qq[already contains "$target"];
+
+ @!grid[$row; $col] = $digit;
+}
+
+#------------------------------------------------------------------------------
+method display( --> Str:D )
+#------------------------------------------------------------------------------
+{
+ my Str $display = '';
+
+ for 0 .. 8 -> UInt $row
+ {
+ $display ~= ' [';
+
+ for 0 .. 8 -> UInt $col
+ {
+ $display ~= ' ' ~ @!grid[$row; $col];
+ }
+
+ $display ~= " ]\n";
+ }
+
+ return $display;
+}
+
+#------------------------------------------------------------------------------
+method is-valid( --> Bool:D )
+#------------------------------------------------------------------------------
+{
+ return self!rows-are-valid &&
+ self!cols-are-valid &&
+ self!boxes-are-valid;
+}
+
+#------------------------------------------------------------------------------
+method count-empty-cells( --> UInt:D )
+#------------------------------------------------------------------------------
+{
+ my UInt $count = 0;
+
+ for 0 .. 8 -> UInt $row
+ {
+ for 0 .. 8 -> UInt $col
+ {
+ ++$count if @!grid[$row; $col] eq $EMPTY-CELL;
+ }
+ }
+
+ return $count;
+}