Complicating code in C#

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

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

Re: Complicating code in C#

Post by Henk »

Another how can you lose example.

O wait Fairy max searching much deeper. If depth means anything.
[pgn]
[Event "Computer Chess Game"]
[Site "LAPTOP-1FK7MTIP"]
[Date "2021.05.03"]
[Round "-"]
[White "Skipper_8_10"]
[Black "Fairy-Max 4.8V"]
[Result "0-1"]
[TimeControl "180"]
[Annotator "1. +0.45 3... +0.25"]

1. e4 {+0.45/7} e5 2. Nc3 {+0.04/6 4} Nf6 3. f4 {+0.52/7 3} exf4
{+0.25/9 2.9} 4. e5 {+0.85/7 3} Ng8 {+0.59/9 10} 5. Nf3 {+0.51/6 3} g5
{+0.75/8 7} 6. d4 {+0.42/6 3} g4 {+1.14/8 3} 7. Ng1 {+0.45/7 3} Qh4+
{+0.42/8 2.3} 8. Ke2 {+0.25/7 3} g3 {+0.25/8 2.1} 9. Nf3 {+0.92/7 3} Qh6
{+0.09/9 2.2} 10. Nd5 {+0.73/5 3} Qa6+ {+0.08/8 4} 11. Qd3 {+0.95/6 3} Qc6
{+0.07/9 5} 12. Nxf4 {+0.67/6 2.9} b6 {-0.08/8 4} 13. d5 {+1.43/6 2.9} Qa4
{-0.64/9 5} 14. b3 {+1.48/5 2.8} Qa6 {-0.76/9 2.3} 15. hxg3 {+1.33/7 2.8}
Qxd3+ {-0.72/8 6} 16. cxd3 {+1.37/7 2.7} Ne7 {-0.62/8 4} 17. Bb2
{+1.89/6 2.6} Rg8 {-0.84/8 2.9} 18. Rxh7 {+2.31/5 2.6} Nf5 {-0.70/10 4} 19.
Nh5 {+2.42/5 2.6} Nxg3+ {-0.82/10 2.7} 20. Nxg3 {+2.27/7 2.5} Rxg3
{-1.01/10 2.2} 21. Kf2 {+2.42/6 2.5} Rg8 {-0.74/9 4} 22. Nd2 {+1.93/5 2.4}
Bc5+ {-0.79/8 2.4} 23. d4 {+2.39/7 2.4} Be7 {-0.98/10 2.6} 24. Bd3
{+2.34/5 2.3} d6 {-0.76/7 1.6} 25. Nc4 {+2.29/5 2.3} Nd7 {-0.63/8 2.1} 26.
exd6 {+2.22/5 2.2} cxd6 {-0.95/9 1.4} 27. Rh6 {+2.24/6 2.2} Nf6
{-1.28/9 2.4} 28. Nxd6+ {+2.49/6 2.1} Kf8 {-1.23/8 1.5} 29. Nxc8
{+2.76/5 2.1} Rxc8 {-1.13/9 1.8} 30. Rh3 {+2.63/5 2.1} Rd8 {-1.11/8 1.2}
31. Bc1 {+2.96/5 2.0} Ng4+ {-0.94/9 1.7} 32. Kf3 {+2.49/7 2.0} Rxd5
{-1.51/9 1.7} 33. Bh7 {+2.15/7 1.9} Rg7 {-1.59/10 1.6} 34. Bb2
{+2.25/6 1.9} f5 {-0.93/9 1.5} 35. Bc3 {+2.08/5 1.9} Nf6 {+0.92/9 1.1} 36.
Rah1 {+0.14/6 1.8} Ne4 {+0.94/9 1.1} 37. Bb2 {+0.13/5 1.8} Ng5+
{+0.85/8 1.0} 38. Kf2 {-0.18/8 1.7} Nxh3+ {+0.64/9 1.5} 39. Rxh3
{-0.22/7 1.7} Rg4 {+0.77/8 1.1} 40. Rf3 {-0.13/6 1.7} Bh4+ {+0.71/9 1.3}
41. Kf1 {+0.42/7 1.7} f4 {+0.50/10 2.1} 42. Be4 {+0.74/7 1.6} Ra5
{+0.66/10 2.5} 43. a4 {+0.74/7 1.6} Bf6 {+0.56/9 1.4} 44. Bc3 {+0.46/6 1.6}
Rh5 {+0.69/9 1.8} 45. Kg1 {+0.59/6 1.5} Rgh4 {+0.81/9 1.0} 46. b4
{+0.78/7 1.5} Rh1+ {+0.84/9 1.3} 47. Kf2 {+0.94/7 1.5} Bh4+ {+0.72/10 2.0}
48. Ke2 {+0.88/7 1.4} Bg3 {+0.60/10 2.2} 49. d5 {+1.04/6 1.4} Rh6
{+0.48/9 2.4} 50. Be5 {+1.26/6 1.4} Re1+ {+0.58/8 0.9} 51. Kd3
{+1.34/6 1.4} Ke7 {+0.52/9 6} 52. b5 {+1.45/5 1.3} Rh4 {+0.68/8 1.2} 53.
d6+ {+1.79/6 1.3} Ke6 {+1.07/8 0.7} 54. Kd4 {+0.61/5 1.3} Rd1+
{+1.41/9 0.8} 55. Bd3 {-0.92/8 1.3} Bh2 {+1.36/9 0.7} 56. d7 {-0.35/7 1.3}
Kxd7 {+1.45/8 0.9} 57. Rh3 {-0.31/6 1.2} Rxh3 {+2.49/9 0.6} 58. gxh3
{-1.07/8 1.2} Bg1+ {+2.74/12 0.7} 59. Kc3 {-2.74/7 1.2} f3 {+2.75/11 0.7}
60. Kc2 {-3.09/6 1.1} Re1 {+3.20/12 1.0} 61. Bb8 {-3.25/6 1.1} Kc8
{+3.50/13 1.6} 62. Bg3 {-4.04/6 1.1} f2 {+3.85/13 0.8} 63. h4 {-3.73/6 1.1}
f1=Q {+4.46/13 0.7} 64. Bxf1 {-4.47/8 1.1} Rxf1 {+4.46/12 5} 65. Kb2
{-4.15/6 1.0} Bh2 {+4.49/9 0.5} 66. Bxh2 {-5.22/7 1.0} Rf2+ {+4.52/11 0.7}
67. Kc3 {-5.68/8 1.0} Rxh2 {+4.63/11 0.6} 68. h5 {-5.80/7 1.0} Rxh5
{+4.74/11 0.6} 69. Kb2 {-5.79/7 1.0} Rh4 {+5.85/12 1.3} 70. Kb3
{-5.86/7 0.9} Kc7 {+5.96/12 1.2} 71. Ka3 {-6.37/7 0.9} Kd6 {+5.95/12 1.1}
72. Kb2 {-7.75/7 0.9} Rxa4 {+5.95/11 0.9} 73. Kb3 {-7.80/7 0.9} Ra5
{+6.04/12 0.4} 74. Kc4 {-7.87/8 0.9} Ke5 {+6.22/12 0.7} 75. Kb4
{-8.74/8 0.9} Kd4 {+13.69/13 0.7} 76. Kb3 {-8.73/8 0.8} Rxb5+
{+13.71/13 0.8} 77. Ka4 {-9.56/7 0.8} Rb2 {+79.96/17 0.4} 78. Ka3
{-327.60/8 0.8} Kc3 {+79.97/28 0.2} 79. Ka4 {-327.62/8 0.8} Rb1
{+79.98/28 0.2} 80. Ka3 {-327.64/8 0.8} Ra1# {+79.99/28 0.2}
{Xboard adjudication: Checkmate} 0-1
[/pgn]
bennedich
Posts: 3
Joined: Thu May 06, 2021 3:57 am
Full name: Max Bennedich

