The wrong way

Discussion of chess software programming and technical issues.

Moderator: Ras

Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: The wrong way

Post by Sven »

Henk wrote:
jdart wrote:Most programs do not spend most of their time in movegen. Usually it is sub 10% of the total search time. You can profile and see if this is the case for you.

I am not clear if you are using bitboards or not. Finding attacked pieces with bitboards and magic move generation is pretty fast and can be done with no looping at all.

--Jon
Skipper uses 19% percent of it's time for collecting captures.
Other bottleneck is collecting remaining moves after killers. And that's another 10-20%.
In your implementation you are not taking advantage of the power of bitboards. That is the main reason for getting a slow move generator, and probably slow engine code in general.

Besides that, I would strongly recommend that you write a small utility function that calculates the MVV/LVA score for given piece types of moving piece, captured piece, and possibly also promotion piece. This will greatly simplify your code (it will remove a lot of inner switch blocks) without slowing it down; I would even expect it to become slightly faster due to the much more compact code. Also don't bother with the special case of "king capture" in the move generator. By checking each single move whether it captures the king (which is not the case for the majority of moves) you are in fact slower than simply assigning a sufficiently high score to moves capturing the king and letting your move ordering do the rest.

Regarding the use of bitboards, let's take a look at generating knight moves for instance (actually you do the same for kings at least). What you do is the following:
a) you get all possible knight move target squares for the given source square at once (good);
b) now you loop over 8 possible (knight) directions and extract all moves (target squares) for that direction from the previously obtained set of target squares (why do you do that?);
c) finally you loop over all moves in that direction (which must be either 0 or 1 moves since there are never two knight moves going into the same direction), for each one you look up the board to see whether there is an enemy piece present, and if so, you add a capture move to the move list.

This can be done faster. Ignore the intermediate step through the direction. Simply get a bitboard from your board representation that shows all squares occupied by an enemy piece, and do an AND with the set of target squares obtained in step a). The resulting bitboard shows all target squares where the given knight can capture an enemy piece. Then loop over these bits only to fill your move list.

Do the same for kings.

For pawns I see two possible improvements.
- First one: move the whole EP move generation to one central place. Don't test for each single pawn whether it can capture en passant, since there is at most one EP target square for the whole position and therefore there are at most two possible EP moves (if you have two pawns left and right to the enemy pawn that has just made a double step). EP is a very rare move and should be handled like that.
- Second one: generate *all* pawn captures at once using one shift operation, in two steps: one for all captures to the left side and one to the right. To get that done make sure your board representation is able to return a bitboard for all squares occupied by pawns of a given color (I assume you have that already). Figure out how to exclude pawns on a-file for captures to the left, and h-file for captures to the right. I am sure you'll find out the remaining stuff on your own. For this purpose you would also exclude all pawns from your "main loop" that loops over all pieces of the active color.

Sliding move generation is a separate issue. Here you currently do the same stuff with directions as described above, which is really slow. Probably you can get the biggest performance boost by improving this part of your move generator but here you would need a different technology. Most authors of a bitboard program use magic bitboards, rotated bitboards or something like that for an efficient implementation of sliding moves/sliding attacks generation. Maybe you have new ideas in that area. However, if you haven't then it is really hard to get that working without using anything that has been published by others. I suggest you take a look at the bitboards section in the chessprogramming wiki, if you haven't done so yet.

The key point is, e.g. for a given bishop you want to get all attacks to all directions "at once", i.e. with as few operations as possible, and this shall cover all attacks to empty squares as well as attacks (resp. defenses) to enemy or friendly pieces. For capture generation you can use this attack bitboard by simply AND-ing with the bitboard of all enemy pieces, as mentioned above for knights, and there you are. You save one inner loop compared to your current code, provided the code to obtain the attack bitboard is fast (for magic bitboards it is, definitely).

Similar things can be done for generating non-captures, of course, and for in-check detection and legality checking as well.
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: The wrong way

Post by Sven »

Sven Schüle wrote:Similar things can be done for generating non-captures, of course, and for in-check detection and legality checking as well.
And for static evaluation (e.g. mobility) ...

Two other minor issues in your move generator:

1) After doing as described above, you can also omit this piece of code:

Code: Select all

if ((Location.XRayMoves(PieceSort) & opponentPieces) != 0 )
since it will be fully redundant for pawns, knights, and kings, and it will also be part of the algorithm already for sliders, so that you would not save any time but would actually be slower with it.

2) Just as a cleanup: you have an unused "default" case in your code for a white king capturing black pieces.

And don't forget: making the move generator faster alone will not help a lot regarding playing strength, so don't expect too much. Reducing the overall effort spent in capture generation from 19% to, say, 9% would mean that the whole engine would now do the same as before in 90% instead of 100% of its time, so it would be a 10% => roughly 10 ELO points improvement. Applying the same kind of algorithmic improvements to your static evaluation function might give some more percents if you evaluate mobility and/or king safety (including attacks to the king). But major improvements of playing strength would certainly require other changes, e.g. in search, time control, static evaluation. So after doing these (obvious) speed improvements I would focus on improving the effective branching factor of your search. With an EBF of, say, 4 you need an overall NPS speedup factor of 16 (!!) to get two full plies deeper in the same time on average. But you would achieve the same with unchanged NPS by bringing down EBF from 4 to 3 for a depth increase from 8 to 10, for instance.
Henk
Posts: 7251
Joined: Mon May 27, 2013 10:31 am

