#!/usr/add-on/perl5002/bin/perl
#=============================================================================
# POTM ENTRY: Dilbot
# Author:     Mike Anderson
# E-mail:     mranderson@att.com
# Version:    1.10
# Date:       6/29/97
# Language:   Perl 5
#                       "Here be dragons and ogres!
#                        Travelers, walk not alone."
#=============================================================================

$debug = 0;

$NAME = "Dilbot";
die "$NAME: no temp file name from mediator\n" unless ($#ARGV >= 0);

$result = ;
chop $result if (substr($result, -1, 1) eq "\n");
$result =~ tr/a-z/A-Z/;
($retCode, $retRow, $retCol) = split(/\s+/, $result);
$retRow =~ tr/A-I/1-9/;

if ($retCode eq "INIT")
{
    die "$NAME: got bad \"$result\" from mediator\n"
        unless (&validLoc($retRow, $retCol));

    $idleSince = $turn = 1;
    ($myRow, $myCol) = ($retRow, $retCol);
    if ($myRow == 1)
    {
        ($hisRow, $hisCol) = (9, 26);
        $hisDir = -1;
        $myDir  = 1;
        $state  = 0;
    }
    else
    {
        ($hisRow, $hisCol) = (1, 1);
        $hisDir = 1;
        $myDir  = -1;
        $state  = 1;
    }
    ($hisHomeRow,   $hisHomeCol)      = ($hisRow, $hisCol);
    ($hisRowChange, $hisColChange)    = (0, 0);
    ($hisHeight, $hisWidth, $hisDist) = &hisDistance($myRow, $myCol);

    for ($row = 1; $row <= 9; $row++)
    {
        for ($col = 1; $col <= 26; $col++)
        {
            $board[$row][$col] = ".";
        }
    }
    $moves    = 0;
    $ouches   = 0;
    $fire     = 0;
    $zapBias  = -1;
    $command  = $state == 0 ? "WALL H 26" : &wall(2);
}
else
{
    &restore();
    if ($opCode eq "WALL")
    {
        if ($retCode eq "OK")
        {
            $board[$opRow][$opCol] = "W";

            # he still hasn't moved from home due to blocking
            if ($state == 0)
            {
                # time to make a run if he can't possibly make it
                # to my home
                if ($hisDist-1 > 100-$turn)
                {
                    $state   = 3;
                    $command = &zapProp();
                }
                else # keep him blocked in at his home
                {
                    $command = $turn % 2 ? "WALL H 26" : "WALL I 25";
                }
            }
            else # where is he now so can determine effectiveness of wall
            {
                $command = "RADAR";
            }
        }
        elsif ($retCode eq "OOPS") # found him
        {
            $state = 1 if ($state == 0); # he has escaped from home
            # next command depends on where he is relative to me
            $command = &hisLoc($opRow, $opCol);
            # wall him if a ZAP isn't a priority
            $command = &wall(2) unless ($command =~ /^ZAP/);
        }
        elsif ($retCode eq "ERROR")
        {
            die "$NAME: got WALL result \"$result\"\n" if ($debug);
            $command = "RADAR";
        }
        else
        {
            die "$NAME: got bad WALL result \"$result\" from mediator\n";
        }
    }
    elsif ($opCode eq "RADAR")
    {
        die "$NAME: got bad RADAR result \"$result\" from mediator\n"
            if ($retCode ne "AT" or not &validLoc($retRow, $retCol));

        # next command depends on where he is relative to me
        $command = &hisLoc($retRow, $retCol);
    }
    elsif ($opCode eq "ZAP")
    {
        $opRow =~ tr/1-9/A-I/;
        if ($retCode eq "HIT")
        {
            # zap him again and again until the showdown ends!
            $command = "ZAP $opRow $opCol";
        }
        elsif ($retCode eq "MISS" || $retCode eq "BOOM")
        {
            if ($state == 1)
            {
                # keep firing along my row or col since he's near it
                # until he gets too close to me
                $command = --$fire > 0 ? "ZAP $opRow $opCol" : "RADAR";
            }
            elsif ($state == 2)
            {
                # alternate zap along row and column since he's too close
                $zapBias *= -1;
                $command  = &zap($zapBias);
            }
            elsif ($state == 3)
            {
                # move now that I've cleared the way, hopefully
                $command = "MOVE $opRow $opCol";
            }
            else
            {
                die "$NAME: got bad ZAP state \"$state\"\n" if ($debug);
                $command = "RADAR";
            }
        }
        elsif ($retCode eq "ERROR")
        {
            die "$NAME: got ZAP result \"$result\"\n" if ($debug);
            $command = $state == 3 ? "HELP" : "RADAR";
        }
        else
        {
            die "$NAME: got bad ZAP result \"$result\" from mediator\n";
        }
    }
    elsif ($opCode eq "MOVE")
    {
        if ($retCode eq "OK")
        {
            $ouches = 0;
            ($myRow, $myCol) = ($opRow, $opCol);
            # check to see if he has moved some since I have now move some
            #$command = ++$moves == 2 ? "RADAR" : &zapProp();
            $command = &zapProp();
        }
        elsif ($retCode eq "OUCH")
        {
            if (++$ouches == 1) # first ouch, someone replaced the wall!
            {
                # try the other way (it's mirror square) in the right
                # direction
                if ($opRow == $myRow)
                {
                    if (&validLoc($opRow+$myDir, $opCol-$myDir))
                    {
                        $opRow += $myDir;
                        $opCol -= $myDir;
                    }
                }
                else
                {
                    if (&validLoc($opRow-$myDir, $opCol+$myDir))
                    {
                        $opRow -= $myDir;
                        $opCol += $myDir;
                    }
                }
                $opRow   =~ tr/1-9/A-I/;
                $command =  "ZAP $opRow $opCol";
            }
            else # ouched again, now randomly zap then move
            {
                $command = &zapRand();
            }
        }
        elsif ($retCode eq "HELLO")
        {
            # zap him now!
            $ouches  =  0;
            $command = &hisLoc($opRow, $opCol);
        }
        elsif ($retCode eq "ERROR")
        {
            die "$NAME: got MOVE result \"$result\"\n" if ($debug);
            $ouches  = 0;
            $command = "HELP";
        }
        else
        {
            die "$NAME: got bad MOVE result \"$result\" from mediator\n";
        }
    }
    elsif ($opCode eq "HELP")
    {
        die "$NAME: got bad HELP result \"$result\" from mediator\n"
            if ($retCode ne "LOC" or not &validLoc($retRow, $retCol));

        ($myRow, $myCol) = ($retRow, $retCol);
        $command = &zapProp();
    }
    else
    {
        die "$NAME: bad opCode \"$opCode\"\n" if ($debug);
        $command = "RADAR";
    }
}

die "$NAME: no command to output\n" if ($debug and not $command);
&save();
print "$command\n";
exit;

sub zapProp # random with bias in proportion to height/wdith from him
{
    local($height) = abs($myRow-$hisRow);
    local($width)  = abs($myCol-$hisCol);
    local($bias);

    # random row or column change biased by height/width from him
                                            # change row  col
    $bias = int(rand($height+$width))+1 <= $height ? 1 : -1;
    return &zap($bias);
}

sub zapRand
{
    local($bias);
    if (rand() < .5) # flipping a coin
    {                                    # change row  col
        $bias = &validLoc($myRow+$myDir, $myCol) ? 1 : -1;
    }
    else
    {                                    # change col  row
        $bias = &validLoc($myRow, $myCol+$myDir) ? -1 : 1;
    }
    return &zap($bias);
}

sub zap
{
    local($bias) = @_; # < 0 (-1) means change col to zap along the row
    local($row, $col);
    if ($bias < 0) # zap in row
    {
        $row = $myRow;
        $col = $myCol+$myDir;
    }
    else # zap in col
    {
        $row = $myRow+$myDir;
        $col = $myCol;
    }
    $row =~ tr/1-9/A-I/;
    return "ZAP $row $col";
}

sub wall
{
    # try best guess to where he's going next based on previous change or
    # amount of height and width to cover to get to me on a diagonal if
    # can't tell from change

    local($start) = @_;
    local($ret);
    if ($hisRowChange != $hisColChange)
    {
        $ret = &rowColWall($hisRowChange > $hisColChange, $start, $hisDist);
    }
    else
    {
        $ret = &heightWidthWall($hisHeight - $hisWidth, $start, $hisDist);
    }

    if (not $ret) # no appropriate free spot for a wall found
    {
        if ($start > 1)
        {
            $ret = &wall(1); # try placing one directly in front of him
        }
        else # last resort - drop it on him to see if he's still there
        {
            local($row) = $hisRow;
            $row =~ tr/1-9/A-I/;
            $ret = "WALL $row $hisCol";
        }
    }
    return $ret;
}

sub rowColWall
{
    # find next spot in diag across his path starting from row or col
    # he may go to next on up/down

    local($biasRow, $start, $dist) = @_;
    local($row, $col);
    local($ret) = "";
    for (local($d) = $start; $d <= $dist; $d++)
    {
        for (local($i) = 0; $i <= $d; $i++)
        {
            if ($biasRow)
            {
                $row = $hisRow + ($d-$i)*$hisDir;
                $col = $hisCol + $i*$hisDir;
            }
            else # bias torward col
            {
                $row = $hisRow + $i*$hisDir;
                $col = $hisCol + ($d-$i)*$hisDir;
            }
            $ret = &checkWall($row, $col, $start);
            return $ret if ($ret);
        }
    }
    return $ret;
}

sub heightWidthWall
{
    # find next spot in diag across his path starting from the center and
    # alternating outward biasing to the longer of height or width first

    local($bias, $start, $dist) = @_;
    local($row, $col, $curBias);
    local($ret) = "";
    for (local($d) = $start; $d <= $dist; $d++)
    {
        local($d2) = int $d/2;
        for (local($i) = 0; $i <= $d2; $i++)
        {
            if ($d%2 == 0 && $i == 0) # diagonal loc always first
            {
                $row = $hisRow + $d2*$hisDir;
                $col = $hisCol + $d2*$hisDir;
                $ret = &checkWall($row, $col, $start);
                return $ret if ($ret);
                next;
            }

            # flip a coin if he's on the corner of a square
            $curBias = $bias == 0 ? (rand() < .5 ? 1 : -1) : $bias;
            if ($curBias > 0) # bias to height
            {
                $a = $d2+$i;
                $a++ if ($d%2 == 1); # odd only
                $a *= $hisDir;
                $b = ($d2-$i)*$hisDir;
            }
            else # bias to width
            {
                $a = ($d2-$i)*$hisDir;
                $b = $d2+$i;
                $b++ if ($d%2 == 1); # odd only
                $b *= $hisDir;
            }
            $row = $hisRow + $a;
            $col = $hisCol + $b;
            $ret = &checkWall($row, $col, $start);
            return $ret if ($ret);

            # try its opposite
            $row = $hisRow + $b;
            $col = $hisCol + $a;
            $ret = &checkWall($row, $col, $start);
            return $ret if ($ret);
        }
    }
    return $ret;
}

sub checkWall
{
    local($row, $col, $start) = @_;

    # bad wall or spot taken when dist more than 1 from him
    if ($row < 1 || $row > 9 || $col < 1 || $col > 26 ||
        $row == $myRow || $col == $myCol ||
        $start > 1 && $board[$row][$col] eq "W")
    {
        return "";
    }

    $row =~ tr/1-9/A-I/;
    return "WALL $row $col";
}

sub hisLoc
{
    # update all his positional info
    local($row, $col)  = @_;
    $hisRowChange      = ($row-$hisRow) * $hisDir;
    $hisColChange      = ($col-$hisCol) * $hisDir;
    if ($hisRowChange || $hisColChange)
    {
        $idleSince = $turn;
    }
    local($distChange) = (&hisDistance($row, $col))[2];
    ($hisRow, $hisCol) = ($row, $col);
    ($hisHeight, $hisWidth, $hisDist) = &hisDistance($myRow, $myCol);
    $board[$hisRow][$hisCol] = "." if ($board[$hisRow][$hisCol] eq "W");

    if ($state == 3) # making my run
    {
        $moves = 0;
        return &zapProp();
    }
    # time to make my run if he can't possibly make it to my home now
    # and he's been idle for a bit
    elsif ($hisDist-1 > 100-$turn && $turn-$idleSince >= 2)
    {
        $state = 3;
        return &zapProp();
    }
    elsif ($hisHeight == 2 && $hisWidth == 2) # opposite me by 2 squares
    {
        $state = 1;
        return "RADAR"; # where is he now?
    }
    elsif ($hisHeight <= 2 && $hisWidth <= 2) # really close to me
    {
        # don't know whether he'll end up in my row or col - so alt zap
        # and hope I get him first

        $state   = 2;
        $zapBias = $hisHeight-$hisWidth < 0 ? -1 : 1;
        return &zap($zapBias);
    }
    elsif ($hisHeight <= 2) # nearing my row
    {
        $state = 1;
        local($a) = $hisWidth-2;
        local($b) = (100-$hisDist+2)-$turn;
        $fire = $a < $b ? $a : $b;
        return &zap(-1); # zap along row for awhile
    }
    elsif ($hisWidth <= 2) # nearing my col
    {
        $state = 1;
        local($a) = $hisHeight-2;
        local($b) = (100-$hisDist+2)-$turn;
        $fire = $a < $b ? $a : $b;
        return &zap(1); # zap along col for awhile
    }
    else # just keeping trying to rack up ouches
    {
        $state = 1;
        # drop the wall closer if he didn't move far or at all
        return &wall($distChange <= 1 ? 1 : 2);
    }
}

sub hisDistance
{
    local($row, $col) = @_;
    local($height) = abs($hisRow-$row);
    local($width)  = abs($hisCol-$col);
    return ($height, $width, $height+$width);
}

sub validLoc
{
    local($row, $col) = @_;
    return $row =~ /^[1-9]$/ &&
           $col =~ /^[1-9]\d?$/ && $col >= 1 && $col <= 26;
}

sub save
{
    open(TMP, ">$ARGV[0]")
        or die "$NAME: cannot open() $ARGV[0] for writing: $!\n";
    print TMP "$turn\n";
    print TMP "$state\n";
    print TMP "$myRow\n";
    print TMP "$myCol\n";
    print TMP "$myDir\n";
    print TMP "$command\n";
    print TMP "$hisRow\n";
    print TMP "$hisCol\n";
    print TMP "$hisDir\n";
    print TMP "$hisHomeRow\n";
    print TMP "$hisHomeCol\n";
    print TMP "$hisRowChange\n";
    print TMP "$hisColChange\n";
    print TMP "$moves\n";
    print TMP "$ouches\n";
    print TMP "$fire\n";
    print TMP "$zapBias\n";
    print TMP "$idleSince\n";
    for (local($row) = 1; $row <= 9; $row++)
    {
        for (local($col) = 1; $col <= 26; $col++)
        {
            print TMP $board[$row][$col];
        }
    }
    print TMP "\n";
    close(TMP);
}

sub restore
{
    open(TMP, "<$ARGV[0]")
        or die "$NAME: cannot open() $ARGV[0] for reading: $!\n";
    $turn         = ; chop $turn;
    $state        = ; chop $state;
    $myRow        = ; chop $myRow;
    $myCol        = ; chop $myCol;
    $myDir        = ; chop $myDir;
    $command      = ; chop $command;
    $hisRow       = ; chop $hisRow;
    $hisCol       = ; chop $hisCol;
    $hisDir       = ; chop $hisDir;
    $hisHomeRow   = ; chop $hisHomeRow;
    $hisHomeCol   = ; chop $hisHomeCol;
    $hisRowChange = ; chop $hisRowChange;
    $hisColChange = ; chop $hisColChange;
    $moves        = ; chop $moves;
    $ouches       = ; chop $ouches;
    $fire         = ; chop $fire;
    $zapBias      = ; chop $zapBias;
    $idleSince    = ; chop $idleSince;
    for (local($row) = 1; $row <= 9; $row++)
    {
        for (local($col) = 1; $col <= 26; $col++)
        {
            $board[$row][$col] = getc(TMP);
        }
    }
    close(TMP);

    ($opCode, $opRow, $opCol) = split(/\s+/, $command);
    $opRow =~ tr/A-I/1-9/;
    $command = "";
    ($hisHeight, $hisWidth, $hisDist) = &hisDistance($myRow, $myCol);
    $turn++;
    srand(time() + $turn);
}