p4-fox

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;
}

Readability

Things are a bit dense sometimes, but not to the point of golfing.

Consistency

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.

Clarity of intent

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 Xes, prepends S to all the values in the array, joins them into one big string, splits on SX, then uses max to find the (earliest) string with the highest number of Ses, 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.

Algorithmic efficiency

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.

Idiomatic use of Perl 6

The .= not on line 28 is decidedly cute.

Brevity

One has the feeling that the program isn't any longer than it has to be.