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 »

Henk wrote: Tue Apr 13, 2021 8:22 pm
Sven wrote: Tue Apr 13, 2021 7:05 pm What is the meaning of endBitCoord and moveBB?

Code: Select all

  var endBitCoord = UBit(move.End.Index); // = (End).ToBitBoardValue();
  var moveBB = move.BitBoardValue;          // = (Start.ToBitBoardValue() | (End).ToBitBoardValue();  
That would mean: endBitCoord ^ moveBB == ((Start.ToBitBoardValue() | (End).ToBitBoardValue()) ^ (End).ToBitBoardValue();


(A | B) ^ B <=> A

So I better write

Code: Select all

copyPieces[(int)kind] ^= move.Start.Index.ToBitBoardValue();
O wait maybe better assign move.StartBB to each precomputed move just like I did with move.BitBoardValue.

Code: Select all

 
       private ulong[] MoveAux(IMoveBase move)
        {
            var pieceSort = move.PieceSort;
            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 =   OccupierColSign(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 == ColorSign.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 (GetColSign2(pieceSort) == ColorSign.White)
                {
                    copyPieces[WHITE_PIECES] ^= moveBB;
                }
                else
                {
                    copyPieces[BLACK_PIECES] ^= moveBB;
                }
            }         
            return copyPieces;
        }
 
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 »

Henk wrote: Tue Apr 13, 2021 8:22 pm
Sven wrote: Tue Apr 13, 2021 7:05 pm What is the meaning of endBitCoord and moveBB?

Code: Select all

  var endBitCoord = UBit(move.End.Index); // = (End).ToBitBoardValue();
  var moveBB = move.BitBoardValue;          // = (Start.ToBitBoardValue() | (End).ToBitBoardValue();  
That would mean: endBitCoord ^ moveBB == ((Start.ToBitBoardValue() | (End).ToBitBoardValue()) ^ (End).ToBitBoardValue();


(A | B) ^ B <=> A

So I better write

Code: Select all

copyPieces[(int)kind] ^= move.Start.Index.ToBitBoardValue();
O wait maybe better assign move.StartBB to each precomputed move just like I did with move.BitBoardValue.
Can you explain it without code? 🙃 Just what the two variables are meant for, what they represent?

Btw (A | B) ^ B <=> A is wrong if A and B can overlap ... In that case XOR B clears bits that are also part of A.
Sven Schüle (engine author: Jumbo, KnockOut, Surprise)
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

Sven wrote: Wed Apr 14, 2021 12:03 am
Henk wrote: Tue Apr 13, 2021 8:22 pm
Sven wrote: Tue Apr 13, 2021 7:05 pm What is the meaning of endBitCoord and moveBB?

Code: Select all

  var endBitCoord = UBit(move.End.Index); // = (End).ToBitBoardValue();
  var moveBB = move.BitBoardValue;          // = (Start.ToBitBoardValue() | (End).ToBitBoardValue();  
That would mean: endBitCoord ^ moveBB == ((Start.ToBitBoardValue() | (End).ToBitBoardValue()) ^ (End).ToBitBoardValue();


(A | B) ^ B <=> A

So I better write

Code: Select all

copyPieces[(int)kind] ^= move.Start.Index.ToBitBoardValue();
O wait maybe better assign move.StartBB to each precomputed move just like I did with move.BitBoardValue.
Can you explain it without code? 🙃 Just what the two variables are meant for, what they represent?

Btw (A | B) ^ B <=> A is wrong if A and B can overlap ... In that case XOR B clears bits that are also part of A.
(A | B) , B did overlap but A,B don't so (A | B) ^ B <=> A
Because A = Start field of a move, B = End field of a move.

So for example in case of a pawn x pawn capture you only need to clear start field of the move in Pieces[pawn] bit board.

Code: Select all

copyPieces[(int)kind] ^= move.Start.BitBoard;
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

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.

Code: Select all

   1      48            8              4    d2d4
   2       5           11            129    d2d4  g8f6
   3      48           13            616    d2d4  g8f6  g1f3
   4       7           15           1353    d2d4  b8c6  g1f3  g8f6
   5      50           37          11813    d2d4  b8c6  g1f3  g8f6  b1c3
   6      11           60          24771    d2d4  g8f6  b1c3  b8c6  g1f3  d7d5
   7      51          101          58692    d2d4  g8f6  b1c3  b8c6  g1f3  d7d5  c1e3
   8      15          140          98123    d2d4  g8f6  g1f3  d7d5  b1c3  c8e6  c1e3  b8c6
   9      30          293         216667    d2d4  g8f6  b1c3  d7d5  g1f3  b8c6  c1f4
  10      22          911         709620    d2d4  g8f6  b1c3  d7d5  g1f3  b8c6  e2e3  a8b8  f1d3  c8e6
  11      30         2653        2152557    d2d4  g8f6  b1c3  d7d5  g1f3  b7b6  c1e3  b8c6  f3e5  c8b7  f2f4
  12      21         4150        3608862    d2d4  g8f6  b1c3  d7d5  c1f4  c8e6  g1f3  b8c6  g2g3
  13      28        11522        8016578    d2d4  d7d5  b1c3  b8c6  f2f3  f7f5  c1f4  a7a6  e2e3  g8f6  a1c1  c8e6  f1d3
  14      25        57321       37683014    d2d4  g8f6  c2c4  c7c6  g1f3  d7d5  e2e3  c8f5  b1c3  e7e6  c4d5  e6d5  f1d3  f5d3
 
Update. But depth means nothing. Being only an upperbound. Also speed means nothing if it skips important moves.

Code: Select all

   1      50            8              4    d2d4
   2      12           10            129    d2d4  b8c6
   3      54           12            505    d2d4  b8c6  b1c3
   4      14           14           1684    d2d4  d7d5  b1c3  c8f5
   4      16           15           2158    b1c3  e7e5  d2d4  f8d6
   5      50           28          11596    b1c3  d7d5  d2d4  c8f5  c1e3
   6      18           39          19839    b1c3  b8c6  g1f3  g8f6  d2d4  d7d5
   7      47           79          58696    b1c3  e7e5  g1f3  b8c6  e2e4  f8d6  f1c4
   7      48           85          64243    d2d4  d7d5  b1c3  b8c6  c1e3
   8      14          137         129420    d2d4  d7d5  b1c3  b8c6  g1f3  c8f5  c1e3  g8f6
   8      20          195         192722    e2e4  e7e5  b1c3  b8c6  g1f3  f8c5  f1c4  g8f6
   9      35          635         631738    e2e4  f7f5  e4f5  d7d5  f1d3  g8h6  b1c3  c8f5  g1f3
   9      36          679         676696    d2d4  d7d5  b1c3  b8c6  g1f3  c8f5  c1f4  g8f6  e2e3
  10      33         1131        1109218    d2d4  d7d5  g1f3  b8c6  b1c3  c8f5  e2e3  g8f6  f1d3  f6e4
  11      47         2532        2678755    d2d4  d7d5  b1c3  b8c6  e2e3  g8f6  g1f3  c8f5  f1d3  f6e4  c1d2
  12      38         4308        4527612    d2d4  d7d5  b1c3  b8c6  c1f4  c8f5  e2e3
  13      39        12434       11869842    d2d4  c7c5  d4c5  e7e5  g1f3  b8c6  c1e3  g8f6  b1c3  f6g4  c3e4  g4e3  f2e3
  14      40        40173       32656035    d2d4  c7c5  d4c5  g8f6  b1c3  e7e5
  
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

Current interface of chessboard. Previous generic one was an efficiency disaster.
Bytes() not necessary if you use zobrist key.

Code: Select all

 
 public interface IChessBoard
    {
        IChessBoard Move(IMoveBase move);
        IChessBoard PutOnBoard(PieceTypeEnum PieceSort, ulong coord);
        ColorSign OccupierColSign(ulong bit);
        Kind PieceKind(ulong bit);
        bool Occupied(ulong bit);
        PieceTypeEnum PieceType(ulong bit);
        UInt64 WhitePieces { get; }
        UInt64 BlackPieces { get;}
        UInt64 Kings { get; }
        UInt64 Queens { get; }
        UInt64 Rooks { get; }
        UInt64 Bishops { get; }
        UInt64 Knights { get; }
        UInt64 Pawns { get; }
        bool    BareKing(ColorSign colorSign);
        UInt64 Bits(Kind kind, ColorSign colorSign);
        UInt64 Bits(ColorSign colorSign);
        UInt64 Bits(PieceTypeEnum pieceType);
        UInt64 Bits(Kind kind);
        UInt64 Occupiers { get; }
        UInt64 Sliders(ColorSign colSign);
        byte[] Bytes();
    }
}
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

hi, hi, hi.

Code: Select all

 
        public double ComputeFeatureValue(IChessPosition pos, ColorSign side, Func<ISquares<ISquareEval>, IChessPosition, PieceTypeEnum, double> func)
        {
            double result = 0;
            for (var kind = Pawn_Kind; kind <= King_Kind; kind++)
            {
                var type = ConvertToPieceType(kind, side);
                result += func(SquaresEval, pos, type);
            }
            return result;
        }
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

Looks like Skipper still unable to win from Stockfish.

[pgn]
[Event "Computer Chess Game"]
[Site "LAPTOP-1FK7MTIP"]
[Date "2021.04.21"]
[Round "-"]
[White "Skipper_8_5"]
[Black "Stockfish 13"]
[Result "0-1"]
[TimeControl "300"]
[Annotator "1. +0.10 1... -0.27"]

1. e4 {+0.10/10} e5 {-0.27/29 20} 2. d4 {+0.10/9 6} exd4 {+0.00/24 2.6} 3.
Qxd4 {+0.00/9 6} Nc6 {+0.40/25 6} 4. Qd1 {+0.20/8 6} Nf6 {+0.85/23 3} 5.
Nd2 {+0.03/8 6} Qe7 {+1.30/28 23} 6. f3 {-0.17/9 5} d5 {+1.86/22 4} 7. Bd3
{-0.15/8 5} b6 {+2.14/27 21} 8. Nh3 {+0.26/9 5} Bxh3 {+2.35/24 3} 9. gxh3
{+0.22/9 5} O-O-O {+2.08/28 11} 10. a3 {+0.16/8 5} Nxe4 {+4.23/26 17} 11.
fxe4 {+1.42/9 5} Qh4+ {+5.81/23 3} 12. Ke2 {+1.24/9 5} Nd4+ {+8.21/25 0.1}
13. Kf1 {+2.18/8 5} Qxh3+ {+8.75/25 0.1} 14. Kf2 {+0.88/9 5} Bc5
{+8.82/27 1.5} 15. Ba6+ {-0.20/9 5} Kb8 {+10.28/25 3} 16. b4 {-0.48/9 4}
Rd6 {+10.60/24 0.1} 17. bxc5 {+0.23/8 4} Rf6+ {+16.58/25 2.9} 18. Nf3
{-3.34/10 4} Rxf3+ {+99.75/37 0.2} 19. Ke1 {-3.35/9 4} Qh4+ {+99.77/44 0.1}
20. Kd2 {-4.15/9 4} Qf4+ {+99.79/47 0.1} 21. Ke1 {-5.76/10 4} Qxe4+
{+99.81/51 0.1} 22. Be2 {-7.07/10 4} Re8 {+99.83/55 0.2} 23. cxb6
{-7.52/8 4} Qh4+ {+99.85/55 0.1} 24. Kd2 {-4.98/9 4} Qf2 {+99.87/56 0.1}
25. bxc7+ {-327.54/10 4} Kxc7 {+99.89/67 0.1} 26. Bb2 {-327.56/9 4} Rxe2+
{+99.91/78 0.1} 27. Kc1 {-327.58/9 4} Rxc2+ {+99.93/176 0.1} 28. Kb1
{-327.60/10 3} Rxb2+ {+99.95/245 0.1} 29. Kc1 {-327.62/10 3} Nb3+
{+99.97/245 0.1} 30. Qxb3 {-327.64/11 3} Qd2# {+99.99/245 0.2}
{Xboard adjudication: Checkmate} 0-1
[/pgn]
RoadWarrior
Posts: 73
Joined: Fri Jan 13, 2012 12:39 am
Location: London, England

Re: Complicating code in C#

Post by RoadWarrior »

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?
There are two types of people in the world: Avoid them both.
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 »

Is that the number of nodes actually visited, or the number of leaves determined via bulk-counting?
Sven Schüle (engine author: Jumbo, KnockOut, Surprise)
Henk
Posts: 7216
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

The number of nodes visited while counting null move and standingpats as 1.

By the way I get 150km/sec if I do nothing in evaluation except returning sign * psqValue.
If I try to evaluate pawnshields, isolated pawns etc then speed drops below 90kN/sec.

I also think my immutable transposition table slows down performance. But I will probably need it when I make my engine multi-threaded.

Code: Select all

public class TranspositionTable<IKey, IValue> 
{
  ..
  readonly ImmutableDictionary<IKey, IValue> rep;
 
Maybe if I use zobrist key I can gain some extra speed. I now use a .net hashalhorithm. But to use it I have to convert position into bytes

Code: Select all

 public class HashKeyFactory: IKeyFactory
    {
        readonly HashAlgorithm hashAlgorithm;

        public HashKeyFactory()
        {
            hashAlgorithm = MD5.Create();
        }
   }
  
 
Trying to improve speed by replacing prioq.Sort with a bubble up (bubblesort) for first 3 or 4 moves did not help much.