Location: PHPKode > scripts > PHP Chess > php-chess/class.phpchess.php
<?php
/**
 * PHPChess 1.0
 *
 * Online chess game
 *
 * @author: Carlos Reche
 * @e-mail: hide@address.com
 *
 * Jan 31, 2005
 *
 *
 *
 * Feb 7, 05 - bug reported and fixed by Ulrik S. Kofod,
 *             on function PHPChess_Board::movePiece(), line 451
 * Feb 8, 05 - grammars corrections sent by Randy Rowe
 * Mar 14, 06 - bug reported and fixed by Pablo Díaz Anzalone and
 *              João Antônio Navarini, when runing on servers with 
 *              "magic quotes" activated.
 */





/**
 *  class PHPChess
 *
 *  Main class. Will control all functionalities of the other classes.
 */
class PHPChess
{

    /*@#+
    *  @access private
    */
    var $board;     // (object) Board object. Contains all informations of the board squares and pieces

    var $interface; // (object) Displays the chess board and HTML form
    //@#+



    /*
    *  @function PHPChess()
    *
    *  @access public
    *
    *  Contructor.
    */
    function PHPChess()
    {
        $this->board     = new PHPChess_Board();
        $this->interface = new PHPChess_Interface($this->board);



        if (isset($_POST['white']))
        {
            $this->board->white->email = trim($_POST['white']);
        }
        if (isset($_POST['black']))
        {
            $this->board->black->email = trim($_POST['black']);
        }



        if (isset($_POST['mov_start'], $_POST['mov_end']))
        {
            if (preg_match("/^[A-H][1-8]$/i", $_POST['mov_start'])  &&  preg_match("/^[A-H][1-8]$/i", $_POST['mov_end']))
            {
                $x_start = $this->interface->valueOfX($_POST['mov_start']{0});
                $y_start = $_POST['mov_start']{1};
                $x_end   = $this->interface->valueOfX($_POST['mov_end']{0});
                $y_end   = $_POST['mov_end']{1};

                $this->confirmMove($x_start, $y_start, $x_end, $y_end);
            }
            else
            {
                $this->interface->error("Invalid move: <strong>" . $_POST['mov_start'] . $_POST['mov_end'] . "</strong>");
            }
        }
    }





    /*
    *  @function setImageDir( string $dir )
    *
    *  @access public
    *
    *  Defines a directory for pieces images. It must be a valid directory, and image names
    *  should be: "white_king.png", "black_king.png", "white_queen.png" etc.
    *  Pieces names are "king", "queen", "bishop", "knight", "rook" and "pawn".
    */
    function setImageDir($dir)
    {
        $dir = (string)$dir;

        if (!is_dir($dir))
        {
            $this->interface->error("Image dir does not exist.");
            return false;
        }

        if (!preg_match("/^.*?\\/$/", $dir))
        {
            $dir .= "/";
        }

        $this->interface->image_dir = $dir;
        return true;
    }





    /*
    *  @function showCoordinates( bool $show )
    *
    *  @access public
    *
    *  Sets if should display the square coordinates
    */
    function showCoordinates($show = true)
    {
        $this->interface->show_coordinates = (bool)$show;
    }





    /*
    *  @function squareSize( bool $show )
    *
    *  @access public
    *
    *  Sets the size (in pixels) of the board squares
    */
    function squareSize($size)
    {
        $this->interface->square_size = (int)$size;
    }





    /*
    *  @function board( [ bool $return ] )
    *
    *  @access public
    *
    *  Prints the HTML code of the board. If you want the HTML code to be returned as a string,
    *  not printed, set the boolean parameter $return to TRUE.
    */
    function board($bool_return = false)
    {
        return $this->interface->board($bool_return);
    }



    /*
    *  @function form( [ bool $return ] )
    *
    *  @access public
    *
    *  Prints the HTML code of the form. If you want the HTML code to be returned as a string,
    *  not printed, set the boolean parameter $return to TRUE.
    */
    function form($bool_return = false)
    {
        return $this->interface->form($bool_return);
    }



    /*
    *  @function message( [ bool $return ] )
    *
    *  @access public
    *
    *  Prints a message from the system.
    */
    function message($bool_return = false)
    {
        return $this->interface->message($bool_return);
    }



    /*
    *  @function setSMTP [ bool $return ] )
    *
    *  @access public
    *
    *  Defines a SMTP server to send emails.
    */
    function setSMTP($smtp)
    {
        $this->interface->setSMTP($smtp);
    }



    /*
    *  @function confirmMove( int $x_start, int $y_start, int $x_end, int $y_end )
    *
    *  @access private
    *
    *  Called in class constructor if player has moved a piece.
    */
    function confirmMove($x_start, $y_start, $x_end, $y_end)
    {
        $mail_pattern = "/^\s*[\w\d]+([.-][\w\d]+)*@[\w\d]+([.-][\w\d]+)*\\.[\w\d]{2,4}\s*$/i";

        if (!preg_match($mail_pattern, $this->board->white->email)  ||  !preg_match($mail_pattern, $this->board->black->email))
        {
            $this->interface->setMessage("<strong>Error!</strong> There seems to be at least one invalid email address!");
            return false;
        }



        $square =& $this->board->square[$x_start][$y_start];
        $piece  =& $square->piece;


        if (!$square->hasPiece()  ||  ($piece->color != $this->board->turn()))
        {
            $this->interface->setMessage("Please move a <strong>" . $this->board->turn() . "</strong> piece.");
            return false;
        }

        else if (!$piece->validateMove($this->board, $x_end, $y_end))
        {
            $move = $this->interface->valueOfX($x_start) . $y_start . $this->interface->valueOfX($x_end) . $y_end;
            $this->interface->setMessage("<strong>Notice!</strong> " . $move . " is not a valid move.");
            return false;
        }

        else if (!$this->board->movePiece($x_start, $y_start, $x_end, $y_end))
        {
            $this->interface->setMessage("<strong>Notice!</strong> Your King can not be on a square controlled by the opponent.");
            return false;
        }


        $this->board->has_moved = true;

        $this->board->verifyCheckMate();

        return true;
    }
}







class PHPChess_Board
{

    var $square;   // (array)  Multi-dimensional array of objects (type: PHPChess_Square)

    var $white;    // (object) White player (type: PHPChess_Player)
    var $black;    // (object) Black player (type: PHPChess_Player)
    var $user;     // (object) Alias to $white or $black, depending of the turn
    var $opponent; // (object) Alias to $white or $black, depending of the turn

    var $history;       // (array)  Game history
    var $removed_piece; // (object) Reference to the object of the last removed piece, or false if the square was empty

    var $has_moved;     // (bool) Will be "true" if the player has maden a move, or "false" if hasn't.
    var $is_check;      // (bool) Will be "true" if $user King is under attack
    var $is_check_mate; // (bool) Will be "true" if $user capture $opponent's King
    var $is_stalemate;  // (bool) If the player who must move has no legal move (and is NOT in check), the game ends as a draw (a stalemate)




