aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2025-08-23 11:42:29 +1000
committerPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2025-08-23 11:42:29 +1000
commitfdd4f0381c39b40614795ab0e17577ff0ac632fe (patch)
tree075859152d569361a85b717469ab1e6f52c18b40
parent89c11ad8c143a56322780e2bbdfeddd09951a749 (diff)
downloadperlweeklychallenge-club-fdd4f0381c39b40614795ab0e17577ff0ac632fe.tar.gz
perlweeklychallenge-club-fdd4f0381c39b40614795ab0e17577ff0ac632fe.tar.bz2
perlweeklychallenge-club-fdd4f0381c39b40614795ab0e17577ff0ac632fe.zip
Perl & Raku solutions to Tasks 1 & 2 for Week 335
-rw-r--r--challenge-335/athanasius/perl/ch-1.pl174
-rw-r--r--challenge-335/athanasius/perl/ch-2.pl295
-rw-r--r--challenge-335/athanasius/raku/ch-1.raku164
-rw-r--r--challenge-335/athanasius/raku/ch-2.raku294
4 files changed, 927 insertions, 0 deletions
diff --git a/challenge-335/athanasius/perl/ch-1.pl b/challenge-335/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..46192cdbc8
--- /dev/null
+++ b/challenge-335/athanasius/perl/ch-1.pl
@@ -0,0 +1,174 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 335
+=========================
+
+TASK #1
+-------
+*Common Characters*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of words.
+
+Write a script to return all characters that is in every word in the given array
+including duplicates.
+
+Example 1
+
+ Input: @words = ("bella", "label", "roller")
+ Output: ("e", "l", "l")
+
+Example 2
+
+ Input: @words = ("cool", "lock", "cook")
+ Output: ("c", "o")
+
+Example 3
+
+ Input: @words = ("hello", "world", "pole")
+ Output: ("l", "o")
+
+Example 4
+
+ Input: @words = ("abc", "def", "ghi")
+ Output: ()
+
+Example 5
+
+ Input: @words = ("aab", "aac", "aaa")
+ Output: ("a", "a")
+
+=cut
+################################################################################
+
+#--------------------------------------#
+# Copyright © 2025 PerlMonk Athanasius #
+#--------------------------------------#
+
+#===============================================================================
+=comment
+
+Interface
+---------
+1. If no command-line arguments are given, the test suite is run. Otherwise:
+2. A list of words is entered on the command-line.
+
+=cut
+#===============================================================================
+
+use v5.32; # Enables strictures
+use warnings;
+use Const::Fast;
+use Set::Bag;
+use Test::More;
+
+const my $USAGE => <<END;
+Usage:
+ perl $0 [<words> ...]
+ perl $0
+
+ [<words> ...] A list of words
+END
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 335, Task #1: Common Characters (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ if (scalar @ARGV == 0)
+ {
+ run_tests();
+ }
+ else
+ {
+ my @words = @ARGV;
+
+ printf "Input: \@words = (%s)\n", join ', ', map { qq["$_"] } @words;
+
+ my $common = find_common_chars( \@words );
+
+ printf "Output: (%s)\n", join ', ', map { qq["$_"] } @$common;
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find_common_chars
+#-------------------------------------------------------------------------------
+{
+ my ($words) = @_;
+ my $shared = Set::Bag->new;
+
+ if (scalar @$words > 0)
+ {
+ $shared->insert( $_ => 1 ) for split //, $words->[ 0 ];
+
+ for my $i (1 .. $#$words)
+ {
+ my $chars = Set::Bag->new;
+ $chars->insert( $_ => 1 ) for split //, $words->[ $i ];
+
+ $shared = $shared & $chars; # Keep only the intersection
+ }
+ }
+
+ my @common;
+ push @common, ($_) x $shared->grab( $_ ) for sort $shared->elements;
+
+ return \@common;
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $words_str, $exp_str) = split / \| /x, $line;
+
+ for ($test_name, $words_str, $exp_str)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my @words = split / \s+ /x, $words_str;
+ my $common = find_common_chars( \@words );
+ my @expected = split / \s+ /x, $exp_str;
+
+ is_deeply $common, \@expected, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1|bella label roller|e l l
+Example 2|cool lock cook |c o
+Example 3|hello world pole |l o
+Example 4|abc def ghi |
+Example 5|aab aac aaa |a a
diff --git a/challenge-335/athanasius/perl/ch-2.pl b/challenge-335/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..2eaebea10a
--- /dev/null
+++ b/challenge-335/athanasius/perl/ch-2.pl
@@ -0,0 +1,295 @@
+#!perl
+
+################################################################################
+=comment
+
+Perl Weekly Challenge 335
+=========================
+
+TASK #2
+-------
+*Find Winner*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of all moves by the two players.
+
+Write a script to find the winner of the TicTacToe game if found based on the
+moves provided in the given array.
+
+UPDATE: Order move is in the order - A, B, A, B, A, ….
+
+Example 1
+
+ Input: @moves = ([0,0],[2,0],[1,1],[2,1],[2,2])
+ Output: A
+
+ Game Board:
+ [ A _ _ ]
+ [ B A B ]
+ [ _ _ A ]
+
+Example 2
+
+ Input: @moves = ([0,0],[1,1],[0,1],[0,2],[1,0],[2,0])
+ Output: B
+
+ Game Board:
+ [ A A B ]
+ [ A B _ ]
+ [ B _ _ ]
+
+Example 3
+
+ Input: @moves = ([0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2])
+ Output: Draw
+
+ Game Board:
+ [ A A B ]
+ [ B B A ]
+ [ A B A ]
+
+Example 4
+
+ Input: @moves = ([0,0],[1,1])
+ Output: Pending
+
+ Game Board:
+ [ A _ _ ]
+ [ _ B _ ]
+ [ _ _ _ ]
+
+Example 5
+
+ Input: @moves = ([1,1],[0,0],[2,2],[0,1],[1,0],[0,2])
+ Output: B
+
+ Game Board:
+ [ B B B ]
+ [ A A _ ]
+ [ _ _ A ]
+
+=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 move coordinates (0, 1, or 2) is entered on the command-
+ line. For example, the moves [0,0],[1,1] are entered as follows:
+
+ >perl ch-2.pl 0 0 1 1
+
+Assumptions
+-----------
+1. A move to a square already filled is an error, as is a move to a nonexistent
+ square.
+2. Even if it is not winnable, a game is not considered Drawn until all the
+ squares have been filled.
+3. Once the game has been decided (won or drawn), any further moves are simply
+ ignored.
+4. The Game Board for Example 1 is incorrect; it should be:
+ [ A _ _ ]
+ [ _ A _ ]
+ [ B B A ]
+
+=cut
+#===============================================================================
+
+use v5.32; # Enables strictures
+use warnings;
+use Const::Fast;
+use List::Util qw( pairs );
+use Test::More;
+
+const my $USAGE => <<END;
+Usage:
+ perl $0 [<coords> ...]
+ perl $0
+
+ [<coords> ...] List of move coordinates, e.g., 0 0 2 0 1 1 2 1 2 2
+END
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 335, Task #2: Find Winner (Perl)\n\n";
+}
+
+#===============================================================================
+MAIN:
+#===============================================================================
+{
+ my $argc = scalar @ARGV;
+
+ if ($argc == 0)
+ {
+ run_tests();
+ }
+ elsif ($argc % 2 == 1)
+ {
+ error( 'Expected an even number of command-line arguments, found ' .
+ $argc );
+ }
+ else
+ {
+ my @coords = @ARGV;
+
+ / ^ [012] $ /x or error( qq[Invalid coordinate "$_"] ) for @coords;
+
+ my @moves = get_moves( \@coords );
+
+ printf "Input: \@moves = (%s)\n",
+ join ', ', map { '[' . join( ', ', @$_ ) . ']' } @moves;
+
+ my ($winner, $grid) = find_winner( \@moves );
+
+ print "Output: $winner\n\nGame Board:\n";
+
+ printf "[ %s %s %s ]\n", @$_ for @$grid;
+ }
+}
+
+#-------------------------------------------------------------------------------
+sub find_winner
+#-------------------------------------------------------------------------------
+{
+ my ($moves) = @_;
+ my $winner = 'Pending';
+ my $player = 'A';
+ my @grid;
+ push @grid, [ ('_') x 3 ] for 1 .. 3;
+
+ for my $move (@$moves)
+ {
+ my ($row, $col) = @$move;
+
+ if ($grid[ $row ][ $col ] eq '_')
+ {
+ $grid[ $row ][ $col ] = $player;
+ }
+ else
+ {
+ die "\nERROR: Illegal move: [$row, $col]\n";
+ }
+
+ $winner = check_grid( \@grid );
+
+ last unless $winner eq 'Pending';
+
+ $player = $player eq 'A' ? 'B' : 'A';
+ }
+
+ return ($winner, \@grid);
+}
+
+#-------------------------------------------------------------------------------
+sub check_grid
+#-------------------------------------------------------------------------------
+{
+ my ($grid) = @_;
+
+ # Has the game been won? Test columns first, then rows, then diagonals
+
+ for my $coords ([ 0, 0, 0, 1, 0, 2 ], [ 1, 0, 1, 1, 1, 2 ],
+ [ 2, 0, 2, 1, 2, 2 ], [ 0, 0, 1, 0, 2, 0 ],
+ [ 0, 1, 1, 1, 2, 1 ], [ 0, 2, 1, 2, 2, 2 ],
+ [ 0, 0, 1, 1, 2, 2 ], [ 0, 2, 1, 1, 2, 0 ])
+ {
+ if (line_complete( $coords, $grid ))
+ {
+ return $grid->[ $coords->[ 0 ] ][ $coords->[ 1 ] ]; # Winner
+ }
+ }
+
+ # No winner yet; is the game drawn?
+
+ for my $i (0 .. 2)
+ {
+ for my $j (0 .. 2)
+ {
+ return 'Pending' if $grid->[ $i ][ $j ] eq '_'; # No
+ }
+ }
+
+ return 'Draw'; # Yes
+}
+
+#-------------------------------------------------------------------------------
+sub line_complete
+#-------------------------------------------------------------------------------
+{
+ my ($coords, $grid) = @_;
+
+ my $square_a = $grid->[ $coords->[ 0 ] ][ $coords->[ 1 ] ];
+ my $square_b = $grid->[ $coords->[ 2 ] ][ $coords->[ 3 ] ];
+ my $square_c = $grid->[ $coords->[ 4 ] ][ $coords->[ 5 ] ];
+
+ return ($square_a eq 'A' || $square_a eq 'B') &&
+ $square_a eq $square_b &&
+ $square_a eq $square_c;
+}
+
+#-------------------------------------------------------------------------------
+sub get_moves
+#-------------------------------------------------------------------------------
+{
+ my ($coords) = @_;
+
+ return map { [ @$_ ] } pairs @$coords;
+}
+
+#-------------------------------------------------------------------------------
+sub run_tests
+#-------------------------------------------------------------------------------
+{
+ print "Running the test suite\n";
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+
+ my ($test_name, $moves_str, $expected) = split / \| /x, $line;
+
+ for ($test_name, $moves_str, $expected)
+ {
+ s/ ^ \s+ //x;
+ s/ \s+ $ //x;
+ }
+
+ my @coords = split / [\s,]+ /x, $moves_str;
+ my @moves = get_moves ( \@coords );
+ my ($winner, undef) = find_winner( \@moves );
+
+ is $winner, $expected, $test_name;
+ }
+
+ done_testing;
+}
+
+#-------------------------------------------------------------------------------
+sub error
+#-------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+################################################################################
+
+__DATA__
+Example 1|0,0 2,0 1,1 2,1 2,2 |A
+Example 2|0,0 1,1 0,1 0,2 1,0 2,0 |B
+Example 3|0,0 1,1 2,0 1,0 1,2 2,1 0,1 0,2 2,2|Draw
+Example 4|0,0 1,1 |Pending
+Example 5|1,1 0,0 2,2 0,1 1,0 0,2 |B
diff --git a/challenge-335/athanasius/raku/ch-1.raku b/challenge-335/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..138dc06561
--- /dev/null
+++ b/challenge-335/athanasius/raku/ch-1.raku
@@ -0,0 +1,164 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 335
+=========================
+
+TASK #1
+-------
+*Common Characters*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of words.
+
+Write a script to return all characters that is in every word in the given array
+including duplicates.
+
+Example 1
+
+ Input: @words = ("bella", "label", "roller")
+ Output: ("e", "l", "l")
+
+Example 2
+
+ Input: @words = ("cool", "lock", "cook")
+ Output: ("c", "o")
+
+Example 3
+
+ Input: @words = ("hello", "world", "pole")
+ Output: ("l", "o")
+
+Example 4
+
+ Input: @words = ("abc", "def", "ghi")
+ Output: ()
+
+Example 5
+
+ Input: @words = ("aab", "aac", "aaa")
+ Output: ("a", "a")
+
+=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 list of words is entered on the command-line.
+
+=end comment
+#===============================================================================
+
+use Test;
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 335, Task #1: Common Characters (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ *@words where { .all ~~ Str:D } #= A list of words
+)
+#===============================================================================
+{
+ "Input: @words = (%s)\n".printf: @words\.map( { qq["$_"] } ).join: ', ';
+
+ my Str @common = find-common-chars( @words );
+
+ "Output: (%s)\n"\ .printf: @common.map( { qq["$_"] } ).join: ', ';
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub find-common-chars( List:D[Str:D] $words --> List:D[Str:D] )
+#-------------------------------------------------------------------------------
+{
+ my BagHash $common = BagHash.new;
+
+ if $words.elems > 0
+ {
+ $common.add( $_ ) for $words[ 0 ].split: '', :skip-empty;
+
+ for 1 .. $words.end -> UInt $i
+ {
+ my Bag $chars = Bag.new: $words[ $i ].split: '', :skip-empty;
+
+ $common ∩= $chars; # Keep only the intersection
+ }
+ }
+
+ return $common ?? |$common.kxxv.sort !! ();
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $words-str, $exp-str) = $line.split: / \| /;
+
+ for $test-name, $words-str, $exp-str
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Str @words = $words-str.split: / \s+ /, :skip-empty;
+ my Str @common = find-common-chars( @words );
+ my Str @expected = $exp-str\ .split: / \s+ /, :skip-empty;
+
+ is-deeply @common, @expected, $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|bella label roller|e l l
+ Example 2|cool lock cook |c o
+ Example 3|hello world pole |l o
+ Example 4|abc def ghi |
+ Example 5|aab aac aaa |a a
+ END
+}
+
+################################################################################
diff --git a/challenge-335/athanasius/raku/ch-2.raku b/challenge-335/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..6f69443cb8
--- /dev/null
+++ b/challenge-335/athanasius/raku/ch-2.raku
@@ -0,0 +1,294 @@
+use v6d;
+
+################################################################################
+=begin comment
+
+Perl Weekly Challenge 335
+=========================
+
+TASK #2
+-------
+*Find Winner*
+
+Submitted by: Mohammad Sajid Anwar
+
+You are given an array of all moves by the two players.
+
+Write a script to find the winner of the TicTacToe game if found based on the
+moves provided in the given array.
+
+UPDATE: Order move is in the order - A, B, A, B, A, ….
+
+Example 1
+
+ Input: @moves = ([0,0],[2,0],[1,1],[2,1],[2,2])
+ Output: A
+
+ Game Board:
+ [ A _ _ ]
+ [ B A B ]
+ [ _ _ A ]
+
+Example 2
+
+ Input: @moves = ([0,0],[1,1],[0,1],[0,2],[1,0],[2,0])
+ Output: B
+
+ Game Board:
+ [ A A B ]
+ [ A B _ ]
+ [ B _ _ ]
+
+Example 3
+
+ Input: @moves = ([0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2])
+ Output: Draw
+
+ Game Board:
+ [ A A B ]
+ [ B B A ]
+ [ A B A ]
+
+Example 4
+
+ Input: @moves = ([0,0],[1,1])
+ Output: Pending
+
+ Game Board:
+ [ A _ _ ]
+ [ _ B _ ]
+ [ _ _ _ ]
+
+Example 5
+
+ Input: @moves = ([1,1],[0,0],[2,2],[0,1],[1,0],[0,2])
+ Output: B
+
+ Game Board:
+ [ B B B ]
+ [ A A _ ]
+ [ _ _ A ]
+
+=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 move coordinates (0, 1, or 2) is entered on the command-
+ line. For example, the moves [0,0],[1,1] are entered as follows:
+
+ >raku ch-2.raku 0 0 1 1
+
+Assumptions
+-----------
+1. A move to a square already filled is an error, as is a move to a nonexistent
+ square.
+2. Even if it is not winnable, a game is not considered Drawn until all the
+ squares have been filled.
+3. Once the game has been decided (won or drawn), any further moves are simply
+ ignored.
+4. The Game Board for Example 1 is incorrect; it should be:
+ [ A _ _ ]
+ [ _ A _ ]
+ [ B B A ]
+
+=end comment
+#===============================================================================
+
+use Test;
+
+subset Index of Int where 2 >= * >= 0;
+subset Move of List where (Index, Index);
+subset Square of Str where { $_ eq < A B _ >.any };
+subset Row of List where (Square, Square, Square);
+subset Grid of List where (Row, Row, Row);
+subset Winner of Str where { $_ eq < A B Draw Pending >.any };
+
+#-------------------------------------------------------------------------------
+BEGIN
+#-------------------------------------------------------------------------------
+{
+ "\nChallenge 335, Task #2: Find Winner (Raku)\n".put;
+}
+
+#===============================================================================
+multi sub MAIN
+(
+ #| List of move coordinates, e.g., 0 0 2 0 1 1 2 1 2 2
+
+ *@coords where { .elems > 0 && .elems %% 2 && .all ~~ Index:D }
+)
+#===============================================================================
+{
+ my Move @moves = get-moves( @coords );
+
+ "Input: \@moves = (%s)\n".printf:
+ @moves.map( { '[' ~ .join( ', ' ) ~ ']' } ).join: ', ';
+
+ my (Winner $winner, Grid $grid) = find-winner( @moves );
+
+ "Output: $winner\n\nGame Board:".put;
+
+ "[ %s %s %s ]\n".printf: @$_ for @$grid;
+
+ CATCH
+ {
+ when X::AdHoc
+ {
+ "\n%s\n".printf: .message;
+ exit 0;
+ }
+ }
+}
+
+#===============================================================================
+multi sub MAIN() # No input: run the test suite
+#===============================================================================
+{
+ run-tests();
+}
+
+#-------------------------------------------------------------------------------
+sub find-winner( List:D[Move:D] $moves --> List:D[Winner:D, Grid:D] )
+#-------------------------------------------------------------------------------
+{
+ my Winner $winner = 'Pending';
+ my Grid $grid = [ [ '_' xx 3 ] xx 3 ];
+ my Str $player = 'A';
+
+ for @$moves -> Move $move
+ {
+ my Index ($row, $col) = @$move;
+
+ if $grid[ $row; $col ] eq '_'
+ {
+ $grid[ $row; $col ] = $player;
+ }
+ else
+ {
+ die "ERROR: Illegal move: [$row, $col]";
+ }
+
+ $winner = check-grid( $grid );
+
+ last unless $winner eq 'Pending';
+
+ $player = $player eq 'A' ?? 'B' !! 'A';
+ }
+
+ return $winner, $grid;
+}
+
+#-------------------------------------------------------------------------------
+sub check-grid( Grid:D $grid --> Winner:D )
+#-------------------------------------------------------------------------------
+{
+ # Has the game been won? Test columns first, then rows, then diagonals
+
+ for [ 0, 0, 0, 1, 0, 2 ], [ 1, 0, 1, 1, 1, 2 ], [ 2, 0, 2, 1, 2, 2 ],
+ [ 0, 0, 1, 0, 2, 0 ], [ 0, 1, 1, 1, 2, 1 ], [ 0, 2, 1, 2, 2, 2 ],
+ [ 0, 0, 1, 1, 2, 2 ], [ 0, 2, 1, 1, 2, 0 ] -> Array $coords
+ {
+ if line-complete( $coords, $grid )
+ {
+ return $grid[ $coords[ 0 ]; $coords[ 1 ] ]; # Winner
+ }
+ }
+
+ # No winner yet; is the game drawn?
+
+ for 0 .. 2 -> UInt $i
+ {
+ for 0 .. 2 -> UInt $j
+ {
+ return 'Pending' if $grid[ $i; $j ] eq '_'; # No
+ }
+ }
+
+ return 'Draw'; # Yes
+}
+
+#-------------------------------------------------------------------------------
+sub line-complete( List:D[Index:D] $coords, Grid:D $grid --> Bool:D )
+#-------------------------------------------------------------------------------
+{
+ my Str $square-a = $grid[ $coords[ 0 ] ][ $coords[ 1 ] ];
+ my Str $square-b = $grid[ $coords[ 2 ] ][ $coords[ 3 ] ];
+ my Str $square-c = $grid[ $coords[ 4 ] ][ $coords[ 5 ] ];
+
+ return ($square-a eq 'A' || $square-a eq 'B') &&
+ $square-a eq $square-b &&
+ $square-a eq $square-c;
+}
+
+#-------------------------------------------------------------------------------
+sub get-moves
+(
+ List:D[Index:D] $coords where { .elems %% 2 }
+--> List:D[Move:D]
+)
+#-------------------------------------------------------------------------------
+{
+ return $coords.rotor( 2 ).map( { [ +$_[ 0 ], +$_[ 1 ] ] } ).list;
+}
+
+#-------------------------------------------------------------------------------
+sub run-tests()
+#-------------------------------------------------------------------------------
+{
+ 'Running the test suite'.put;
+
+ for test-data.lines -> Str $line
+ {
+ my Str ($test-name, $moves-str, $expected) = $line.split: / \| /;
+
+ for $test-name, $moves-str, $expected
+ {
+ s/ ^ \s+ //;
+ s/ \s+ $ //;
+ }
+
+ my Index @coords = $moves-str.split( / \s+ || \, /, :skip-empty )
+ .map: { .Int };
+ my Move @moves = get-moves( @coords );
+ my (Winner $winner, Grid) = find-winner( @moves );
+
+ is $winner, $expected, $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|0,0 2,0 1,1 2,1 2,2 |A
+ Example 2|0,0 1,1 0,1 0,2 1,0 2,0 |B
+ Example 3|0,0 1,1 2,0 1,0 1,2 2,1 0,1 0,2 2,2|Draw
+ Example 4|0,0 1,1 |Pending
+ Example 5|1,1 0,0 2,2 0,1 1,0 0,2 |B
+ END
+}
+
+################################################################################