diff options
| author | PerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com> | 2022-02-20 22:27:10 +1000 |
|---|---|---|
| committer | PerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com> | 2022-02-20 22:27:10 +1000 |
| commit | e7baaf61556465b0769869b03422066438dee388 (patch) | |
| tree | 2be7fc352e8d6253efd213ea76ad18f0845ec7f1 | |
| parent | 9c7e8b8c714868fd4e9fe7ed7eff9f141f038069 (diff) | |
| download | perlweeklychallenge-club-e7baaf61556465b0769869b03422066438dee388.tar.gz perlweeklychallenge-club-e7baaf61556465b0769869b03422066438dee388.tar.bz2 perlweeklychallenge-club-e7baaf61556465b0769869b03422066438dee388.zip | |
Perl & Raku solutions to Tasks 1 & 2 of the Weekly Challenge 152
| -rw-r--r-- | challenge-152/athanasius/perl/ch-1.pl | 225 | ||||
| -rw-r--r-- | challenge-152/athanasius/perl/ch-2.pl | 192 | ||||
| -rw-r--r-- | challenge-152/athanasius/raku/ch-1.raku | 187 | ||||
| -rw-r--r-- | challenge-152/athanasius/raku/ch-2.raku | 152 |
4 files changed, 756 insertions, 0 deletions
diff --git a/challenge-152/athanasius/perl/ch-1.pl b/challenge-152/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..9377b26e34 --- /dev/null +++ b/challenge-152/athanasius/perl/ch-1.pl @@ -0,0 +1,225 @@ +#!perl + +############################################################################### +=comment + +Perl Weekly Challenge 152 +========================= + +TASK #1 +------- +*Triangle Sum Path* + +Submitted by: Mohammad S Anwar + +You are given a triangle array. + +Write a script to find the minimum sum path from top to bottom. + +Example 1: + + Input: $triangle = [ [1], [5,3], [2,3,4], [7,1,0,2], [6,4,5,2,8] ] + + 1 + 5 3 + 2 3 4 + 7 1 0 2 + 6 4 5 2 8 + + Output: 8 + + Minimum Sum Path = 1 + 3 + 2 + 0 + 2 => 8 + +Example 2: + + Input: $triangle = [ [5], [2,3], [4,1,5], [0,1,2,3], [7,2,4,1,9] ] + + 5 + 2 3 + 4 1 5 + 0 1 2 3 + 7 2 4 1 9 + + Output: 9 + + Minimum Sum Path = 5 + 2 + 1 + 0 + 1 => 9 + +=cut +############################################################################### + +#--------------------------------------# +# Copyright © 2022 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=comment + +Interface +--------- +1. Following the Examples, I have assumed that array elements are integers. +2. The array elements are entered on the command line as a linear sequence + beginning with the top-most element and continuing down and to the right + until the final (bottom- and right-most) element. For example, the triangu- + lar array: 1 + 2 3 + 4 5 6 is entered as: perl ch-1.pl 1 2 3 4 5 6 +3. If the constant $VERBOSE is set to a true value (the default), the individ- + ual elements comprising the minimum sum path are shown in addition to the + minimum sum. + +Algorithm +--------- +From the Examples, it appears that a "path" is simply a selection of elements +drawn from the triangular array, with exactly one element drawn from each array +level. Hence, the solution is found by taking the smallest element from each +level, and summing these elements. + +The following test is used to determine whether the number of elements entered +on the command line is a triangular number: + + "... an integer _x_ is triangular if and only if 8_x_ + 1 is a square." [1] + +Reference +--------- +[1] https://en.wikipedia.org/wiki/Triangular_number#Triangular_roots_and_tests_ + for_triangular_numbers + +=cut +#============================================================================== + +use strict; +use warnings; +use Const::Fast; +use Regexp::Common qw( number ); + +const my $VERBOSE => 1; +const my $USAGE => +"Usage: + perl $0 [<items> ...] + + [<items> ...] A triangular array of integers, in linear order\n"; + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + $| = 1; + print "\nChallenge 152, Task #1: Triangle Sum Path (Perl)\n\n"; +} + +#============================================================================== +MAIN: +#============================================================================== +{ + my $items = parse_command_line(); + my $triangle = build_triangle( $items ); + + printf "Input: \$triangle = %s\n", sprint_triangle( $triangle ); + + my @min_sum_path; + push @min_sum_path, min( $triangle->[ $_ ]->@* ) for 0 .. $#$triangle; + + my $min_sum = 0; + $min_sum += $_ for @min_sum_path; + + print "Output: $min_sum\n"; + + if ($VERBOSE) + { + printf "\n Minimum Sum Path: %s = %d\n", + join( ' + ', @min_sum_path ), $min_sum; + } +} + +#------------------------------------------------------------------------------ +sub build_triangle +#------------------------------------------------------------------------------ +{ + my ($items) = @_; + my $row_idx = 0; + my $index = 0; + my @triangle; + + while ($index <= $#ARGV) + { + my @row; + push @row, $items->[ $index++ ] for 1 .. $row_idx + 1; + push @triangle, [ @row ]; + + ++$row_idx; + } + + return \@triangle; +} + +#------------------------------------------------------------------------------ +sub sprint_triangle +#------------------------------------------------------------------------------ +{ + my ($triangle) = @_; + + my @rows; + + for my $row (@$triangle) + { + push @rows, '[' . join( ',', @$row ) . ']'; + } + + return '[ ' . join( ', ', @rows ) . ' ]'; +} + +#------------------------------------------------------------------------------ +sub min +#------------------------------------------------------------------------------ +{ + my @array = @_; + my $min = shift @array; + + for (@array) + { + $min = $_ if $_ < $min; + } + + return $min; +} + +#------------------------------------------------------------------------------ +sub parse_command_line +#------------------------------------------------------------------------------ +{ + my $args = scalar @ARGV; + $args > 0 or error( 'No command line arguments' ); + + is_triangular( $args ) + or error( "Array size ($args) is not a triangular number" ); + + for (@ARGV) + { + / ^ $RE{num}{int} $ /x + or error( qq["$_" is not a valid integer] ); + } + + return \@ARGV; +} + +#------------------------------------------------------------------------------ +sub is_triangular +#------------------------------------------------------------------------------ +{ + my ($num) = @_; + my $x = 8 * $num + 1; + my $root = int sqrt $x; + + return $root * $root == $x; +} + +#------------------------------------------------------------------------------ +sub error +#------------------------------------------------------------------------------ +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +############################################################################### diff --git a/challenge-152/athanasius/perl/ch-2.pl b/challenge-152/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..1d54531b75 --- /dev/null +++ b/challenge-152/athanasius/perl/ch-2.pl @@ -0,0 +1,192 @@ +#!perl + +############################################################################### +=comment + +Perl Weekly Challenge 152 +========================= + +TASK #2 +------- +*Rectangle Area* + +Submitted by: Mohammad S Anwar + +You are given coordinates bottom-left and top-right corner of two rectangles in +a 2D plane. + +Write a script to find the total area covered by the two rectangles. + +Example 1: + + Input: Rectangle 1 => (-1,0), (2,2) + Rectangle 2 => (0,-1), (4,4) + + Output: 22 + +Example 2: + + Input: Rectangle 1 => (-3,-1), (1,3) + Rectangle 2 => (-1,-3), (2,2) + + Output: 25 + +=cut +############################################################################### + +#--------------------------------------# +# Copyright © 2022 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=comment + +Interface +--------- +Command-line input is 8 real numbers: the coordinates of the bottom-left and +top-right corners, in (x, y) order, of 2 rectangles. Rectangles must have non- +zero areas. Output is the total area covered by the 2 rectangles. If the +constant $VERBOSE is set to a true value (the default), the output is followed +by an explanation. + +Algorithm +--------- +Consider 2 rectangles: + + +------------a1 + | | + | c-----K--------b1 + | | | | + a0------J-----d | + | | + | | + b0--------------+ + +The overlapping area, rectangle cJdK, has corners (J, K) where: + the x-coordinate of J is the larger of a0x and b0x; + the y-coordinate of J is the larger of a0y and b0y; + the x-coordinate of K is the smaller of a1x and b1x; and + the y-coordinate of K is the smaller of a1y and b1y. + +This holds true for all configurations of the 2 rectangles, provided that K is +above and to the right of J; otherwise, the rectangles do not overlap. + +The combined area is the sum of the areas of the 2 rectangles less the area of +their overlap (if any). + +=cut +#============================================================================== + +use strict; +use warnings; +use Const::Fast; +use Regexp::Common qw( number ); + +const my $VERBOSE => 1; +const my $USAGE => +"Usage: + perl $0 [<c> ...] + + [<c> ...] 8 coordinates (real numbers) describing 2 rectangles\n"; + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + $| = 1; + print "\nChallenge 152, Task #2: Rectangle Area (Perl)\n\n"; +} + +#============================================================================== +MAIN: +#============================================================================== +{ + my ($a0x, $a0y, $a1x, $a1y, $b0x, $b0y, $b1x, $b1y) = parse_command_line(); + + print "Input: Rectangle 1 => ($a0x, $a0y), ($a1x, $a1y)\n"; + print " Rectangle 2 => ($b0x, $b0y), ($b1x, $b1y)\n"; + + my $area1 = ($a1x - $a0x) * ($a1y - $a0y); + my $area2 = ($b1x - $b0x) * ($b1y - $b0y); + + # Common/shared rectangle + + my $c0x = max( $a0x, $b0x ); + my $c0y = max( $a0y, $b0y ); + my $c1x = min( $a1x, $b1x ); + my $c1y = min( $a1y, $b1y ); + my $area3 = ($c1x > $c0x && $c1y > $c0y) ? + ($c1x - $c0x) * ($c1y - $c0y) : 0; + + my $total = $area1 + $area2 - $area3; + + print "\nOutput: $total\n"; + + if ($VERBOSE) + { + print "\nExplanation\n-----------\n", + "The rectangles have areas of $area1 and $area2, ", + "respectively,\n", + "and a shared (i.e., overlapping) area of $area3\n", + "Total area is $area1 + $area2 - $area3 = $total\n"; + } +} + +#------------------------------------------------------------------------------ +sub max +#------------------------------------------------------------------------------ +{ + my ($p, $q) = @_; + + return $p > $q ? $p : $q; +} + +#------------------------------------------------------------------------------ +sub min +#------------------------------------------------------------------------------ +{ + my ($p, $q) = @_; + + return $p < $q ? $p : $q; +} + +#------------------------------------------------------------------------------ +sub parse_command_line +#------------------------------------------------------------------------------ +{ + my $args = scalar @ARGV; + $args == 8 or error( "Expected 8 command line arguments, found $args" ); + + my @c = @ARGV; + + for (@c) + { + / ^ $RE{num}{real} $ /x + or error( qq["$_" is not a valid real number] ); + } + + $c[ 2 ] > $c[ 0 ] + or error( "Rect 1: the right corner is not right of the left corner" ); + + $c[ 3 ] > $c[ 1 ] + or error( "Rect 1: the top corner is not above the bottom corner" ); + + $c[ 6 ] > $c[ 4 ] + or error( "Rect 2: the right corner is not right of the left corner" ); + + $c[ 7 ] > $c[ 5 ] + or error( "Rect 2: the top corner is not above the bottom corner" ); + + return @c; +} + +#------------------------------------------------------------------------------ +sub error +#------------------------------------------------------------------------------ +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +############################################################################### diff --git a/challenge-152/athanasius/raku/ch-1.raku b/challenge-152/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..ed892d456f --- /dev/null +++ b/challenge-152/athanasius/raku/ch-1.raku @@ -0,0 +1,187 @@ +use v6d; + +############################################################################### +=begin comment + +Perl Weekly Challenge 152 +========================= + +TASK #1 +------- +*Triangle Sum Path* + +Submitted by: Mohammad S Anwar + +You are given a triangle array. + +Write a script to find the minimum sum path from top to bottom. + +Example 1: + + Input: $triangle = [ [1], [5,3], [2,3,4], [7,1,0,2], [6,4,5,2,8] ] + + 1 + 5 3 + 2 3 4 + 7 1 0 2 + 6 4 5 2 8 + + Output: 8 + + Minimum Sum Path = 1 + 3 + 2 + 0 + 2 => 8 + +Example 2: + + Input: $triangle = [ [5], [2,3], [4,1,5], [0,1,2,3], [7,2,4,1,9] ] + + 5 + 2 3 + 4 1 5 + 0 1 2 3 + 7 2 4 1 9 + + Output: 9 + + Minimum Sum Path = 5 + 2 + 1 + 0 + 1 => 9 + +=end comment +############################################################################### + +#--------------------------------------# +# Copyright © 2022 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=begin comment + +Interface +--------- +1. Following the Examples, I have assumed that array elements are integers. +2. The array elements are entered on the command line as a linear sequence + beginning with the top-most element and continuing down and to the right + until the final (bottom- and right-most) element. For example, the triangu- + lar array: 1 + 2 3 + 4 5 6 is entered as: raku ch-1.raku 1 2 3 4 5 6 +3. If the constant $VERBOSE is set to True (the default), the individual elem- + ents comprising the minimum sum path are shown in addition to the minimum + sum. + +Algorithm +--------- +From the Examples, it appears that a "path" is simply a selection of elements +drawn from the triangular array, with exactly one element drawn from each array +level. Hence, the solution is found by taking the smallest element from each +level, and summing these elements. + +The following test is used to determine whether the number of elements entered +on the command line is a triangular number: + + "... an integer _x_ is triangular if and only if 8_x_ + 1 is a square." [1] + +Reference +--------- +[1] https://en.wikipedia.org/wiki/Triangular_number#Triangular_roots_and_tests_ + for_triangular_numbers + +=end comment +#============================================================================== + +my Bool constant $VERBOSE = True; + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + "\nChallenge 152, Task #1: Triangle Sum Path (Raku)\n".put; +} + +#============================================================================== +sub MAIN +( + #| A triangular array of integers, in linear order + + *@items where { is-triangular( .elems ) && .all ~~ Int:D } +) +#============================================================================== +{ + my Array[Int] @triangle = build-triangle( @items ); + + "Input: \$triangle = %s\n".printf: sprint-triangle( @triangle ); + + my Int @min-sum-path; + @min-sum-path.push: @triangle[ $_ ].min for 0 .. @triangle.end; + + my Int $min-sum = [+] @min-sum-path; + + "Output: $min-sum".put; + + if $VERBOSE + { + "\n Minimum Sum Path: %s = %d\n".printf: + @min-sum-path.join( ' + ' ), $min-sum; + } +} + +#------------------------------------------------------------------------------ +sub build-triangle( Array:D[Int:D] $items --> Array:D[Array:D[Int:D]] ) +#------------------------------------------------------------------------------ +{ + my Array[Int] @triangle = Array[Array[Int]].new; + my UInt $row-idx = 0; + my UInt $index = 0; + + while $index <= $items.end + { + my Int @row = Array[Int].new; + + for 1 .. $row-idx + 1 + { + @row.push: $items[ $index++ ]; + } + + @triangle.push: @row; + + ++$row-idx; + } + + return @triangle; +} + +#------------------------------------------------------------------------------ +sub sprint-triangle( Array:D[Array:D[Int:D]] $triangle --> Str:D ) +#------------------------------------------------------------------------------ +{ + my Str @rows; + + for @$triangle -> Array[Int] $row + { + @rows.push: '[' ~ @$row.join( ',' ) ~ ']'; + } + + return '[ ' ~ @rows.join( ', ' ) ~ ' ]'; +} + +#------------------------------------------------------------------------------ +sub is-triangular( UInt:D $num --> Bool:D ) +#------------------------------------------------------------------------------ +{ + my UInt $x = 8 * $num + 1; + + my UInt $root = $x.sqrt.floor; + + return $root * $root == $x; +} + +#------------------------------------------------------------------------------ +sub USAGE() +#------------------------------------------------------------------------------ +{ + my Str $usage = $*USAGE; + + $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/; + + $usage.put; +} + +############################################################################## diff --git a/challenge-152/athanasius/raku/ch-2.raku b/challenge-152/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..31b1d95c17 --- /dev/null +++ b/challenge-152/athanasius/raku/ch-2.raku @@ -0,0 +1,152 @@ +use v6d; + +############################################################################### +=begin comment + +Perl Weekly Challenge 152 +========================= + +TASK #2 +------- +*Rectangle Area* + +Submitted by: Mohammad S Anwar + +You are given coordinates bottom-left and top-right corner of two rectangles in +a 2D plane. + +Write a script to find the total area covered by the two rectangles. + +Example 1: + + Input: Rectangle 1 => (-1,0), (2,2) + Rectangle 2 => (0,-1), (4,4) + + Output: 22 + +Example 2: + + Input: Rectangle 1 => (-3,-1), (1,3) + Rectangle 2 => (-1,-3), (2,2) + + Output: 25 + +=end comment +############################################################################### + +#--------------------------------------# +# Copyright © 2022 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=begin comment + +Interface +--------- +Command-line input is 8 real numbers: the coordinates of the bottom-left and +top-right corners, in (x, y) order, of 2 rectangles. Rectangles must have non- +zero areas. Output is the total area covered by the 2 rectangles. If the +constant $VERBOSE is set to True (the default), the output is followed by an +explanation. + +Note re: the Windows command prompt: + +If the first number on the command line is negative, the minus sign will be +interpreted as a command-line flag, resulting in a "Usage" error. To enter a +negative number as the first argument, precede the arguments with a double +hyphen: + + raku ch-2.raku -- -1 0 2 2 0 -1 4 4 + +Algorithm +--------- +Consider 2 rectangles: + + +------------a1 + | | + | c-----K--------b1 + | | | | + a0------J-----d | + | | + | | + b0--------------+ + +The overlapping area, rectangle cJdK, has corners (J, K) where: + the x-coordinate of J is the larger of a0x and b0x; + the y-coordinate of J is the larger of a0y and b0y; + the x-coordinate of K is the smaller of a1x and b1x; and + the y-coordinate of K is the smaller of a1y and b1y. + +This holds true for all configurations of the 2 rectangles, provided that K is +above and to the right of J; otherwise, the rectangles do not overlap. + +The combined area is the sum of the areas of the 2 rectangles less the area of +their overlap (if any). + +=end comment +#============================================================================== + +my Bool constant $VERBOSE = True; + +subset Coord of Real; + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + "\nChallenge 152, Task #2: Rectangle Area (Raku)\n".put; +} + +#============================================================================== +sub MAIN +( + #| 8 coordinates (real numbers) describing 2 rectangles + + *@c where { .elems == 8 && .all ~~ Coord:D && + @c[ 2 ] > @c[ 0 ] && @c[ 3 ] > @c[ 1 ] && + @c[ 6 ] > @c[ 4 ] && @c[ 7 ] > @c[ 5 ] } +) +#============================================================================== +{ + my Coord ($a0x, $a0y, $a1x, $a1y, $b0x, $b0y, $b1x, $b1y) = @c; + + "Input: Rectangle 1 => ($a0x, $a0y), ($a1x, $a1y)".put; + " Rectangle 2 => ($b0x, $b0y), ($b1x, $b1y)".put; + + my Coord $area1 = ($a1x - $a0x) * ($a1y - $a0y); + my Coord $area2 = ($b1x - $b0x) * ($b1y - $b0y); + + # Common/shared rectangle + + my Coord $c0x = ($a0x, $b0x).max; + my Coord $c0y = ($a0y, $b0y).max; + my Coord $c1x = ($a1x, $b1x).min; + my Coord $c1y = ($a1y, $b1y).min; + my Coord $area3 = ($c1x > $c0x && $c1y > $c0y) ?? + ($c1x - $c0x) * ($c1y - $c0y) !! 0; + + my Coord $total = $area1 + $area2 - $area3; + + "\nOutput: $total".put; + + if $VERBOSE + { + "\nExplanation\n-----------".put; + "The rectangles have areas of $area1 and $area2, respectively,".put; + "and a shared (i.e., overlapping) area of $area3".put; + "Total area is $area1 + $area2 - $area3 = $total".put; + } +} + +#------------------------------------------------------------------------------ +sub USAGE() +#------------------------------------------------------------------------------ +{ + my Str $usage = $*USAGE; + + $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/; + + $usage.put; +} + +############################################################################## |
