aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2025-08-28 23:44:24 +1000
committerPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2025-08-28 23:44:24 +1000
commitb44326538e5cc10135aeea3d146e7aac2747cd65 (patch)
tree7feed5988547f7ae4a593ea1d9e939a8115610d2
parent879e6779474591e9050c4093847eb45a8fae96e0 (diff)
downloadperlweeklychallenge-club-b44326538e5cc10135aeea3d146e7aac2747cd65.tar.gz
perlweeklychallenge-club-b44326538e5cc10135aeea3d146e7aac2747cd65.tar.bz2
perlweeklychallenge-club-b44326538e5cc10135aeea3d146e7aac2747cd65.zip
Perl & Raku solutions to Tasks 1 & 2 for Week 336
-rw-r--r--challenge-336/athanasius/perl/ch-1.pl249
-rw-r--r--challenge-336/athanasius/perl/ch-2.pl256
-rw-r--r--challenge-336/athanasius/raku/ch-1.raku280
-rw-r--r--challenge-336/athanasius/raku/ch-2.raku252
4 files changed, 1037 insertions, 0 deletions
diff --git a/challenge-336/athanasius/perl/ch-1.pl b/challenge-336/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..de49ab58bc
--- /dev/null
+++ b/challenge-336/athanasius/perl/ch-1.pl
@@ -0,0 +1,249 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 336
+=========================
+
+TASK #1
+-------
+*Equal Group*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of integers.
+
+Write a script to return true if the given array can be divided into one or more
+groups: each group must be of the same size as the others, with at least two
+members, and with all members having the same value.
+
+Example 1
+
+ Input: @ints = (1,1,2,2,2,2)
+ Output: true
+
+ Groups: (1,1), (2,2), (2,2)
+
+Example 2
+
+ Input: @ints = (1,1,1,2,2,2,3,3)
+ Output: false
+
+ Groups: (1,1,1), (2,2,2), (3,3)
+
+Example 3
+
+ Input: @ints = (5,5,5,5,5,5,7,7,7,7,7,7)
+ Output: true
+
+ Groups: (5,5,5,5,5,5), (7,7,7,7,7,7)
+
+Example 4
+
+ Input: @ints = (1,2,3,4)
+ Output: false
+
+Example 5
+
+ Input: @ints = (8,8,9,9,10,10,11,11)
+ Output: true
+
+ Groups: (8,8), (9,9), (10,10), (11,11)
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2025 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A non-empty list of integers is entered on the command-line.
+
+Algorithm
+---------
+The integers in the input list are merely markers -- they could just as well be
+characters or words. The order in which they appear is also irrelevant. What
+matters is the frequency with which they appear in the list:
+
+(1) If any list element occurs only once, no solution is possible.
+
+(2) Otherwise, a solution is possible if and only if the frequencies share a
+ common divisor greater than 1. This gives rise to the following algorithm:
+
+ 1. For each element in the list, find its frequency, f.
+ 2. For each f, find the bag (multiset) containing the prime factors of f.
+ 3. Find X, the intersection of all bags found in step 2.
+ 4. If X is empty, no solution is possible.
+ 5. Otherwise, find p, the product of all the members of X.
+ 6. A solution may now be generated by partitioning the elements of the
+ original list into same-element bags, each of size p.
+
+=cut
+#===============================================================================
+
+use v5.38.2; # Enables strictures
+use warnings;
+use Const::Fast;
+use Math::Prime::Util qw( factor );
+use Regexp::Common qw( number );
+use Set::Bag;
+use Test::More;
+
+const my $USAGE => <<END;
+Usage:
+ perl $0 [<ints> ...]
+ perl $0
+
+ [<ints> ...] A non-empty list of integers
+END
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 336, Task #1: Equal Group (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ if (scalar @ARGV == 0)
+ {
+ run_tests();
+ }
+ else
+ {
+ my @ints = @ARGV;
+
+ for (@ints)
+ {
+ / ^ $RE{num}{int} $ /x or error( qq["$_" is not a valid integer] );
+ }
+
+ printf "Input: \@ints = (%s)\n", join ',', @ints;
+
+ my ($result, $groups) = equal_groups( \@ints );
+
+ printf "Output: %s\n", $result ? 'true' : 'false';
+
+ if ($result)
+ {
+ printf "\nGroups: %s\n",
+ join ', ', map { '(' . join( ',', @$_ ) . ')' } @$groups;
+ }
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub equal_groups
+#-------------------------------------------------------------------------------
+{
+ my ($ints) = @_;
+ my $result = '';
+ my $groups = [];
+ my %count;
+ ++$count{ $_ } for @$ints;
+
+ my @counts = sort { $a <=> $b } values %count;
+
+ if ($counts[ 0 ] > 1) # If the smallest count is 1, no solution is possible
+ {
+ my $common_prime_factors = Set::Bag->new;
+ $common_prime_factors->insert( $_ => 1 ) for factor( $counts[ 0 ] );
+
+ for my $i (1 .. $#counts)
+ {
+ my $prime_factors = Set::Bag->new;
+ $prime_factors ->insert( $_ => 1 ) for factor( $counts[ $i ] );
+
+ $common_prime_factors &= $prime_factors; # Keep the intersection
+ }
+
+ my @factors = $common_prime_factors->elements;
+
+ if (scalar @factors > 0)
+ {
+ $result = 1;
+ $groups = make_groups( \%count, \@factors );
+ }
+ }
+
+ return ($result, $groups);
+}
+
+#-------------------------------------------------------------------------------
+sub make_groups
+#-------------------------------------------------------------------------------
+{
+ my ($count, $factors) = @_;
+ my @groups;
+ my $product = 1;
+ $product *= $_ for @$factors;
+
+ for my $n (sort { $a <=> $b } keys %$count)
+ {
+ my $multiplier = $count->{ $n } / $product;
+
+ push @groups, [ ($n) x $product ] for 1 .. $multiplier;
+ }
+
+ return \@groups;
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $ints_str, $expected_str, $groups_str) =
+ split / \| /x, $line;
+
+ for ($test_name, $ints_str, $expected_str, $groups_str)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my @ints = split / \s+ /x, $ints_str;
+ my ($result, $groups) = equal_groups( \@ints );
+ my $expected = $expected_str eq 'true';
+ my @group_str = split / \; \s* /x, $groups_str;
+ my @exp_groups = map { [ split / \s+ /x, $_ ] } @group_str;
+
+ is $result, $expected, "$test_name: Result";
+ is_deeply $groups, \@exp_groups, "$test_name: Groups" if $result;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1|1 1 2 2 2 2 |true |1 1; 2 2; 2 2
+Example 2|1 1 1 2 2 2 3 3 |false|1 1 1; 2 2 2; 3 3
+Example 3|5 5 5 5 5 5 7 7 7 7 7 7|true |5 5 5 5 5 5; 7 7 7 7 7 7
+Example 4|1 2 3 4 |false|
+Example 5|8 8 9 9 10 10 11 11 |true |8 8; 9 9; 10 10; 11 11
diff --git a/challenge-336/athanasius/perl/ch-2.pl b/challenge-336/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..a834ebfead
--- /dev/null
+++ b/challenge-336/athanasius/perl/ch-2.pl
@@ -0,0 +1,256 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 336
+=========================
+
+TASK #2
+-------
+*Final Score*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of scores by a team.
+
+Write a script to find the total score of the given team. The score can be any
+integer, +, C or D. The + adds the sum of previous two scores. The score C
+invalidates the previous score. The score D will double the previous score.
+
+Example 1
+
+ Input: @scores = ("5","2","C","D","+")
+ Output: 30
+
+ Round 1: 5
+ Round 2: 5 + 2
+ Round 3: 5 (invalidate the previous score 2)
+ Round 4: 5 + 10 (double the previous score 5)
+ Round 5: 5 + 10 + 15 (sum of previous two scores)
+
+ Total Scores: 30
+
+Example 2
+
+ Input: @scores = ("5","-2","4","C","D","9","+","+")
+ Output: 27
+
+ Round 1: 5
+ Round 2: 5 + (-2)
+ Round 3: 5 + (-2) + 4
+ Round 4: 5 + (-2) (invalidate the previous score 4)
+ Round 5: 5 + (-2) + (-4) (double the previous score -2)
+ Round 6: 5 + (-2) + (-4) + 9
+ Round 7: 5 + (-2) + (-4) + 9 + 5 (sum of previous two scores)
+ Round 8: 5 + (-2) + (-4) + 9 + 5 + 14 (sum of previous two scores)
+
+ Total Scores: 27
+
+Example 3
+
+ Input: @scores = ("7","D","D","C","+","3")
+ Output: 45
+
+ Round 1: 7
+ Round 2: 7 + 14 (double the previous score 7)
+ Round 3: 7 + 14 + 28 (double the previous score 14)
+ Round 4: 7 + 14 (invalidate the previous score 28)
+ Round 5: 7 + 14 + 21 (sum of previous two scores)
+ Round 6: 7 + 14 + 21 + 3
+
+ Total Scores: 45
+
+Example 4
+
+ Input: @scores = ("-5","-10","+","D","C","+")
+ Output: -55
+
+ Round 1: (-5)
+ Round 2: (-5) + (-10)
+ Round 3: (-5) + (-10) + (-15) (sum of previous two scores)
+ Round 4: (-5) + (-10) + (-15) + (-30) (double the previous score -15)
+ Round 5: (-5) + (-10) + (-15) (invalidate the previous score -30)
+ Round 6: (-5) + (-10) + (-15) + (-25) (sum of previous two scores)
+
+ Total Scores: -55
+
+Example 5
+
+ Input: @scores = ("3","6","+","D","C","8","+","D","-2","C","+")
+ Output: 128
+
+ Round 1: 3
+ Round 2: 3 + 6
+ Round 3: 3 + 6 + 9 (sum of previous two scores)
+ Round 4: 3 + 6 + 9 + 18 (double the previous score 9)
+ Round 5: 3 + 6 + 9 (invalidate the previous score 18)
+ Round 6: 3 + 6 + 9 + 8
+ Round 7: 3 + 6 + 9 + 8 + 17 (sum of previous two scores)
+ Round 8: 3 + 6 + 9 + 8 + 17 + 34 (double the previous score 17)
+ Round 9: 3 + 6 + 9 + 8 + 17 + 34 + (-2)
+ Round 10: 3 + 6 + 9 + 8 + 17 + 34 (invalidate the previous score -2)
+ Round 11: 3 + 6 + 9 + 8 + 17 + 34 + 51 (sum of previous two scores)
+
+ Total Scores: 128
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2025 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A non-empty list of scores is entered on the command-line.
+
+Assumptions
+-----------
+During score evaluation, it is an error if a "+" score is not preceded by at
+least two other (integer) scores. Likewise, it is an error if a "C" or a "D"
+score is not preceded by at least one other (integer) score.
+
+=cut
+#===============================================================================
+
+use v5.38.2; # Enables strictures
+use warnings;
+use Const::Fast;
+use List::Util qw( sum0 );
+use Regexp::Common qw( number );
+use Test::More;
+use Try::Tiny;
+
+const my $USAGE => <<END;
+Usage:
+ perl $0 [<scores> ...]
+ perl $0
+
+ [<scores> ...] A non-empty list of scores (integers, '+', 'C', 'D')
+END
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 336, Task #2: Final Score (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ if (scalar @ARGV == 0)
+ {
+ run_tests();
+ }
+ else
+ {
+ my @scores = @ARGV;
+
+ for (@scores)
+ {
+ / ^ $RE{num}{int} | [+CD] $ /x
+ or error( qq["$_" is not a valid score] );
+ }
+
+ printf "Input: \@scores = (%s)\n", join ',', map { qq["$_"] } @scores;
+
+ try
+ {
+ my $score = find_final_score( \@scores );
+
+ print "Output: $score\n";
+ }
+ catch
+ {
+ print "\nERROR: $_";
+ };
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find_final_score
+#-------------------------------------------------------------------------------
+{
+ my ($scores) = @_;
+ my @queue;
+
+ for my $score (@$scores)
+ {
+ my $q_len = scalar @queue;
+
+ if ($score eq '+')
+ {
+ die qq[Illegal "+" score placement\n] if $q_len < 2;
+ push @queue, $queue[ -2 ] + $queue[ -1 ];
+ }
+ elsif ($score eq 'C')
+ {
+ die qq[Illegal "C" score placement\n] if $q_len < 1;
+ pop @queue;
+ }
+ elsif ($score eq 'D')
+ {
+ die qq[Illegal "D" score placement\n] if $q_len < 1;
+ push @queue, 2 * $queue[ -1 ];
+ }
+ else # $score must be an integer
+ {
+ push @queue, $score;
+ }
+ }
+
+ return sum0 @queue;
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $scores_str, $expected) = split / \| /x, $line;
+
+ for ($test_name, $scores_str, $expected)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my @scores = split / \s+ /x, $scores_str;
+ my $score = find_final_score( \@scores );
+
+ is $score, $expected, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1| 5 2 C D + | 30
+Example 2| 5 -2 4 C D 9 + + | 27
+Example 3| 7 D D C + 3 | 45
+Example 4|-5 -10 + D C + |-55
+Example 5| 3 6 + D C 8 + D -2 C +|128
diff --git a/challenge-336/athanasius/raku/ch-1.raku b/challenge-336/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..7d2b43affe
--- /dev/null
+++ b/challenge-336/athanasius/raku/ch-1.raku
@@ -0,0 +1,280 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 336
+=========================
+
+TASK #1
+-------
+*Equal Group*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of integers.
+
+Write a script to return true if the given array can be divided into one or more
+groups: each group must be of the same size as the others, with at least two
+members, and with all members having the same value.
+
+Example 1
+
+ Input: @ints = (1,1,2,2,2,2)
+ Output: true
+
+ Groups: (1,1), (2,2), (2,2)
+
+Example 2
+
+ Input: @ints = (1,1,1,2,2,2,3,3)
+ Output: false
+
+ Groups: (1,1,1), (2,2,2), (3,3)
+
+Example 3
+
+ Input: @ints = (5,5,5,5,5,5,7,7,7,7,7,7)
+ Output: true
+
+ Groups: (5,5,5,5,5,5), (7,7,7,7,7,7)
+
+Example 4
+
+ Input: @ints = (1,2,3,4)
+ Output: false
+
+Example 5
+
+ Input: @ints = (8,8,9,9,10,10,11,11)
+ Output: true
+
+ Groups: (8,8), (9,9), (10,10), (11,11)
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2025 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A non-empty list of integers is entered on the command-line.
+3. If the first integer is negative, it must be preceded by "--" to indicate
+ that it is not a command-line flag.
+
+Algorithm
+---------
+The integers in the input list are merely markers -- they could just as well be
+characters or words. The order in which they appear is also irrelevant. What
+matters is the frequency with which they appear in the list:
+
+(1) If any list element occurs only once, no solution is possible.
+
+(2) Otherwise, a solution is possible if and only if the frequencies share a
+ common divisor greater than 1. This gives rise to the following algorithm:
+
+ 1. For each element in the list, find its frequency, f.
+ 2. For each f, find the bag (multiset) containing the prime factors of f.
+ 3. Find X, the intersection of all bags found in step 2.
+ 4. If X is empty, no solution is possible.
+ 5. Otherwise, find p, the product of all the members of X.
+ 6. A solution may now be generated by partitioning the elements of the
+ original list into same-element bags, each of size p.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+subset Result of List where (Bool, Array[Array[Int]]);
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 336, Task #1: Equal Group (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ #| A non-empty list of integers
+
+ *@ints where { .elems > 0 && .all ~~ Int:D }
+)
+#===============================================================================
+{
+ "Input: \@ints = (%s)\n".printf: @ints.join: ',';
+
+ my Result $result = equal-groups( @ints );
+
+ "Output: %s\n".printf: $result[ 0 ] ?? 'true' !! 'false';
+
+ if $result[ 0 ]
+ {
+ "\nGroups: %s\n".printf:
+ $result[ 1 ].map( { '(' ~ .join( ',' ) ~ ')' } ).join: ', ';
+ }
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub equal-groups( List:D[Int:D] $ints --> Result:D )
+#-------------------------------------------------------------------------------
+{
+ my Array[Int] @groups;
+ my Bool $result = False;
+ my UInt %count{Int};
+ ++%count{ +$_ } for @$ints;
+ my UInt @counts = %count.values.sort;
+
+ if @counts[ 0 ] > 1 # If the smallest count is 1, no solution is possible
+ {
+ my BagHash[Int] $common-factors = BagHash[Int].new;
+ $common-factors.add: $_ for factor( @counts[ 0 ] );
+
+ for 1 .. @counts.end -> UInt $i
+ {
+ my BagHash[Int] $factors = BagHash[Int].new;
+ $factors.add: $_ for factor( @counts[ $i ] );
+
+ # Keep only the intersection
+ $common-factors = BagHash[Int].new-from-pairs:
+ ($common-factors ∩ $factors).list;
+ }
+
+ my UInt @factors = $common-factors.kxxv.sort;
+
+ if @factors.elems > 0
+ {
+ $result = True;
+ @groups = make-groups( %count, @factors );
+ }
+ }
+
+ return $result, @groups;
+}
+
+#-------------------------------------------------------------------------------
+sub make-groups
+(
+ Hash:D $count,
+ List:D[UInt:D] $factors
+--> Array:D[Array:D[Int:D]]
+)
+#-------------------------------------------------------------------------------
+{
+ my Array[Int] @groups;
+
+ my UInt $product = 1;
+ $product *= $_ for @$factors;
+
+ for $count.keys.sort -> Int $n
+ {
+ my UInt $multiplier = $count{ $n } div $product;
+
+ push @groups, Array[Int].new: $n xx $product for 1 .. $multiplier;
+ }
+
+ return @groups;
+}
+
+#-------------------------------------------------------------------------------
+sub factor( UInt:D $int --> List:D[UInt:D] )
+#-------------------------------------------------------------------------------
+{
+ # Code adapted from "Finding prime factors using Raku" by Andrew Shitov,
+ # https://andrewshitov.com/2019/09/09/finding-prime-factors-using-perl-6/
+
+ my Rat $n = $int.Rat;
+ my UInt @primes = grep { .is-prime }, 1 .. *;
+ my UInt $pos = 0;
+ my UInt @factors;
+
+ while $n > 1
+ {
+ my UInt $factor = @primes[ $pos++ ];
+
+ next unless $n %% $factor;
+
+ $pos = 0;
+ $n /= $factor;
+
+ @factors.push: $factor;
+ }
+
+ return @factors;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $ints-str, $expected-str, $groups-str) =
+ $line.split: / \| /;
+
+ for $test-name, $ints-str, $expected-str, $groups-str
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Int @ints = $ints-str.split( / \s+ /, :skip-empty )
+ .map: { .Int };
+ my Result $result = equal-groups( @ints );
+ my Bool $expected = $expected-str eq 'true';
+ my Str @group-str = $groups-str.split: / \; \s* /, :skip-empty;
+ my Array[Int] @groups = @group-str.map:
+ {
+ Array[Int].new:
+ .split( / \s+ /, :skip-empty ).map: { .Int }
+ };
+
+ is $result[ 0 ], $expected, "$test-name: Result";
+ is-deeply $result[ 1 ], @groups, "$test-name: Groups" if $result[ 0 ];
+ }
+
+ done-testing;
+}
+
+#-------------------------------------------------------------------------------
+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|1 1 2 2 2 2 |true |1 1; 2 2; 2 2
+ Example 2|1 1 1 2 2 2 3 3 |false|1 1 1; 2 2 2; 3 3
+ Example 3|5 5 5 5 5 5 7 7 7 7 7 7|true |5 5 5 5 5 5; 7 7 7 7 7 7
+ Example 4|1 2 3 4 |false|
+ Example 5|8 8 9 9 10 10 11 11 |true |8 8; 9 9; 10 10; 11 11
+ END
+}
+
+################################################################################
diff --git a/challenge-336/athanasius/raku/ch-2.raku b/challenge-336/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..8efdacd2f8
--- /dev/null
+++ b/challenge-336/athanasius/raku/ch-2.raku
@@ -0,0 +1,252 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 336
+=========================
+
+TASK #2
+-------
+*Final Score*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of scores by a team.
+
+Write a script to find the total score of the given team. The score can be any
+integer, +, C or D. The + adds the sum of previous two scores. The score C
+invalidates the previous score. The score D will double the previous score.
+
+Example 1
+
+ Input: @scores = ("5","2","C","D","+")
+ Output: 30
+
+ Round 1: 5
+ Round 2: 5 + 2
+ Round 3: 5 (invalidate the previous score 2)
+ Round 4: 5 + 10 (double the previous score 5)
+ Round 5: 5 + 10 + 15 (sum of previous two scores)
+
+ Total Scores: 30
+
+Example 2
+
+ Input: @scores = ("5","-2","4","C","D","9","+","+")
+ Output: 27
+
+ Round 1: 5
+ Round 2: 5 + (-2)
+ Round 3: 5 + (-2) + 4
+ Round 4: 5 + (-2) (invalidate the previous score 4)
+ Round 5: 5 + (-2) + (-4) (double the previous score -2)
+ Round 6: 5 + (-2) + (-4) + 9
+ Round 7: 5 + (-2) + (-4) + 9 + 5 (sum of previous two scores)
+ Round 8: 5 + (-2) + (-4) + 9 + 5 + 14 (sum of previous two scores)
+
+ Total Scores: 27
+
+Example 3
+
+ Input: @scores = ("7","D","D","C","+","3")
+ Output: 45
+
+ Round 1: 7
+ Round 2: 7 + 14 (double the previous score 7)
+ Round 3: 7 + 14 + 28 (double the previous score 14)
+ Round 4: 7 + 14 (invalidate the previous score 28)
+ Round 5: 7 + 14 + 21 (sum of previous two scores)
+ Round 6: 7 + 14 + 21 + 3
+
+ Total Scores: 45
+
+Example 4
+
+ Input: @scores = ("-5","-10","+","D","C","+")
+ Output: -55
+
+ Round 1: (-5)
+ Round 2: (-5) + (-10)
+ Round 3: (-5) + (-10) + (-15) (sum of previous two scores)
+ Round 4: (-5) + (-10) + (-15) + (-30) (double the previous score -15)
+ Round 5: (-5) + (-10) + (-15) (invalidate the previous score -30)
+ Round 6: (-5) + (-10) + (-15) + (-25) (sum of previous two scores)
+
+ Total Scores: -55
+
+Example 5
+
+ Input: @scores = ("3","6","+","D","C","8","+","D","-2","C","+")
+ Output: 128
+
+ Round 1: 3
+ Round 2: 3 + 6
+ Round 3: 3 + 6 + 9 (sum of previous two scores)
+ Round 4: 3 + 6 + 9 + 18 (double the previous score 9)
+ Round 5: 3 + 6 + 9 (invalidate the previous score 18)
+ Round 6: 3 + 6 + 9 + 8
+ Round 7: 3 + 6 + 9 + 8 + 17 (sum of previous two scores)
+ Round 8: 3 + 6 + 9 + 8 + 17 + 34 (double the previous score 17)
+ Round 9: 3 + 6 + 9 + 8 + 17 + 34 + (-2)
+ Round 10: 3 + 6 + 9 + 8 + 17 + 34 (invalidate the previous score -2)
+ Round 11: 3 + 6 + 9 + 8 + 17 + 34 + 51 (sum of previous two scores)
+
+ Total Scores: 128
+
+=end comment
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2025 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=begin comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A non-empty list of scores is entered on the command-line.
+3. If the first score is a negative integer, it must be preceded by "--" to
+ signal that it is not a command-line flag.
+
+Assumptions
+-----------
+During score evaluation, it is an error if a "+" score is not preceded by at
+least two other (integer) scores. Likewise, it is an error if a "C" or a "D"
+score is not preceded by at least one other (integer) score.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+subset Score where * ~~ Int:D | '+' | 'C' | 'D';
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 336, Task #2: Final Score (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ #| A non-empty list of scores (integers, '+', 'C', 'D')
+
+ *@scores where { .elems > 0 && .all ~~ Score:D }
+)
+#===============================================================================
+{
+ CATCH
+ {
+ when X::AdHoc
+ {
+ "\nERROR: { .message }".put;
+ exit 0;
+ }
+ }
+
+ "Input: @scores = (%s)\n".printf: @scores.map( { qq["$_"] } ).join: ',';
+
+ my Int $score = find-final-score( @scores );
+
+ "Output: $score".put;
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub find-final-score( List:D[Score:D] $scores --> Int:D )
+#-------------------------------------------------------------------------------
+{
+ my Score @queue;
+
+ for @$scores -> Score $score
+ {
+ my UInt $q-len = @queue.elems;
+
+ given $score
+ {
+ when '+' {
+ die qq[Illegal "+" score placement] if 2 > $q-len;
+ @queue.push: [+] @queue[ *-2 .. *-1 ];
+ }
+
+ when 'C' {
+ die qq[Illegal "C" score placement] if 1 > $q-len;
+ @queue.pop;
+ }
+
+ when 'D' {
+ die qq[Illegal "D" score placement] if 1 > $q-len;
+ @queue.push: 2 * @queue[ *-1 ];
+ }
+
+ default { # $score must be an integer
+ @queue.push: $score;
+ }
+ }
+ }
+
+ return [+] @queue;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $scores-str, $expected) = $line.split: / \| /;
+
+ for $test-name, $scores-str, $expected
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Str @strings = $scores-str.split: / \s+ /, :skip-empty;
+ my Score @scores = @strings.map: { / ^ <[+CD]> $ / ?? $_ !! .Int };
+ my Int $score = find-final-score( @scores );
+
+ is $score, $expected.Int, $test-name;
+ }
+
+ done-testing;
+}
+
+#-------------------------------------------------------------------------------
+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| 5 2 C D + | 30
+ Example 2| 5 -2 4 C D 9 + + | 27
+ Example 3| 7 D D C + 3 | 45
+ Example 4|-5 -10 + D C + |-55
+ Example 5| 3 6 + D C 8 + D -2 C +|128
+ END
+}
+
+################################################################################