Re: Complicating code in C#

Post by bennedich »

Do you have the full code posted somewhere? I'm happy to take a look. I wrote an engine in Java some time ago and spent some time optimizing it, many of the tricks I did should apply to C# too. One thing I can recommend, if you want to improve performance, is to not do any allocations at all in your time sensitive code. (Basically avoid using the `new` operator.) Instead preallocate and reuse all objects and arrays.
Henk
Posts: 7220
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

No I don't have the code posted somewhere.

Already have a problem with DoMove for it makes a copy of a position.
That would mean I have to preallocate millions of positions ?

But I will try to reduce allocations.
Maybe also remove virtual methods.

Last days busy with removing ChessPosition objects for all data is stored in ChessBoard (objects) now.
Currently a ChessPosition object still contains one ChessBoard object . Very much work to rewrite the code for zillions of ChessPosition references in my code right now.

Terrible.
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: Complicating code in C#

Post by mvanthoor »

Henk wrote: Thu May 06, 2021 6:56 pm No I don't have the code posted somewhere.

Already have a problem with DoMove for it makes a copy of a position.
That would mean I have to preallocate millions of positions ?
No, you allocate one board, and then make and unmake your move on that board.
Last days busy with removing ChessPosition objects for all data is stored in ChessBoard (objects) now.
Currently a ChessPosition object still contains one ChessBoard object . Very much work to rewrite the code for zillions of ChessPosition references in my code right now.

