Download the raw code.
#
# game of picking pairs of stones
# our strategy here is simply to search for the biggest chunk
# of stones and take the 2 of the middle
#
my ($rock-num, $player-name) = $*IN.lines;
exit say "aborted" unless $rock-num.defined and $player-name.defined;
my $is-computer = $player-name eq "computer";
my @rocks;
if $is-computer {
say "computer takes 0,1";
@rocks = 2..$rock-num-1;
} else {
say "human to move: " ~ join "," ,^$rock-num;
my ($a, $b) = $*IN.get.split(/\,/);
@rocks = $b+1..$rock-num-1,^$a;
}
loop {
my ($a, $b) = get-max @rocks;
last unless $a.defined and $b.defined;
$is-computer .= not;
if $is-computer {
say "computer takes $a,$b";
} else {
say "human to move: " ~ join ",", @rocks.grep({$_.defined});
($a, $b) = $*IN.get.split(/\,/);
}
for ^@rocks -> $i { undefine @rocks[$i] if $a|$b ~~ @rocks[$i] };
}
say ($is-computer ?? "computer" !! "human") ~ " wins";
sub get-max ( @array ) {
my @numbers = gather for (
max { $^a.comb.grep("S") > $^b.comb.grep("S") },
([~] "S" X @array.map({ $_ // "X" })).split(/SX/)
).match(/S(\d+)/,:g) -> $/ { take +$/[0] };
return @numbers > 1 ?? @numbers[@numbers/2-1,@numbers/2] !! Any;
}
Things are a bit dense sometimes, but not to the point of golfing.
I'm not sure whether I should admire or condemn the way the execution of the first move is mixed up with setting up the board itself. The author gains one thing from this that none of the other solutions ran into: after the board has been set up, no attention whatsoever has to be paid to the border between the "last" stone and the "first" one, emulating a ring. Because in this solution, it never was a ring.
Ok, so get-max
takes the whole array containing the board situation, with
position numbers for stones and Any
for holes where stones used to be,
turns the Any
values to X
es, prepends S
to all the values in the array,
joins them into one big string, split
s on SX
, then uses max
to find
the (earliest) string with the highest number of S
es, and then extracts the
position numbers again using .match(:g)
. All in four lines of code!
On some level I have to admire the data manipulation crammed into that space, but I can't help feeling that there has to be another way. Maybe something a teeny bit longer but a lot clearer?
@array
is not a very clear name for a parameter. Especially not an array
with as much structure as this one.
The smartmatch on line 36 causes even removed rocks to be unnecessarily
re-removed. It's not noticeable, just a bit careless. It's a consequence
of the choice of data structure rather than the aspect of the code —
the array is a mix of integers and Any
objects.
This algorithm is clearly wrong. Here's one case where it plays imperfectly:
11
computer
computer takes 0,1
human to move: 2,3,4,5,6,7,8,9,10
5,6
computer takes 8,9
human to move: 2,3,4,7,10
2,3
human wins
But for all its incorrectness, it's probably the fastest one out there. And its algorithm, for all its simplicity, is well-chosen.
The .= not
on line 28 is decidedly cute.
One has the feeling that the program isn't any longer than it has to be.