    function PHPChess_Board()
    {
        $this->square = array();

        $this->white    =  new PHPChess_Player("white");
        $this->black    =  new PHPChess_Player("black");
        $this->user     =& $this->white;
        $this->opponent =& $this->black;

        $this->history       = array();
        $this->removed_piece = false;

        $this->has_moved     = false;
        $this->is_check      = false;
        $this->is_check_mate = false;
        $this->is_draw       = false;



        // Fill array $this->square with objects (type: PHPChess_Square)
        for ($x = 1; $x <= 8; $x++)
        {
            if (!isset($this->square[$x]))
            {
                $this->square[$x] = array();
            }

            for ($y = 1; $y <= 8; $y++)
            {
                $this->square[$x][$y] = new PHPChess_Square($x, $y);
            }
        }


        // Adds pieces to the square objects (default start positions)
        $this->addRook("black", 1, 8);
        $this->addKnight("black", 2, 8);
        $this->addBishop("black", 3, 8);
        $this->addQueen("black", 4, 8);
        $this->addKing("black", 5, 8);
        $this->addBishop("black", 6, 8);
        $this->addKnight("black", 7, 8);
        $this->addRook("black", 8, 8);

        $this->addRook("white", 1, 1);
        $this->addKnight("white", 2, 1);
        $this->addBishop("white", 3, 1);
        $this->addQueen("white", 4, 1);
        $this->addKing("white", 5, 1);
        $this->addBishop("white", 6, 1);
        $this->addKnight("white", 7, 1);
        $this->addRook("white", 8, 1);

        for ($i = 1; $i <= 8; $i++)
        {
            $this->addPawn("black", $i, 7);
            $this->addPawn("white", $i, 2);
        }



        if (isset($_GET['game_data']))
        {
            $this->unpackData($_GET['game_data']);
        }

        $this->updateData();
    }



    function turn($this_user = true)
    {
        return ((count($this->history) % 2 == (($this_user) ? 0 : 1))  ?  "white"  :  "black");
    }



    function movePiece($x_start, $y_start, $x_end, $y_end, $really_move = true)
    {
        if (!$this->square[$x_start][$y_start]->hasPiece())
        {
            return false;
        }


        // Grabs the pieces, removing them from the board
        $piece_mov =& $this->removePiece($x_start, $y_start);
        $piece_cap =& $this->removePiece($x_end, $y_end);


        // Checks for "en passant" capture
        if ($piece_mov->isPawn()  &&  (abs($x_start - $x_end) == 1)  &&  !$piece_cap)
        {
            $piece_cap =& $this->removePiece($x_end, $y_start);
        }


        // Puts the piece on the target square
        $this->square[$x_end][$y_end]->piece =& $piece_mov;


        // Updates the squares controlled by the opponent
        $this->updateData();



        // Checks if the King did not stay on a square that is under attack by moving the piece

        if (!$piece_mov->isKing())
        {
            $king   =& $this->{$piece_mov->color}->pieces['king'];
            $king_square =& $this->square[$king->x][$king->y];
        }
        else
        {
            $king_square =& $this->square[$x_end][$y_end];
        }

        foreach ($king_square->controlled_by as $p)
        {
            if ($p->color != $piece_mov->color)
            {
                $this->removePiece($x_end, $y_end);

                $this->square[$piece_mov->x][$piece_mov->y]->piece =& $piece_mov;

                if ($piece_cap)
                {
                    $this->square[$piece_cap->x][$piece_cap->y]->piece =& $piece_cap;
                }

                return false;
            }
        }


        // If was only checking
        if (!$really_move)
        {
            $this->removePiece($x_end, $y_end);

            $this->square[$piece_mov->x][$piece_mov->y]->piece =& $piece_mov;

            if ($piece_cap)
            {
                $this->square[$piece_cap->x][$piece_cap->y]->piece =& $piece_cap;
            }

            return true;
        }


        $this->removed_piece =& $piece_cap;


        $piece_mov->x = $x_end;
        $piece_mov->y = $y_end;
        $piece_mov->num_moves++;




        /**
         * bug reported and fixed by Ulrik S. Kofod
         * Feb 9, 2005
         *
         * if your king were under attack, there was an specific situation
         * that let you move another piece even if your king still were under
         * check.
         */
        if ($piece_mov->isKing())
        {
            $this->{$piece_mov->color}->pieces['king']->x = $x_end;
            $this->{$piece_mov->color}->pieces['king']->y = $y_end;
        }





        // Checks for "castling"
        if ($piece_mov->isKing()  &&  (abs($x_start - $x_end) == 2))
        {
            if ($x_start > $x_end)
            {
                $rook =& $this->removePiece(1, $piece_mov->y);
                $this->square[4][$piece_mov->y]->piece =& $rook;

                $rook->x = 4;
            }
            else
            {
                $rook =& $this->removePiece(8, $piece_mov->y);
                $this->square[6][$piece_mov->y]->piece =& $rook;

                $rook->x = 6;
            }

            $rook->num_moves++;
        }

        // Checks for "queening"
        else if ($piece_mov->isPawn()  &&  (($piece_mov->y == 1)  ||  ($piece_mov->y == 8)))
        {
            $this->addQueen($piece_mov->color, $piece_mov->x, $piece_mov->y, $piece_mov->num_moves);
        }


        $this->history[] = $x_start . $y_start . $x_end . $y_end;

        return true;
    }



    function removePiece($x, $y)
    {
        $removed_piece =& $this->square[$x][$y]->piece;

        // Destroys only the square reference to the piece, without changing the piece value to "false"
        unset($this->square[$x][$y]->piece);
        $this->square[$x][$y]->piece = false;

        return $removed_piece;
    }





    function verifyCheckMate()
    {
        $this->is_check      = false;
        $this->is_check_mate = false;
        $this->is_draw       = false;


        $player =& $this->{$this->turn()};


        $king        =& $player->pieces['king'];
        $king_square =& $this->square[$king->x][$king->y];


        $this->updateData();



        foreach ($king_square->controlled_by as $piece)
        {
            if ($piece->color != $king->color)
            {
                $this->is_check = true;
                break;
            }
        }



        foreach ($king->controlled_squares as $move)
        {
            if ($king->validateMove($this, $move{0}, $move{1}))
            {
                if ($this->movePiece($king->x, $king->y, $move{0}, $move{1}, false))
                {
                    return false;
                }
            }
        }



        for ($x = 1; $x <= 8; $x++)
        {
            for ($y = 1; $y <= 8; $y++)
            {
                $square =& $this->square[$x][$y];
                $piece  =& $square->piece;

                if (!$square->hasPiece()  ||  ($piece->color != $king->color)  ||  $piece->isKing())
                {
                    continue;
                }


                foreach ($piece->controlled_squares as $move)
                {
                    $x_start = $piece->x;
                    $y_start = $piece->y;
                    $x_end   = $move{0};
                    $y_end   = $move{1};

                    if ($this->movePiece($x_start, $y_start, $x_end, $y_end, false))
                    {
                        return false;
                    }

                }
            }
        }


        if ($this->is_check)
        {
            $this->is_check_mate = true;
            return true;
        }
        else
        {
            $this->is_draw = true;
            return false;
        }
    }





    function addKing($color, $x, $y, $num_moves = 0)
    {
        $this->square[$x][$y]->piece =& $this->$color->addPiece(new PHPChess_King($color, $x, $y, $num_moves));
    }

    function addQueen($color, $x, $y, $num_moves = 0)
    {
        $this->square[$x][$y]->piece =& $this->$color->addPiece(new PHPChess_Queen($color, $x, $y, $num_moves));
    }

    function addBishop($color, $x, $y, $num_moves = 0)
    {
        $this->square[$x][$y]->piece =& $this->$color->addPiece(new PHPChess_Bishop($color, $x, $y, $num_moves));
    }

    function addKnight($color, $x, $y, $num_moves = 0)
    {
        $this->square[$x][$y]->piece =& $this->$color->addPiece(new PHPChess_Knight($color, $x, $y, $num_moves));
    }

