Complicating code in C#

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

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

Re: Complicating code in C#

Post by Henk »

RoadWarrior wrote: Thu Apr 22, 2021 7:32 pm
Henk wrote: Mon Apr 12, 2021 11:01 pm I think I fixed the bug in movecount.
Now it is back to normal. That is less than 80kn/sec.
Perft in my C# chess engine runs at about 80M nodes per second on a single core of a mid-range Intel i7-7700 CPU running at 3.6 GHz. So shurely shome mishtake?
Test which computes these four perfts take about 8 seconds. So maybe I get hardly 1M nodes per second.
But for each perft it first builds the hashtables per square needed for magic bitboard movegeneration. While they should be only computed once.

Code: Select all

            TestPerft(6, "8/5bk1/8/2Pp4/8/1K6/8/8 w - d6 0 1", 824064);
            TestPerft(6, "8/5k2/8/2Pp4/2B5/1K6/8/8 w - d6 0 1 ", 1440467);   
            TestPerft(3, "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/2KR3R b kq - 1 1", 79803);
            TestPerft(5, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 4865609);
Also when making a move it makes a copy of the array of bitboards.

Code: Select all

   private ulong[] MoveAux(IMoveBase move)
        {
 
            var pieceSort = move.PieceSort;  // pieceType
            var kind = GetKind(pieceSort);

            var endBitCoord = move.End.BitBoard;
            var moveBB = move.BitBoardValue;

            ulong[] copyPieces = new ulong[NBITBOARDS];
            if (Occupied(endBitCoord))
            {
             
                var endKind = PieceKind(endBitCoord);
                var color =   OccupierColor(endBitCoord);

                Pieces.CopyTo(copyPieces, 0);

                if (endKind == kind)
                {
                    copyPieces[(int)kind] ^= move.Start.BitBoard;
                }
                else
                {
                    copyPieces[(int)endKind] ^= endBitCoord;
                    copyPieces[(int)kind] ^= moveBB;
                }



                if (color == White)
                {
                    copyPieces[WHITE_PIECES] ^= endBitCoord;
                    copyPieces[BLACK_PIECES] ^= moveBB;
                }
                else
                {
                    copyPieces[BLACK_PIECES] ^= endBitCoord;
                    copyPieces[WHITE_PIECES] ^= moveBB;
                }

              
            }
            else
            {
                Pieces.CopyTo(copyPieces, 0);
                copyPieces[(int)kind] ^= moveBB;
                if (GetColSign(pieceSort) == White)
                {
                    copyPieces[WHITE_PIECES] ^= moveBB;
                }
                else
                {
                    copyPieces[BLACK_PIECES] ^= moveBB;

                }
            }         
            return copyPieces;

        }
  
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

Sven wrote: Thu Apr 22, 2021 9:39 pm Is that the number of nodes actually visited, or the number of leaves determined via bulk-counting?
O wait that was a question for Roadwarrior.
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

Without TTable, With Null move pruning:

Code: Select all

  10      12         1183        3119486    e2e4  e7e5  g1f3  g8f6  f1d3  b8c6  e1g1  d7d5  b1c3  c8e6
  10      20         1726        4698849    d2d4  d7d5  g1f3  b8c6  b1c3  c8f5  e2e3  g8f6  f1d3  e7e6
  11      17         3333        9167261    d2d4  d7d5  g1f3  b8c6  b1c3  c8f5  e2e3  c6b4  f1b5  c7c6  b5a4
With TTable, Without Null move pruning

Code: Select all

  10       2         1063        2420012    e2e4  e7e5  b1c3  b8c6  g1f3  g8f6  d2d4  e5d4  f3d4  f8d6
  10      17         1401        3158225    d2d4  d7d5  b1c3  g8f6  g1f3  c8e6  e2e3  b8d7  f1d3  c7c5
  11      18         2183        4961996    d2d4  d7d5  b1c3  g8f6  e2e3  c8e6  g1f3  b8c6
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

With TTable + Null move pruning

