< Summary

Line coverage
96%
Covered lines: 375
Uncovered lines: 15
Coverable lines: 390
Total lines: 777
Line coverage: 96.1%
Branch coverage
89%
Covered branches: 237
Total branches: 265
Branch coverage: 89.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .ctor()100%22100%
File 1: Default()100%11100%
File 1: get_BoardHash()100%11100%
File 1: get_Pieces()100%11100%
File 1: get_Occupancies()100%11100%
File 1: get_PieceMapping()100%11100%
File 1: get_SideToMove()100%11100%
File 1: get_EnPassantSquare()100%11100%
File 1: get_Castle()100%11100%
File 1: get_Moves()100%11100%
File 1: get_LastDrawKiller()100%11100%
File 1: get_MoveCount()100%11100%
File 1: get_BestMove()100%11100%
File 1: get_Phase()100%11100%
File 1: set_Phase(...)100%11100%
File 1: get_ClippedPhase()100%11100%
File 1: AddPiece(...)100%11100%
File 1: RemovePiece(...)100%11100%
File 1: GetPieceOn(...)50%22100%
File 1: GetPieceOn(...)100%44100%
File 1: IsInCheck(...)100%11100%
File 1: MakeMove(...)100%88100%
File 1: HandleCastle(...)80%1010100%
File 1: HandleCapture(...)100%22100%
File 1: FlipSideToMove()100%11100%
File 1: UpdateEnPassant(...)100%44100%
File 1: UpdateCastlingRights(...)100%22100%
File 1: MoveRookFrom(...)100%11100%
File 1: UnmakeMove(...)88.88%181896.66%
File 1: EnPassantSquareFor(...)100%22100%
File 1: Equals(...)66.66%1818100%
File 1: Equals(...)0%4260%
File 1: GetHashCode()100%210%
File 1: ToString()0%620%
File 1: IsDraw()100%44100%
File 2: ParseFEN(...)100%11100%
File 2: ParseEnPassant(...)100%22100%
File 2: ParseCastling(...)100%1212100%
File 2: ParseSideToMove(...)100%22100%
File 2: ParsePieces(...)100%88100%
File 2: SymbolToPiece(...)63.63%111187.5%
File 2: SymbolToSide(...)100%22100%
File 3: GenerateMoves()100%11100%
File 3: GenerateKingMoves()100%44100%
File 3: FindBestMove(...)100%11100%
File 3: GenerateQueenMoves()100%66100%
File 3: GenerateRookMoves()100%66100%
File 3: GenerateKnightMoves()100%66100%
File 3: GenerateBishopMoves()100%66100%
File 3: GeneratePawnMoves()100%22100%
File 3: GenerateEnPassants(...)100%44100%
File 3: GeneratePawnPushes(...)100%1818100%
File 3: GeneratePawnAttacks(...)75%4477.77%
File 3: GenerateCastleMoves()100%4646100%
File 3: IsSquareAttacked(...)100%1212100%
File 3: AddPawnMove(...)100%2020100%
File 3: AddMoveToMovesList(...)100%22100%
File 3: IsSquareCapture(...)100%11100%
File 3: MakeNullMove()100%11100%
File 3: UndoNullMove()100%11100%
File 4: .cctor()100%11100%
File 4: op_Equality(...)100%210%
File 4: op_Inequality(...)100%210%
File 4: NullRespectingSequenceEqual(...)50%12860%

File(s)

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

