Hi Mark, Lucas,
Three posts coming up with the basic structure of the whole thing.
After an couple of days of frustration, I'm not really any further. I spent a lot of time testing the functions on their own using structs, references, anything I could think of,
but saw no improvement. In fact, when I changed the board class to a struct, and accessed the pieces without using a method, the perft went from ca. 2300knps to 1800knps.
This was despite single function tests suggesting that fweer method calls and structs by reference was faster.
I'm not sure if I am doing something working with IsAttacked() as pointed out previously, or if I'm missing something generally with the way C# works. I've also spent
a lot of time browsing online documentation, but it hasn't really helped. (apart from making this C++ vs C# discussion look very tame
)
MoveGeneration is as follows: (shortened where code is repeated):
Code: Select all
public static void GenerateMoves(Board board, List<Move> moveList)
{
int pceStartIndex = 0;
int pceSq = 0;
if (board.sideToMove == Colour.Dark)
{
// bP
pceStartIndex = BoardConstants.StartIndex[Pieces.bP];
pceSq = board.pceLst[pceStartIndex++];
while (pceSq != Squares.OffBoard)
{
GenerateBlackPawnMoves(board, moveList, pceSq);
pceSq = board.pceLst[pceStartIndex++];
}
// bN
pceStartIndex = BoardConstants.StartIndex[Pieces.bN];
pceSq = board.pceLst[pceStartIndex++];
while (pceSq != Squares.OffBoard)
{
GenerateNonSlideMoves(board, moveList, Pieces.NDirections, pceSq, Pieces.bN);
pceSq = board.pceLst[pceStartIndex++];
}
// bB
pceStartIndex = BoardConstants.StartIndex[Pieces.bB];
pceSq = board.pceLst[pceStartIndex++];
while (pceSq != Squares.OffBoard)
{
GenerateSliderMoves(board, moveList, Pieces.BDirections, pceSq, Pieces.bB);
pceSq = board.pceLst[pceStartIndex++];
}
// ...........etc for other pieces, then
//bK
pceStartIndex = BoardConstants.StartIndex[Pieces.bK];
pceSq = board.pceLst[pceStartIndex];
Debug.Assert(pceSq != Squares.OffBoard);
GenerateNonSlideMoves(board, moveList, Pieces.QDirections, pceSq, Pieces.bK);
if ((board.currentCastlePermission & BoardConstants.permk) != 0)
GenerateBKSCAMoves(board, moveList);
if ((board.currentCastlePermission & BoardConstants.permq) != 0)
GenerateBQSCAMoves(board, moveList);
//................
}
private static void GenerateNonSlideMoves(Board board, List<Move> moveList, int[] moveArray, int from, int pce)
{
int moves = 0;
int toSq = 0;
int inc = 0;
while (moveArray[moves] != 0)
{
inc = moveArray[moves];
toSq = from + inc;
if (CanLandOrTake(board, pce, toSq) == true)
{
moveList.Add(new Move(from, toSq, Pieces.NoPiece, board.GetPiece(toSq), Moves.FlagNone));
}
moves++;
}
}
private static void GenerateSliderMoves(Board board, List<Move> moveList, int[] moveArray, int from, int pce)
{
Debug.Assert(Pieces.PieceIsValid(pce));
Debug.Assert(Pieces.PieceSlides(pce));
int moves = 0;
int toSq = 0;
int inc = 0;
while (moveArray[moves] != 0)
{
inc = moveArray[moves];
toSq = from + inc;
while (CanLandOrTake(board, pce, toSq) == true)
{
moveList.Add(new Move(from, toSq, Pieces.NoPiece, board.GetPiece(toSq), Moves.FlagNone));
if (board.GetPiece(toSq) != Pieces.NoPiece) break;
toSq += inc;
}
moves++;
}
}
// one of the castle gen moves
private static void GenerateWKSCAMoves(Board board, List<Move> list)
{
if (board.GetPiece(Squares.G1) == Pieces.NoPiece && board.GetPiece(Squares.F1) == Pieces.NoPiece)
{
list.Add(new Move(Squares.E1, Squares.G1, Pieces.NoPiece, Pieces.NoPiece, Moves.FlagCastled));
}
}
private static void GenerateWhitePawnMoves(Board board, List<Move> moveList, int frSq)
{
// one and two squares
if (board.GetPiece(frSq + Squares.dirN) == Pieces.NoPiece)
{
AddWhitePawnMove(frSq, frSq + Squares.dirN, Pieces.NoPiece, moveList);
if (Ranks.RankBoard[frSq] == Ranks.Rank2 && board.GetPiece(frSq + Squares.dirN + Squares.dirN) == Pieces.NoPiece)
{
moveList.Add(new Move(frSq, frSq + Squares.dirN + Squares.dirN, Pieces.NoPiece, Pieces.NoPiece, Moves.FlagNone));
}
}
if (Pieces.PieceColourIs(board.GetPiece(frSq + Squares.dirNW)) == Colour.Dark)
{
AddWhitePawnMove(frSq, frSq + Squares.dirNW, board.GetPiece(frSq + Squares.dirNW), moveList);
}
if (Pieces.PieceColourIs(board.GetPiece(frSq + Squares.dirNE)) == Colour.Dark)
{
AddWhitePawnMove(frSq, frSq + Squares.dirNE, board.GetPiece(frSq + Squares.dirNE), moveList);
}
if (board.enPassantSqaure == (frSq + Squares.dirNW) && board.enPassantSqaure != Squares.OffBoard)
{
moveList.Add(new Move(frSq, frSq + Squares.dirNW, Pieces.NoPiece, Pieces.NoPiece, Moves.FlagEnPassant));
}
if (board.enPassantSqaure == (frSq + Squares.dirNE) && board.enPassantSqaure != Squares.OffBoard)
{
moveList.Add(new Move(frSq, frSq + Squares.dirNE, Pieces.NoPiece, Pieces.NoPiece, Moves.FlagEnPassant));
}
}
private static void AddWhitePawnMove(int frSq, int tosq, int cap, List<Move> list)
{
if (Ranks.RankBoard[tosq] == Ranks.Rank8)
{
list.Add(new Move(frSq, tosq, Pieces.wQ, cap, Moves.FlagNone));
list.Add(new Move(frSq, tosq, Pieces.wR, cap, Moves.FlagNone));
list.Add(new Move(frSq, tosq, Pieces.wB, cap, Moves.FlagNone));
list.Add(new Move(frSq, tosq, Pieces.wN, cap, Moves.FlagNone));
}
else
{
list.Add(new Move(frSq, tosq, Pieces.NoPiece, cap, Moves.FlagNone));
}
}
And, if that wasn't enough for sore eyes, some of the is attacked()....
Code: Select all
public static bool SqIsAttackedBySide(int sq, int side, Board board)
{
if (side == Colour.Light)
{
return AttackedByLight(sq, board);
}
else
{
return AttackedByDark(sq, board);
}
}
private static bool AttackedByDark(int sq, Board board)
{
int pceStartIndex = 0;
int deltaFromTo = 0;
int attackersSq = 0;
int defSq = 0;
int inc = 0;
// bP
pceStartIndex = BoardConstants.StartIndex[Pieces.bP];
attackersSq = board.pceLst[pceStartIndex++];
while (attackersSq != Squares.OffBoard)
{
deltaFromTo = sq - attackersSq;
if ((AttackConstants.AttackBitArray[deltaFromTo + AttackConstants.DeltaShift] & AttackConstants.bP_Bit) != 0)
{
return true;
}
attackersSq = board.pceLst[pceStartIndex++];
}
// bN
pceStartIndex = BoardConstants.StartIndex[Pieces.bN];
attackersSq = board.pceLst[pceStartIndex++];
while (attackersSq != Squares.OffBoard)
{
deltaFromTo = sq - attackersSq;
if ((AttackConstants.AttackBitArray[deltaFromTo + AttackConstants.DeltaShift] & AttackConstants.N_Bit) != 0)
{
return true;
}
attackersSq = board.pceLst[pceStartIndex++];
}
// bB
pceStartIndex = BoardConstants.StartIndex[Pieces.bB];
attackersSq = board.pceLst[pceStartIndex++];
while (attackersSq != Squares.OffBoard)
{
deltaFromTo = sq - attackersSq;
if ((AttackConstants.AttackBitArray[deltaFromTo + AttackConstants.DeltaShift] & AttackConstants.B_Bit) != 0)
{
inc = AttackConstants.AttackStepArray[deltaFromTo + AttackConstants.DeltaShift];
defSq = attackersSq + inc;
Debug.Assert(Squares.SquareOnBoard(defSq));
while (defSq != sq)
{
if (board.GetPiece(defSq) != Pieces.NoPiece) break;
defSq += inc;
}
if (defSq == sq) return true;
}
attackersSq = board.pceLst[pceStartIndex++];
}
.. which used the attack delta lookup to see if an attack can occure, before looping between the pcesq and target squares to see if the
attacking piece reaches it target
Next post, the makemove..