    function addRook($color, $x, $y, $num_moves = 0)
    {
        $this->square[$x][$y]->piece =& $this->$color->addPiece(new PHPChess_Rook($color, $x, $y, $num_moves));
    }

    function addPawn($color, $x, $y, $num_moves = 0)
    {
        $this->square[$x][$y]->piece =& $this->$color->addPiece(new PHPChess_Pawn($color, $x, $y, $num_moves));
    }





    // Updates all relevant data for each move (sets all the squares controlled by the opponent)
    function updateData()
    {
        $opponent_color = $this->turn(false);

        for ($x = 1; $x <= 8; $x++)
        {
            for ($y = 1; $y <= 8; $y++)
            {
                $this->square[$x][$y]->controlled_by = array();
            }
        }

        for ($x = 1; $x <= 8; $x++)
        {
            for ($y = 1; $y <= 8; $y++)
            {
                $square =& $this->square[$x][$y];

                if ($square->hasPiece()  &&  ($square->piece->color == $opponent_color))
                {
                    $square->piece->getControlledSquares($this);
                }
            }
        }
    }





    function packData()
    {
        $packed_data = array(
            'history' => implode("-", $this->history),
            'white'   => $this->white->email,
            'black'   => $this->black->email,
        );

        return urlencode(serialize($packed_data));
    }





    function unpackData($packed_data)
    {
        if (!is_string($packed_data))
        {
            return false;
        }

        $data = unserialize(stripslashes($packed_data));


        $this->white->email = $data['white'];
        $this->black->email = $data['black'];


        $history = explode("-", $data['history']);

        foreach ($history as $i => $move)
        {
            if (!preg_match("/^[1-8]{4}$/", $move))
            {
                continue;
            }

            $this->movePiece($move{0}, $move{1}, $move{2}, $move{3});
        }


        $this->user     =& $this->{$this->turn(true)};
        $this->opponent =& $this->{$this->turn(false)};

        $this->verifyCheckMate();

        return true;
    }
}







class PHPChess_Player
{
    var $color;  // (string) Pieces color
    var $pieces; // (array) Array of objects. Each element is a piece of this player

    var $email;  // (string) Player's e-mail



    function PHPChess_Player($color, $email = "")
    {
        $this->color  = (string)$color;
        $this->pieces = array();
        $this->email  = (string)$email;
    }



    function addPiece($piece)
    {
        if ($piece->isKing())
        {
            return $this->pieces['king'] = $piece;
        }
        else
        {
            return $this->pieces[] = $piece;
        }
    }



    function setEmail($email)
    {
        $this->email = (string)$email;
    }
}







class PHPChess_Square
{
    var $x;             // (int)   Square X coordinate
    var $y;             // (int)   Square Y coordinate
    var $piece;         // (mixed) Will be "false" if the square is empty, or object if it has a piece
    var $controlled_by; // (array) Array with the opponent's pieces that control this square

    function PHPChess_Square($x, $y)
    {
        $this->x             = (int)$x;
        $this->y             = (int)$y;
        $this->piece         = false;
        $this->controlled_by = array();
    }

    function hasPiece()
    {
        return (bool)$this->piece;
    }
}







class PHPChess_Piece
{
    var $color;              // (string)
    var $name;               // (string)
    var $x;                  // (int)
    var $y;                  // (int)
    var $num_moves;          // (int)
    var $controlled_squares; // (array) Squares this piece may walk to

    function PHPChess_Piece($color, $x, $y, $num_moves = 0)
    {
        $this->color              = (string)$color;
        $this->name               = "";
        $this->x                  = (int)$x;
        $this->y                  = (int)$y;
        $this->num_moves          = (int)$num_moves;
        $this->controlled_squares = array();
    }

    function getName()
    {
        return strtolower(preg_replace("/^PHPChess_/i", "", get_class($this)));
    }
    function isKing()
    {
        return preg_match("/king/i", get_class($this));
    }
    function isQueen()
    {
        return preg_match("/queen/i", get_class($this));
    }
    function isBishop()
    {
        return preg_match("/bishop/i", get_class($this));
    }
    function isKnight()
    {
        return preg_match("/knight/i", get_class($this));
    }
    function isRook()
    {
        return preg_match("/rook/i", get_class($this));
    }
    function isPawn()
    {
        return preg_match("/pawn/i", get_class($this));
    }
}





class PHPChess_King extends PHPChess_Piece
{
    function PHPChess_King($color, $x, $y)
    {
        parent::PHPChess_Piece($color, $x, $y);
    }



    // Controlled squares are those controlled by this piece for this or the next turn. They could be blank
    // squares, a square with an opponent's piece on it, or even a square with a piece of the same color
    // (because if the piece is captured, that square will be controlled on the next turn).
    function validateMove(&$board, $x, $y, $piece_turn = true)
    {
        if (($x < 1)  ||  ($x > 8)  ||  ($y < 1)  ||  ($y > 8))
        {
            return false;
        }


        $x_variation = abs($this->x - $x);
        $y_variation = abs($this->y - $y);


        if ($x_variation == 0)
        {
            if ($y_variation != 1)
            {
                return false;
            }
        }
        else if ($x_variation == 1)
        {
            if (($y_variation != 0)  &&  ($y_variation != 1))
            {
                return false;
            }
        }

        // Castling
        else if (($x_variation == 2)  &&  ($this->num_moves == 0))
        {
            if ($y_variation != 0)
            {
                return false;
            }


            if ($x < $this->x)
            {
                // If King walked to "A" file, checks if the square beside the Rook is vacant
                if ($board->square[2][$this->y]->hasPiece())
                {
                    return false;
                }

                $rook =& $board->square[1][$this->y]->piece;
                $_x = -1;
            }
            else
            {
                $rook =& $board->square[8][$this->y]->piece;
                $_x = +1;
            }

            // Checks if rook has not moved yet
            if ($rook  &&  $rook->isRook()  &&  ($rook->num_moves == 0))
            {
                // Checks if king is not under attack
                foreach ($board->square[$this->x][$this->y]->controlled_by as $p)
                {
                    if ($p->color != $this->color)
                    {
                        return false;
                    }
                }

                // Checks if all of the squares between the king and rook are vacant
                // Checks if king is not passing through or arriving upon a square controlled by the opponent
                for ($i = 1; $i <= 2; $i++)
                {
                    $square =& $board->square[  ($this->x + ($i * $_x))  ][$this->y];

                    if ($square->hasPiece())
                    {
                        return false;
                    }

                    foreach ($square->controlled_by as $p)
                    {
                        if ($p->color != $this->color)
                        {
                            return false;
                        }
                    }
                }

                return true;
            }

            return false;
        }

        else
        {
            // $x_variation is not 0, 1 or 2
            return false;
        }


        $square =& $board->square[$x][$y];

        if (($this->color != $board->turn())  ||  (count($square->controlled_by) == 0))
        {
            if (!$square->hasPiece()  ||  ($square->piece->color != $this->color)  ||  !$piece_turn)
            {
                return true;
            }
        }

        return false;
    }



    function getControlledSquares(&$board)
    {
        $this->controlled_squares = array();

        $piece_turn = ($this->color == $board->turn());


        for ($x = ($this->x - 1); $x <= ($this->x + 1); $x++)
        {
            for ($y = ($this->y - 1); $y <= ($this->y + 1); $y++)
            {
                if ($this->validateMove($board, $x, $y, $piece_turn))
                {

                    $this->controlled_squares[] = $x . $y;

                    if (!$piece_turn)
                    {
                        $board->square[$x][$y]->controlled_by[] =& $this;
                    }
                }
            }
        }


        if ($this->num_moves == 0)
        {
            for ($x = ($this->x - 2); $x <= ($this->x + 2); $x += 4)
            {
                if ($this->validateMove($board, $x, $this->y, $piece_turn))
                {
                    $this->controlled_squares[] = $x . $this->y;
                }
            }
        }

        return $this->controlled_squares;
    }
}





