第一回 オフラインリアルタイムどう書くの回答例


[Tick-Tack-Toeの判定](http://nabetani.sakura.ne.jp/hena/1/)をとりあえず書いたコード。これはないなーってコードだけど一応。

```TickTackToe.pm
package TickTackToe;
use strict;
use warnings;
use Exporter qw(import);

our @EXPORT_OK = qw/solve/;

our @LINES = (
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
    [1, 4, 7],
    [2, 5, 6],
    [3, 6, 9],
    [1, 5, 9],
    [3, 5, 7],
);

sub contains {
    my ($item, @list) = @_;
    my %flags = map {$_ => 1} @list;
    return $flags{$item};
}

sub solve($) {
    my $input = shift;
    my @nums = (split //, $input)[0 .. 8];

    my $player = 0;
    my @players = qw/o x/;

    my %done;
    my @slot = (-1) x @LINES;
    my @slot_count = (0) x @LINES;
    for my $n (@nums) {
        return "Foul : $players[!$player] won." if $done{$n}++;

        for (0 .. $#LINES) {
            next if $slot_count[$_] < 0;
            next unless contains $n => @{$LINES[$_]};
            if ($slot[$_] == ! $player) {
                $slot_count[$_] = -1;
            } else {
                $slot[$_] = $player;
                $slot_count[$_]++;
                return "$players[$player] won." if $slot_count[$_] >= 3;
            }
        }

        $player = ! $player;
    }

    return "Draw game.";
}

1;
```

```tick_tack_toe.t
use strict;
use warnings;
use TickTackToe qw(solve);
use Test::More;

while (<DATA>) {
    tr/\r\n//d;
    my ($no, $input, $output) = split /\t/, $_, 3;
    is +(solve $input), $output;
}

done_testing;

__END__
1	79538246	x won.
2	35497162193	x won.
3	61978543	x won.
4	254961323121	x won.
5	6134278187	x won.
6	4319581	Foul : x won.
7	9625663381	Foul : x won.
8	7975662	Foul : x won.
9	2368799597	Foul : x won.
10	18652368566	Foul : x won.
11	965715	o won.
12	38745796	o won.
13	371929	o won.
14	758698769	o won.
15	42683953	o won.
16	618843927	Foul : o won.
17	36535224	Foul : o won.
18	882973	Foul : o won.
19	653675681	Foul : o won.
20	9729934662	Foul : o won.
21	972651483927	Draw game.
22	5439126787	Draw game.
23	142583697	Draw game.
24	42198637563	Draw game.
25	657391482	Draw game.
```

ちょっと直した版。でもいまいち。

```TickTackToe.pm
package TickTackToe;
use strict;
use warnings;
use Exporter qw(import);

our @EXPORT_OK = qw/solve/;
our $SIZE = 3;

sub lines($) {
    my @lines;
    for my $x (0 .. $SIZE - 1) {
        push @lines, [map { $_ + $x * $SIZE } 0 .. $SIZE - 1];
    }

    for my $y (0 .. $SIZE - 1) {
        push @lines, [map { $_ * $SIZE + $y } 0 .. $SIZE - 1];
    }

    push @lines, [map { $_ * $SIZE + $_ } 0 .. $SIZE - 1];
    push @lines, [map { $_ * $SIZE + $SIZE - $_ - 1} 0 .. $SIZE - 1];

    return @lines;
}

sub contains {
    my ($item, @list) = @_;
    my %flags = map {$_ => 1} @list;
    return $flags{$item};
}

{
    my $lines;
    sub winner {
        my @board = @_;
        $lines ||= [lines $SIZE];

        for my $slots (@$lines) {
            my %sum;
            $sum{$board[$_]}++ for grep {defined $board[$_]} @$slots;

            defined $sum{$_} && $sum{$_} >= $SIZE and return $_ for 0, 1;
        }

        return -1;
    }
}

sub solve($) {
    my $input = shift;

    my @nums = grep {defined $_} (map {$_ - 1} split //, $input)[0 .. $SIZE ** 2 - 1];

    my $player = 0;
    my @players = qw/o x/;

    my @board;
    for my $n (@nums) {
        return "Foul : $players[!$player] won." if defined $board[$n];
        $board[$n] = $player;

        my $winner = winner @board;
        if ($winner >= 0) {
            return "$players[$winner] won.";
        }

        $player = $player ? 0 : 1;
    }

    return "Draw game.";
}

1;
```