Re: The wrong way

Post by Henk »

Yes that's why this thread is called "The wrong way". Improving speed of move generation is a lot of work but not giving much profit. Although the remaining non captures cost another 20%

By the way I have already changed the knight moves the way you described for that is the easy part.

Sliders will be difficult so I don't know.


Strange that (deep) futility pruning does not improve my engine.
I have no idea how to further improve LMR and null move pruning.
Last edited by Henk on Wed Dec 23, 2015 11:55 pm, edited 2 times in total.
S.Taylor
Posts: 8514
Joined: Thu Mar 09, 2006 3:25 am
Location: Jerusalem Israel

Re: The wrong way

Post by S.Taylor »

Henk wrote:This is Skippers code for collecting captures. I tried to make it faster. But did not help much.

Code: Select all

      public void QCollectMoves(List<MoveBase> moves)
        {
            var whitePieces = Board.WhitePieces;
            var blackPieces = Board.BlackPieces;
            ColorSign colorSign = (ColorSign)Board.CurPlayer;

            var curPieces = colorSign == ColorSign.White ? whitePieces : blackPieces;


            var opponentPieces = Board.Occupiers & ~curPieces;
            var pawns = Board.Pawns;

 
            IField Location = null;
            if (colorSign == ColorSign.White)
            {
                if ((curPieces & pawns & ChessBoard.SEVENTH_ROW_BITMAP) != 0)
                {
                    CPosition.fieldIterator.Reset(curPieces & pawns & ChessBoard.SEVENTH_ROW_BITMAP);
                    while ((Location = CPosition.fieldIterator.Next(ChessBoard)) != null)
                    {
                        Location.AddPromotionsWhitePawn(moves, this);
                    }
                }
           }
           else if ( (curPieces & pawns & ChessBoard.SECOND_ROW_BITMAP) != 0)
           {
                CPosition.fieldIterator.Reset(curPieces & pawns & ChessBoard.SECOND_ROW_BITMAP);
                while ((Location = CPosition.fieldIterator.Next(ChessBoard)) != null)
                {
                    Location.AddPromotionsBlackPawn(moves, this);
                }
            }

            BitBoardIndex bbIndex = BitBoardIndex.Instance;
            UInt64 bits = curPieces;
            while (bits != 0)
            {
                var bit = bits & (~bits + 1);
                bits &= bits - 1;
                Location = ChessBoard.GetField(bbIndex, bit);
                ChessPiece.Sort PieceSort = Location.Occupier.PieceSort;

                if ((Location.XRayMoves(PieceSort) & opponentPieces) != 0 )
                {


                    int dir;
                    switch (PieceSort)
                    {
                        case ChessPiece.Sort.whiteKing:
                            dir = 8;
                            DirectionMoves allMovesKing = ((Field) Location).dirMoves[(int)ChessPiece.Sort.whiteKing];
                            while (dir-- > 0)
                            {

                                var dirMoves2 = allMovesKing[dir];
                                int n = dirMoves2.Count;
                                for (int i = 0; i < n; i++)
                                {
                                    var move = dirMoves2[i];
                                    ChessPiece capture = move.End.Occupier;
                                    if (capture == null)
                                    {
                                    }
                                    else
                                    {
                                      
                                        if (capture.Colour == ColorSign.Black)
                                        {
                                            switch (capture.PieceKind)
                                            {
                                                case ChessPiece.Kind.King:
                                                    moves.Clear();
                                                    moves.Add(move);
                                                    return;
                                                case ChessPiece.Kind.Pawn:
                                                    move.Value = ChessPiece.PK_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Knight:
                                                    move.Value = ChessPiece.NK_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Bishop:
                                                    move.Value = ChessPiece.BK_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Rook:
                                                    move.Value = ChessPiece.RK_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Queen:
                                                    move.Value = ChessPiece.QK_MVVA_VALUE;
                                                    break;

                                                default:
                                                    move.Value = ChessPiece.MVVAPoints(capture.PieceKind) - ChessPiece.KING_VALUE;
                                                    moves.Add(move);
                                                    break;
                                            }
                                             moves.Add(move);
                                        }
                                        break;
                                    }
                                }
                            }
                            break;
                        case ChessPiece.Sort.blackKing:
                            dir = 8;
                            DirectionMoves allMovesKingB = ((Field)Location).dirMoves[(int)ChessPiece.Sort.blackKing];
                            while (dir-- > 0)
                            {

                                var dirMoves2 = allMovesKingB[dir];
                                int n = dirMoves2.Count;
                                for (int i = 0; i < n; i++)
                                {
                                    var move = dirMoves2[i];
                                    ChessPiece capture = move.End.Occupier;
                                    if (capture == null)
                                    {
                                    }
                                    else
                                    {
 
                                        if (capture.Colour == ColorSign.White)
                                        {
                                            switch (capture.PieceKind)
                                            {
                                                case ChessPiece.Kind.King:
                                                    moves.Clear();
                                                    moves.Add(move);
                                                    return;
                                                case ChessPiece.Kind.Pawn:
                                                    move.Value = ChessPiece.PK_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Knight:
                                                    move.Value = ChessPiece.NK_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Bishop:
                                                    move.Value = ChessPiece.BK_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Rook:
                                                    move.Value = ChessPiece.RK_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Queen:
                                                    move.Value = ChessPiece.QK_MVVA_VALUE;
                                                    break;

                                             }
                                            moves.Add(move);

                                        }
                                        break;
                                    }
                                }
                            }
                            break;
                        case ChessPiece.Sort.whiteKnight:
                            dir = 8;
                            DirectionMoves allMovesKnight = ((Field)Location).dirMoves[(int)ChessPiece.Sort.whiteKnight];
                            while (dir-- > 0)
                            {
                                var dirMoves2 = allMovesKnight[dir];
                                int n = dirMoves2.Count;
                                for (int i = 0; i < n; i++)
                                {
                                    var move = dirMoves2[i];
                                    ChessPiece capture = move.End.Occupier;
                                    if (capture == null)
                                    {
                                    }
                                    else
                                    {
                                        if (capture.Colour == ColorSign.Black)
                                        {
                                            switch (capture.PieceKind)
                                            {
                                                case ChessPiece.Kind.King:
                                                    moves.Clear();
                                                    moves.Add(move);
                                                    return;
                                                case ChessPiece.Kind.Pawn:
                                                    move.Value = ChessPiece.PN_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Knight:
                                                    move.Value = ChessPiece.NN_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Bishop:
                                                    move.Value = ChessPiece.BN_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Rook:
                                                    move.Value = ChessPiece.RN_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Queen:
                                                    move.Value = ChessPiece.QN_MVVA_VALUE;
                                                    break;

 
                                            }
                                            moves.Add(move);
                                        }
                                        break;
                                    }
                                }
                            }
                            break;
                        case ChessPiece.Sort.blackKnight:
                            dir = 8;
                            DirectionMoves allMovesKnightB = ((Field)Location).dirMoves[(int)ChessPiece.Sort.blackKnight];
                           while (dir-- > 0)
                            {
                                var dirMoves2 = allMovesKnightB[dir];
                                int n = dirMoves2.Count;
                                for (int i = 0; i < n; i++)
                                {
                                    var move = dirMoves2[i];
                                    ChessPiece capture = move.End.Occupier;
                                    if (capture == null)
                                    {
                                    }
                                    else
                                    {
                                        if (capture.Colour == ColorSign.White)
                                        {
                                            switch (capture.PieceKind)
                                            {
                                                case ChessPiece.Kind.King:
                                                    moves.Clear();
                                                    moves.Add(move);
                                                    return;
                                                case ChessPiece.Kind.Pawn:
                                                    move.Value = ChessPiece.PN_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Knight:
                                                    move.Value = ChessPiece.NN_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Bishop:
                                                    move.Value = ChessPiece.BN_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Rook:
                                                    move.Value = ChessPiece.RN_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Queen:
                                                    move.Value = ChessPiece.QN_MVVA_VALUE;
                                                    break;

  
                                            }
                                            moves.Add(move);
                                        }
                                        break;
                                    }
                                }
                            }
                            break;
                        case ChessPiece.Sort.whitePawn:
                            DirectionMoves allMoves = ((Field)Location).dirMoves[(int)ChessPiece.Sort.whitePawn];
                            int colNr = Location.ColNr;
                            if (colNr > ChessBoard.FIRSTCOLUMN)
                            {
                                MoveBase move = allMoves[(int)Pawn.Direction.LEFT][0];
                                Debug.Assert(!(move is EPMove));

                                ChessPiece capture = move.End.Occupier;
                                if (capture != null && capture.Colour == ColorSign.Black) // capture
                                {
                                    switch (capture.PieceKind)
                                    {
                                        case ChessPiece.Kind.King:
                                            moves.Clear();
                                            moves.Add(move);
                                            return;
                                        case ChessPiece.Kind.Pawn:
                                            move.Value = ChessPiece.PP_MVVA_VALUE;
                                            break;
                                        case ChessPiece.Kind.Knight:
                                            move.Value = ChessPiece.NP_MVVA_VALUE;
                                            break;
                                        case ChessPiece.Kind.Bishop:
                                            move.Value = ChessPiece.BP_MVVA_VALUE;
                                            break;

                                        case ChessPiece.Kind.Rook:
                                            move.Value = ChessPiece.RP_MVVA_VALUE;
                                            break;

                                        case ChessPiece.Kind.Queen:
                                            move.Value = ChessPiece.QP_MVVA_VALUE;
                                            break;

 
                                    }
                                    moves.Add(move);

                                }
                                var epMoves = allMoves[(int)Pawn.Direction.EP_LEFT];
                                if (epMoves.Count > 0)
                                {
                                    move = epMoves[0];
                                    if (move.CanMove(colorSign, this, Location))
                                    {
                                            Debug.Assert((move is EPMove));

                                            capture = (ChessPiece)move.GetCapture(this);
                                            switch (capture.PieceKind)
                                            {
                                                case ChessPiece.Kind.King:
                                                    moves.Clear();
                                                    moves.Add(move);
                                                    return;
                                                case ChessPiece.Kind.Pawn:
                                                    move.Value = ChessPiece.PP_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Knight:
                                                    move.Value = ChessPiece.NP_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Bishop:
                                                    move.Value = ChessPiece.BP_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Rook:
                                                    move.Value = ChessPiece.RP_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Queen:
                                                    move.Value = ChessPiece.QP_MVVA_VALUE;
                                                    break;


                                            }
                                            moves.Add(move);

                                     }
                                }
                            }
                            if (colNr < ChessBoard.LASTCOLUMN)
                            {
                                MoveBase move = allMoves[(int)Pawn.Direction.RIGHT][0];
                                Debug.Assert(!(move is EPMove));

                                ChessPiece capture = move.End.Occupier;
                                if (capture != null && capture.Colour == ColorSign.Black) // capture
                                {
                                     switch (capture.PieceKind)
                                    {
                                        case ChessPiece.Kind.King:
                                            moves.Clear();
                                            moves.Add(move);
                                            return;
                                        case ChessPiece.Kind.Pawn:
                                            move.Value = ChessPiece.PP_MVVA_VALUE;
                                            break;
                                        case ChessPiece.Kind.Knight:
                                            move.Value = ChessPiece.NP_MVVA_VALUE;
                                            break;
                                        case ChessPiece.Kind.Bishop:
                                            move.Value = ChessPiece.BP_MVVA_VALUE;
                                            break;

                                        case ChessPiece.Kind.Rook:
                                            move.Value = ChessPiece.RP_MVVA_VALUE;
                                            break;

                                        case ChessPiece.Kind.Queen:
                                            move.Value = ChessPiece.QP_MVVA_VALUE;
                                            break;


                                    }

                                    moves.Add(move);
                                }
                                var epMoves = allMoves[(int)Pawn.Direction.EP_RIGHT];
                                if (epMoves.Count > 0)
                                {
                                    move = epMoves[0];
                                    if (move.CanMove(colorSign, this, Location))
                                    {
                                        Debug.Assert((move is EPMove));

                                            capture = (ChessPiece)move.GetCapture(this);
                                            switch (capture.PieceKind)
                                            {
                                                case ChessPiece.Kind.King:
                                                    moves.Clear();
                                                    moves.Add(move);
                                                    return;
                                                case ChessPiece.Kind.Pawn:
                                                    move.Value = ChessPiece.PP_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Knight:
                                                    move.Value = ChessPiece.NP_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Bishop:
                                                    move.Value = ChessPiece.BP_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Rook:
                                                    move.Value = ChessPiece.RP_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Queen:
                                                    move.Value = ChessPiece.QP_MVVA_VALUE;
                                                    break;


                                            }
                                            moves.Add(move);

                                     }
                                }
                            }
                            break;

                        case ChessPiece.Sort.blackPawn:
                            DirectionMoves allMovesBP = ((Field)Location).dirMoves[(int)ChessPiece.Sort.blackPawn];
                            int colNrBP = Location.ColNr;
                            if (colNrBP > ChessBoard.FIRSTCOLUMN)
                            {
                                MoveBase move = allMovesBP[(int)Pawn.Direction.LEFT][0];
                                Debug.Assert(!(move is EPMove));

                                ChessPiece occupier = move.End.Occupier;
                                if (occupier != null && occupier.Colour == ColorSign.White) // capture
                                {
                                    IPiece capture = move.End.Occupier;
                                    switch (capture.PieceKind)
                                    {
                                        case ChessPiece.Kind.King:
                                            moves.Clear();
                                            moves.Add(move);
                                            return;
                                        case ChessPiece.Kind.Pawn:
                                            move.Value = ChessPiece.PP_MVVA_VALUE;
                                            break;
                                        case ChessPiece.Kind.Knight:
                                            move.Value = ChessPiece.NP_MVVA_VALUE;
                                            break;
                                        case ChessPiece.Kind.Bishop:
                                            move.Value = ChessPiece.BP_MVVA_VALUE;
                                            break;

                                        case ChessPiece.Kind.Rook:
                                            move.Value = ChessPiece.RP_MVVA_VALUE;
                                            break;

                                        case ChessPiece.Kind.Queen:
                                            move.Value = ChessPiece.QP_MVVA_VALUE;
                                            break;
  
                                    }
                                    moves.Add(move);

                                }
                              
                                var epMoves = allMovesBP[(int)Pawn.Direction.EP_LEFT];
                                if (epMoves.Count > 0)
                                {
                                    move = epMoves[0];

                                    if (move.CanMove(colorSign, this, Location))
                                    {
                                        Debug.Assert((move is EPMove));

                                            var capture = move.GetCapture(this);
                                            switch (capture.PieceKind)
                                            {
                                                case ChessPiece.Kind.King:
                                                    moves.Clear();
                                                    moves.Add(move);
                                                    return;
                                                case ChessPiece.Kind.Pawn:
                                                    move.Value = ChessPiece.PP_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Knight:
                                                    move.Value = ChessPiece.NP_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Bishop:
                                                    move.Value = ChessPiece.BP_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Rook:
                                                    move.Value = ChessPiece.RP_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Queen:
                                                    move.Value = ChessPiece.QP_MVVA_VALUE;
                                                    break;


                                            }
                                            moves.Add(move);
                                    }
                                }
                            }
                            if (colNrBP < ChessBoard.LASTCOLUMN)
                            {
                                MoveBase move = allMovesBP[(int)Pawn.Direction.RIGHT][0];
                                Debug.Assert(!(move is EPMove));

                                ChessPiece occupier = move.End.Occupier;
                                if (occupier != null && occupier.Colour == ColorSign.White) // capture
                                {
                                    IPiece capture = move.End.Occupier;
                                    switch (capture.PieceKind)
                                    {
                                        case ChessPiece.Kind.King:
                                            moves.Clear();
                                            moves.Add(move);
                                            return;
                                        case ChessPiece.Kind.Pawn:
                                            move.Value = ChessPiece.PP_MVVA_VALUE;
                                            break;
                                        case ChessPiece.Kind.Knight:
                                            move.Value = ChessPiece.NP_MVVA_VALUE;
                                            break;
                                        case ChessPiece.Kind.Bishop:
                                            move.Value = ChessPiece.BP_MVVA_VALUE;
                                            break;

                                        case ChessPiece.Kind.Rook:
                                            move.Value = ChessPiece.RP_MVVA_VALUE;
                                            break;

                                        case ChessPiece.Kind.Queen:
                                            move.Value = ChessPiece.QP_MVVA_VALUE;
                                            break;

                                    }

                                    moves.Add(move);
                                }
                                var epMoves = allMovesBP[(int)Pawn.Direction.EP_RIGHT];
                                if (epMoves.Count > 0)
                                {
                                    move = epMoves[0];
                                    if (move.CanMove(colorSign, this, Location))
                                    {
                                        Debug.Assert((move is EPMove));

                                           IPiece capture = move.GetCapture(this);
                                            switch (capture.PieceKind)
                                            {
                                                case ChessPiece.Kind.King:
                                                    moves.Clear();
                                                    moves.Add(move);
                                                    return;
                                                case ChessPiece.Kind.Pawn:
                                                    move.Value = ChessPiece.PP_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Knight:
                                                    move.Value = ChessPiece.NP_MVVA_VALUE;
                                                    break;
                                                case ChessPiece.Kind.Bishop:
                                                    move.Value = ChessPiece.BP_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Rook:
                                                    move.Value = ChessPiece.RP_MVVA_VALUE;
                                                    break;

                                                case ChessPiece.Kind.Queen:
                                                    move.Value = ChessPiece.QP_MVVA_VALUE;
                                                    break;


                                            }
                                            moves.Add(move);
                                   }
                                }
                            }
                            break;


                        case ChessPiece.Sort.whiteQueen:

                            dir = 8;
                            var xrayMoves = ((Field)Location).dirMoves[(int)ChessPiece.Sort.whiteQueen];
                            var dirMoves = Location.DirMoves(ChessPiece.Sort.whiteQueen);
                            while (dir-- > 0)
                            {

                                if ((dirMoves[dir] & blackPieces) == 0)
                                {
                                    // all moves in a direction don't hit any opponent pieces
                                    continue;
                                }

                                var xrayDirMoves = xrayMoves[dir];
                                int n = xrayDirMoves.Count;
                                for (int i = 0; i < n;)
                                {
                                    var move = xrayDirMoves[i++];
                                    // check each move, there can only be one capture in a direction
                                    ChessPiece capture = move.End.Occupier;

                                    if (capture == null)
                                    {
                                    }
                                    else if (capture.Colour == ColorSign.Black)
                                    {
                                         switch (capture.PieceKind)
                                        {
                                            case ChessPiece.Kind.King:
                                                moves.Clear();
                                                moves.Add(move);
                                                return;
                                            case ChessPiece.Kind.Pawn:
                                                move.Value = ChessPiece.PQ_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Knight:
                                                move.Value = ChessPiece.NQ_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Bishop:
                                                move.Value = ChessPiece.BQ_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Rook:
                                                move.Value = ChessPiece.RQ_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Queen:
                                                move.Value = ChessPiece.QQ_MVVA_VALUE;
                                                break;
 
                                        }
                                       
                                        moves.Add(move);

                                        break;
                                    }
                                    else break;
                                }
                            }
                            break;
                        case ChessPiece.Sort.blackQueen:

                            dir = 8;
                            var xrayMovesBQ = ((Field)Location).dirMoves[(int)ChessPiece.Sort.blackQueen];
                            var dirMovesBQ = Location.DirMoves(ChessPiece.Sort.blackQueen);
                            while (dir-- > 0)
                            {

                                if ((dirMovesBQ[dir] & whitePieces) == 0)
                                {
                                    // all moves in a direction don't hit any opponent pieces
                                    continue;
                                }

                                var xrayDirMoves = xrayMovesBQ[dir];
                                int n = xrayDirMoves.Count;
                                for (int i = 0; i < n;)
                                {
                                    var move = xrayDirMoves[i++];
                                    // check each move, there can only be one capture in a direction
                                    ChessPiece capture = move.End.Occupier;

                                    if (capture == null)
                                    {
                                    }
                                    else if (capture.Colour == ColorSign.White)
                                    {
                                        switch (capture.PieceKind)
                                        {
                                            case ChessPiece.Kind.King:
                                                moves.Clear();
                                                moves.Add(move);
                                                return;
                                            case ChessPiece.Kind.Pawn:
                                                move.Value = ChessPiece.PQ_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Knight:
                                                move.Value = ChessPiece.NQ_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Bishop:
                                                move.Value = ChessPiece.BQ_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Rook:
                                                move.Value = ChessPiece.RQ_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Queen:
                                                move.Value = ChessPiece.QQ_MVVA_VALUE;
                                                break;
 
                                        }
                                       
                                        moves.Add(move);

                                        break;
                                    }
                                    else break;
                                }
                            }
                            break;
                        case ChessPiece.Sort.whiteRook:
                      
                            dir = 4;
                            var xrayMovesR = ((Field)Location).dirMoves[(int)ChessPiece.Sort.whiteRook];
                            var dirMovesR = Location.DirMoves(ChessPiece.Sort.whiteRook);
                            while (dir-- > 0)
                            {

                                if ((dirMovesR[dir] & blackPieces) == 0)
                                {
                                    // all moves in a direction don't hit any opponent pieces
                                    continue;
                                }

                                var xrayDirMoves = xrayMovesR[dir];
                                int n = xrayDirMoves.Count;
                                for (int i = 0; i < n;)
                                {
                                    var move = xrayDirMoves[i++];
                                    // check each move, there can only be one capture in a direction
                                    ChessPiece capture = move.End.Occupier;
                                    if (capture == null)
                                    {
                                    }
                                    else if (capture.Colour == ColorSign.Black)
                                    {
                                        switch (capture.PieceKind)
                                        {
                                            case ChessPiece.Kind.King:
                                                moves.Clear();
                                                moves.Add(move);
                                                return;
                                            case ChessPiece.Kind.Pawn:
                                                move.Value = ChessPiece.PR_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Knight:
                                                move.Value = ChessPiece.NR_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Bishop:
                                                move.Value = ChessPiece.BR_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Rook:
                                                move.Value = ChessPiece.RR_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Queen:
                                                move.Value = ChessPiece.QR_MVVA_VALUE;
                                                break;
 
                                        }
                                      
                                        moves.Add(move);

                                        break;
                                    }
                                    else break;
                                }
                            }
                            break;
                        case ChessPiece.Sort.blackRook:
                            dir = 4;
                            var xrayMovesRB = ((Field)Location).dirMoves[(int)ChessPiece.Sort.blackRook];
                            var dirMovesRB = Location.DirMoves(ChessPiece.Sort.blackRook);
                            while (dir-- > 0)
                            {

                                if ((dirMovesRB[dir] & whitePieces) == 0)
                                {
                                    // all moves in a direction don't hit any opponent pieces
                                    continue;
                                }

                                var xrayDirMoves = xrayMovesRB[dir];
                                int n = xrayDirMoves.Count;
                                for (int i = 0; i < n;)
                                {
                                    var move = xrayDirMoves[i++];
                                    // check each move, there can only be one capture in a direction
                                    ChessPiece capture = move.End.Occupier;
                                    if (capture == null)
                                    {
                                    }
                                    else if (capture.Colour == ColorSign.White)
                                    {
                                        switch (capture.PieceKind)
                                        {
                                            case ChessPiece.Kind.King:
                                                moves.Clear();
                                                moves.Add(move);
                                                return;
                                            case ChessPiece.Kind.Pawn:
                                                move.Value = ChessPiece.PR_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Knight:
                                                move.Value = ChessPiece.NR_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Bishop:
                                                move.Value = ChessPiece.BR_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Rook:
                                                move.Value = ChessPiece.RR_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Queen:
                                                move.Value = ChessPiece.QR_MVVA_VALUE;
                                                break;
 
                                        }
                                        moves.Add(move);

                                        break;
                                    }
                                    else break;
                                }
                            }
                            break;
                        case ChessPiece.Sort.whiteBishop:
                            dir = 4;
                            var xrayMovesB = ((Field)Location).dirMoves[(int)ChessPiece.Sort.whiteBishop];
                            var dirMovesB = Location.DirMoves(ChessPiece.Sort.whiteBishop);
                            while (dir-- > 0)
                            {

                                if ((dirMovesB[dir] & blackPieces) == 0)
                                {
                                    // all moves in a direction don't hit any opponent pieces
                                    continue;
                                }

                                var xrayDirMoves = xrayMovesB[dir];
                                int n = xrayDirMoves.Count;
                                for (int i = 0; i < n;)
                                {
                                    var move = xrayDirMoves[i++];
                                    // check each move, there can only be one capture in a direction
                                    ChessPiece capture = move.End.Occupier;
                                    if (capture == null)
                                    {
                                    }
                                    else if (capture.Colour == ColorSign.Black)
                                    {
                                        switch (capture.PieceKind)
                                        {
                                            case ChessPiece.Kind.King:
                                                moves.Clear();
                                                moves.Add(move);
                                                return;
                                            case ChessPiece.Kind.Pawn:
                                                move.Value = ChessPiece.PB_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Knight:
                                                move.Value = ChessPiece.NB_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Bishop:
                                                move.Value = ChessPiece.BB_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Rook:
                                                move.Value = ChessPiece.RB_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Queen:
                                                move.Value = ChessPiece.QB_MVVA_VALUE;
                                                break;
                                       }

                                        moves.Add(move);
                                        break;
                                    }
                                    else break;
                                }
                            }
                            break;

                        case ChessPiece.Sort.blackBishop:

                            dir = 4;
                            var xrayMovesBB = ((Field)Location).dirMoves[(int)ChessPiece.Sort.blackBishop];
                            var dirMovesBB = Location.DirMoves(ChessPiece.Sort.blackBishop);
                            while (dir-- > 0)
                            {

                                if ((dirMovesBB[dir] & whitePieces) == 0)
                                {
                                    // all moves in a direction don't hit any opponent pieces
                                    continue;
                                }

                                var xrayDirMoves = xrayMovesBB[dir];
                                int n = xrayDirMoves.Count;
                                for (int i = 0; i < n;)
                                {
                                    var move = xrayDirMoves[i++];
                                    // check each move, there can only be one capture in a direction
                                    ChessPiece capture = move.End.Occupier;
                                    if (capture == null)
                                    {
                                    }
                                    else if (capture.Colour == ColorSign.White)
                                    {
                                        switch (capture.PieceKind)
                                        {
                                            case ChessPiece.Kind.King:
                                                moves.Clear();
                                                moves.Add(move);
                                                return;
                                            case ChessPiece.Kind.Pawn:
                                                move.Value = ChessPiece.PB_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Knight:
                                                move.Value = ChessPiece.NB_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Bishop:
                                                move.Value = ChessPiece.BB_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Rook:
                                                move.Value = ChessPiece.RB_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Queen:
                                                move.Value = ChessPiece.QB_MVVA_VALUE;
                                                break;
                                            default:
                                                move.Value = ChessPiece.MVVAPoints(capture.PieceKind);
                                                break;
                                        }

                                        moves.Add(move);
                                        break;
                                    }
                                    else break;
                                }
                            }
                            break;
                    }

                }

            }


        }