class PHPChess_Queen extends PHPChess_Piece
{
    function PHPChess_Queen($color, $x, $y)
    {
        parent::PHPChess_Piece($color, $x, $y);
    }



    function validateMove(&$board, $x, $y, $piece_turn = true)
    {
        if (($x < 1)  ||  ($x > 8)  ||  ($y < 1)  ||  ($y > 8))
        {
            return false;
        }

        $x_variation = abs($this->x - $x);
        $y_variation = abs($this->y - $y);


        if (($x_variation == 0)  &&  ($y_variation == 0))
        {
            return false;
        }
        else if (($x_variation != 0)  &&  ($y_variation != 0))
        {
            if ($x_variation != $y_variation)
            {
                return false;
            }
        }

        $_x = ($x < $this->x)  ?  -1  :  (($x > $this->x)  ?  +1  :  0);
        $_y = ($y < $this->y)  ?  -1  :  (($y > $this->y)  ?  +1  :  0);

        $i = $this->x + $_x;
        $j = $this->y + $_y;

        while (($i != $x)  ||  ($j != $y))
        {
            if ($board->square[$i][$j]->hasPiece())
            {
                return false;
            }

            $i += $_x;
            $j += $_y;
        }

        $square =& $board->square[$x][$y];

        if (!$square->hasPiece()  ||  ($square->piece->color != $this->color)  ||  !$piece_turn)
        {
            return true;
        }

        return false;
    }



    function getControlledSquares(&$board)
    {
        $this->controlled_squares = array();

        $piece_turn = ($this->color == $board->turn());


        $x_directions = $y_directions = array(-1, 0, 1);

        foreach ($x_directions as $_x)
        {
            foreach ($y_directions as $_y)
            {
                if (($_x == 0)  &&  ($_y == 0))
                {
                    continue;
                }

                $x = $this->x + $_x;
                $y = $this->y + $_y;

                while ($this->validateMove($board, $x, $y, $piece_turn))
                {

                    $this->controlled_squares[] = $x . $y;

                    if (!$piece_turn)
                    {
                        $board->square[$x][$y]->controlled_by[] =& $this;
                    }


                    $x += $_x;
                    $y += $_y;
                }
            }
        }

        return $this->controlled_squares;
    }
}





class PHPChess_Bishop extends PHPChess_Piece
{
    function PHPChess_Bishop($color, $x, $y)
    {
        parent::PHPChess_Piece($color, $x, $y);
    }



    function validateMove(&$board, $x, $y, $piece_turn = true)
    {
        if (($x < 1)  ||  ($x > 8)  ||  ($y < 1)  ||  ($y > 8))
        {
            return false;
        }

        $x_variation = abs($this->x - $x);
        $y_variation = abs($this->y - $y);


        if (($x_variation == 0)  ||  ($y_variation == 0)  ||  ($x_variation != $y_variation))
        {
            return false;
        }

        $_x = ($x < $this->x)  ?  -1  :  +1;
        $_y = ($y < $this->y)  ?  -1  :  +1;

        $i = $this->x + $_x;
        $j = $this->y + $_y;

        while (($i != $x)  ||  ($j != $y))
        {
            if ($board->square[$i][$j]->hasPiece())
            {
                return false;
            }

            $i += $_x;
            $j += $_y;
        }

        $square =& $board->square[$x][$y];

        if (!$square->hasPiece()  ||  ($square->piece->color != $this->color)  ||  !$piece_turn)
        {
            return true;
        }

        return false;
    }



    function getControlledSquares(&$board)
    {
        $this->controlled_squares = array();

        $piece_turn = ($this->color == $board->turn());


        $x_directions = $y_directions = array(-1, 1);

        foreach ($x_directions as $_x)
        {
            foreach ($y_directions as $_y)
            {
                $x = $this->x + $_x;
                $y = $this->y + $_y;

                while ($this->validateMove($board, $x, $y, $piece_turn))
                {

                    $this->controlled_squares[] = $x . $y;

                    if (!$piece_turn)
                    {
                        $board->square[$x][$y]->controlled_by[] =& $this;
                    }


                    $x += $_x;
                    $y += $_y;
                }
            }
        }

        return $this->controlled_squares;
    }
}





class PHPChess_Knight extends PHPChess_Piece
{
    function PHPChess_Knight($color, $x, $y)
    {
        parent::PHPChess_Piece($color, $x, $y);
    }



    function validateMove(&$board, $x, $y, $piece_turn = true)
    {
        if (($x < 1)  ||  ($x > 8)  ||  ($y < 1)  ||  ($y > 8))
        {
            return false;
        }

        $x_variation = abs($this->x - $x);
        $y_variation = abs($this->y - $y);


        if (  !((($x_variation == 1) && ($y_variation == 2))  ||  (($x_variation == 2) && ($y_variation == 1)))  )
        {
            return false;
        }


        $square =& $board->square[$x][$y];

        if (!$square->hasPiece()  ||  ($square->piece->color != $this->color)  ||  !$piece_turn)
        {
            return true;
        }

        return false;
    }



    function getControlledSquares(&$board)
    {
        $this->controlled_squares = array();

        $piece_turn = ($this->color == $board->turn());


        $x_positions = $y_positions = array(-2, -1, 1, 2);

        foreach ($x_positions as $_x)
        {
            foreach ($y_positions as $_y)
            {
                if (abs($_x) == abs($_y))
                {
                    continue;
                }

                $x = $this->x + $_x;
                $y = $this->y + $_y;

                if ($this->validateMove($board, $x, $y, $piece_turn))
                {

                    $this->controlled_squares[] =  $x . $y;

                    if (!$piece_turn)
                    {
                        $board->square[$x][$y]->controlled_by[] =& $this;
                    }
                }
            }
        }

        return $this->controlled_squares;
    }
}





class PHPChess_Rook extends PHPChess_Piece
{
    function PHPChess_Rook($color, $x, $y)
    {
        parent::PHPChess_Piece($color, $x, $y);
    }



    function validateMove(&$board, $x, $y, $piece_turn = true)
    {
        if (($x < 1)  ||  ($x > 8)  ||  ($y < 1)  ||  ($y > 8))
        {
            return false;
        }

        $x_variation = abs($this->x - $x);
        $y_variation = abs($this->y - $y);


        if (!(($x_variation == 0)  ^  ($y_variation == 0)))
        {
            return false;
        }

        $_x = ($x < $this->x)  ?  -1  :  (($x > $this->x)  ?  +1  :  0);
        $_y = ($y < $this->y)  ?  -1  :  (($y > $this->y)  ?  +1  :  0);

        $i = $this->x + $_x;
        $j = $this->y + $_y;

        while (($i != $x)  ||  ($j != $y))
        {
            if ($board->square[$i][$j]->hasPiece())
            {
                return false;
            }

            $i += $_x;
            $j += $_y;
        }

        $square =& $board->square[$x][$y];

        if (!$square->hasPiece()  ||  ($square->piece->color != $this->color)  ||  !$piece_turn)
        {
            return true;
        }

        return false;

    }



