aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad Sajid Anwar <Mohammad.Anwar@yahoo.com>2024-09-29 19:43:13 +0100
committerGitHub <noreply@github.com>2024-09-29 19:43:13 +0100
commitb1cd8eaccef473671cddffe2a8fd68f8ed0d99ed (patch)
tree24c10a4ca0044dce9f0de8506bb86fe01897612b
parentb49b67ce568b3f8929823965bb905d221895b761 (diff)
parentd7f8cc252462bac699dcf32d80224fe4ffc19e99 (diff)
downloadperlweeklychallenge-club-b1cd8eaccef473671cddffe2a8fd68f8ed0d99ed.tar.gz
perlweeklychallenge-club-b1cd8eaccef473671cddffe2a8fd68f8ed0d99ed.tar.bz2
perlweeklychallenge-club-b1cd8eaccef473671cddffe2a8fd68f8ed0d99ed.zip
Merge pull request #10922 from PerlMonk-Athanasius/branch-for-challenge-288
Perl & Raku solutions to Tasks 1 & 2 for Week 288
-rw-r--r--challenge-288/athanasius/perl/ch-1.pl200
-rw-r--r--challenge-288/athanasius/perl/ch-2.pl425
-rw-r--r--challenge-288/athanasius/raku/ch-1.raku198
-rw-r--r--challenge-288/athanasius/raku/ch-2.raku406
4 files changed, 1229 insertions, 0 deletions
diff --git a/challenge-288/athanasius/perl/ch-1.pl b/challenge-288/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..fd42ec5ca8
--- /dev/null
+++ b/challenge-288/athanasius/perl/ch-1.pl
@@ -0,0 +1,200 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 288
+=========================
+
+TASK #1
+-------
+*Closest Palindrome*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given a string, $str, which is an integer.
+
+Write a script to find out the closest palindrome, not including itself. If
+there are more than one then return the smallest.
+
+ The closest is defined as the absolute difference minimized between two
+ integers.
+
+Example 1
+
+ Input: $str = "123"
+ Output: "121"
+
+Example 2
+
+ Input: $str = "2"
+ Output: "1"
+
+ There are two closest palindrome "1" and "3". Therefore we return the smallest
+ "1".
+
+Example 3
+
+ Input: $str = "1400"
+ Output: "1441"
+
+Example 4
+
+ Input: $str = "1001"
+ Output: "999"
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Assumption
+----------
+The input integer is unsigned (non-negative).
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A string representing an unsigned integer is entered on the command-line.
+
+=cut
+#===============================================================================
+
+use v5.32; # Enables strictures and warnings
+use Const::Fast;
+use Regexp::Common qw( number );
+use Test::More;
+
+const my $USAGE => <<END;
+Usage:
+ Usage:
+ perl $0 <str>
+ perl $0
+
+ <str> A string representing an unsigned integer
+END
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 288, Task #1: Closest Palindrome (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ my $argc = scalar @ARGV;
+
+ if ($argc == 0)
+ {
+ run_tests();
+ }
+ elsif ($argc == 1)
+ {
+ my $str = $ARGV[ 0 ];
+ $str =~ / ^ $RE{num}{int} $ /x
+ or error( qq["$str" is not a valid integer] );
+ $str >= 0 or error( "$str is negative" );
+ $str += 0; # Normalize
+
+ print qq[Input: \$str = "$str"\n];
+
+ my $closest_palindrome = find_closest_palindrome( $str );
+
+ print qq[Output: "$closest_palindrome"\n];
+ }
+ else
+ {
+ error( "Expected 1 or 0 command-line arguments, found $argc" );
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find_closest_palindrome
+#-------------------------------------------------------------------------------
+{
+ my ($int) = @_;
+
+ return 1 if $int == 0;
+
+ my $above = $int + 1;
+ ++$above until is_palindrome( $above );
+
+ my $below = $int - 1;
+ --$below until is_palindrome( $below );
+
+ my $above_diff = $above - $int;
+ my $below_diff = $int - $below;
+
+ return ($above_diff < $below_diff) ? $above : $below;
+}
+
+#-------------------------------------------------------------------------------
+sub is_palindrome
+#-------------------------------------------------------------------------------
+{
+ my ($int) = @_;
+ my @digits = split //, $int;
+
+ for my $i (0 .. int( $#digits / 2 ))
+ {
+ return 0 unless $digits[ $i ] == $digits[ $#digits - $i ];
+ }
+
+ return 1;
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $str, $expected) = split / \| /x, $line;
+
+ for ($test_name, $str, $expected)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my $closest_palindrome = find_closest_palindrome( $str );
+
+ is $closest_palindrome, $expected, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1| 123| 121
+Example 2| 2| 1
+Example 3| 1400| 1441
+Example 4| 1001| 999
+Zero | 0| 1
+One | 1| 0
+1 Million|1000000|999999
+Odd | 12721| 12621
diff --git a/challenge-288/athanasius/perl/ch-2.pl b/challenge-288/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..2d520dd368
--- /dev/null
+++ b/challenge-288/athanasius/perl/ch-2.pl
@@ -0,0 +1,425 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 288
+=========================
+
+TASK #2
+-------
+*Contiguous Block*
+
+Submitted by: Peter Campbell Smith
+
+You are given a rectangular matrix where all the cells contain either x or o.
+
+Write a script to determine the size of the largest contiguous block.
+
+ A contiguous block consists of elements containing the same symbol which
+ share an edge (not just a corner) with other elements in the block, and
+ where there is a path between any two of these elements that crosses only
+ those shared edges.
+
+Example 1
+
+ Input: $matrix = [
+ ['x', 'x', 'x', 'x', 'o'],
+ ['x', 'o', 'o', 'o', 'o'],
+ ['x', 'o', 'o', 'o', 'o'],
+ ['x', 'x', 'x', 'o', 'o'],
+ ]
+ Output: 11
+
+ There is a block of 9 contiguous cells containing 'x'.
+ There is a block of 11 contiguous cells containing 'o'.
+
+Example 2
+
+ Input: $matrix = [
+ ['x', 'x', 'x', 'x', 'x'],
+ ['x', 'o', 'o', 'o', 'o'],
+ ['x', 'x', 'x', 'x', 'o'],
+ ['x', 'o', 'o', 'o', 'o'],
+ ]
+ Output: 11
+
+ There is a block of 11 contiguous cells containing 'x'.
+ There is a block of 9 contiguous cells containing 'o'.
+
+Example 3
+
+ Input: $matrix = [
+ ['x', 'x', 'x', 'o', 'o'],
+ ['o', 'o', 'o', 'x', 'x'],
+ ['o', 'x', 'x', 'o', 'o'],
+ ['o', 'o', 'o', 'x', 'x'],
+ ]
+ Output: 7
+
+ There is a block of 7 contiguous cells containing 'o'.
+ There are two other 2-cell blocks of 'o'.
+ There are three 2-cell blocks of 'x' and one 3-cell.
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A single string is entered on the command-line, e.g., "xxxo xooo xooo xxoo".
+ This string comprises whitespace-separated rows; each row contains only the
+ characters 'x' or 'o'. The matrix represented must be rectangular and not
+ empty.
+3. If $VERBOSE is set to a true value, the output is augmented with a matrix
+ diagram showing the different contiguous blocks found, and identifying the
+ largest.
+
+=cut
+#===============================================================================
+
+use v5.32; # Enables strictures and warnings
+use Const::Fast;
+use Test::More;
+
+const my $VERBOSE => 1;
+const my $USAGE => <<END;
+Usage:
+ perl $0 <matrix-str>
+ perl $0
+
+ <matrix-str> Non-empty matrix string, e.g., "xxxo xooo xooo xxoo"
+END
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 288, Task #2: Contiguous Block (Perl)\n\n";
+}
+
+#===============================================================================
+package Matrix
+#===============================================================================
+{
+ use List::Util qw( max );
+ use Moo;
+ use namespace::clean;
+
+ #---------------------------------------------------------------------------
+ # Attributes
+ #---------------------------------------------------------------------------
+
+ has matrix => ( is => 'ro' );
+ has rows => ( is => 'ro' );
+ has cols => ( is => 'ro' );
+
+ #---------------------------------------------------------------------------
+ around BUILDARGS => sub
+ #---------------------------------------------------------------------------
+ {
+ my ($orig, $class, @args) = @_;
+
+ return { matrix_str => $args[ 0 ] };
+ };
+
+ #---------------------------------------------------------------------------
+ sub BUILD
+ #---------------------------------------------------------------------------
+ {
+ my ($self, $args) = @_;
+
+ my @rows = split / \s+ /x, $args->{ matrix_str };
+
+ scalar @rows > 0 or error( 'Empty matrix' );
+
+ my $cols = length $rows[ 0 ];
+
+ $rows[ 0 ] =~ / ^ [xo]+ $ /x or error( 'Invalid character' );
+
+ my @matrix = [ split //, $rows[ 0 ] ];
+
+ for my $i (1 .. $#rows)
+ {
+ $rows[ $i ] =~ / ^ [xo]+ $ /x or error( 'Invalid character' );
+ length $rows[ $i ] == $cols or error( 'Ragged array' );
+
+ push @matrix, [ split //, $rows[ $i ] ];
+ }
+
+ $self->{ matrix } = \@matrix;
+ $self->{ rows } = scalar @matrix;
+ $self->{ cols } = scalar @{ $matrix[ 0 ] };
+ }
+
+ #---------------------------------------------------------------------------
+ sub find_largest_block_size
+ #---------------------------------------------------------------------------
+ {
+ my ($self) = @_;
+ my $x_num = 0;
+ my $o_num = 0;
+
+ for my $row (0 .. $self->rows - 1)
+ {
+ for my $col (0 .. $self->cols - 1)
+ {
+ my $elem = $self->matrix->[ $row ][ $col ];
+
+ if ($elem eq 'x' || $elem eq 'o')
+ {
+ my $num = ($elem eq 'x') ? $x_num++ : $o_num++;
+
+ $self->_find_block( $elem, "$elem$num", $row, $col );
+ }
+ }
+ }
+
+ return $self->_count_blocks();
+ }
+
+ #---------------------------------------------------------------------------
+ sub _find_block
+ #---------------------------------------------------------------------------
+ {
+ my ($self, $elem, $marker, $row, $col ) = @_;
+
+ $self->matrix->[ $row ][ $col ] = $marker;
+
+ $self->_search_up ( $elem, $marker, $row, $col );
+ $self->_search_left ( $elem, $marker, $row, $col );
+ $self->_search_right( $elem, $marker, $row, $col );
+ $self->_search_down ( $elem, $marker, $row, $col );
+ }
+
+ #---------------------------------------------------------------------------
+ sub _search_up
+ #---------------------------------------------------------------------------
+ {
+ my ($self, $elem, $marker, $row, $col ) = @_;
+
+ if ($row > 0)
+ {
+ my $up_row = $row - 1;
+
+ if ($self->matrix->[ $up_row ][ $col ] eq $elem)
+ {
+ $self->matrix->[ $up_row ][ $col ] = $marker;
+
+ $self->_find_block( $elem, $marker, $up_row, $col );
+ }
+ }
+ }
+
+ #---------------------------------------------------------------------------
+ sub _search_left
+ #---------------------------------------------------------------------------
+ {
+ my ($self, $elem, $marker, $row, $col ) = @_;
+
+ if ($col > 0)
+ {
+ my $left_col = $col - 1;
+
+ if ($self->matrix->[ $row ][ $left_col ] eq $elem)
+ {
+ $self->matrix->[ $row ][ $left_col ] = $marker;
+
+ $self->_find_block( $elem, $marker, $row, $left_col );
+ }
+ }
+ }
+
+ #---------------------------------------------------------------------------
+ sub _search_right
+ #---------------------------------------------------------------------------
+ {
+ my ($self, $elem, $marker, $row, $col ) = @_;
+
+ if ($col < $self->cols - 1)
+ {
+ my $right_col = $col + 1;
+
+ if ($self->matrix->[ $row ][ $right_col ] eq $elem)
+ {
+ $self->matrix->[ $row ][ $right_col ] = $marker;
+
+ $self->_find_block( $elem, $marker, $row, $right_col );
+ }
+ }
+ }
+
+ #---------------------------------------------------------------------------
+ sub _search_down
+ #---------------------------------------------------------------------------
+ {
+ my ($self, $elem, $marker, $row, $col ) = @_;
+
+ if ($row < $self->rows - 1)
+ {
+ my $down_row = $row + 1;
+
+ if ($self->matrix->[ $down_row ][ $col ] eq $elem)
+ {
+ $self->matrix->[ $down_row ][ $col ] = $marker;
+
+ $self->_find_block( $elem, $marker, $down_row, $col );
+ }
+ }
+ }
+
+ #---------------------------------------------------------------------------
+ sub _count_blocks
+ #---------------------------------------------------------------------------
+ {
+ my ($self) = @_;
+ my %block_counts;
+
+ for my $row (0 .. $self->rows - 1)
+ {
+ for my $col (0 .. $self->cols - 1)
+ {
+ ++$block_counts{ $self->matrix->[ $row ][ $col ] };
+ }
+ }
+
+ my $max_size = max values %block_counts;
+ my @max_blocks;
+
+ while (my ($key, $value) = each %block_counts)
+ {
+ push @max_blocks, $key if $value == $max_size;
+ }
+
+ @max_blocks = sort {
+ substr( $a, 0, 1 ) cmp substr( $b, 0, 1 )
+ ||
+ substr( $a, 1 ) <=> substr( $b, 1 )
+
+ } @max_blocks;
+
+ return ($max_size, \@max_blocks);
+ }
+
+ #---------------------------------------------------------------------------
+ sub print # Adapted from print_matrix() in ch-2.pl for Week 266
+ #---------------------------------------------------------------------------
+ {
+ my ($self, $prefix) = @_;
+ my $tab = ' ' x length $prefix;
+ my @width = (1) x scalar @{ $self->matrix->[ 0 ] };
+
+ for my $row (@{ $self->matrix })
+ {
+ for my $i (0 .. $#$row)
+ {
+ my $w = length $row->[ $i ];
+
+ $width[ $i ] = $w if $w > $width[ $i ];
+ }
+ }
+
+ print "$prefix\[\n";
+
+ for my $row (@{ $self->matrix })
+ {
+ my @row_str;
+
+ for my $i (0 .. $#$row)
+ {
+ push @row_str, sprintf '%*s', $width[ $i ], $row->[ $i ];
+ }
+
+ printf "%s [%s]\n", $tab, join ', ', map { "'$_'" } @row_str;
+ }
+
+ print "$tab]\n";
+ }
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ my $argc = scalar @ARGV;
+
+ if ($argc == 0)
+ {
+ run_tests();
+ }
+ elsif ($argc == 1)
+ {
+ my $matrix = Matrix->new( $ARGV[ 0 ] );
+
+ $matrix->print( 'Input: $matrix = ', $matrix );
+
+ my ($max_size, $max_blocks) = $matrix->find_largest_block_size;
+
+ print "Output: $max_size\n";
+
+ if ($VERBOSE)
+ {
+ print "\n";
+
+ $matrix->print( 'Blocks: ' );
+
+ printf "Largest block%s: %s\n", scalar @$max_blocks > 1 ? 's' : '',
+ join ', ', @$max_blocks;
+ }
+ }
+ else
+ {
+ error( "Expected 1 or 0 command-line arguments, found $argc" );
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $matrix_str, $expected) = split / \| /x, $line;
+
+ for ($test_name, $matrix_str, $expected)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my $matrix = Matrix->new( $matrix_str );
+ my ($max_size) = $matrix->find_largest_block_size;
+
+ is $max_size, $expected, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1|xxxxo xoooo xoooo xxxoo|11
+Example 2|xxxxx xoooo xxxxo xoooo|11
+Example 3|xxxoo oooxx oxxoo oooxx| 7
diff --git a/challenge-288/athanasius/raku/ch-1.raku b/challenge-288/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..9f703ba951
--- /dev/null
+++ b/challenge-288/athanasius/raku/ch-1.raku
@@ -0,0 +1,198 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 288
+=========================
+
+TASK #1
+-------
+*Closest Palindrome*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given a string, $str, which is an integer.
+
+Write a script to find out the closest palindrome, not including itself. If
+there are more than one then return the smallest.
+
+ The closest is defined as the absolute difference minimized between two
+ integers.
+
+Example 1
+
+ Input: $str = "123"
+ Output: "121"
+
+Example 2
+
+ Input: $str = "2"
+ Output: "1"
+
+ There are two closest palindrome "1" and "3". Therefore we return the smallest
+ "1".
+
+Example 3
+
+ Input: $str = "1400"
+ Output: "1441"
+
+Example 4
+
+ Input: $str = "1001"
+ Output: "999"
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Assumption
+----------
+The input integer is unsigned (non-negative).
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A string representing an unsigned integer is entered on the command-line.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 288, Task #1: Closest Palindrome (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ Str:D $str #= A string representing an unsigned integer
+)
+#===============================================================================
+{
+ $str ~~ Int:D or error( qq["$str" is not a valid integer] );
+
+ my Int $int = +$str;
+
+ $int >= 0 or error( "$int is negative" );
+
+ print qq[Input: \$str = "$int"\n];
+
+ my UInt $closest-palindrome = find-closest-palindrome( $int );
+
+ print qq[Output: "$closest-palindrome"\n];
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub find-closest-palindrome( UInt:D $int --> UInt:D )
+#-------------------------------------------------------------------------------
+{
+ return 1 if $int == 0;
+
+ my UInt $above = $int + 1;
+ ++$above until is-palindrome( $above );
+
+ my UInt $below = $int - 1;
+ --$below until is-palindrome( $below );
+
+ my UInt $above-diff = $above - $int;
+ my UInt $below-diff = $int - $below;
+
+ return ($above-diff < $below-diff) ?? $above !! $below;
+}
+
+#-------------------------------------------------------------------------------
+sub is-palindrome( UInt:D $int --> Bool:D )
+#-------------------------------------------------------------------------------
+{
+ my UInt @digits = $int.split( '', :skip-empty ).map: { .Int };
+
+ for 0 .. (@digits.end / 2).floor -> UInt $i
+ {
+ return False unless @digits[ $i ] == @digits[ @digits.end - $i ];
+ }
+
+ return True;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $str, $expected) = $line.split: / \| /;
+
+ for $test-name, $str, $expected
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my UInt $closest-palindrome = find-closest-palindrome( $str.Int );
+
+ is $closest-palindrome, $expected.Int, $test-name;
+ }
+
+ done-testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error( Str:D $message )
+#-------------------------------------------------------------------------------
+{
+ "ERROR: $message".put;
+
+ USAGE();
+
+ exit 0;
+}
+
+#-------------------------------------------------------------------------------
+sub USAGE()
+#-------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s:g/ ($*PROGRAM-NAME) /raku $0/;
+
+ $usage.put;
+}
+
+#-------------------------------------------------------------------------------
+sub test-data( --> Str:D )
+#-------------------------------------------------------------------------------
+{
+ return q:to/END/;
+ Example 1| 123| 121
+ Example 2| 2| 1
+ Example 3| 1400| 1441
+ Example 4| 1001| 999
+ Zero | 0| 1
+ One | 1| 0
+ 1 Million|1000000|999999
+ Odd | 12721| 12621
+ END
+}
+
+################################################################################
diff --git a/challenge-288/athanasius/raku/ch-2.raku b/challenge-288/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..49a4aff10d
--- /dev/null
+++ b/challenge-288/athanasius/raku/ch-2.raku
@@ -0,0 +1,406 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 288
+=========================
+
+TASK #2
+-------
+*Contiguous Block*
+
+Submitted by: Peter Campbell Smith
+
+You are given a rectangular matrix where all the cells contain either x or o.
+
+Write a script to determine the size of the largest contiguous block.
+
+ A contiguous block consists of elements containing the same symbol which
+ share an edge (not just a corner) with other elements in the block, and
+ where there is a path between any two of these elements that crosses only
+ those shared edges.
+
+Example 1
+
+ Input: $matrix = [
+ ['x', 'x', 'x', 'x', 'o'],
+ ['x', 'o', 'o', 'o', 'o'],
+ ['x', 'o', 'o', 'o', 'o'],
+ ['x', 'x', 'x', 'o', 'o'],
+ ]
+ Output: 11
+
+ There is a block of 9 contiguous cells containing 'x'.
+ There is a block of 11 contiguous cells containing 'o'.
+
+Example 2
+
+ Input: $matrix = [
+ ['x', 'x', 'x', 'x', 'x'],
+ ['x', 'o', 'o', 'o', 'o'],
+ ['x', 'x', 'x', 'x', 'o'],
+ ['x', 'o', 'o', 'o', 'o'],
+ ]
+ Output: 11
+
+ There is a block of 11 contiguous cells containing 'x'.
+ There is a block of 9 contiguous cells containing 'o'.
+
+Example 3
+
+ Input: $matrix = [
+ ['x', 'x', 'x', 'o', 'o'],
+ ['o', 'o', 'o', 'x', 'x'],
+ ['o', 'x', 'x', 'o', 'o'],
+ ['o', 'o', 'o', 'x', 'x'],
+ ]
+ Output: 7
+
+ There is a block of 7 contiguous cells containing 'o'.
+ There are two other 2-cell blocks of 'o'.
+ There are three 2-cell blocks of 'x' and one 3-cell.
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2024 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A single string is entered on the command-line, e.g., "xxxo xooo xooo xxoo".
+ This string comprises whitespace-separated rows; each row contains only the
+ characters 'x' or 'o'. The matrix represented must be rectangular and not
+ empty.
+3. If VERBOSE is set to True, the output is augmented with a matrix diagram
+ showing the different contiguous blocks found, and identifying the largest.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+my Bool constant VERBOSE = True;
+
+subset Result of List where (UInt, Array[Str]);
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 288, Task #2: Contiguous Block (Raku)\n".put;
+}
+
+#===============================================================================
+class Matrix
+#===============================================================================
+{
+ subset Elem of Str where * ~~ 'x' | 'o';
+ subset MatrixT of Array where * ~~ Array[Array[Str]];
+
+ #---------------------------------------------------------------------------
+ # Attributes
+ #---------------------------------------------------------------------------
+
+ has MatrixT $!matrix is built;
+ has UInt $!rows is built;
+ has UInt $!cols is built;
+ has Str $!matrix-str is built;
+
+ #---------------------------------------------------------------------------
+ method new( Str:D $matrix-str )
+ #---------------------------------------------------------------------------
+ {
+ return self.bless( :$matrix-str );
+ }
+
+ #---------------------------------------------------------------------------
+ submethod TWEAK()
+ #---------------------------------------------------------------------------
+ {
+ my Str @rows = $!matrix-str.split: / \s+ /, :skip-empty;
+ @rows.elems > 0 or error( 'Empty matrix' );
+ my UInt $cols = @rows[ 0 ].chars;
+
+ @rows[ 0 ] ~~ / ^ <[ x o ]>+ $ / or error( 'Invalid character' );
+
+ $!matrix = Array[Array[Str]].new;
+
+ $!matrix.push: Array[Str].new: @rows[ 0 ].split: '', :skip-empty;
+
+ for 1 .. @rows.end -> UInt $i
+ {
+ @rows[ $i ] ~~ / ^ <[ x o ]>+ $ / or error( 'Invalid character' );
+ @rows[ $i ].chars == $cols or error( 'Ragged array' );
+
+ $!matrix.push: Array[Str].new: @rows[ $i ].split: '', :skip-empty;
+ }
+
+ $!rows = $!matrix.elems;
+ $!cols = $!matrix[ 0 ].elems;
+ }
+
+ #---------------------------------------------------------------------------
+ method find-largest-block-size( --> Result:D )
+ #---------------------------------------------------------------------------
+ {
+ my UInt $x-num = 0;
+ my UInt $o-num = 0;
+
+ for 0 .. $!rows - 1 -> UInt $row
+ {
+ for 0 .. $!cols - 1 -> UInt $col
+ {
+ my Str $elem = $!matrix[ $row; $col ];
+
+ if $elem ~~ Elem:D
+ {
+ my UInt $num = ($elem eq 'x') ?? $x-num++ !! $o-num++;
+
+ self!find-block( $elem, "$elem$num", $row, $col );
+ }
+ }
+ }
+
+ return self!count-blocks;
+ }
+
+ #---------------------------------------------------------------------------
+ method !find-block( Elem:D $elem, Str:D $marker, Int:D $row, Int:D $col )
+ #---------------------------------------------------------------------------
+ {
+ $!matrix[ $row; $col ] = $marker;
+
+ self!search-up( $elem, $marker, $row, $col );
+ self!search-left( $elem, $marker, $row, $col );
+ self!search-right( $elem, $marker, $row, $col );
+ self!search-down( $elem, $marker, $row, $col );
+ }
+
+ #---------------------------------------------------------------------------
+ method !search-up( Elem:D $elem, Str:D $marker, Int:D $row, Int:D $col )
+ #---------------------------------------------------------------------------
+ {
+ if $row > 0
+ {
+ my Int $up-row = $row - 1;
+
+ if $!matrix[ $up-row; $col ] eq $elem
+ {
+ $!matrix[ $up-row; $col ] = $marker;
+
+ self!find-block( $elem, $marker, $up-row, $col );
+ }
+ }
+ }
+
+ #---------------------------------------------------------------------------
+ method !search-left( Elem:D $elem, Str:D $marker, Int:D $row, Int:D $col )
+ #---------------------------------------------------------------------------
+ {
+ if $col > 0
+ {
+ my Int $left-col = $col - 1;
+
+ if $!matrix[ $row; $left-col ] eq $elem
+ {
+ $!matrix[ $row; $left-col ] = $marker;
+
+ self!find-block( $elem, $marker, $row, $left-col );
+ }
+ }
+ }
+
+ #---------------------------------------------------------------------------
+ method !search-right( Elem:D $elem, Str:D $marker, Int:D $row, Int:D $col )
+ #---------------------------------------------------------------------------
+ {
+ if $col < $!cols - 1
+ {
+ my Int $right-col = $col + 1;
+
+ if $!matrix[ $row; $right-col ] eq $elem
+ {
+ $!matrix[ $row; $right-col ] = $marker;
+
+ self!find-block( $elem, $marker, $row, $right-col );
+ }
+ }
+ }
+
+ #---------------------------------------------------------------------------
+ method !search-down( Elem:D $elem, Str:D $marker, Int:D $row, Int:D $col )