What?
Henk
Posts: 7251
Joined: Mon May 27, 2013 10:31 am

Re: The wrong way

Post by Henk »

Devil made me do it. Had no better ideas and I was bored.
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: The wrong way

Post by Sven »

Henk wrote:Yes that's why this thread is called "The wrong way". Improving speed of move generation is a lot of work but not giving much profit. Although the remaining non captures cost another 20%

By the way I have already changed the knight moves the way you described for that is the easy part.

Sliders will be difficult so I don't know.


Strange that (deep) futility pruning does not improve my engine.
I have no idea how to further improve LMR and null move pruning.
As I tried to explain, substantial speed improvements are more effective with a lower branching factor.

So what is your current effective branching factor, roughly?
(Take an "average middlegame position", let your engine search it to a fixed depth N, then calculate the N-th root of the total node count; the result might be roughly the same as "nodes_for_iteration(N) / nodes_for_iteration(N-1)".)

Roughly how many percent of all nodes are QS nodes, including leaves of full-width search?

Are you sure that your quiescence search is correct?
Henk
Posts: 7251
Joined: Mon May 27, 2013 10:31 am

Re: The wrong way

Post by Henk »

Changed pawns and king captures too. Only 1-3 percent faster. Hardly measurable. Pawns spoiled the party. Also because I had to make an exception for En Passant moves. Might be that code for handling pawn captures is slower now.