    function getControlledSquares(&$board)
    {
        $this->controlled_squares = array();

        $piece_turn = ($this->color == $board->turn());


        $x_directions = $y_directions = array(-1, 0, 1);

        foreach ($x_directions as $_x)
        {
            foreach ($y_directions as $_y)
            {
                if (!(($_x == 0)  ^  ($_y == 0)))
                {
                    continue;
                }

                $x = $this->x + $_x;
                $y = $this->y + $_y;

                while ($this->validateMove($board, $x, $y, $piece_turn))
                {

                    $this->controlled_squares[] =  $x . $y;

                    if (!$piece_turn)
                    {
                        $board->square[$x][$y]->controlled_by[] =& $this;
                    }


                    $x += $_x;
                    $y += $_y;
                }
            }
        }

        return $this->controlled_squares;
    }
}





class PHPChess_Pawn extends PHPChess_Piece
{
    function PHPChess_Pawn($color, $x, $y)
    {
        parent::PHPChess_Piece($color, $x, $y);
    }



    function validateMove(&$board, $x, $y, $piece_turn = true)
    {
        if (($x < 1)  ||  ($x > 8)  ||  ($y < 1)  ||  ($y > 8))
        {
            return false;
        }
        else if (($this->y < $y)  &&  ($this->color != "white")  ||  ($this->y > $y)  &&  ($this->color != "black"))
        {
            return false;
        }

        $x_variation = abs($this->x - $x);
        $y_variation = abs($this->y - $y);

        $_y = ($this->color == "white")  ?  +1  :  -1;


        if ($x_variation == 0)
        {
            $i = $this->x;
            $j = $this->y + $_y;

            $square =& $board->square[$i][$j];

            if (!$square->hasPiece())
            {
                if ($y_variation == 1)
                {
                    return true;
                }
                else if (($y_variation == 2)  &&  ($this->num_moves == 0))
                {
                    $j += $_y;

                    $square =& $board->square[$i][$j];

                    if (!$square->hasPiece())
                    {
                        return true;
                    }
                }
            }
        }

        else if ($x_variation == 1)
        {
            if ($y_variation == 1)
            {
                $square =& $board->square[$x][$y];

                if ($square->hasPiece())
                {
                    if (($square->piece->color != $this->color)  ||  !$piece_turn)
                    {
                        return true;
                    }
                }

                // "En passant" (capture while passing)
                else
                {
                    $square =& $board->square[$x][$this->y];

                    if ($square->hasPiece()  &&  $square->piece->isPawn()  &&  ($square->piece->color != $this->color))
                    {
                        $move =  end($board->history);
                        $pawn =& $square->piece;

                        if (($pawn->num_moves == 1)  &&  ($pawn->x == $move{2})  &&  ($pawn->y == $move{3}))
                        {
                            return true;
                        }
                    }
                }
            }
        }

        return false;
    }



    function getControlledSquares(&$board)
    {
        $this->controlled_squares = array();

        $piece_turn = ($this->color == $board->turn());


        $_y = ($this->color == "white")  ?  +1  :  -1;

        for ($x = ($this->x - 1); $x <= ($this->x + 1); $x += 2)
        {
            $y = $this->y;

            if ($this->validateMove($board, $x, $y, $piece_turn))
            {
                $this->controlled_squares[] = $x . $y;

                if (!$piece_turn)
                {
                    $board->square[$x][$y]->controlled_by[] =& $this;
                }
            }

            $y = $this->y + $_y;

            if ($this->validateMove($board, $x, $y, $piece_turn))
            {
                $this->controlled_squares[] = $x . $y;

                if (!$piece_turn)
                {
                    $board->square[$x][$y]->controlled_by[] =& $this;
                }
            }
        }


        $x = $this->x;
        $y = $this->y + $_y;

        if ($this->validateMove($board, $x, $y, $piece_turn))
        {
            $this->controlled_squares[] = $x . $y;

            if ($this->num_moves == 0)
            {
                $y += $_y;

                if ($this->validateMove($board, $x, $y, $piece_turn))
                {
                    $this->controlled_squares[] = $x . $y;
                }
            }
        }

        return $this->controlled_squares;
    }
}

























class PHPChess_Interface
{

    var $board;             // (object) An alias to "PHPChess::board" property

    var $script_file;       // (string)
    var $javascript_loaded; // (bool)
    var $smtp_server;       // (string)
    var $message;           // (string)

    var $image_dir;         // (string)
    var $square_size;       // (int)
    var $show_coordinates;  // (bool)




    function PHPChess_Interface(&$board)
    {
        if (!is_object($board))
        {
            $this->error("Wrong parameters for &quot;PHPChess::interface&quot; property!");
            return false;
        }

        $this->board =& $board;


        $this->javascript_loaded = false;
        $htis->smtp_server       = "";
        $this->message           = "";
        $this->script_file       = (isset($_SERVER['SCRIPT_NAME'])  ?  $_SERVER['SCRIPT_NAME']  :
                                   (isset($_SERVER['PHP_SELF'])     ?  $_SERVER['PHP_SELF']     :  "#"));

        $this->image_dir        = "";
        $this->square_size      = 35;
        $this->show_coordinates = false;
    }





    // Email users
    function emailPlayers()
    {
        if ($this->smtp_server != "")
        {
            @ini_set("SMTP", $this->smtp_server);
        }

        $sent = true;

        if (!isset($_SERVER['HTTP_HOST'])  ||  ($this->script_file == "#"))
        {
            $this->error("Internal server error! PHP Chess could not send an e-mail with the link to access the board.");
            return false;
        }


        if (isset($_SERVER['SERVER_PROTOCOL']))
        {
            $scheme = explode("/", strtolower($_SERVER['SERVER_PROTOCOL']));
            $link = $scheme[0] . "://" . $_SERVER['HTTP_HOST'];
        }
        else
        {
            $link = "http://" . $_SERVER['HTTP_HOST'];
        }

        $link .= (!preg_match("/^\\//s", $this->script_file))  ?  "/"  :  "";
        $link .= $this->script_file . "?game_data=" . $this->board->packData();



        // Email opponent
        $headers  = "From: # PHP Chess <" . $this->board->user->email . ">\r\n";
        $headers .= "Reply-To: " . $this->board->user->email . "\r\n";
        $headers .= "X-Mailer: PHP/" . phpversion();

        if (count($this->board->history) == 1)
        {
            $subject  = $this->board->user->email . " has invited you to start a new chess game!";

            $message  = "PHP Chess 1.0\n==================================================\n\n";
            $message .= "Your friend at " . $this->board->user->email . " has invited you to start a new ";
            $message .= "chess game! Click on the link\n" . $link . "\nto go to the board and move your piece.\n\n";
            $message .= (!empty($_POST['message'])) ? ("Opponent's message:\n\"".$_POST['message']."\"\n\n") : "";
            $message .= "__________________________________________________\nPHP Chess by Carlos Reche\n";
        }
        else
        {
            $authentication_code = $this->authenticationCode($this->board->opponent->color);

            $subject  = "Your opponent has moved! Authentication #" . $authentication_code;

            $message  = "PHP Chess 1.0\n==================================================\n\n";
            $message .= "Your opponent has moved! Click on the link\n" . $link . "\nto go to the board ";
            $message .= "and move your piece.\n\n";
            $message .= (!empty($_POST['message'])) ? ("Opponent's message:\n\"".$_POST['message']."\"\n\n") : "";
            $message .= "Authentication code: #" . $authentication_code;
            $message .= "\n\n__________________________________________________\nPHP Chess by Carlos Reche\n";
        }

        if(!@mail($this->board->opponent->email, $subject, $message, $headers))
        {
            $this->error("Could NOT email your opponent!");
            $sent = false;
        }


        // Email user
        $authentication_code = $this->authenticationCode($this->board->user->color);

        $headers  = "From: # PHP Chess <" . $this->board->opponent->email . ">\r\n";
        $headers .= "Reply-To: hide@address.com\r\n";
        $headers .= "X-Mailer: PHP/" . phpversion();

        $subject  = "Next authentication should be #" . $authentication_code;

        $message  = "PHP Chess 1.0\n==================================================\n\n";
        $message .= "Your move was sent to " . $this->board->opponent->email . "\n\n";
        $message .= (!empty($_POST['message']))  ?  ("Message:\n\"" . $_POST['message'] . "\"\n\n")  :  "";
        $message .= "Authentication code: #" . $authentication_code . "\n\n";
        $message .= "PHP Chess generates an authentication code on each move. When you receive the response from your ";
        $message .= "opponent, this code will assure you that it has been made right after the move sent now.";
        $message .= "\n\n__________________________________________________\nPHP Chess by Carlos Reche\n";

        if(!@mail($this->board->user->email, $subject, $message, $headers))
        {
            $this->error("Could NOT send you a confirmation email!");
            $sent = false;
        }

        /* Helps when debugging
        if (!$this->board->is_check_mate)
        {
            echo '<a href="' . $link . '"> NEXT &gt;&gt; </a>';
        }
        */

        return $sent;
    }