Terrible.
I've seen posts about these problems at least 5 years back. I still wonder why you don't just start over with what you know now, avoid the mistakes you made earlier, and be done with it.
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
bennedich
Posts: 3
Joined: Thu May 06, 2021 3:57 am
Full name: Max Bennedich

Re: Complicating code in C#

Post by bennedich »

Henk wrote: Thu May 06, 2021 6:56 pm Already have a problem with DoMove for it makes a copy of a position.
That would mean I have to preallocate millions of positions ?
As mentioned above, it'd be enough with just a single instance, and do the moves and then (after the recursive call) unmove on that same instance.

As an implementation detail, I copy the state before I do the move, and perform the unmove by simply copying it back. But importantly, I do this without repeated allocations -- I just pre-allocate one board per ply (e.g. 63 instances) to keep the copies.
bennedich
Posts: 3
Joined: Thu May 06, 2021 3:57 am
Full name: Max Bennedich

Re: Complicating code in C#

Post by bennedich »

Henk wrote: Thu May 06, 2021 6:56 pm Maybe also remove virtual methods.
Virtual methods suggest to me that you're using some inheritance model? Yes, I'd stay away from that in your time sensitive code, as it might slow things down and prevent the compiler from doing certain optimizations. Efficient code in Java and C# is unfortunately not always very idiomatic...

With that said, whenever you do changes that sacrifice readability and flexibility, I'd make sure to benchmark that you're indeed getting a decent performance gain out of it, or it's not worth it.
Henk
Posts: 7220
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

The reason I started to make copies was because I thought it would be easier to implement multi threading later.
But if it makes your code already three times slower ...
Best start by making code multi threaded. Then you know which objects should be immutable.
spirch
Posts: 95
Joined: Fri Nov 09, 2012 12:36 am

Re: Complicating code in C#

Post by spirch »

multi thread is easy when you start with it in mind

my c# engine does multi thread without issue, nothing special in it, no oriented object no inheritance nothing complex

not even transposition table or hash (like zobrist)

single thread look like this

Code: Select all

 A B C D E F G H
1 R ■ ■ Q ■ R K ■  Kind of perft: Full Perft, doing all move/undo to the last depth
2 P p ■ P ■ ■ P P  Started 6 depths at 2021-05-06 23:33:56 finished at 2021-05-06 23:34:31
3 q ■ ■ ■ ■ N ■ ■  FEN: r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1
4 B B P ■ P ■ ■ ■
5 n P ■ ■ ■ ■ ■ ■  Expected Moves   : 706,045,033
6 ■ b ■ ■ ■ n b N  Total Moves Done : 706,045,033
7 P p p p ■ p p p  Time Taken       : 34,856ms
8 r ■ ■ ■ k ■ ■ r  Moves per second : 20,256,054
multi thread look like this

Code: Select all

  A B C D E F G H
1 R ■ ■ Q ■ R K ■  Kind of perft: Full Perft, doing all move/undo to the last depth
2 P p ■ P ■ ■ P P  Started 6 depths at 2021-05-06 23:35:32 finished at 2021-05-06 23:35:35
3 q ■ ■ ■ ■ N ■ ■  FEN: r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1
4 B B P ■ P ■ ■ ■
5 n P ■ ■ ■ ■ ■ ■  Expected Moves   : 706,045,033
6 ■ b ■ ■ ■ n b N  Total Moves Done : 706,045,033
7 P p p p ■ p p p  Time Taken       : 2,527ms
8 r ■ ■ ■ k ■ ■ r  Moves per second : 279,400,487
Henk
Posts: 7220
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

I now have quiescence search alloc free.

Schwindel game below. See ending.


[pgn]
[Event "Computer Chess Game"]
[Site "LAPTOP-1FK7MTIP"]
[Date "2021.05.13"]
[Round "-"]
[White "Skipper_8_13"]
[Black "Fairy-Max 4.8V"]
[Result "1/2-1/2"]
[TimeControl "180"]
[Annotator "1. +0.18 5... -0.04"]

