< Summary

Information
Class: Rudim.Board.PawnStructureEvaluation
Assembly: Rudim
File(s): /home/runner/work/rudim/rudim/Rudim/Board/PawnStructureEvaluation.cs
Line coverage
100%
Covered lines: 60
Uncovered lines: 0
Coverable lines: 60
Total lines: 117
Line coverage: 100%
Branch coverage
100%
Covered branches: 42
Total branches: 42
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%2424100%
Evaluate(...)100%11100%
ScoreDoubledPawns(...)100%66100%
ScorePawnFeatures(...)100%1212100%

File(s)

/home/runner/work/rudim/rudim/Rudim/Board/PawnStructureEvaluation.cs

#LineLine coverage
 1using Rudim.Common;
 2using System.Numerics;
 3
 4namespace Rudim.Board
 5{
 6    public static class PawnStructureEvaluation
 7    {
 8        private const int DoubledPawnPenalty = 10;
 9        private const int IsolatedPawnPenalty = 20;
 10
 11        // Passed pawn bonus indexed by row (0 = rank 8, 7 = rank 1).
 12        // Row 0 and row 7 are 0 because white pawns promote at rank 8 (row 0) before reaching it,
 13        // and rank 1 (row 7) is below white's starting rank so unreachable as a pawn.
 14        // Smaller row index = further advanced for white = larger bonus.
 115        private static readonly int[] PassedPawnBonus = [0, 100, 70, 50, 30, 20, 10, 0];
 16
 117        private static readonly ulong[] FileMasks = new ulong[8];
 118        private static readonly ulong[] AdjacentFileMasks = new ulong[8];
 19
 20        // PassedPawnMasks[side, square] = squares in front of the pawn on same/adjacent files
 121        private static readonly ulong[,] PassedPawnMasks = new ulong[2, Constants.Squares];
 22
 23        static PawnStructureEvaluation()
 24        {
 1825            for (int file = 0; file < 8; file++)
 26            {
 827                ulong mask = 0;
 14428                for (int row = 0; row < 8; row++)
 6429                    mask |= 1ul << (row * 8 + file);
 830                FileMasks[file] = mask;
 31            }
 32
 1833            for (int file = 0; file < 8; file++)
 34            {
 835                AdjacentFileMasks[file] = 0;
 1536                if (file > 0) AdjacentFileMasks[file] |= FileMasks[file - 1];
 1537                if (file < 7) AdjacentFileMasks[file] |= FileMasks[file + 1];
 38            }
 39
 13040            for (int sq = 0; sq < Constants.Squares; sq++)
 41            {
 6442                int file = sq & 7;
 6443                int row = sq >> 3;
 44
 6445                ulong whiteMask = 0;
 57646                for (int r = 0; r < row; r++)
 47                {
 22448                    whiteMask |= 1ul << (r * 8 + file);
 42049                    if (file > 0) whiteMask |= 1ul << (r * 8 + file - 1);
 42050                    if (file < 7) whiteMask |= 1ul << (r * 8 + file + 1);
 51                }
 6452                PassedPawnMasks[(int)Side.White, sq] = whiteMask;
 53
 6454                ulong blackMask = 0;
 57655                for (int r = row + 1; r < 8; r++)
 56                {
 22457                    blackMask |= 1ul << (r * 8 + file);
 42058                    if (file > 0) blackMask |= 1ul << (r * 8 + file - 1);
 42059                    if (file < 7) blackMask |= 1ul << (r * 8 + file + 1);
 60                }
 6461                PassedPawnMasks[(int)Side.Black, sq] = blackMask;
 62            }
 163        }
 64
 65        // Returns score from white's perspective (positive = good for white)
 66        public static int Evaluate(BoardState boardState)
 67        {
 383968868            ulong whitePawns = boardState.Pieces[(int)Side.White, (int)Piece.Pawn].Board;
 383968869            ulong blackPawns = boardState.Pieces[(int)Side.Black, (int)Piece.Pawn].Board;
 70
 383968871            int score = 0;
 383968872            score += ScoreDoubledPawns(whitePawns, blackPawns);
 383968873            score += ScorePawnFeatures(whitePawns, blackPawns);
 383968874            return score;
 75        }
 76
 77        private static int ScoreDoubledPawns(ulong whitePawns, ulong blackPawns)
 78        {
 383968879            int score = 0;
 6911438480            for (int file = 0; file < 8; file++)
 81            {
 3071750482                int whiteCount = BitOperations.PopCount(whitePawns & FileMasks[file]);
 3071750483                int blackCount = BitOperations.PopCount(blackPawns & FileMasks[file]);
 3193978184                if (whiteCount > 1) score -= (whiteCount - 1) * DoubledPawnPenalty;
 3266411385                if (blackCount > 1) score += (blackCount - 1) * DoubledPawnPenalty;
 86            }
 383968887            return score;
 88        }
 89
 90        private static int ScorePawnFeatures(ulong whitePawns, ulong blackPawns)
 91        {
 383968892            int score = 0;
 383968893            ulong wp = whitePawns;
 2954116794            while (wp != 0)
 95            {
 2570147996                int sq = BitOperations.TrailingZeroCount(wp);
 2570147997                wp &= wp - 1;
 2570147998                if ((whitePawns & AdjacentFileMasks[sq & 7]) == 0)
 377843499                    score -= IsolatedPawnPenalty;
 25701479100                if ((blackPawns & PassedPawnMasks[(int)Side.White, sq]) == 0)
 690599101                    score += PassedPawnBonus[sq >> 3];
 102            }
 103
 3839688104            ulong bp = blackPawns;
 28275913105            while (bp != 0)
 106            {
 24436225107                int sq = BitOperations.TrailingZeroCount(bp);
 24436225108                bp &= bp - 1;
 24436225109                if ((blackPawns & AdjacentFileMasks[sq & 7]) == 0)
 4449301110                    score += IsolatedPawnPenalty;
 24436225111                if ((whitePawns & PassedPawnMasks[(int)Side.Black, sq]) == 0)
 943684112                    score -= PassedPawnBonus[7 - (sq >> 3)];
 113            }
 3839688114            return score;
 115        }
 116    }
 117}