    function board($bool_return = false)
    {
        if ($this->board->user->color == "white")
        {
            $x_start     = 1;
            $x_increment = +1;

            $y_start     = 8;
            $y_increment = -1;
        }
        else
        {
            $x_start     = 8;
            $x_increment = -1;

            $y_start     = 1;
            $y_increment = +1;
        }


        $table_style  = "margin: 0px auto; text-align: center; cursor: default; ";
        $table_style .= " border: " . floor($this->square_size / 7) . "px double #a6735b;";
        $table_style .= " font-family: Bookman Old Style, Arial Unicode MS; font-size: " . floor($this->square_size * 5 / 7)  . "px;";


        $html_board = "\n".'<table cellspacing="0" cellpadding="0" style="' . $table_style . '" id="board">';

        for ($y = $y_start; (($y-1 & 8) == 0); $y += $y_increment)
        {
            $html_board .= "\n".'    <tr>';

            for ($x = $x_start; (($x-1 & 8) == 0); $x += $x_increment)
            {
                if ((($x % 2) == 0)  ?  ((($y % 2) == 1) ? true : false)  :  ((($y % 2) == 1) ? false : true))
                {
                    $class = "light";
                    $color = "#e7d0a7";
                }
                else
                {
                    $class = "dark";
                    $color = "#a6735b";
                }


                $square =& $this->board->square[$x][$y];
                $piece  =& $square->piece;


                $events  = ' onmouseover="javascript: highlightSquare('.$x.', '.$y.', true);"';
                $events .= ' onmouseout="javascript: highlightSquare('.$x.', '.$y.', false);"';
                $events .= ' onclick="javascript: selectSquare('.$x.', '.$y.');"';

                if ($square->hasPiece())
                {
                    $square_contents = $this->printPiece($piece);
                }
                else
                {
                    $square_contents  = '<div style="width: ' . $this->square_size . 'px;';
                    $square_contents .= ' height: ' . $this->square_size . 'px;"></div>';
                }



                $html_board .= "\n".'        <td id="square_' . ($x.$y) . '"' . $events;
                $html_board .= ' style="width: ' . $this->square_size . 'px; height: ' . $this->square_size . 'px;';
                $html_board .= ' vertical-align: middle; border: 1px solid ' . $color . '; background-color: ' . $color.';"';
                $html_board .= ' class="' . $class . '">';

                $html_board .= "\n".'            ' . $square_contents;
                $html_board .= "\n".'        </td>';
            }

            $html_board .= "\n".'    </tr>';
        }

        $html_board .= "\n".'</table>';


        $html_board .= "\n\n".'<script type="text/javascript">';

        if (!empty($_POST['mov_start']))
        {
            $html_board .= "\n    selectSquare(".$this->valueOfX($_POST['mov_start']{0}).", ".$_POST['mov_start']{1}.");";
        }
        if (!empty($_POST['mov_end']))
        {
            $html_board .= "\n    selectSquare(".$this->valueOfX($_POST['mov_end']{0}).", ".$_POST['mov_end']{1}.");";
        }

        $html_board .= "\n</script>\n\n";



        $html = $this->loadJavaScript(true);


        if (!$this->show_coordinates)
        {
            return $this->printHTML(($html . $html_board), $bool_return);
        }


        // Outer table (with board coordinates)
        $html .= "\n".'<table style="margin: 0px auto; text-align: center; font-family: Tahoma, Arial;';
        $html .=      ' font-size: 11px; font-weight: bold; color: #777;"';
        $html .=      ' cellspacing="0" cellpadding="0">';

        for ($y = $y_start; (($y-1 & 8) == 0); $y += $y_increment)
        {
            $html .= "\n".'    <tr><td style="height: ' . $this->square_size . 'px;';
            $html .= ' padding-right: 7px; vertical-align: middle;">' . $y . '</td>';

            if ($y == $y_start)
            {
                 $html .= '<td colspan="8" rowspan="8">' . $html_board . '</td>';
            }

            $html .= '</tr>';
        }

        $html .= "\n".'    <tr><td>&nbsp;</td>';

        for ($x = $x_start; (($x-1 & 8) == 0); $x += $x_increment)
        {
            $html .= '<td style="width: ' . $this->square_size . 'px; padding-top: 2px;">' . $this->valueOfX($x) . '</td>';
        }

        $html .= "\n".'    </tr>';
        $html .= "\n".'</table>';

        return $this->printHTML($html, $bool_return);
    }





