public function ValidateFENStringStructureAndValues($FEN, &$errors) { $fields; // Stores each field (space delimited item). $row; // Stores each row of the FEN. $nWKingCount = 0; $nBKingCount = 0; // The number of kings found. $errors = ""; if ($FEN == null) { return false; } // Makes sure all groups and board ranks are available. $fields = preg_split('/ /', $FEN); if (count($fields) != 6) { $errors = "The number of fields in the FEN is not 6."; return false; } // If the board setup field ends with a "/" (PHPChess returned fen) then remove it. if (STRING_UTILS::last_char($fields[0]) == "/") { $fields[0] = substr($fields[0], 0, count($fields[0]) - 2); } $row = preg_split('/\\//', $fields[0]); if (count($row) != 8) { $errors = "The number of ranks in the board setup field is not 8."; return false; } // Now check the values for each group are acceptable. // First check that the board characters are acceptable. for ($i = 0; $i < strlen($fields[0]); $i++) { if (!($fields[0][$i] == 'K' || $fields[0][$i] == 'Q' || $fields[0][$i] == 'R' || $fields[0][$i] == 'B' || $fields[0][$i] == 'N' || $fields[0][$i] == 'P' || $fields[0][$i] == 'k' || $fields[0][$i] == 'q' || $fields[0][$i] == 'r' || $fields[0][$i] == 'b' || $fields[0][$i] == 'n' || $fields[0][$i] == 'p' || $fields[0][$i] == '/' || $fields[0][$i] == '1' || $fields[0][$i] == '2' || $fields[0][$i] == '3' || $fields[0][$i] == '4' || $fields[0][$i] == '5' || $fields[0][$i] == '6' || $fields[0][$i] == '7' || $fields[0][$i] == '8')) { $errors = "The FEN string contains an invalid character: \n" + $fields[0][$i]; return false; } if ($fields[0][$i] == 'K') { $nWKingCount++; } if ($fields[0][$i] == 'k') { $nBKingCount++; } } // Make sure there is one white and one black king if ($nWKingCount == 0) { $errors .= "No White king.\n"; } if ($nBKingCount == 0) { $errors .= "No Black king.\n"; } if ($nWKingCount > 1) { $errors .= "There can only be one king for black.\n"; } if ($nBKingCount > 1) { $errors .= "There can only be one king for black.\n"; } // Check if the player turn is either w or b. if (!($fields[1] == "w" || $fields[1] == "b")) { $errors .= "Player turn must be either 'w' or 'b'.\n"; } // Check the castling characters are valid. There must be 1 to 4 characters. // If a '-' then no other character should be there. If no '-' then only k,K,q and Q allowed. if (strlen($fields[2]) < 1 || strlen($fields[2]) > 4) { $errors .= "The castling field of the FEN is invalid.\n"; } if (strstr($fields[2], "-") && strlen($fields[2]) != 1) { $errors .= "Cannot have a '-' in the castling field with other characters.\n"; } if (strlen($fields[2]) > 1) { for ($i = 0; $i < strlen($fields[2]); $i++) { if (!($fields[2][$i] == 'k' || $fields[2][$i] == 'K' || $fields[2][$i] == 'Q' || $fields[2][$i] == 'q')) { $errors .= "The castling field contained an invalid character.\n"; } } } // The en-passant field can only be '-' or a valid tile (a1 through to h8) if ($fields[3] != "-") { if ($this->ConvertAlgebraicNotationTileToInteger($fields[3]) == -1) { $errors .= "The en passant field must contain a valid tile reference.\n"; } } // The 50 move field must be non negative. if ($this->IsStringNonNegativeNumber($fields[4]) == false) { $errors .= "The 50 move (half move count) field must be non negative.\n"; } // The full move number must be non negative. if ($this->IsStringNonNegativeNumber($fields[5]) == false) { $errors .= "The full move field must be non negative\n"; } if ($errors == "") { return true; } else { return false; } }
private function ConvertSANtoCoordinates($szMove, &$nStartTile, &$nEndTile, &$promotionType) { $nTmp; if (strlen($szMove) < 2) { return false; } //promotionType = PIECE_TYPE::UNDEFINED; // If there is a p in the text then remove it since we don't need to be told the move was en passant $nTmp = strpos($szMove, 'ep'); if ($nTmp !== FALSE) { $szMove = substr($szMove, 0, $nTmp) . substr($szMove, $nTmp, strlen($szMove) - $nTmp - 2); } // Check if the move was check or checkmate and remove the last character if (STRING_UTILS::last_char($szMove) == "+") { //$szMove.Replace("+", ""); $szMove = substr($szMove, 0, strlen($szMove) - 1); } $nTmp = strpos($szMove, '#'); if ($nTmp !== FALSE) { // Extract the move made which caused the checkmate. Note that crafty sends 0-1 {black mates} after the #. //$szMove = $szMove.Substring(0, $szMove.Length - 1); $szMove = substr($szMove, 0, $nTmp); } // Determine the type of the move (castle-, check+, mate#, promotion=) if ($szMove == "O-O") { if ($this->PlayerTurn == 0) { $nStartTile = 4; $nEndTile = 6; } else { $nStartTile = 60; $nEndTile = 62; } return true; } else { if ($szMove == "O-O-O") { if ($this->PlayerTurn == 0) { $nStartTile = 4; $nEndTile = 2; } else { $nStartTile = 60; $nEndTile = 58; } return true; } else { if (strpos($szMove, '=') !== FALSE) { // Get type to promote to if (STRING_UTILS::last_char($szMove) == "N") { $promotionType = PIECE_TYPE::KNIGHT; } else { if (STRING_UTILS::last_char($szMove) == "B") { $promotionType = PIECE_TYPE::BISHOP; } else { if (STRING_UTILS::last_char($szMove) == "R") { $promotionType = PIECE_TYPE::ROOK; } else { if (STRING_UTILS::last_char($szMove) == "Q") { $promotionType = PIECE_TYPE::QUEEN; } } } } // Get tiles $szMove = substr($szMove, 0, strlen($szMove) - 2); // Check if there is an x to indicate a piece was taken. if ($szMove . Contains("x")) { $nEndTile = $this->ChessUtils->ConvertAlgebraicNotationTileToInteger($szMove . Substring(2)); if ($this->PlayerTurn == 0) { $nStartTile = $this->ChessUtils->ConvertAlgebraicNotationTileToInteger($szMove . Substring(0, 1) + "7"); } else { $nStartTile = $this->ChessUtils->ConvertAlgebraicNotationTileToInteger($szMove . Substring(0, 1) + "2"); } } else { $nEndTile = $this->ChessUtils->ConvertAlgebraicNotationTileToInteger($szMove); if ($this->PlayerTurn == 0) { $nStartTile = $nEndTile - 8; } else { $nStartTile = $nEndTile + 8; } } if ($nStartTile == -1 || $nEndTile == -1) { return false; } return true; } else { if ($szMove[strlen($szMove) - 1] == 'Q' || $szMove[strlen($szMove) - 1] == 'B' || $szMove[strlen($szMove) - 1] == 'N' || $szMove[strlen($szMove) - 1] == 'R') { // Get type to promote to if ($szMove[strlen($szMove) - 1] == 'N') { $promotionType = PIECE_TYPE::KNIGHT; } else { if ($szMove[strlen($szMove) - 1] == 'B') { $promotionType = PIECE_TYPE::BISHOP; } else { if ($szMove[strlen($szMove) - 1] == 'R') { $promotionType = PIECE_TYPE::ROOK; } else { if ($szMove[strlen($szMove) - 1] == 'Q') { $promotionType = PIECE_TYPE::QUEEN; } } } } // Remove the last char and then continue on finding the start and end tiles for this move. $szMove = substr($szMove, 0, strlen($szMove) - 1); // Make sure the move is valid for further processing. if (strlen($szMove) < 2) { return false; } } } } } // Last two chars indicate the end tile. Remove them from the string. $nEndTile = $this->ChessUtils->ConvertAlgebraicNotationTileToInteger(substr(strlen($szMove) - 2)); if ($nEndTile == -1) { return false; } $szMove = substr($szMove, 0, strlen($szMove) - 2); // Check if a piece was taken and remove the x character if so. if ($szMove[strlen($szMove) - 1] == 'x') { $szMove = substr($szMove, 0, strlen($szMove) - 1); } // Check the remaining length. if (strlen($szMove) == 0) { if ($this->PlayerTurn == 0) { //if (rgBoard[nEndTile / 8 - 1, nEndTile % 8] > 0) nStartTile = nEndTile - 8; //else nStartTile = nEndTile - 16; //return true; // If the end tile is not rank 4 that means a pawn couldn't have moved two tiles // up the rank. if ($nEndTile / 8 != 3) { // Check for a single move forward. if ($this->Board[$this->floor[$nEndTile] - 1][$nEndTile % 8] == PIECE_TYPE::PAWN) { $nStartTile = $nEndTile - 8; } else { if ($this->nEnPassantSquare == nEndTile) { // Check if there is a white pawn to the left/right one rank down from the end tile if ($nEndTile % 8 == 0) { if ($this->Board[$this->floor[$nEndTile] - 1][$nEndTile % 8 + 1] == PIECE_TYPE::PAWN) { $nStartTile = $nEndTile - 7; } else { return false; } } else { if ($nEndTile % 8 > 0 && $nEndTile % 8 < 7) { if ($this->Board[$this->floor[$nEndTile] - 1][$nEndTile % 8 - 1] == PIECE_TYPE::PAWN) { $nStartTile = $nEndTile - 9; } else { if ($this->Board[$this->floor[$nEndTile] - 1][$nEndTile % 8 + 1] == PIECE_TYPE::PAWN) { $nStartTile = $nEndTile - 7; } else { return false; } } } else { if ($this->Board[$this->floor[$nEndTile] - 1][$nEndTile % 8 - 1] == PIECE_TYPE::PAWN) { $nStartTile = $nEndTile - 9; } else { return false; } } } } } } else { // Check for a single move forward. if ($this->Board[$this->floor[$nEndTile] - 1][$nEndTile % 8] == PIECE_TYPE::PAWN) { $nStartTile = $nEndTile - 8; } else { if ($this->Board[$this->floor[$nEndTile] - 2][$nEndTile % 8] == PIECE_TYPE::PAWN) { $nStartTile = $nEndTile - 16; } else { // Check if there is a black pawn to the left/right one rank up from the end tile if ($nEndTile % 8 == 0) { if ($this->Board[$this->floor[$nEndTile] + 1][$nEndTile % 8 + 1] == PIECE_TYPE::PAWN + 6) { $nStartTile = $nEndTile + 9; } else { return false; } } else { if ($nEndTile % 8 > 0 && $nEndTile % 8 < 7) { if ($this->Board[$this->floor[$nEndTile] + 1][$nEndTile % 8 - 1] == PIECE_TYPE::PAWN + 6) { $nStartTile = $nEndTile + 7; } else { if ($this->Board[$this->floor[$nEndTile] + 1][$nEndTile % 8 + 1] == PIECE_TYPE::PAWN + 6) { $nStartTile = $nEndTile + 9; } else { return false; } } } else { if ($this->Board[$this->floor[$nEndTile] + 1][$nEndTile % 8 - 1] == PIECE_TYPE::PAWN + 6) { $nStartTile = $nEndTile + 7; } else { return false; } } } } } } } else { //if ($this->Board[nEndTile / 8 + 1, nEndTile % 8] > 0) nStartTile = nEndTile + 8; //else nStartTile = nEndTile + 16; //return true; // If the end tile is not rank 5 that means a pawn couldn't have moved two tiles // down the rank. if ($this->floor[$nEndTile] != 4) { if ($this->Board[$this->floor[$nEndTile] + 1][$nEndTile % 8] == PIECE_TYPE::PAWN + 6) { $nStartTile = $nEndTile + 8; } else { if ($this->nEnPassantSquare == $nEndTile) { $nStartTile = -1; return false; } } } else { if ($this->Board[$this->floor[$nEndTile] + 1][$nEndTile % 8] == PIECE_TYPE::PAWN + 6) { $nStartTile = $nEndTile + 8; } else { if ($this->Board[$this->floor[$nEndTile] + 2][$nEndTile % 8] == PIECE_TYPE::PAWN + 6) { $nStartTile = $nEndTile + 16; } else { $nStartTile = -1; return false; } } } } return true; } if (strlen($szMove) == 1) { if ($szMove == 'a' || $szMove == 'b' || $szMove == 'c' || $szMove == 'd' || $szMove == 'e' || $szMove == 'f' || $szMove == 'g' || $szMove == 'h') { if ($this->PlayerTurn == 0) { $nTmp = $nEndTile / 8; } else { $nTmp = $this->floor[$nEndTile] + 2; } $nStartTile = $this->ChessUtils->ConvertAlgebraicNotationTileToInteger(substr($szMove, 0, 1) . $nTmp); } else { if ($szMove == 'K') { $nStartTile = $this->ObtainAttackingTileForPieceType(PIECE_TYPE::KING, $nEndTile); } else { if ($szMove == 'Q') { $nStartTile = $this->ObtainAttackingTileForPieceType(PIECE_TYPE::QUEEN, $nEndTile); } else { if ($szMove == 'R') { $nStartTile = $this->ObtainAttackingTileForPieceType(PIECE_TYPE::ROOK, $nEndTile); } else { if ($szMove == 'B') { $nStartTile = $this->ObtainAttackingTileForPieceType(PIECE_TYPE::BISHOP, $nEndTile); } else { if ($szMove == 'N') { $nStartTile = $this->ObtainAttackingTileForPieceType(PIECE_TYPE::KNIGHT, $nEndTile); } } } } } } if ($nStartTile == -1) { return false; } return true; } if (strlen($szMove) == 2) { // Find the piece type for the given file or rank if ($szMove[0] == 'K') { $nStartTile = $this->FindPieceLocatedInFileOrRank(PIECE_TYPE::KING, $szMove[1]); } else { if ($szMove[0] == 'Q') { $nStartTile = $this->FindPieceLocatedInFileOrRank(PIECE_TYPE::QUEEN, $szMove[1]); } else { if ($szMove[0] == 'R') { $nStartTile = $this->FindPieceLocatedInFileOrRank(PIECE_TYPE::ROOK, $szMove[1]); } else { if ($szMove[0] == 'B') { $nStartTile = $this->FindPieceLocatedInFileOrRank(PIECE_TYPE::BISHOP, $szMove[1]); } else { if ($szMove[0] == 'N') { $nStartTile = $this->FindPieceLocatedInFileOrRank(PIECE_TYPE::KNIGHT, $szMove[1]); } else { } } } } } if ($nStartTile == -1) { return false; } return true; } if (strlen($szMove) == 3) { $nStartTile = $this->ChessUtils->ConvertAlgebraicNotationTileToInteger(substr($szMove, 1)); if ($nStartTile == -1) { return false; } return true; } return false; }