CPG/Chess/nimfish/nimfishpkg/eval.nim

266 lines
8.2 KiB
Nim

# Copyright 2024 Mattia Giambirtone & All Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
## Position evaluation utilities
import board
type
Score* = int32
# Stolen from https://www.chessprogramming.org/PeSTO's_Evaluation_Function
const
PAWN_MIDDLEGAME_SCORES: array[64, Score] = [
0, 0, 0, 0, 0, 0, 0, 0,
98, 134, 61, 95, 68, 126, 34, -11,
-6, 7, 26, 31, 65, 56, 25, -20,
-14, 13, 6, 21, 23, 12, 17, -23,
-27, -2, -5, 12, 17, 6, 10, -25,
-26, -4, -4, -10, 3, 3, 33, -12,
-35, -1, -20, -23, -15, 24, 38, -22,
0, 0, 0, 0, 0, 0, 0, 0,
]
PAWN_ENDGAME_SCORES: array[64, Score] = [
0, 0, 0, 0, 0, 0, 0, 0,
178, 173, 158, 134, 147, 132, 165, 187,
94, 100, 85, 67, 56, 53, 82, 84,
32, 24, 13, 5, -2, 4, 17, 17,
13, 9, -3, -7, -7, -8, 3, -1,
4, 7, -6, 1, 0, -5, -1, -8,
13, 8, 8, 10, 13, 0, 2, -7,
0, 0, 0, 0, 0, 0, 0, 0,
]
KNIGHT_MIDDLEGAME_SCORES: array[64, Score] = [
-167, -89, -34, -49, 61, -97, -15, -107,
-73, -41, 72, 36, 23, 62, 7, -17,
-47, 60, 37, 65, 84, 129, 73, 44,
-9, 17, 19, 53, 37, 69, 18, 22,
-13, 4, 16, 13, 28, 19, 21, -8,
-23, -9, 12, 10, 19, 17, 25, -16,
-29, -53, -12, -3, -1, 18, -14, -19,
-105, -21, -58, -33, -17, -28, -19, -23,
]
KNIGHT_ENDGAME_SCORES: array[64, Score] = [
-58, -38, -13, -28, -31, -27, -63, -99,
-25, -8, -25, -2, -9, -25, -24, -52,
-24, -20, 10, 9, -1, -9, -19, -41,
-17, 3, 22, 22, 22, 11, 8, -18,
-18, -6, 16, 25, 16, 17, 4, -18,
-23, -3, -1, 15, 10, -3, -20, -22,
-42, -20, -10, -5, -2, -20, -23, -44,
-29, -51, -23, -15, -22, -18, -50, -64,
]
BISHOP_MIDDLEGAME_SCORES: array[64, Score] = [
-29, 4, -82, -37, -25, -42, 7, -8,
-26, 16, -18, -13, 30, 59, 18, -47,
-16, 37, 43, 40, 35, 50, 37, -2,
-4, 5, 19, 50, 37, 37, 7, -2,
-6, 13, 13, 26, 34, 12, 10, 4,
0, 15, 15, 15, 14, 27, 18, 10,
4, 15, 16, 0, 7, 21, 33, 1,
-33, -3, -14, -21, -13, -12, -39, -21,
]
BISHOP_ENDGAME_SCORES: array[64, Score] = [
-14, -21, -11, -8, -7, -9, -17, -24,
-8, -4, 7, -12, -3, -13, -4, -14,
2, -8, 0, -1, -2, 6, 0, 4,
-3, 9, 12, 9, 14, 10, 3, 2,
-6, 3, 13, 19, 7, 10, -3, -9,
-12, -3, 8, 10, 13, 3, -7, -15,
-14, -18, -7, -1, 4, -9, -15, -27,
-23, -9, -23, -5, -9, -16, -5, -17,
]
ROOK_MIDDLEGAME_SCORES: array[64, Score] = [
32, 42, 32, 51, 63, 9, 31, 43,
27, 32, 58, 62, 80, 67, 26, 44,
-5, 19, 26, 36, 17, 45, 61, 16,
-24, -11, 7, 26, 24, 35, -8, -20,
-36, -26, -12, -1, 9, -7, 6, -23,
-45, -25, -16, -17, 3, 0, -5, -33,
-44, -16, -20, -9, -1, 11, -6, -71,
-19, -13, 1, 17, 16, 7, -37, -26,
]
ROOK_ENDGAME_SCORES: array[64, Score] = [
13, 10, 18, 15, 12, 12, 8, 5,
11, 13, 13, 11, -3, 3, 8, 3,
7, 7, 7, 5, 4, -3, -5, -3,
4, 3, 13, 1, 2, 1, -1, 2,
3, 5, 8, 4, -5, -6, -8, -11,
-4, 0, -5, -1, -7, -12, -8, -16,
-6, -6, 0, 2, -9, -9, -11, -3,
-9, 2, 3, -1, -5, -13, 4, -20,
]
QUEEN_MIDDLEGAME_SCORES: array[64, Score] = [
-28, 0, 29, 12, 59, 44, 43, 45,
-24, -39, -5, 1, -16, 57, 28, 54,
-13, -17, 7, 8, 29, 56, 47, 57,
-27, -27, -16, -16, -1, 17, -2, 1,
-9, -26, -9, -10, -2, -4, 3, -3,
-14, 2, -11, -2, -5, 2, 14, 5,
-35, -8, 11, 2, 8, 15, -3, 1,
-1, -18, -9, 10, -15, -25, -31, -50,
]
QUEEN_ENDGAME_SCORES: array[64, Score] = [
-9, 22, 22, 27, 27, 19, 10, 20,
-17, 20, 32, 41, 58, 25, 30, 0,
-20, 6, 9, 49, 47, 35, 19, 9,
3, 22, 24, 45, 57, 40, 57, 36,
-18, 28, 19, 47, 31, 34, 39, 23,
-16, -27, 15, 6, 9, 17, 10, 5,
-22, -23, -30, -16, -16, -23, -36, -32,
-33, -28, -22, -43, -5, -32, -20, -41,
]
KING_MIDDLEGAME_SCORES: array[64, Score] = [
-65, 23, 16, -15, -56, -34, 2, 13,
29, -1, -20, -7, -8, -4, -38, -29,
-9, 24, 2, -16, -20, 6, 22, -22,
-17, -20, -12, -27, -30, -25, -14, -36,
-49, -1, -27, -39, -46, -44, -33, -51,
-14, -14, -22, -46, -44, -30, -15, -27,
1, 7, -8, -64, -43, -16, 9, 8,
-15, 36, 12, -54, 8, -28, 24, 14,
]
KING_ENDGAME_SCORES: array[64, Score] = [
-74, -35, -18, -18, -11, 15, 4, -17,
-12, 17, 14, 17, 17, 38, 23, 11,
10, 17, 23, 15, 20, 45, 44, 13,
-8, 22, 24, 27, 26, 33, 26, 3,
-18, -4, 21, 24, 27, 23, 9, -11,
-19, -3, 11, 21, 23, 16, 7, -9,
-27, -11, 4, 13, 14, 4, -5, -17,
-53, -34, -21, -11, -28, -14, -24, -43
]
MIDDLEGAME_TABLES: array[6, array[64, Score]] = [
BISHOP_MIDDLEGAME_SCORES,
KING_MIDDLEGAME_SCORES,
KNIGHT_MIDDLEGAME_SCORES,
PAWN_MIDDLEGAME_SCORES,
QUEEN_MIDDLEGAME_SCORES,
ROOK_MIDDLEGAME_SCORES
]
ENDGAME_TABLES: array[6, array[64, Score]] = [
BISHOP_ENDGAME_SCORES,
KING_ENDGAME_SCORES,
KNIGHT_ENDGAME_SCORES,
PAWN_ENDGAME_SCORES,
QUEEN_ENDGAME_SCORES,
ROOK_ENDGAME_SCORES
]
proc getPieceValue(kind: PieceKind): Score =
## Returns the absolute value of a piece
case kind:
of Pawn:
return Score(100)
of Bishop:
return Score(330)
of Knight:
return Score(280)
of Rook:
return Score(525)
of Queen:
return Score(950)
else:
discard
proc getPieceScore*(board: Chessboard, square: Square): Score =
## Returns the value of the piece located at
## the given square
return board.getPiece(square).kind.getPieceValue()
proc getGamePhase(board: Chessboard): int =
## Computes the game phase according to
## how many pieces are left on the board
result = 0
for sq in board.getOccupancy():
case board.getPiece(sq).kind:
of Bishop, Knight:
inc(result)
of Queen:
inc(result, 4)
of Rook:
inc(result, 2)
else:
discard
# Caps the value in case of early
# promotions
result = min(24, result)
proc evaluatePiecePositions(board: ChessBoard): Score =
## Returns the evaluation of the current
## material's position relative to white
let
middleGamePhase = board.getGamePhase()
endGamePhase = 24 - middleGamePhase
var
# White, Black
middleGameScores: array[2, Score] = [0, 0]
endGameScores: array[2, Score] = [0, 0]
for sq in board.getOccupancy():
let piece = board.getPiece(sq)
middleGameScores[piece.color.int] += MIDDLEGAME_TABLES[piece.kind.int][sq.int]
endGameScores[piece.color.int] += ENDGAME_TABLES[piece.kind.int][sq.int]
let
sideToMove = board.position.sideToMove
nonSideToMove = sideToMove.opposite()
middleGameScore = middleGameScores[sideToMove.int] - middleGameScores[nonSideToMove.int]
endGameScore = endGameScores[sideToMove.int] - endGameScores[nonSideToMove.int]
result = Score((middleGameScore * middleGamePhase + endGameScore * endGamePhase) div 24)
proc evaluateMaterial(board: ChessBoard): Score =
## Returns the material evaluation of the
## current position relative to white (positive
## if in white's favor, negative otherwise)
var
whiteScore: Score
blackScore: Score
for sq in board.getOccupancyFor(White):
whiteScore += board.getPieceScore(sq)
for sq in board.getOccupancyFor(Black):
blackScore += board.getPieceScore(sq)
result = whiteScore - blackScore
if board.position.sideToMove == Black:
result *= -1
proc evaluate*(board: Chessboard): Score =
## Evaluates the current position
result = board.evaluateMaterial()
result += board.evaluatePiecePositions()