
sub getData {
    my($pieceData, $boardData, $timerFile) = ('t', '.......... .......... .......... .......... .......... .......... .ll....jjt .jl....jtt .jl....jtt jjoo..oott ooool.ootz oolll..tzz jjj.t.ttzj oojtt.tt.j oosst.ttjj sssss.ttjj ssss.ittjj zz.tjjzzoo issoo.iooj .zzzzllloo', $_[0]);
    my $elapsed;
    if (defined $ENV{REQUEST_METHOD}) {
        die unless $ENV{REQUEST_METHOD} eq 'POST';
        local $/ = undef;
        my $arguments = <>;
        foreach my $argument (split(/&/o, $arguments)) {
            $argument =~ /^(.*?)(?:=(.*))?$/os or die;
            my $name = $1;
            my $value = $2;
            foreach my $string ($name, $value) {
                if (defined($string)) {
                    $string =~ tr/+/ /; # convert + to spaces
                    $string =~ s/% # a percent symbol
                                 ( # followed by
                    [0-9A-Fa-f]{2} # 2 hexidecimal characters
                                 ) # which we shall put in $1
                     /chr(hex($1)) # and convert back into a character
                            /egox; # (evaluate, globally, optimised, with comments)
                } else {
                    $string = '';
                }
                }
            if ($name eq 'piece') {
                $pieceData = $value;
            } elsif ($name eq 'board') {
                $boardData = $value;
            } else {
                warn "unknown argument '$name'\n";
            }
        }
        if ($boardData =~ m/^[ .]+$/os) {
            $elapsed = -1;
            open(TIMER, '>', $timerFile) or die;
            print TIMER time;
            close TIMER;
            warn "START OF GAME\n";
        } else {
            open(TIMER, '<', $timerFile) or die;
            $elapsed = time - <TIMER>;
            close TIMER;
        }
    } else {
        warn "DEBUGGING MODE\n";
        $elapsed = -2;
    }
    return ($pieceData, $boardData, $elapsed);
}

sub drop {
    my($piece, $board, $posx, $angle) = @_;
    my $pieceHeight = $piece->height($angle);
    my $boardHeight = $board->height;
    my $posy = $board->depth - $pieceHeight + 1; # we know it fits at this posy (might be off the board)
    while (($posy+$pieceHeight-1 < $boardHeight) and (not hasOverlap($piece, $board, $posx, $posy, $angle))) {
        $posy += 1;
    }
    return $posy-1;
}

sub hasOverlap {
    my($piece, $board, $posx, $posy, $angle) = @_;
    my $pieceWidth = $piece->width($angle);
    my $pieceHeight = $piece->height($angle);
    for (my $y = $pieceHeight-1; $y >= 0; $y -= 1) {
        next if $posy + $y < 0; # off the board
        for (my $x = 0; $x < $pieceWidth; $x += 1) {
            next if $piece->cell($angle, $x, $y) eq '.';
            next if $board->cell($posx + $x, $posy + $y) eq '.';
            return 1;
        }
    }
    return 0;
}

sub countOverhang {
    my($piece, $board, $posx, $posy, $angle) = @_;
    my $pieceWidth = $piece->width($angle);
    my $pieceHeight = $piece->height($angle);
    my $boardHeight = $board->height;
    my $count = 0;
    for (my $x = 0; $x < $pieceWidth; $x += 1) {
        # find lowest filled square on piece
        my $lowestPieceY;
        for (my $y = $pieceHeight-1; $y >= 0; $y -= 1) {
            if ($piece->cell($angle, $x, $y) ne '.') {
                $lowestPieceY = $y;
                last;
            }
        }
        # starting below that square and going down, count empties
        overhang: for (my $y = $posy + $lowestPieceY + 1; $y < $boardHeight; $y += 1) {
            if ($board->cell($posx + $x, $y) eq '.') {
                $count += 1;
            } else {
                last overhang;
            }
        }
    }
    return $count;
}

