package gambit object Eval: val pieceValue: Array[Int] = Array(0, 100, 320, 330, 500, 900, 20000) private val mgValue = Array(0, 100, 320, 330, 500, 900, 0) private val egValue = Array(0, 120, 320, 330, 540, 940, 0) private val phaseWeight = Array(0, 0, 1, 1, 2, 4, 0) private val maxPhase = 24 private val pawnMg = Array( 0, 0, 0, 0, 0, 0, 0, 0, 50, 50, 50, 50, 50, 50, 50, 50, 10, 10, 20, 30, 30, 20, 10, 10, 5, 5, 10, 25, 25, 10, 5, 5, 0, 0, 0, 20, 20, 0, 0, 0, 5, -5, -10, 0, 0, -10, -5, 5, 5, 10, 10, -20, -20, 10, 10, 5, 0, 0, 0, 0, 0, 0, 0, 0 ) private val pawnEg = Array( 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 80, 80, 80, 80, 80, 50, 50, 50, 50, 50, 50, 50, 50, 30, 30, 30, 30, 30, 30, 30, 30, 20, 20, 20, 20, 20, 20, 20, 20, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0 ) private val knightTable = Array( -50, -40, -30, -30, -30, -30, -40, -50, -40, -20, 0, 0, 0, 0, -20, -40, -30, 0, 10, 15, 15, 10, 0, -30, -30, 5, 15, 20, 20, 15, 5, -30, -30, 0, 15, 20, 20, 15, 0, -30, -30, 5, 10, 15, 15, 10, 5, -30, -40, -20, 0, 5, 5, 0, -20, -40, -50, -40, -30, -30, -30, -30, -40, -50 ) private val bishopTable = Array( -20, -10, -10, -10, -10, -10, -10, -20, -10, 0, 0, 0, 0, 0, 0, -10, -10, 0, 5, 10, 10, 5, 0, -10, -10, 5, 5, 10, 10, 5, 5, -10, -10, 0, 10, 10, 10, 10, 0, -10, -10, 10, 10, 10, 10, 10, 10, -10, -10, 5, 0, 0, 0, 0, 5, -10, -20, -10, -10, -10, -10, -10, -10, -20 ) private val rookTable = Array( 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 10, 10, 10, 10, 10, 5, -5, 0, 0, 0, 0, 0, 0, -5, -5, 0, 0, 0, 0, 0, 0, -5, -5, 0, 0, 0, 0, 0, 0, -5, -5, 0, 0, 0, 0, 0, 0, -5, -5, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0, 5, 5, 0, 0, 0 ) private val queenTable = Array( -20, -10, -10, -5, -5, -10, -10, -20, -10, 0, 0, 0, 0, 0, 0, -10, -10, 0, 5, 5, 5, 5, 0, -10, -5, 0, 5, 5, 5, 5, 0, -5, 0, 0, 5, 5, 5, 5, 0, -5, -10, 5, 5, 5, 5, 5, 0, -10, -10, 0, 5, 0, 0, 0, 0, -10, -20, -10, -10, -5, -5, -10, -10, -20 ) private val kingMg = Array( -30, -40, -40, -50, -50, -40, -40, -30, -30, -40, -40, -50, -50, -40, -40, -30, -30, -40, -40, -50, -50, -40, -40, -30, -30, -40, -40, -50, -50, -40, -40, -30, -20, -30, -30, -40, -40, -30, -30, -20, -10, -20, -20, -20, -20, -20, -20, -10, 20, 20, 0, 0, 0, 0, 20, 20, 20, 30, 10, 0, 0, 10, 30, 20 ) private val kingEg = Array( -50, -40, -30, -20, -20, -30, -40, -50, -30, -20, -10, 0, 0, -10, -20, -30, -30, -10, 20, 30, 30, 20, -10, -30, -30, -10, 30, 40, 40, 30, -10, -30, -30, -10, 30, 40, 40, 30, -10, -30, -30, -10, 20, 30, 30, 20, -10, -30, -30, -30, 0, 0, 0, 0, -30, -30, -50, -30, -30, -30, -30, -30, -30, -50 ) private val mgTables = Array(null, pawnMg, knightTable, bishopTable, rookTable, queenTable, kingMg) private val egTables = Array(null, pawnEg, knightTable, bishopTable, rookTable, queenTable, kingEg) private val passedMg = Array(0, 8, 16, 28, 48, 78, 120, 0) private val passedEg = Array(0, 18, 30, 52, 90, 140, 210, 0) private val doubledPenalty = 14 private val isolatedPenalty = 12 private val bishopPairBonus = 32 private val rookOpenBonus = 22 private val rookSemiOpenBonus = 11 private val mobilityWeight = Array(0, 0, 4, 5, 3, 2, 0) private val shieldBonus = 9 private val ringAttackPenalty = 8 inline private def pstIndex(color: Int, sq: Int): Int = if color == Color.White then (7 - Square.rankOf(sq)) * 8 + Square.fileOf(sq) else Square.rankOf(sq) * 8 + Square.fileOf(sq) def evaluate(pos: Position): Int = val board = pos.board val pawns = Array(0L, 0L) val bishops = Array(0, 0) var mg = 0 var eg = 0 var phase = 0 var flat = 0 var sq = 0 while sq < 128 do if Square.onBoard(sq) then val p = board(sq) if p != Piece.Empty then val pieceType = p & 7 val color = p >> 3 val idx = pstIndex(color, sq) val mgTerm = mgValue(pieceType) + mgTables(pieceType)(idx) val egTerm = egValue(pieceType) + egTables(pieceType)(idx) phase += phaseWeight(pieceType) if color == Color.White then mg += mgTerm eg += egTerm else mg -= mgTerm eg -= egTerm pieceType match case Piece.Pawn => pawns(color) |= Bitboards.bit(Bitboards.sq64(sq)) case Piece.Bishop => bishops(color) += 1 case _ => () sq += 1 flat += mobility(pos, Color.White) - mobility(pos, Color.Black) flat += kingSafety(pos, Color.White) - kingSafety(pos, Color.Black) if bishops(Color.White) >= 2 then flat += bishopPairBonus if bishops(Color.Black) >= 2 then flat -= bishopPairBonus flat += rookFiles(pos, Color.White, pawns) - rookFiles(pos, Color.Black, pawns) val (pawnMgTerm, pawnEgTerm) = pawnStructure(pawns) mg += pawnMgTerm + flat eg += pawnEgTerm + flat val boundedPhase = if phase > maxPhase then maxPhase else phase val blended = (mg * boundedPhase + eg * (maxPhase - boundedPhase)) / maxPhase if pos.sideToMove == Color.White then blended else -blended private def pawnStructure(pawns: Array[Long]): (Int, Int) = var mg = 0 var eg = 0 var color = 0 while color < 2 do val sign = if color == Color.White then 1 else -1 val own = pawns(color) val enemy = pawns(color ^ 1) var f = 0 while f < 8 do val onFile = Bitboards.count(own & Bitboards.fileMask(f)) if onFile > 1 then mg -= sign * doubledPenalty * (onFile - 1) eg -= sign * doubledPenalty * (onFile - 1) if onFile > 0 && (own & Bitboards.adjacentFiles(f)) == 0L then mg -= sign * isolatedPenalty * onFile eg -= sign * isolatedPenalty * onFile f += 1 var bb = own while bb != 0L do val s = java.lang.Long.numberOfTrailingZeros(bb) bb &= bb - 1 val ahead = if color == Color.White then Bitboards.passedWhite(s) else Bitboards.passedBlack(s) if (ahead & enemy) == 0L then val relRank = if color == Color.White then Bitboards.rankOf(s) else 7 - Bitboards.rankOf(s) mg += sign * passedMg(relRank) eg += sign * passedEg(relRank) color += 1 (mg, eg) private def mobility(pos: Position, color: Int): Int = val board = pos.board var total = 0 var sq = 0 while sq < 128 do if Square.onBoard(sq) then val p = board(sq) if p != Piece.Empty && (p >> 3) == color then val pt = p & 7 pt match case Piece.Knight => total += stepCount(board, sq, color, Directions.knight) * mobilityWeight(pt) case Piece.Bishop => total += slideCount(board, sq, color, Directions.bishop) * mobilityWeight(pt) case Piece.Rook => total += slideCount(board, sq, color, Directions.rook) * mobilityWeight(pt) case Piece.Queen => val q = slideCount(board, sq, color, Directions.bishop) + slideCount(board, sq, color, Directions.rook) total += q * mobilityWeight(pt) case _ => () sq += 1 total private def stepCount(board: Array[Int], sq: Int, color: Int, offsets: Array[Int]): Int = var c = 0 var i = 0 while i < offsets.length do val t = sq + offsets(i) if Square.onBoard(t) && (board(t) == Piece.Empty || (board(t) >> 3) != color) then c += 1 i += 1 c private def slideCount(board: Array[Int], sq: Int, color: Int, offsets: Array[Int]): Int = var c = 0 var i = 0 while i < offsets.length do var t = sq + offsets(i) var stop = false while Square.onBoard(t) && !stop do val tp = board(t) if tp == Piece.Empty then c += 1 t += offsets(i) else if (tp >> 3) != color then c += 1 stop = true i += 1 c private def kingSafety(pos: Position, color: Int): Int = val kingSq = pos.kingSquare(color) val enemy = color ^ 1 val board = pos.board var score = 0 val forward = if color == Color.White then 16 else -16 val ownPawn = Piece.make(color, Piece.Pawn) var d = -1 while d <= 1 do val shieldSq = kingSq + forward + d if Square.onBoard(shieldSq) && board(shieldSq) == ownPawn then score += shieldBonus val shieldSq2 = kingSq + forward * 2 + d if Square.onBoard(shieldSq2) && board(shieldSq2) == ownPawn then score += shieldBonus / 2 d += 1 var attackers = 0 var i = 0 while i < Directions.king.length do val ring = kingSq + Directions.king(i) if Square.onBoard(ring) && pos.isAttacked(ring, enemy) then attackers += 1 i += 1 score - attackers * ringAttackPenalty private def rookFiles(pos: Position, color: Int, pawns: Array[Long]): Int = val board = pos.board val rook = Piece.make(color, Piece.Rook) var score = 0 var sq = 0 while sq < 128 do if Square.onBoard(sq) && board(sq) == rook then val f = Square.fileOf(sq) val own = pawns(color) & Bitboards.fileMask(f) val enemy = pawns(color ^ 1) & Bitboards.fileMask(f) if own == 0L && enemy == 0L then score += rookOpenBonus else if own == 0L then score += rookSemiOpenBonus sq += 1 score