    function form($bool_return = false)
    {
        $qs = array();

        foreach ($_GET as $var => $value)
        {
            $qs[] = $var . "=" . urlencode($value);
        }

        $query_string = (count($qs) > 0)  ?  ("?" . implode("&amp;", $qs))  :  "";

        $disabled = "";


        $html = "";

        if ($this->board->has_moved)
        {
            $message  = "Could not send emails!";

            if ($this->emailPlayers())
            {
                $message  = 'Move sent!';
                $disabled = ' disabled="disabled"';
            }

            $html .= '<script type="text/javascript">alert("' . $message . '");</script>';
        }



        $html .= $this->loadJavaScript(true);


        $html .= "\n".'<form action="' . $this->script_file . $query_string . '" method="post" id="phpchess_form"';
        $html .=         ' style="margin: 20px 0px;" onsubmit="javascript: return validateForm();">';
        $html .= "\n".'  <table border="0" id="form_table" cellspacing="0" cellpadding="3" style="margin: 0px auto;">';
        $html .= "\n".'    <tr>';
        $html .= "\n".'      <td colspan="2" style="text-align: center;">';
        $html .= "\n".'        <input type="hidden" name="mov_start" id="mov_start"';
        $html .=                 ' value="' . (!empty($_POST['mov_start'])  ?  $_POST['mov_start']  :  "") . '" />';
        $html .= "\n".'        <input type="hidden" name="mov_end" id="mov_end"';
        $html .=                 ' value="' . (!empty($_POST['mov_end'])  ?  $_POST['mov_end']  :  "") . '" />';
        $html .= "\n".'        <input type="submit" id="submit_button" value="Move"' . $disabled;
        $html .=                 ' style="margin-bottom: 20px; width: 250px; height: 30px;" />';
        $html .= "\n".'      </td>';
        $html .= "\n".'    </tr>';
        $html .= "\n".'    <tr>';
        $html .= "\n".'      <td>Your e-mail:</td>';
        $html .= "\n".'      <td><input type="text" name="' . $this->board->user->color . '"';
        $html .=               ' id="' . $this->board->user->color . '" value="' . $this->board->user->email . '"';
        $html .= "\n".         ' style="width: 100%;"' . $disabled . ' /></td>';
        $html .= "\n".'    </tr>';
        $html .= "\n".'    <tr>';
        $html .= "\n".'      <td>Opponent\'s e-mail:</td>';
        $html .= "\n".'      <td><input type="text" name="' . $this->board->opponent->color . '"';
        $html .=               ' id="' . $this->board->opponent->color . '" value="' . $this->board->opponent->email . '"';
        $html .= "\n".         ' style="width: 100%;"' . $disabled . ' /></td>';
        $html .= "\n".'    </tr>';
        $html .= "\n".'    <tr>';
        $html .= "\n".'      <td style="vertical-align: top;">Message:<br /><em>(Optional)</em></td>';
        $html .= "\n".'      <td>';
        $html .= "\n".'        <textarea name="message" id="message" style="vertical-align: top;"' . $disabled . '>';
        $html .=               (isset($_POST['message'])  ?  $_POST['message']  :  "") . '</textarea>';
        $html .= "\n".'      </td>';
        $html .= "\n".'    </tr>';
        $html .= "\n".'  </table>';
        $html .= "\n".'</form>';

        return $this->printHTML($html, $bool_return);
    }





    function loadJavaScript($bool_return = false)
    {
        if ($this->javascript_loaded)
        {
            return false;
        }

        $this->javascript_loaded = true;


        $html = '
<script type="text/javascript">


    /*
    *  Custom object types
    */

    function PHPChess_Board()
    {
        this.turn = "' . $this->board->turn() . '";

        this.square = new Array(8);
        this.form   = new PHPChess_Form();
        this.move   = new PHPChess_Move();

        this.show     = _PHPChess_Show;
        this.valueOfX = _PHPChess_Board_valueOfX;

        for (x = 1; x <= 8; x++)
        {
            this.square[x] = new Array(8);

            for (y = 1; y <= 8; y++)
            {
                this.square[x][y] = new PHPChess_Square(x, y);
            }
        }
    }

    function _PHPChess_Show(piece_color, piece_name)
    {
        var isIE = (navigator.appName.match(/Internet\s+Explorer/i));
        var text = "", color = ((piece_color == "white") ? "#ffd" : "#633"), html = "";

        switch (piece_name)
        {
            // Internet Explorer does not support the Unicode Characters of the pieces
            case "king":   (text = !isIE  ?  "&#9818;"  :  "&#920;");  break;
            case "queen":  (text = !isIE  ?  "&#9819;"  :  "&#1363;"); break;
            case "bishop": (text = !isIE  ?  "&#9821;"  :  "&#1347;"); break;
            case "knight": (text = !isIE  ?  "&#9822;"  :  "&#1355;"); break;
            case "rook":   (text = !isIE  ?  "&#9820;"  :  "&#9633;"); break;
            case "pawn":   (text = !isIE  ?  "&#9823;"  :  "&#8710;"); break;
        }

        html = "<span style=\"color: " + color + ";\" class=\"" + piece_color + "_piece\">" + text + "</span>";

        document.write(html);
    }



    function _PHPChess_Board_valueOfX(value)
    {
        value = new String(value);

        if (value.match(/^[1-8]$/))
        {
            return String.fromCharCode(64 + parseInt(value));
        }
        else if (value.match(/^[A-H]$/i))
        {
            value = value.toUpperCase();
            return (parseInt(value.charCodeAt(0)) - 64);
        }
        else
        {
            return false;
        }
    }



    function PHPChess_Square(x, y)
    {
        this.x     = x;
        this.y     = y;
        this.piece = null;
    }



    function PHPChess_Piece(x, y, color, controlled_squares)
    {
        this.x     = x;
        this.y     = y;
        this.color = color;
        this.controlled_squares = controlled_squares;
    }



    function PHPChess_Move()
    {
        this.start   = false;
        this.end     = false;
        this.removed = "";

        this.go   = _PHPChess_Move_go;
        this.undo = _PHPChess_Move_undo;
    }

    function _PHPChess_Move_go()
    {
        start_square = document.getElementById("square_" + this.start.charAt(0) + this.start.charAt(1));
        end_square   = document.getElementById("square_" + this.end.charAt(0) + this.end.charAt(1));

        this.removed           = end_square.innerHTML;
        end_square.innerHTML   = start_square.innerHTML;
        start_square.innerHTML = "&nbsp;";
    }

    function _PHPChess_Move_undo()
    {
        if (this.removed == "")
        {
            return false;
        }

        start_square = document.getElementById("square_" + this.start.charAt(0) + this.start.charAt(1));
        end_square   = document.getElementById("square_" + this.end.charAt(0) + this.end.charAt(1));

        start_square.innerHTML = end_square.innerHTML;
        end_square.innerHTML   = this.removed;
        this.removed           = "";

        this.start = false;
        this.end   = false;
    }



    function PHPChess_Form()
    {
        this.setStart   = _PHPChess_Form_setStart;
        this.setEnd     = _PHPChess_Form_setEnd;
        this.unsetStart = _PHPChess_Form_unsetStart;
        this.unsetEnd   = _PHPChess_Form_unsetEnd;
    }

    function _PHPChess_Form_setStart()
    {
        value = Board.valueOfX(Board.move.start.charAt(0)) + String(Board.move.start.charAt(1));
        document.getElementById("mov_start").value = value;
    }

    function _PHPChess_Form_setEnd()
    {
        value = Board.valueOfX(Board.move.end.charAt(0)) + String(Board.move.end.charAt(1));
        document.getElementById("mov_end").value = value;
    }

    function _PHPChess_Form_unsetStart()
    {
        document.getElementById("mov_start").value = "";
    }

    function _PHPChess_Form_unsetEnd()
    {
        document.getElementById("mov_end").value = "";
    }




    /*
    * Custom functions
    */
    function highlightSquare(x, y, highlight)
    {
        if (highlight == null)
        {
            highlight = true;
        }

        if (Board.move.end)
        {
            return false;
        }
        else if (Board.move.start)
        {
            x_start = Board.move.start.charAt(0);
            y_start = Board.move.start.charAt(1);

            controlled_squares = Board.square[x_start][y_start].piece.controlled_squares.toString();

            if (!controlled_squares.match(  eval("/" + String(x) + String(y) + "/")  ))
            {
                return false;
            }
        }
        else
        {
            piece = Board.square[x][y].piece;

            if ((piece == null)  ||  (piece.color != Board.turn)  ||  (piece.controlled_squares.length == 0))
            {
                return false;
            }
        }


        objStyle = document.getElementById(("square_" + x) + y).style;

        objStyle.border = ((highlight)  ?  "1px solid #00f"  :  "");
        objStyle.cursor = "pointer";
    }





    function selectSquare(x, y)
    {
        var coordinate = String(x) + String(y);

        if (Board.move.end)
        {
            if ((coordinate != Board.move.start)  &&  (coordinate != Board.move.end))
            {
                x_start = Board.move.start.charAt(0);
                y_start = Board.move.start.charAt(1);
                x_end   = Board.move.end.charAt(0);
                y_end   = Board.move.end.charAt(1);


                document.getElementById("square_" + String(x_start) + String(y_start)).style.border = "";
                document.getElementById("square_" + String(x_end) + String(y_end)).style.border = "";

                Board.move.undo();
                Board.form.unsetStart();
                Board.form.unsetEnd();
            }

            return true;
        }

        else if (Board.move.start)
        {
            x_start = Board.move.start.charAt(0);
            y_start = Board.move.start.charAt(1);

            controlled_squares = Board.square[x_start][y_start].piece.controlled_squares.toString();

            if (!controlled_squares.match(  eval("/" + coordinate + "/")  ))
            {
                document.getElementById("square_" + String(x_start) + String(y_start)).style.border = "";

                Board.move.start = false;
                Board.form.unsetStart();

                return false;
            }


            document.getElementById("square_" + coordinate).style.border = "1px solid #f00";

            Board.move.end = coordinate;
            Board.move.go();
            Board.form.setEnd();

            return true;
        }


        piece = Board.square[x][y].piece;

        if ((piece == null)  ||  (piece.color != Board.turn)  ||  (piece.controlled_squares.length == 0))
        {
            return false;
        }

        document.getElementById("square_" + coordinate).style.border = "1px solid #f00";

        Board.move.start = coordinate;
        Board.form.setStart();

        return true;
    }



    function validateForm()
    {
        var mov_start   = document.getElementById("mov_start").value;
        var mov_end     = document.getElementById("mov_end").value;
        var email_white = document.getElementById("white");
        var email_black = document.getElementById("black");

        if ((mov_start == "")  ||  (mov_end == ""))
        {
            alert("Move a piece on the board first!");
            return false;
        }
        else if (email_white.value.replace(/^\s+/, "").replace(/\s+$/, "") == "")
        {
            who = (Board.turn != "white")  ?  " opponent\'s"  :  "";
            alert("Type your" + who + " email!");
            email_white.value = "";
            email_white.focus();
            return false;
        }
        else if (email_black.value.replace(/^\s+/, "").replace(/\s+$/, "") == "")
        {
            who = (Board.turn != "black")  ?  " opponent\'s"  :  "";
            alert("Type your" + who + " email!");
            email_black.value = "";
            email_black.focus();
            return false;
        }

        return true;
    }





    /*
    *  Starts main object
    */

    Board = new PHPChess_Board();
';
        if (!$this->board->has_moved  &&  !$this->board->is_check_mate  &&  !$this->board->is_stalemate)
        {
            $this->board->updateData();

            for ($x = 1; $x <= 8; $x++)
            {
                for ($y = 1; $y <= 8; $y++)
                {
                    if (!$this->board->square[$x][$y]->hasPiece())
                    {
                        continue;
                    }

                    $piece =& $this->board->square[$x][$y]->piece;

                    $controlled_squares = $piece->getControlledSquares($this->board);
                    $parameters         = $piece->x . ', ' . $piece->y . ', "' . $piece->color . '", new Array()';

                    $html .= "\n".'    Board.square[' . $x . '][' . $y . '].piece = new PHPChess_Piece(' . $parameters . ');';

                    if (count($controlled_squares) > 0)
                    {
                        $html .= "\n".'    Board.square[' . $x . '][' . $y . '].piece.controlled_squares';
                        $html .= '.push(' . implode(", ", $controlled_squares) . ');';
                    }
                }
            }
        }

        $html .= "\n\n</script>";


        return $this->printHTML($html, $bool_return);
    }




