This is my response to The Weekly Challenge #335.
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")
#! /usr/bin/env raku
unit sub MAIN (*@words where @words.elems > 0, :v(:$verbose)); # [1]
my @bags = @words>>.comb>>.Bag; # [2]
say ": Bags; { @bags.raku }" if $verbose;
my $intersection = [(&)] @bags; # [3]
say ": Common: { $intersection.raku }" if $verbose;
say "(" ~ $intersection.kxxv.sort.map( '"' ~ * ~ '"' ).join(", ") ~ ")"; # [4]
[1] At least one word, without any rules on the content.
[2] Split each word into an array of individual characters
(with comb
). Then we coerce each of these arrays into a Bag
,
a hash like structure that gives us the frequency of each unique value.
See docs.raku.org/routine/Bag for more information about Bag
.
[3]
Use the intersection operator (&)
on the entire array with the Reduction Metaoperator []
to get the
intersection.
See
docs.raku.org/routine/(&),%20infix%20%E2%88%A9
for more information about the intersection operator (&)
.
See
docs.raku.org/language/operators#Reduction_metaoperators for more
information about the Reduction Metaoperator []
.
[4] Pretty print the intersection, i.e. the result.
Running it:
$ ./common-characters bella label roller
("e", "l", "l")
$ ./common-characters cool lock cook
("c", "o")
$ ./common-characters hello world pole
("l", "o")
$ ./common-characters abc def ghi
()
$ ./common-characters aab aac aaa
("a", "a")
Looking good.
With verbose mode:
$ ./common-characters -v bella label roller
: Bags; [("l"=>2,"a"=>1,"e"=>1,"b"=>1).Bag, \
("a"=>1,"b"=>1,"e"=>1,"l"=>2).Bag, \
("r"=>2,"l"=>2,"o"=>1,"e"=>1).Bag]
: Common: ("l"=>2,"e"=>1).Bag
("e", "l", "l")
$ ./common-characters -v cool lock cook
: Bags; [("c"=>1,"l"=>1,"o"=>2).Bag, \
("k"=>1,"l"=>1,"c"=>1,"o"=>1).Bag, \
("k"=>1,"o"=>2,"c"=>1).Bag]
: Common: ("c"=>1,"o"=>1).Bag
("c", "o")
$ ./common-characters -v hello world pole
: Bags; [("l"=>2,"e"=>1,"o"=>1,"h"=>1).Bag, \
("l"=>1,"o"=>1,"d"=>1,"w"=>1,"r"=>1).Bag, \
("p"=>1,"l"=>1,"o"=>1,"e"=>1).Bag]
: Common: ("o"=>1,"l"=>1).Bag
("l", "o")
$ ./common-characters -v abc def ghi
: Bags; [("b"=>1,"c"=>1,"a"=>1).Bag, \
("d"=>1,"f"=>1,"e"=>1).Bag, \
("h"=>1,"i"=>1,"g"=>1).Bag]
: Common: ().Bag
()
$ ./common-characters -v aab aac aaa
: Bags; [("a"=>2,"b"=>1).Bag, ("c"=>1,"a"=>2).Bag, ("a"=>3).Bag]
: Common: ("a"=>2).Bag
("a", "a")
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 ]
Note that the Game Board shown for the first example above is wrong. See the verbose mode output later on for the correct board.
File: find-winner
#! /usr/bin/env raku
unit sub MAIN (*@moves where @moves.elems > 0 # [1]
&& all(@moves) ~~/^<[012]> \, <[012]>$/, # [1a]
:v(:$verbose));
my @board = [ [ '-', '-', '-' ], ['-', '-', '-'], ['-', '-', '-'] ]; # [2]
my $player = 'A'; # [3]
for @moves -> $move # [4]
{
my ($x, $y) = $move.split(","); # [5]
die "Position $x,$y already taken" unless @board[$x][$y] eq "-"; # [6]
@board[$x][$y] = $player; # [7]
$player = $player eq 'A' ?? 'B' !! 'A'; # [8]
}
if $verbose
{
say ": " ~ @board[0].join(" ");
say ": " ~ @board[1].join(" ");
say ": " ~ @board[2].join(" ");
}
for 'A', 'B' -> $ab # [9]
{
if all(@board[0;*]) eq $ab || # [10]
all(@board[1;*]) eq $ab || # [10a]
all(@board[2;*]) eq $ab || # [10b]
all(@board[*;0]) eq $ab || # [10c]
all(@board[*;1]) eq $ab || # [10d]
all(@board[*;2]) eq $ab || # [10e]
@board[0][0] eq @board[1][1] eq @board[2][2] eq $ab || # [10f]
@board[2][0] eq @board[1][1] eq @board[0][2] eq $ab # [10g]
{
say $ab; # [11]
exit; # [11a]
}
}
say @moves.elems == 9 ?? 'Draw' !! 'Pending'; # [12]
[1] An array of moves, where each move contains the row (0-2) and column numbers (0-2) separated by a comma [1a]. At least one move.
[2] Set up the initially empty board.
[3] Start with player 'A'.
[4] For each move.
[5] Get the column (x) and row (y) values.
[6] Terminate the program if the current position already is in use.
[7] Set the current player's mark on the current position.
[8] Prepare for the next iteration of the loop by changing users.
[9] For each user,
[10] Does the user have the entire column 0, 1 or 2 [10a/b/c], row 0, 1 or 2 [10c/d/e], or one of the diagonals [10f/g]?
[11] If so, we have a winner. Say so and exit [11a].
[12] No winner. The message is 'Pending' if we have untaken positions, and 'Draw' if the board is full.
Running it:
$ ./find-winner 0,0 2,0 1,1 2,1 2,2
A
$ ./find-winner 0,0 1,1 0,1 0,2 1,0 2,0
B
$ ./find-winner 0,0 1,1 2,0 1,0 1,2 2,1 0,1 0,2 2,2
Draw
$ ./find-winner 0,0 1,1
Pending
$ ./find-winner 1,1 0,0 2,2 0,1 1,0 0,2
B
Looking good.
With verbose mode:
$ ./find-winner -v 0,0 2,0 1,1 2,1 2,2
: A - -
: - A -
: B B A
A
$ ./find-winner -v 0,0 1,1 0,1 0,2 1,0 2,0
: A A B
: A B -
: B - -
B
$ ./find-winner -v 0,0 1,1 2,0 1,0 1,2 2,1 0,1 0,2 2,2
: A A B
: B B A
: A B A
Draw
$ ./find-winner -v 0,0 1,1
: A - -
: - B -
: - - -
Pending
$ ./find-winner -v 1,1 0,0 2,2 0,1 1,0 0,2
: B B B
: A A -
: - - A
B
And that's it.