Code: Select all

  10      12          668        1436502    e2e4  e7e5  g1f3  g8f6  f1d3  b8c6  e1g1  d7d5  b1c3  c8e6
  10      17          829        1798089    d2d4  d7d5  b1c3  g8f6  g1f3  c8e6  c1f4  b8d7  e2e3  c7c5
  11      18         1888        4046940    d2d4  d7d5  g1f3  b8c6  e2e3  g8f6  f1d3  c6b4  e1g1  b4d3  c2d3
  11      20         2233        4837747    e2e4  e7e5  g1f3  b8c6  f1d3  g8f6  e1g1  f8d6  c2c4  e8g8  b1c3
Looks like something wrong with null move pruning
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

Already found it null move reduction = Max(2, depth/3) but should be at least 3 to have an effect.

With Max(3, depth/3)

Code: Select all

  10      12          544        1121343    e2e4  e7e5  g1f3  g8f6  f1d3  b8c6  e1g1  d7d5  b1c3  c8e6
  10      17          674        1398662    d2d4  d7d5  b1c3  g8f6  g1f3  c8e6  c1f4  b8d7  e2e3  c7c5
  11      18         1399        2796590    d2d4  d7d5  b1c3  g8f6  e2e3  c8e6  g1f3  b8c6  f1d3  c6b4  e1g1
  11      20         1688        3416920    e2e4  e7e5  g1f3  b8c6  f1d3  g8f6  e1g1  f8d6  c2c4  e8g8  b1c3
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

This one taking 2.3 miniutes to compute on my computer (i5). That is 156 seconds. So that would be about 1.24 MN/s

Code: Select all

          TestPerft(5, "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -  0 1", 193690690);
But leave nodes are not processed.

Code: Select all

  if (depth == 1)
  {
         res= (ulong)legalMoves.Count;
  }
By the way I have not set processor to maximum to prevent overheating.
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

Pfft. Stockfish computes perft KiwiPete 5 within 10 seconds while Skipper needs 2 minutes for that.

Do they use special tricks to get that fast.
User avatar
lithander
Posts: 880
Joined: Sun Dec 27, 2020 2:40 am
Location: Bremen, Germany
Full name: Thomas Jahn

Re: Complicating code in C#

Post by lithander »

Code: Select all

  
White >> r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -  0 1
White >> perft 5

Moves:    193,690,690
Seconds:  19.2562
Moves/s:  10,058,640
Operation took 19.2617s
My engine is also written in C# but I don't use bitboards but an array of pieces.

Code: Select all

private Piece[] _state = new Piece[64];
Perft is implemented as a recursive function:

Code: Select all

        private static long Perft(Board board, int depth)
        {
            var moves = new LegalMoves(board);
            if (depth == 1) //no need to apply the moves before counting them
                return moves.Count;

            long sum = 0;
            Board next = new Board(board);
            foreach (var move in moves)
            {
                next.Copy(board);
                next.Play(move);
                sum += Perft(next, depth - 1);
            }
            return sum;
        }
LegalMoves is derived from an actual List<Piece> with a custom constructor that takes in a board and collects all the moves like this:

Code: Select all

        
        public LegalMoves(Board reference) : base(40)
        {
            _reference = reference;
            _reference.CollectMoves(Consider);
            _reference = null;
        }

        public void Consider(Move move)
        {
            //only add if the move doesn't result in a check for active color
            _tempBoard.Copy(_reference);
            _tempBoard.Play(move);
            if (_tempBoard.IsChecked(_reference.ActiveColor))
                return;

            Add(move);
        }
        
        public void CollectMoves(Action<Move> moveHandler)
        {
            for (int square = 0; square < 64; square++)
                CollectMoves(moveHandler, square);
        }
As you can see that's a very straight forward (wasteful) implementation and not optimized for speed and so I would expect that any bitboard based engine should be much faster than the 20s it took me to compute the perft on my machine. (Ryzen 3600)
Minimal Chess (simple, open source, C#) - Youtube & Github
Leorik (competitive, in active development, C#) - Github & Lichess
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

Or maybe Ryzen 3600 is five times faster than i5 processor on my notebook.

I am using similar perft implementation.
So only difference would be implemtation of collecting legal moves or PlayMove.

Don't understand I already spent much time on optimization.
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Complicating code in C#

Post by Sven »

Could it be the way how you deal with objects in general? E.g. passing them by value, making copies, temporary instances, maintaining many small instances, ...
Sven Schüle (engine author: Jumbo, KnockOut, Surprise)