1. e4 {+0.18/9} c5 2. Nf3 {+0.00/8 4} d6 3. Nc3 {+0.26/9 3} Nf6 4. Bc4
{+0.19/8 3} Nc6 5. d3 {+0.26/8 3} g6 {-0.04/8 11} 6. Nd5 {+0.60/8 3} Bg7
{+0.03/8 3} 7. Bg5 {+0.55/7 3} O-O {+0.26/8 4} 8. Nxf6+ {+0.17/7 3} exf6
{-0.05/8 2.9} 9. Bf4 {+0.19/8 3} Ne5 {+0.12/7 3} 10. Bd5 {+0.16/9 3} Qb6
{+0.04/7 4} 11. O-O {+0.73/8 3} Qxb2 {+0.74/8 4} 12. Rb1 {+0.56/8 2.9} Qa3
{+0.74/8 5} 13. Rb3 {+0.95/6 2.9} Qa5 {+0.74/8 3} 14. Bxb7 {+0.38/7 2.8}
Bxb7 {+0.75/10 3} 15. Rxb7 {+0.10/8 2.8} Qxa2 {+0.75/9 3} 16. c4
{+0.12/7 2.7} h6 {+0.78/8 2.6} 17. Be3 {-0.05/7 2.7} Qa3 {+0.76/8 2.2} 18.
Nxe5 {+0.03/8 2.6} fxe5 {+0.80/9 1.9} 19. f4 {-0.21/8 2.6} Qa6
{+0.81/9 2.8} 20. Rb5 {-0.31/8 2.5} exf4 {+0.82/10 2.5} 21. Rxf4
{-0.42/9 2.5} Be5 {+0.80/10 6} 22. Rf3 {-0.31/8 2.4} h5 {+0.70/9 2.2} 23.
d4 {-0.27/8 2.4} cxd4 {+0.70/10 1.7} 24. Bxd4 {-0.35/8 2.3} Bxd4+
{+0.73/10 2.0} 25. Qxd4 {-0.17/7 2.3} Rab8 {+0.74/10 2.3} 26. Rfb3
{+0.48/8 2.2} Rxb5 {+0.74/11 1.6} 27. cxb5 {+0.23/8 2.2} Qb6 {+0.79/10 2.2}
28. Qxb6 {+0.27/11 2.1} axb6 {+0.80/14 2.5} 29. Rd3 {-0.72/10 2.1} Rd8
{+0.70/13 4} 30. h4 {-0.70/10 2.0} f6 {+0.89/14 1.9} 31. Kh2 {-0.80/9 2.0}
Kf7 {+0.91/14 1.3} 32. Kg3 {-0.66/9 2.0} Ke6 {+0.91/14 3} 33. Kf4
{-0.75/10 1.9} Rc8 {+0.91/13 1.4} 34. g4 {-0.80/10 1.9} hxg4 {+1.50/13 1.2}
35. Kxg4 {-1.84/10 1.9} Rc4 {+1.47/14 2.8} 36. Kh3 {-1.81/9 1.8} Rxe4
{+1.59/14 1.1} 37. Rg3 {-3.04/9 1.8} Rb4 {+1.60/15 2.0} 38. Re3+
{-3.16/10 1.8} Kf7 {+1.62/16 1.8} 39. Rd3 {-3.11/11 1.7} d5 {+1.68/16 1.3}
40. Rxd5 {-2.77/10 1.7} Ke6 {+1.49/15 1.3} 41. Rd3 {-2.81/10 1.7} Rxb5
{+1.45/15 1.0} 42. Rg3 {-3.18/10 1.6} Kf5 {+1.47/15 1.4} 43. Rf3+
{-2.66/10 1.6} Ke5 {+0.78/16 1.6} 44. Re3+ {-2.51/11 1.6} Kf4
{+1.46/15 1.5} 45. Rg3 {-2.45/11 1.5} g5 {+1.46/16 1.3} 46. Rg4+
{-2.66/11 1.5} Kf3 {+1.45/16 1.4} 47. Rg3+ {-2.53/11 1.5} Ke4
{+1.45/15 1.2} 48. h5 {-1.95/10 1.4} Rb1 {+1.43/14 1.7} 49. Kh2
{-2.84/9 1.4} Rb2+ {+1.38/13 1.2} 50. Kh3 {-2.41/10 1.4} Kf4 {+1.47/15 1.4}
51. h6 {-1.66/10 1.4} f5 {+1.44/15 0.9} 52. Rxg5 {+0.00/12 1.3} Kxg5
{+3.91/18 0.9} 53. h7 {+0.00/12 1.3} Rb3+ {+3.90/16 1.7} 54. Kg2
{+0.00/10 1.3} Rb2+ {+0.01/15 1.9} 55. Kh3 {+0.00/11 1.3} Rb1
{+0.01/14 0.8} 56. Kg2 {+0.00/9 1.3} Rb2+ {+0.00/15 0.8} 57. Kh3
{+0.00/11 1.2}
{XBoard adjudication: repetition draw} 1/2-1/2
[/pgn]
Henk
Posts: 7220
Joined: Mon May 27, 2013 10:31 am

Re: Complicating code in C#

Post by Henk »

Cleared remainder of search as well. That is no unnecessary allocs, allocate everything in advance and re-use them.

But performance does not get better than 350kn/sec on my machine while Stockfish easily makes 1 million nodes per second.
Might be because of single threading.

By the way code getting more instable and tricky now due to last changes. Maybe I introduced some hidden errors as well.