    function message($bool_return = false)
    {
        if (($this->message == "")  &&  (count($this->board->history) > 0))
        {
            $move = end($this->board->history);

            $x_start = $move{0};
            $y_start = $move{1};
            $x_end   = $move{2};
            $y_end   = $move{3};

            $coordinate = $this->valueOfX($x_start) . $y_start . $this->valueOfX($x_end) . $y_end;


            $piece_mov =& $this->board->square[$x_end][$y_end]->piece;
            $piece_cap =& $this->board->removed_piece;

            $piece_mov_name = $piece_mov->color . ' ' . $piece_mov->getName();


            if ($piece_mov->isKing()  &&  (abs($x_start - $x_end) == 2))
            {
                $this->message = 'Castled ' . $piece_mov_name . ' at ' . $coordinate . '.';
            }

            else if (!$piece_cap)
            {
                $this->message = 'Moved ' . $piece_mov->color . ' ' . $piece_mov->getName() . ' at ' . $coordinate . '.';
            }

            else
            {
                $en_passant = "";

                if ($piece_mov->isPawn()  &&  (abs($x_start - $x_end) == 1)  &&  ($piece_mov->y != $piece_cap->y))
                {
                    $en_passant = " &quot;en passant&quot; ";
                }

                $this->message  = ucfirst($piece_mov_name) . ' captures ';
                $this->message .= $piece_cap->color . ' ' . $piece_cap->getName() . $en_passant . ' at ' . $coordinate . '.';
            }


            if ($this->board->is_check)
            {
                if ($this->board->is_check_mate)
                {
                    $this->message .= ' <strong>Checkmate! ' . ucfirst($piece_mov->color) . ' wins.</strong>';
                }
                else
                {
                    $this->message .= ' <strong>Check!</strong>';
                }
            }
            else if ($this->board->is_draw)
            {
                $this->message .= ' <strong>Stalemate!</strong>';
            }
        }

        return $this->printHTML('<span id="message">' . $this->message . '</span>', $bool_return);
    }

    function setMessage($message)
    {
        $this->message = (string)$message;
    }






    function authenticationCode($color)
    {
        $history = $this->board->history;

        if ($color != $this->board->user->color)
        {
            array_pop($history);
        }

        $hash = md5(implode("", $history));

        for ($code = "", $i = 0; $i < 32; $i += 7)
        {
            $code .= $hash{$i};
        }

        return strtoupper($code);
    }





    function valueOfX($value)
    {
        if (preg_match("/^[1-8]$/", $value))
        {
            return chr(64 + (int)$value);
        }
        else if (preg_match("/^[A-H]$/i", $value))
        {
            return (ord(strtoupper($value)) - 64);
        }
        else
        {
            return false;
        }
    }





    function printPiece($piece)
    {
        if (!is_object($piece))
        {
            return false;
        }

        $piece_file = $this->image_dir . $piece->color . '_' . $piece->getName() . '.gif';


        if (is_file($piece_file))
        {
            $html = '<img src="' . $piece_file . '" alt="" class="' . $piece->color . '_piece" />';
        }
        else
        {
            $html = '<script type="text/javascript">Board.show("' . $piece->color . '", "' . $piece->getName() . '");</script>';
        }

        return $html;
    }




    function printHTML($html, $bool_return = false)
    {
        if ($bool_return)
        {
            return $html;
        }
        else
        {
            echo $html;
            return true;
        }
    }





    function setSMTP($smtp)
    {
        $this->smtp_server = (string)$smtp;
    }





    function error($message = "")
    {
        $message = ($message != "")  ?  (" ".$message)  :  "";

        echo "\n<p>\n    ";
        echo '<span style="padding: 1px 7px; background-color: #ffd7d7; font-family: Verdana;';
        echo ' font-weight: normal; color: #000; font-size: 13px;">';
        echo '<span style="color: #f00; font-weight: bold;">Error!</span>' . $message . '</span>';
        echo "\n</p>\n";
    }
}



?>
Return current item: PHP Chess