Code: Select all

            ....
            CPosition.fieldIterator.Reset(curPieces & pawns & (colorSign == ColorSign.White ? ChessBoard.FIFTH_ROW_BITMAP : ChessBoard.FOURTH_ROW_BITMAP) );
            while ((Location = CPosition.fieldIterator.Next(ChessBoard)) != null)
            {
                DirectionMoves allMoves = ((Field)Location).dirMoves[(int)Location.Occupier.PieceSort];
                var epMoves = allMoves[(int)Pawn.Direction.EP_RIGHT];
                if (epMoves.Count > 0)
                {
                    var move = epMoves[0];
                    if (move.CanMove(colorSign, this, Location))
                    {
                        Debug.Assert((move is EPMove));
                        move.Value = ChessPiece.PP_MVVA_VALUE;
                        moves.Add(move);

                    }
                }

                epMoves = allMoves[(int)Pawn.Direction.EP_LEFT];
                if (epMoves.Count > 0)
                {
                    var move = epMoves[0];
                    if (move.CanMove(colorSign, this, Location))
                    {
                        Debug.Assert((move is EPMove));
                        move.Value = ChessPiece.PP_MVVA_VALUE;
                        moves.Add(move);

                    }
                }
            }

            BitBoardIndex bbIndex = BitBoardIndex.Instance;
            UInt64 bits = curPieces;
            while (bits != 0)
            {
                var bit = bits & (~bits + 1);
                bits &= bits - 1;
                Location = ChessBoard.GetField(bbIndex, bit);
                ChessPiece.Sort PieceSort = Location.Occupier.PieceSort;
                var xrayCaptures = Location.XRayMoves(PieceSort) & opponentPieces;

                if (xrayCaptures != 0 )
                {
                    int dir;
                    switch (PieceSort)
                    {
                         case ChessPiece.Sort.whiteKing:
                         case ChessPiece.Sort.blackKing:
                         case ChessPiece.Sort.whiteKnight:
                         case ChessPiece.Sort.blackKnight:                    
                         case ChessPiece.Sort.whitePawn:
                         case ChessPiece.Sort.blackPawn:

                            do
                            {
                                var Move = xrayCaptures & (~xrayCaptures + 1);
                                xrayCaptures &= xrayCaptures - 1;
                                var move = ((Field)Location).AllMovesDict(PieceSort).Get(Move);
                                AddMove(moves, move, Location.Occupier.PieceKind);
                            }
                            while (xrayCaptures != 0);
                            break;

                            ....
Henk
Posts: 7251
Joined: Mon May 27, 2013 10:31 am

Re: The wrong way

Post by Henk »

By the way substituting AddMove code back makes it one percent faster

Code: Select all

 
// AddMove(moves, move,  Location.Occupier.PieceKind);

                                ChessPiece.Kind capture = move.End.Occupier.PieceKind;
                                switch (Location.Occupier.PieceKind)
                                {
                                    case ChessPiece.Kind.King:
                                        switch (capture)
                                        {
                                            case ChessPiece.Kind.King:
                                                moves.Clear();
                                                moves.Add(move);
                                                return;
                                            case ChessPiece.Kind.Pawn:
                                                move.Value = ChessPiece.PK_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Knight:
                                                move.Value = ChessPiece.NK_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Bishop:
                                                move.Value = ChessPiece.BK_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Rook:
                                                move.Value = ChessPiece.RK_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Queen:
                                                move.Value = ChessPiece.QK_MVVA_VALUE;
                                                break;
                                        }
                                        break;
                                    case ChessPiece.Kind.Knight:
                                        switch (capture)
                                        {
                                            case ChessPiece.Kind.King:
                                                moves.Clear();
                                                moves.Add(move);
                                                return;
                                            case ChessPiece.Kind.Pawn:
                                                move.Value = ChessPiece.PN_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Knight:
                                                move.Value = ChessPiece.NN_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Bishop:
                                                move.Value = ChessPiece.BN_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Rook:
                                                move.Value = ChessPiece.RN_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Queen:
                                                move.Value = ChessPiece.QN_MVVA_VALUE;
                                                break;
                                        }
                                        break;

                                    case ChessPiece.Kind.Pawn:
                                        switch (capture)
                                        {
                                            case ChessPiece.Kind.King:
                                                moves.Clear();
                                                moves.Add(move);
                                                return;
                                            case ChessPiece.Kind.Pawn:
                                                move.Value = ChessPiece.PP_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Knight:
                                                move.Value = ChessPiece.NP_MVVA_VALUE;
                                                break;
                                            case ChessPiece.Kind.Bishop:
                                                move.Value = ChessPiece.BP_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Rook:
                                                move.Value = ChessPiece.RP_MVVA_VALUE;
                                                break;

                                            case ChessPiece.Kind.Queen:
                                                move.Value = ChessPiece.QP_MVVA_VALUE;
                                                break;
                                        }
                                        break;
                                }
                                moves.Add(move);
Dann Corbit
Posts: 12808
Joined: Wed Mar 08, 2006 8:57 pm
Location: Redmond, WA USA

Re: The wrong way

Post by Dann Corbit »

You seem to have a lot of correctness issues.

You are doing the wrong thing, trying to make it faster.
First make it correct, fully correct.
Then make it faster.

The best way to make it faster is not these little tweaky things suggested in the thread which are for the most part a complete waste of time.

The best way to make it faster is to use a better algorithm or improve the algorithm yourself.

Simplify your code. Have you noticed how redundant your code blocks are?
Maybe you are trying to inline. It is too early to do that.

The greatest lesson of Fabian is neither the search nor the eval, but the determined goal of correctness.

Making a wrong program faster just gets you the wrong answer twice as fast. And as you add these tweaky little dumb tricks the code becomes less readable.

AFTER the code is fully correct, then perform a profile. Making the code 50% faster that is taking 1% of the time will not even be noticed as far as Elo goes.

So the title is right. You are doing things the wrong way.
Taking ideas is not a vice, it is a virtue. We have another word for this. It is called learning.
But sharing ideas is an even greater virtue. We have another word for this. It is called teaching.
Henk
Posts: 7251
Joined: Mon May 27, 2013 10:31 am

Re: The wrong way

Post by Henk »

Dann Corbit wrote:You seem to have a lot of correctness issues.
Have not seen proof for that. I use these perft tests. If they all pass I assume no errors in move generation.