diff --git a/html/index.php b/html/index.php index 3ab3363..d67531e 100644 --- a/html/index.php +++ b/html/index.php @@ -4,6 +4,7 @@ require_once '../src/core.php'; use Engine\GameOutcome; use Engine\MinimaxDF; +use Engine\PeSTO; use Engine\PieceValues; use Engine\WeightedHeuristics; use Game\Game; @@ -18,7 +19,8 @@ if (isset($_SESSION["game"])) { $game = Game::fromStartPosition(); $engine = new MinimaxDF(1, new WeightedHeuristics([ [new GameOutcome(), 1.0], - [new PieceValues(), 1.0] + [new PieceValues(), 1.0], + [new PeSTO(), 1.0], ])); $_SESSION["game"] = $game; diff --git a/src/Engine/PeSTO.php b/src/Engine/PeSTO.php new file mode 100644 index 0000000..9e767c0 --- /dev/null +++ b/src/Engine/PeSTO.php @@ -0,0 +1,260 @@ +middlegameTable = [ + PieceType::PAWN->getShort() => array_map( + fn($value) => $value + self::middlegamePieceValues[0], + self::middlegamePawnTable + ), + PieceType::BISHOP->getShort() => array_map( + fn($value) => $value + self::middlegamePieceValues[2], + self::middlegameBishopTable + ), + PieceType::KNIGHT->getShort() => array_map( + fn($value) => $value + self::middlegamePieceValues[1], + self::middlegameKnightTable + ), + PieceType::ROOK->getShort() => array_map( + fn($value) => $value + self::middlegamePieceValues[3], + self::middlegameRookTable + ), + PieceType::QUEEN->getShort() => array_map( + fn($value) => $value + self::middlegamePieceValues[4], + self::middlegameQueenTable + ), + PieceType::KING->getShort() => array_map( + fn($value) => $value + self::middlegamePieceValues[5], + self::middlegameKingTable + ), + ]; + $this->endgameTable = [ + PieceType::PAWN->getShort() => array_map( + fn($value) => $value + self::endgamePieceValues[0], + self::endgamePawnTable + ), + PieceType::BISHOP->getShort() => array_map( + fn($value) => $value + self::endgamePieceValues[2], + self::endgameBishopTable + ), + PieceType::KNIGHT->getShort() => array_map( + fn($value) => $value + self::endgamePieceValues[1], + self::endgameKnightTable + ), + PieceType::ROOK->getShort() => array_map( + fn($value) => $value + self::endgamePieceValues[3], + self::endgameRookTable + ), + PieceType::QUEEN->getShort() => array_map( + fn($value) => $value + self::endgamePieceValues[4], + self::endgameQueenTable + ), + PieceType::KING->getShort() => array_map( + fn($value) => $value + self::endgamePieceValues[5], + self::endgameKingTable + ), + ]; + } + + private function lookupEndgameCounterValue(PieceType $pieceType): float { + return match ($pieceType) { + PieceType::PAWN => 0, + PieceType::BISHOP => 1, + PieceType::KNIGHT => 1, + PieceType::ROOK => 2, + PieceType::QUEEN => 4, + default => 0, + }; + } + + private function lookupPieceValues(Piece $piece): array { + $rank = $piece->getPosition()->rank; + $file = $piece->getPosition()->file; + + $index = $rank * 8 + $file; + if ($piece->getSide() == Side::BLACK) { + $index = 64 - $index; + } + + return [ + $this->middlegameTable[$piece->getType()->getShort()][$index], + $this->endgameTable[$piece->getType()->getShort()][$index], + $this->lookupEndgameCounterValue($piece->getType()), + ]; + } + + public function ratePosition(Game $game): float { + $ownPieces = $game->getPieces($game->getCurrentSide()); + $opponentPieces = $game->getPieces($game->getCurrentSide()->getNext()); + + $ownValues = array_map([$this, "lookupPieceValues"], $ownPieces); + $opponentValues = array_map([$this, "lookupPieceValues"], $opponentPieces); + + $middlegameScore = array_sum( + array_map(fn($values) => $values[0], $ownValues) + ) - array_sum( + array_map(fn($values) => $values[0], $opponentValues) + ); + + $endgameScore = array_sum( + array_map(fn($values) => $values[1], $ownValues) + ) - array_sum( + array_map(fn($values) => $values[1], $opponentValues) + ); + + $phase = array_sum(array_map(fn($values) => $values[2], array_merge($ownValues, $opponentValues))); + $phase = max($phase, 24); + $phase /= 24.0; + + return ($middlegameScore * $phase + $endgameScore * (1 - $phase)) * self::normalizationFactor; + } +} \ No newline at end of file