#LineLine coverage
 1using Rudim.Common;
 2using System;
 3using System.Collections.Generic;
 4using System.Linq;
 5
 6namespace Rudim.Board
 7{
 8    public partial class BoardState : IEquatable<BoardState>
 9    {
 5910        private BoardState()
 11        {
 5912            Pieces = new Bitboard[Constants.Sides, Constants.Pieces];
 5913            Occupancies = new Bitboard[Constants.SidesWithBoth];
 5914            PieceMapping = new Piece[Constants.Squares];
 5915            SideToMove = Side.White;
 5916            EnPassantSquare = Square.NoSquare;
 5917            Castle = Castle.None;
 5918            Moves = new List<Move>(32);
 5919            MoveCount = 0;
 767020            for (int square = 0; square < Constants.Squares; ++square)
 377621                PieceMapping[square] = Piece.None;
 5922            BestMove = Move.NoMove;
 5923        }
 24
 25        public static BoardState Default()
 26        {
 2227            return ParseFEN(Helpers.StartingFEN);
 28        }
 29
 9969426930        public ulong BoardHash { get; set; }
 20707166731        public Bitboard[,] Pieces { get; }
 42166416832        public Bitboard[] Occupancies { get; }
 12896747533        public Piece[] PieceMapping { get; set; }
 23351767934        public Side SideToMove { get; private set; }
 5151464335        public Square EnPassantSquare { get; private set; }
 5888100436        public Castle Castle { get; private set; }
 6773829037        public List<Move> Moves { get; set; }
 3016654838        private int LastDrawKiller { get; set; }
 4092715539        public int MoveCount { get; set; }
 1675498940        public Move BestMove { get; set; }
 41
 42        private int _phase;
 43        public int Phase
 44        {
 3032201645            get => _phase;
 3020146646            set => _phase = value;
 47        }
 48
 429867849        public int ClippedPhase => Math.Min(_phase, GamePhase.TotalPhase);
 50
 51
 52        private void AddPiece(Square square, Side side, Piece piece)
 53        {
 1510158154            Pieces[(int)side, (int)piece] = Pieces[(int)side, (int)piece].SetBit(square);
 1510158155            Occupancies[(int)side] = Occupancies[(int)side].SetBit(square);
 1510158156            Occupancies[(int)Side.Both] = Occupancies[(int)Side.Both].SetBit(square);
 1510158157            PieceMapping[(int)square] = piece;
 1510158158            Phase = GamePhase.AddPhase(Phase, piece);
 1510158159        }
 60
 61        private Piece RemovePiece(Square square)
 62        {
 1509988563            Piece pieceOnSquare = PieceMapping[(int)square];
 1509988564            Pieces[(int)Side.White, (int)pieceOnSquare] = Pieces[(int)Side.White, (int)pieceOnSquare].ClearBit(square);
 1509988565            Pieces[(int)Side.Black, (int)pieceOnSquare] = Pieces[(int)Side.Black, (int)pieceOnSquare].ClearBit(square);
 1509988566            Occupancies[(int)Side.Black] = Occupancies[(int)Side.Black].ClearBit(square);
 1509988567            Occupancies[(int)Side.White] = Occupancies[(int)Side.White].ClearBit(square);
 1509988568            Occupancies[(int)Side.Both] = Occupancies[(int)Side.Both].ClearBit(square);
 1509988569            PieceMapping[(int)square] = Piece.None;
 1509988570            Phase = GamePhase.RemovePhase(Phase, pieceOnSquare);
 1509988571            return pieceOnSquare;
 72        }
 73
 74        public int GetPieceOn(Square square, Side side)
 75        {
 1387414276            Piece piece = PieceMapping[(int)square];
 1387414277            return Occupancies[(int)side].GetBit(square) == 1 ? (int)piece : (int)Piece.None;
 78        }
 79
 80        public int GetPieceOn(Square square)
 81        {
 6978814782            int piece = (int)PieceMapping[(int)square];
 6979117783            if (piece == (int)Piece.None) return -1;
 6978511784            return Occupancies[(int)Side.White].GetBit(square) == 1 ? piece : 6 + piece;
 85        }
 86
 87        public bool IsInCheck(Side side)
 88        {
 583362089            return IsSquareAttacked((Square)Pieces[(int)side, (int)Piece.King].GetLsb(), side.Other());
 90        }
 91        public void MakeMove(Move move)
 92        {
 550097493            Piece capturedPiece = Piece.None;
 550097494            ulong originalBoardHash = BoardHash;
 550097495            Square originalEnPassantSquare = EnPassantSquare;
 550097496            Castle originalCastlingRights = Castle;
 550097497            int originalLastDrawKiller = LastDrawKiller;
 98
 550097499            BoardHash ^= Zobrist.ZobristTable[GetPieceOn(move.Source), (int)move.Source];
 5500974100            Piece movedPiece = RemovePiece(move.Source);
 5500974101            if (movedPiece == Piece.Pawn)
 102            {
 1768804103                LastDrawKiller = MoveCount;
 104            }
 105
 106
 5500974107            if (move.IsCapture())
 108            {
 4070595109                capturedPiece = HandleCapture(move);
 110            }
 111
 5500974112            if (move.IsPromotion())
 113            {
 238372114                movedPiece = move.Type.Piece;
 115            }
 116
 5500974117            if (move.IsCastle())
 118            {
 13831119                HandleCastle(move);
 120            }
 121
 5500974122            AddPiece(move.Target, SideToMove, movedPiece);
 5500974123            BoardHash ^= Zobrist.ZobristTable[GetPieceOn(move.Target), (int)move.Target];
 124
 5500974125            UpdateCastlingRights(move);
 5500974126            UpdateEnPassant(move);
 5500974127            FlipSideToMove();
 128
 5500974129            History.SaveBoardHistory(capturedPiece, originalEnPassantSquare, originalCastlingRights, originalBoardHash, 
 5500974130            BestMove = Move.NoMove;
 5500974131            MoveCount++;
 5500974132        }
 133
 134        private void HandleCastle(Move move)
 135        {
 13831136            switch (move.Target)
 137            {
 138                case Square.c1:
 3678139                    MoveRookFrom(Square.a1, Square.d1, SideToMove);
 3678140                    break;
 141                case Square.g1:
 3834142                    MoveRookFrom(Square.h1, Square.f1, SideToMove);
 3834143                    break;
 144                case Square.c8:
 3463145                    MoveRookFrom(Square.a8, Square.d8, SideToMove);
 3463146                    break;
 147                case Square.g8:
 2856148                    MoveRookFrom(Square.h8, Square.f8, SideToMove);
 149                    break;
 150            }
 2856151        }
 152
 153        private Piece HandleCapture(Move move)
 154        {
 4070595155            Square targetSquare = move.Type == MoveTypes.EnPassant ? EnPassantSquareFor(move) : move.Target;
 156
 4070595157            BoardHash ^= Zobrist.ZobristTable[GetPieceOn(targetSquare), (int)targetSquare];
 4070595158            LastDrawKiller = MoveCount;
 159
 4070595160            return RemovePiece(targetSquare);
 161        }
 162
 163        private void FlipSideToMove()
 164        {
 5741848165            BoardHash = Zobrist.FlipSideToMoveHashes(this, BoardHash);
 5741848166            SideToMove = SideToMove.Other();
 5741848167        }
 168
 169        private void UpdateEnPassant(Move move)
 170        {
 5621411171            Square originalEnPassantSquare = EnPassantSquare;
 5621411172            BoardHash = Zobrist.HashEnPassant(this, BoardHash);
 5621411173            EnPassantSquare = move.Type == MoveTypes.DoublePush ? EnPassantSquareFor(move) : Square.NoSquare;
 5621411174            BoardHash = Zobrist.HashEnPassant(this, BoardHash);
 5621411175            if (originalEnPassantSquare != EnPassantSquare)
 234804176                LastDrawKiller = MoveCount;
 5621411177        }
 178
 179        private void UpdateCastlingRights(Move move)
 180        {
 5500974181            Castle originalCastlingRights = Castle;
 5500974182            BoardHash = Zobrist.HashCastlingRights(this, BoardHash);
 5500974183            Castle &= (Castle)CastlingConstants[(int)move.Source];
 5500974184            Castle &= (Castle)CastlingConstants[(int)move.Target];
 5500974185            BoardHash = Zobrist.HashCastlingRights(this, BoardHash);
 5500974186            if (Castle != originalCastlingRights)
 571418187                LastDrawKiller = MoveCount;
 5500974188        }
 189
 190        private void MoveRookFrom(Square source, Square target, Side sideToMove)
 191        {
 13831192            RemovePiece(source);
 13831193            AddPiece(target, sideToMove, Piece.Rook);
 194
 13831195            int rookIndex = GetPieceOn(target);
 13831196            BoardHash ^= Zobrist.ZobristTable[rookIndex, (int)source];
 13831197            BoardHash ^= Zobrist.ZobristTable[rookIndex, (int)target];
 13831198        }
 199
 200
 201        public void UnmakeMove(Move move)
 202        {
 5500656203            History.BoardHistory history = History.RestoreBoardHistory();
 204
 5500656205            Piece movedPiece = RemovePiece(move.Target);
 5500656206            SideToMove = SideToMove.Other();
 207
 5500656208            if (history.CapturedPiece != Piece.None)
 209            {
 4070566210                if (move.Type == MoveTypes.EnPassant)
 211                {
 10634212                    AddPiece(EnPassantSquareFor(move), SideToMove.Other(), Piece.Pawn);
 213                }
 214                else
 215                {
 4059932216                    AddPiece(move.Target, SideToMove.Other(), history.CapturedPiece);
 217                }
 218            }
 219
 5500656220            if (move.IsCastle())
 221            {
 13829222                switch (move.Target)
 223                {
 224                    case Square.c1:
 3678225                        RemovePiece(Square.d1);
 3678226                        AddPiece(Square.a1, SideToMove, Piece.Rook);
 3678227                        break;
 228                    case Square.g1:
 3833229                        RemovePiece(Square.f1);
 3833230                        AddPiece(Square.h1, SideToMove, Piece.Rook);
 3833231                        break;
 232                    case Square.c8:
 3463233                        RemovePiece(Square.d8);
 3463234                        AddPiece(Square.a8, SideToMove, Piece.Rook);
 3463235                        break;
 236                    case Square.g8:
 2855237                        RemovePiece(Square.f8);
 2855238                        AddPiece(Square.h8, SideToMove, Piece.Rook);
 2855239                        break;
 0240                    default: throw new ArgumentOutOfRangeException(nameof(move.Target));
 241                }
 242            }
 243
 5500656244            AddPiece(move.Source, SideToMove, move.IsPromotion() ? Piece.Pawn : movedPiece);
 5500656245            LastDrawKiller = history.LastDrawKiller;
 5500656246            BoardHash = history.BoardHash;
 5500656247            Castle = history.CastlingRights;
 5500656248            EnPassantSquare = history.EnPassantSquare;
 5500656249            BestMove = history.BestMove;
 5500656250            MoveCount--;
 5500656251        }
 252        private Square EnPassantSquareFor(Move move)
 253        {
 161448254            return move.Target + 8 * (SideToMove == Side.Black ? -1 : 1);
 255        }
 256
 257        public bool Equals(BoardState other)
 258        {
 7259            if (ReferenceEquals(null, other)) return false;
 7260            if (ReferenceEquals(this, other)) return true;
 261
 7262            if (Pieces.Rank != other.Pieces.Rank ||
 7263                Enumerable.Range(0, Pieces.Rank).Any(dimension =>
 14264                    Pieces.GetLength(dimension) != other.Pieces.GetLength(dimension)) ||
 7265                !Pieces.Cast<Bitboard>().SequenceEqual(other.Pieces.Cast<Bitboard>()))
 2266                return false;
 267
 5268            return NullRespectingSequenceEqual(Occupancies, other.Occupancies) &&
 5269                   SideToMove == other.SideToMove && EnPassantSquare == other.EnPassantSquare &&
 5270                   Castle == other.Castle && NullRespectingSequenceEqual(Moves, other.Moves);
 271        }
 272
 273        public override bool Equals(object obj)
 274        {
 0275            if (ReferenceEquals(null, obj)) return false;
 0276            if (ReferenceEquals(this, obj)) return true;
 0277            if (obj.GetType() != this.GetType()) return false;
 0278            return Equals((BoardState)obj);
 279        }
 280
 281        public override int GetHashCode()
 282        {
 0283            return HashCode.Combine(Pieces, Occupancies);
 284        }
 285
 286        public override string ToString()
 287        {
 0288            ulong boardHash = BoardHash;
 0289            return CommonStateNames.TryGetValue(boardHash, out string commonName) ? commonName : boardHash.ToString();
 290        }
 291
 292        public bool IsDraw()
 293        {
 6139108294            if (MoveCount - LastDrawKiller > 100) return true;
 12278002295            if (MoveCount - LastDrawKiller <= 7) return false;
 210296            return History.HasHashAppearedTwice(BoardHash, LastDrawKiller);
 297        }
 298    }
 299}

/home/runner/work/rudim/rudim/Rudim/Board/BoardState.FEN.cs

#LineLine coverage
 1using Rudim.Common;
 2using System;
 3
 4namespace Rudim.Board
 5{
 6    public partial class BoardState
 7    {
 8        public static BoardState ParseFEN(string FEN)
 9        {
 5910            BoardState board = new BoardState();
 5911            string[] sections = FEN.Split(' ');
 5912            ParsePieces(board, sections[0]);
 5913            ParseSideToMove(board, sections[1]);
 5914            ParseCastling(board, sections[2]);
 5915            ParseEnPassant(board, sections[3]);
 16            // ParsePly(board, sections[4]);
 5917            board.BoardHash = Zobrist.GetBoardHash(board);
 5918            return board;
 19        }
 20
 21        private static void ParseEnPassant(BoardState board, string fen)
 22        {
 5923            if (fen != "-")
 824                board.EnPassantSquare = (Square)Enum.Parse(typeof(Square), fen);
 5925        }
 26
 27        private static void ParseCastling(BoardState board, string fen)
 28        {
 55829            foreach (char character in fen)
 30            {
 31                switch (character)
 32                {
 10833                    case 'K': board.Castle |= Castle.WhiteShort; break;
 10834                    case 'Q': board.Castle |= Castle.WhiteLong; break;
 10635                    case 'k': board.Castle |= Castle.BlackShort; break;
 5436                    case 'q': board.Castle |= Castle.BlackLong; break;
 37                }
 38            }
 5939        }
 40
 41        private static void ParseSideToMove(BoardState board, string fen)
 42        {
 5943            board.SideToMove = fen == "w" ? Side.White : Side.Black;
 5944        }
 45
 46        private static void ParsePieces(BoardState board, string fen)
 47        {
 5948            string[] ranks = fen.Split('/');
 49
 106250            for (int rank = 0; rank < 8; rank++)
 51            {
 47252                int index = rank * 8;
 525453                for (int file = 0; file < ranks[rank].Length; file++)
 54                {
 215555                    char symbol = ranks[rank][file];
 215556                    if (char.IsLetter(symbol))
 57                    {
 172558                        board.AddPiece((Square)index, SymbolToSide(symbol), SymbolToPiece(symbol));
 172559                        index++;
 60                    }
 43061                    else if (char.IsDigit(symbol))
 62                    {
 43063                        index += symbol - '0';
 64                    }
 65                }
 66            }
 5967        }
 68        private static Piece SymbolToPiece(char symbol)
 69        {
 172570            switch (char.ToLower(symbol))
 71            {
 88672                case 'p': return Piece.Pawn;
 22673                case 'r': return Piece.Rook;
 19274                case 'n': return Piece.Knight;
 20275                case 'b': return Piece.Bishop;
 10176                case 'q': return Piece.Queen;
 11877                case 'k': return Piece.King;
 78            }
 079            return Piece.None;
 80        }
 81
 82        private static Side SymbolToSide(char symbol)
 83        {
 172584            return char.IsUpper(symbol) ? Side.White : Side.Black;
 85        }
 86
 87    }
 88}

/home/runner/work/rudim/rudim/Rudim/Board/BoardState.Moves.cs

#LineLine coverage
 1using Rudim.Common;
 2using Rudim.Search;
 3using System.Collections.Generic;
 4using System.Threading;
 5
 6namespace Rudim.Board
 7{
 8    public partial class BoardState
 9    {
 10        public void GenerateMoves()
 11        {
 181506012            Moves = new List<Move>();
 13
 181506014            GeneratePawnMoves();
 181506015            GenerateBishopMoves();
 181506016            GenerateKnightMoves();
 181506017            GenerateRookMoves();
 181506018            GenerateQueenMoves();
 181506019            GenerateKingMoves();
 181506020        }
 21
 22        private void GenerateKingMoves()
 23        {
 181506024            int source = Pieces[(int)SideToMove, (int)Piece.King].GetLsb();
 181506025            Bitboard attacks = new Bitboard(Bitboard.KingAttacks[source]);
 26
 1153092927            while (attacks.Board > 0)
 28            {
 971586929                int target = attacks.GetLsb();
 971586930                attacks.ClearBit(target);
 971586931                if (Occupancies[(int)SideToMove].GetBit(target) == 1)
 32                {
 33                    continue;
 34                }
 524602135                AddMoveToMovesList(source, target);
 36            }
 37
 181506038            GenerateCastleMoves();
 181506039        }
 40        public Move FindBestMove(int depth, CancellationToken cancellationToken, ref bool debugMode)
 41        {
 542            IterativeDeepening.Search(this, depth, cancellationToken, ref debugMode);
 543            return IterativeDeepening.BestMove;
 44        }
 45
 46        private void GenerateQueenMoves()
 47        {
 181506048            Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Queen];
 317288149            while (bitboard.Board > 0)
 50            {
 135782151                int source = bitboard.GetLsb();
 135782152                Bitboard attacks = Bitboard.GetQueenAttacksFromTable((Square)source, Occupancies[(int)Side.Both]);
 53
 1672490854                while (attacks.Board > 0)
 55                {
 1536708756                    int target = attacks.GetLsb();
 57
 1536708758                    if (Occupancies[(int)SideToMove].GetBit(target) == 1)
 59                    {
 530977460                        attacks.ClearBit(target);
 530977461                        continue;
 62                    }
 63
 1005731364                    AddMoveToMovesList(source, target);
 65
 1005731366                    attacks.ClearBit(target);
 67                }
 68
 135782169                bitboard.ClearBit(source);
 70            }
 181506071        }
 72
 73        private void GenerateRookMoves()
 74        {
 181506075            Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Rook];
 497484076            while (bitboard.Board > 0)
 77            {
 315978078                int source = bitboard.GetLsb();
 315978079                Bitboard attacks = Bitboard.GetRookAttacksFromTable((Square)source, Occupancies[(int)Side.Both]);
 80
 1910106681                while (attacks.Board > 0)
 82                {
 1594128683                    int target = attacks.GetLsb();
 84
 1594128685                    if (Occupancies[(int)SideToMove].GetBit(target) == 1)
 86                    {
 587229987                        attacks.ClearBit(target);
 587229988                        continue;
 89                    }
 90
 1006898791                    AddMoveToMovesList(source, target);
 92
 1006898793                    attacks.ClearBit(target);
 94                }
 95
 315978096                bitboard.ClearBit(source);
 97            }
 181506098        }
 99
 100        private void GenerateKnightMoves()
 101        {
 1815060102            Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Knight];
 4082633103            while (bitboard.Board > 0)
 104            {
 2267573105                int source = bitboard.GetLsb();
 2267573106                Bitboard attacks = new Bitboard(Bitboard.KnightAttacks[source]);
 107
 15043450108                while (attacks.Board > 0)
 109                {
 12775877110                    int target = attacks.GetLsb();
 111
 12775877112                    if (Occupancies[(int)SideToMove].GetBit(target) == 1)
 113                    {
 3104244114                        attacks.ClearBit(target);
 3104244115                        continue;
 116                    }
 117
 9671633118                    AddMoveToMovesList(source, target);
 119
 9671633120                    attacks.ClearBit(target);
 121                }
 122
 2267573123                bitboard.ClearBit(source);
 124            }
 1815060125        }
 126
 127        private void GenerateBishopMoves()
 128        {
 1815060129            Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Bishop];
 4212586130            while (bitboard.Board > 0)
 131            {
 2397526132                int source = bitboard.GetLsb();
 2397526133                Bitboard attacks = Bitboard.GetBishopAttacksFromTable((Square)source, Occupancies[(int)Side.Both]);
 134
 15034281135                while (attacks.Board > 0)
 136                {
 12636755137                    int target = attacks.GetLsb();
 138
 12636755139                    if (Occupancies[(int)SideToMove].GetBit(target) == 1)
 140                    {
 3043387141                        attacks.ClearBit(target);
 3043387142                        continue;
 143                    }
 144
 9593368145                    AddMoveToMovesList(source, target);
 146
 9593368147                    attacks.ClearBit(target);
 148                }
 149
 2397526150                bitboard.ClearBit(source);
 151            }
 1815060152        }
 153
 154        private void GeneratePawnMoves()
 155        {
 1815060156            Bitboard bitboard = Pieces[(int)SideToMove, (int)Piece.Pawn];
 13420476157            while (bitboard.Board > 0)
 158            {
 11605416159                int source = bitboard.GetLsb();
 11605416160                GeneratePawnPushes(source);
 11605416161                GenerateEnPassants(source);
 11605416162                GeneratePawnAttacks(source);
 163
 11605416164                bitboard.ClearBit(source);
 165            }
 1815060166        }
 167
 168        private void GenerateEnPassants(int source)
 169        {
 11605416170            if (EnPassantSquare == Square.NoSquare)
 11287484171                return;
 172
 317932173            Bitboard attacks = new Bitboard(Bitboard.PawnAttacks[(int)SideToMove, source] & (1ul << (int)EnPassantSquare
 317932174            if (attacks.Board > 0)
 175            {
 16717176                int target = attacks.GetLsb();
 16717177                AddPawnMove(source, target, true, false);
 178            }
 179
 317932180        }
 181
 182
 183        private void GeneratePawnPushes(int source)
 184        {
 11605416185            if (SideToMove == Side.Black)
 186            {
 5761101187                int oneSquarePush = source + 8;
 7142654188                if (Occupancies[(int)Side.Both].GetBit(oneSquarePush) != 0) return;
 4379548189                AddPawnMove(source, oneSquarePush, false, false);
 4379548190                if (source is <= (int)Square.h7 and >= (int)Square.a7)
 191                {
 2901663192                    int twoSquarePush = oneSquarePush + 8;
 3374252193                    if (Occupancies[(int)Side.Both].GetBit(twoSquarePush) != 0) return;
 2429074194                    AddPawnMove(source, twoSquarePush, false, true);
 195                }
 196            }
 197            else
 198            {
 5844315199                int oneSquarePush = source - 8;
 7239904200                if (Occupancies[(int)Side.Both].GetBit(oneSquarePush) != 0) return;
 4448726201                AddPawnMove(source, oneSquarePush, false, false);
 4448726202                if (source is <= (int)Square.h2 and >= (int)Square.a2)
 203                {
 2989384204                    int twoSquarePush = oneSquarePush - 8;
 3277523205                    if (Occupancies[(int)Side.Both].GetBit(twoSquarePush) != 0) return;
 2701245206                    AddPawnMove(source, twoSquarePush, false, true);
 207                }
 208            }
 5638472209        }
 210
 211        private void GeneratePawnAttacks(int source)
 212        {
 11605416213            Bitboard attacks = new Bitboard(Bitboard.PawnAttacks[(int)SideToMove, source] & Occupancies[(int)SideToMove.
 214
 13616736215            while (attacks.Board > 0)
 216            {
 2011320217                int target = attacks.GetLsb();
 218
 2011320219                if (Occupancies[(int)SideToMove].GetBit(target) == 1)
 220                {
 0221                    attacks.ClearBit(target);
 0222                    continue;
 223                }
 224
 2011320225                AddPawnMove(source, target, false, false);
 226
 2011320227                attacks.ClearBit(target);
 228            }
 11605416229        }
 230        private void GenerateCastleMoves()
 231        {
 232            // Squares should be empty and shouldn't castle through check, can avoid checking if landing position is che
 233
 1815060234            Bitboard occ = Occupancies[(int)Side.Both];
 1815060235            if (SideToMove == Side.White)
 236            {
 920811237                if (Castle.HasFlag(Castle.WhiteShort))
 238                {
 537662239                    if (occ.GetBit(Square.f1) == 0 && occ.GetBit(Square.g1) == 0 && !IsSquareAttacked(Square.e1, Side.Bl
 179021240                        Moves.Add(new Move(Square.e1, Square.g1, MoveTypes.Castle));
 241                }
 920811242                if (Castle.HasFlag(Castle.WhiteLong))
 243                {
 579572244                    if (occ.GetBit(Square.d1) == 0 && occ.GetBit(Square.c1) == 0 && occ.GetBit(Square.b1) == 0 && !IsSqu
 183611245                        Moves.Add(new Move(Square.e1, Square.c1, MoveTypes.Castle));
 246                }
 247            }
 248            else
 249            {
 894249250                if (Castle.HasFlag(Castle.BlackShort))
 251                {
 560227252                    if (occ.GetBit(Square.f8) == 0 && occ.GetBit(Square.g8) == 0 && !IsSquareAttacked(Square.e8, Side.Wh
 329146253                        Moves.Add(new Move(Square.e8, Square.g8, MoveTypes.Castle));
 254                }
 894249255                if (Castle.HasFlag(Castle.BlackLong))
 256                {
 732269257                    if (occ.GetBit(Square.d8) == 0 && occ.GetBit(Square.c8) == 0 && occ.GetBit(Square.b8) == 0 && !IsSqu
 335817258                        Moves.Add(new Move(Square.e8, Square.c8, MoveTypes.Castle));
 259                }
 260            }
 1631449261        }
 262
 263        private bool IsSquareAttacked(Square square, Side attackingSide)
 264        {
 8552304265            if ((Bitboard.GetBishopAttacksFromTable(square, Occupancies[(int)Side.Both]).Board & Pieces[(int)attackingSi
 173808266                return true;
 8378496267            if ((Bitboard.GetRookAttacksFromTable(square, Occupancies[(int)Side.Both]).Board & Pieces[(int)attackingSide
 67210268                return true;
 8311286269            if ((Bitboard.GetQueenAttacksFromTable(square, Occupancies[(int)Side.Both]).Board & Pieces[(int)attackingSid
 503385270                return true;
 7807901271            if ((Bitboard.KnightAttacks[(int)square] & Pieces[(int)attackingSide, (int)Piece.Knight].Board) != 0)
 120338272                return true;
 7687563273            if ((Bitboard.PawnAttacks[(int)attackingSide.Other(), (int)square] & Pieces[(int)attackingSide, (int)Piece.P
 264327274                return true;
 7423236275            if ((Bitboard.KingAttacks[(int)square] & Pieces[(int)attackingSide, (int)Piece.King].Board) != 0)
 2276                return true;
 7423234277            return false;
 278        }
 279
 280        private void AddPawnMove(int source, int target, bool enpassant, bool doublePush)
 281        {
 282            // This assumes all incoming pawn moves are valid
 15986630283            if (target is >= (int)Square.a1 and <= (int)Square.h1 || target is <= (int)Square.h8 and >= (int)Square.a8)
 284            {
 214115285                bool capture = IsSquareCapture(target);
 286
 214115287                Moves.Add(new Move((Square)source, (Square)target, capture ? MoveTypes.KnightPromotionCapture : MoveType
 214115288                Moves.Add(new Move((Square)source, (Square)target, capture ? MoveTypes.BishopPromotionCapture : MoveType
 214115289                Moves.Add(new Move((Square)source, (Square)target, capture ? MoveTypes.RookPromotionCapture : MoveTypes.
 214115290                Moves.Add(new Move((Square)source, (Square)target, capture ? MoveTypes.QueenPromotionCapture : MoveTypes
 291            }
 15772515292            else if (enpassant || doublePush)
 293            {
 5147036294                Moves.Add(new Move((Square)source, (Square)target, enpassant ? MoveTypes.EnPassant : MoveTypes.DoublePus
 295            }
 296            else
 297            {
 10625479298                AddMoveToMovesList(source, target);
 299            }
 10625479300        }
 301
 302
 303        private void AddMoveToMovesList(int source, int target)
 304        {
 55262801305            MoveType moveType = IsSquareCapture(target) ? MoveTypes.Capture : MoveTypes.Quiet;
 55262801306            Move move = new Move((Square)source, (Square)target, moveType);
 55262801307            Moves.Add(move);
 55262801308        }
 309        private bool IsSquareCapture(int target)
 310        {
 55476916311            return Occupancies[(int)SideToMove.Other()].GetBit(target) == 1;
 312        }
 313
 314        public void MakeNullMove()
 315        {
 120437316            History.SaveBoardHistory(Piece.None, EnPassantSquare, Castle, BoardHash, LastDrawKiller, BestMove);
 120437317            UpdateEnPassant(Move.NoMove);
 120437318            FlipSideToMove();
 120437319        }
 320
 321        public void UndoNullMove()
 322        {
 120437323            History.BoardHistory history = History.RestoreBoardHistory();
 120437324            FlipSideToMove();
 120437325            LastDrawKiller = history.LastDrawKiller;
 120437326            BoardHash = history.BoardHash;
 120437327            Castle = history.CastlingRights;
 120437328            EnPassantSquare = history.EnPassantSquare;
 120437329        }
 330    }
 331}

/home/runner/work/rudim/rudim/Rudim/Board/BoardState.Static.cs

#LineLine coverage
 1using Rudim.Common;
 2using System.Collections.Generic;
 3using System.Linq;
 4
 5namespace Rudim.Board
 6{
 7    public partial class BoardState
 8    {
 19        private static readonly Dictionary<ulong, string> CommonStateNames = new();
 10
 111        private static readonly int[] CastlingConstants =
 112        {
 113            7, 15, 15, 15, 3, 15, 15, 11,
 114            15, 15, 15, 15, 15, 15, 15, 15,
 115            15, 15, 15, 15, 15, 15, 15, 15,
 116            15, 15, 15, 15, 15, 15, 15, 15,
 117            15, 15, 15, 15, 15, 15, 15, 15,
 118            15, 15, 15, 15, 15, 15, 15, 15,
 119            15, 15, 15, 15, 15, 15, 15, 15,
 120            13, 15, 15, 15, 12, 15, 15, 14
 121        };
 22
 23        static BoardState()
 24        {
 25
 126            CommonStateNames[Zobrist.GetBoardHash(ParseFEN(Helpers.StartingFEN))] = "Starting State";
 127            CommonStateNames[Zobrist.GetBoardHash(ParseFEN(Helpers.EndgameFEN))] = "Endgame State";
 128            CommonStateNames[Zobrist.GetBoardHash(ParseFEN(Helpers.KiwiPeteFEN))] = "KiwiPete State";
 129            CommonStateNames[Zobrist.GetBoardHash(ParseFEN(Helpers.KiwiPeteFEN))] = "Random State";
 130            CommonStateNames[Zobrist.GetBoardHash(ParseFEN(Helpers.AdvancedMoveFEN))] = "Advanced Move State";
 131        }
 32
 33        public static bool operator ==(BoardState left, BoardState right)
 34        {
 035            return Equals(left, right);
 36        }
 37
 38        public static bool operator !=(BoardState left, BoardState right)
 39        {
 040            return !Equals(left, right);
 41        }
 42
 43        // This can move to an extension method
 44        private static bool NullRespectingSequenceEqual<T>(IEnumerable<T> first, IEnumerable<T> second)
 45        {
 1046            if (first == null && second == null)
 47            {
 048                return true;
 49            }
 50
 1051            if (first == null || second == null)
 52            {
 053                return false;
 54            }
 55
 1056            return first.SequenceEqual(second);
 57        }
 58    }
 59}

Methods/Properties

.ctor()
Default()
get_BoardHash()
get_Pieces()
get_Occupancies()
get_PieceMapping()
get_SideToMove()
get_EnPassantSquare()
get_Castle()
get_Moves()
get_LastDrawKiller()
get_MoveCount()
get_BestMove()
get_Phase()
set_Phase(System.Int32)
get_ClippedPhase()
AddPiece(Rudim.Common.Square,Rudim.Common.Side,Rudim.Common.Piece)
RemovePiece(Rudim.Common.Square)
GetPieceOn(Rudim.Common.Square,Rudim.Common.Side)
GetPieceOn(Rudim.Common.Square)
IsInCheck(Rudim.Common.Side)
MakeMove(Rudim.Common.Move)
HandleCastle(Rudim.Common.Move)
HandleCapture(Rudim.Common.Move)
FlipSideToMove()
UpdateEnPassant(Rudim.Common.Move)
UpdateCastlingRights(Rudim.Common.Move)
MoveRookFrom(Rudim.Common.Square,Rudim.Common.Square,Rudim.Common.Side)
UnmakeMove(Rudim.Common.Move)
EnPassantSquareFor(Rudim.Common.Move)
Equals(Rudim.Board.BoardState)
Equals(System.Object)
GetHashCode()
ToString()
IsDraw()
ParseFEN(System.String)
ParseEnPassant(Rudim.Board.BoardState,System.String)
ParseCastling(Rudim.Board.BoardState,System.String)
ParseSideToMove(Rudim.Board.BoardState,System.String)
ParsePieces(Rudim.Board.BoardState,System.String)
SymbolToPiece(System.Char)
SymbolToSide(System.Char)
GenerateMoves()
GenerateKingMoves()
FindBestMove(System.Int32,System.Threading.CancellationToken,System.Boolean&)
GenerateQueenMoves()
GenerateRookMoves()
GenerateKnightMoves()
GenerateBishopMoves()
GeneratePawnMoves()
GenerateEnPassants(System.Int32)
GeneratePawnPushes(System.Int32)
GeneratePawnAttacks(System.Int32)
GenerateCastleMoves()
IsSquareAttacked(Rudim.Common.Square,Rudim.Common.Side)
AddPawnMove(System.Int32,System.Int32,System.Boolean,System.Boolean)
AddMoveToMovesList(System.Int32,System.Int32)
IsSquareCapture(System.Int32)
MakeNullMove()
UndoNullMove()
.cctor()
op_Equality(Rudim.Board.BoardState,Rudim.Board.BoardState)
op_Inequality(Rudim.Board.BoardState,Rudim.Board.BoardState)
NullRespectingSequenceEqual(System.Collections.Generic.IEnumerable`1<T>,System.Collections.Generic.IEnumerable`1<T>)