sub countPotential {
    my($piece, $board, $posx, $posy, $angle) = @_;
    my $pieceWidth = $piece->width($angle);
    my $pieceHeight = $piece->height($angle);
    my $boardHeight = $board->height;
    my $boardWidth = $board->width;
    my $count = 0;
    for (my $x = 0; $x < $pieceWidth; $x += 1) {
        # find lowest filled square on piece
        my $lowestPieceY;
        for (my $y = $pieceHeight-1; $y >= 0; $y -= 1) {
            if ($piece->cell($angle, $x, $y) ne '.') {
                $lowestPieceY = $y;
                last;
            }
        }
        # starting below that square and going down, count '.'s in lines that have but one '.'
        overhang: for (my $y = $posy + $lowestPieceY + 1; $y < $boardHeight; $y += 1) {
            if ($board->cell($posx + $x, $y) eq '.') {
                if ($board->blankCount($y) > 1) {
                    next overhang;
                }
                $count += 1;
            } else {
                last overhang;
            }
        }
    }
    return $count;
}

sub countChasms {
    my($piece, $board, $posx, $posy, $angle) = @_;
    my $pieceWidth = $piece->width($angle);
    my $pieceHeight = $piece->height($angle);
    my $boardHeight = $board->height;
    my $boardWidth = $board->width;
    my $count = 0;
    for (my $boardy = $posy; $boardy < $posy+$pieceHeight; $boardy += 1) {
        my $piecey = $boardy - $posy;
        # find first filled square in piece
        my $firstx = $posx;
        for (my $piecex = 0; $piecex < $pieceWidth; $piecex += 1) {
            if ($piece->cell($angle, $piecex, $piecey) ne '.') {
                last;
            }
            $firstx += 1;
        }
        # see if there is a chasm to the left
        if ($firstx > 0) {
            if ($board->cell($firstx-1, $boardy) eq '.' and
                ($firstx == 1 or $board->cell($firstx-2, $boardy) ne '.')) {
                $count += 1;
                # see if it extends further down [1]
                for ($suby = $boardy; $suby < $boardHeight; $suby += 1) {
                    if ($board->cell($firstx-1, $suby) eq '.' and
                        ($firstx == 1 or $board->cell($firstx-2, $suby) ne '.') and
                        $board->cell($firstx, $suby) ne '.') {
                        $count += 1;
                    } else {
                        last;
                    }
                }
            }
        }
        # find last filled square in piece
        my $lastx = $posx+$pieceWidth-1;
        for (my $piecex = $pieceWidth-1; $piecex >= 0; $piecex -= 1) {
            if ($piece->cell($angle, $piecex, $piecey) ne '.') {
                last;
            }
            $lastx -= 1;
        }
        # see if there is a chasm to the right
        if ($lastx < $boardWidth-1) {
            if ($board->cell($lastx+1, $boardy) eq '.' and
                ($lastx == $boardWidth-2 or $board->cell($lastx+2, $boardy) ne '.')) {
                $count += 1;
                # see if it extends further down [1]
                for ($suby = $boardy; $suby < $boardHeight; $suby += 1) {
                    if ($board->cell($lastx+1, $suby) eq '.' and
                        ($lastx == $boardWidth-2 or $board->cell($lastx+2, $suby) ne '.') and
                        $board->cell($lastx, $suby) ne '.') {
                        $count += 1;
                    } else {
                        last;
                    }
                }
            }
        }
        # [1] note that this will only find anything for the bottom
        # most chasm-creating row of a piece, since the others would
        # find '.' on the board where the piece will be
    }
    return $count;
}

sub countScore {
    my($piece, $board, $posx, $posy, $angle) = @_;
    my $pieceWidth = $piece->width($angle);
    my $pieceHeight = $piece->height($angle);
    my $boardWidth = $board->width;
    my $count = 0;
    line: for (my $y = $posy; $y < $posy + $pieceHeight; $y += 1) {
        for (my $x = 0; $x < $boardWidth; $x += 1) {
            if (($board->cell($x, $y) eq '.') and
                (($x < $posx) or
                 ($x >= $posx + $pieceWidth) or
                 ($piece->cell($angle, $x - $posx, $y - $posy) eq '.'))) {
                next line;
            }
        }
        $count += 1;
    }
    return $count;
}

sub expandScore {
    return 0 if $_[0] == 0;
    return 10 if $_[0] == 1;
    return 25 if $_[0] == 2; # 5 point bonus
    return 40 if $_[0] == 3; # 10 point bonus
    return 55 if $_[0] == 4; # 15 point bonus
    die;
}

1;
