Yes, I did racket -l errortrace -t chess.rkt
and got some more granular output. Even w/o errortrace, I think profile will get me there.
Once I found <https://docs.racket-lang.org/profile/index.html?q=profiler#%28def._%28%28lib._profile%2Fanalyzer..rkt%29._profile%29%29|Section 4.1> , the output became so clear!
Yeah, that is interesting.
Am I right in thinking that typed racket doesn’t support kw-args in it’s dependent function types? https://docs.racket-lang.org/ts-reference/Experimental_Features.html
That is so interesting. I might actually improve a thing or two now. Thanks!
My engine is getting better. It recently beat a 1,900 human player, but I think that was a fluke. I can still beat it if I concentrate. Here’s today’s game where I beat it :) 1. d4 d5 2. Nf3 Nc6 3. e3 Be6 4. Bd3 Qd7 5. a3 O-O-O 6. Nbd2 Nh6 7. h3 Re8 8. b3 f6 9. Bb2 Rd8 10. Qe2 Qe8 11. O-O-O Bf7 12. g4 e5 13. Bb5 exd4 14. Bxc6 Qxc6 15. Nxd4 Qb6 16. Qb5 Qxb5 17. Nxb5 a6 18. Nd4 Re8 19. Rhe1 Bg6 20. f4 c5 21. N4f3 Be7 22. f5 Bf7 23. c4 dxc4 24. bxc4 Bd6 25. Kc2 Ref8 26. Kc3 Be5+ 27. Nxe5 fxe5 28. Ne4 Rd8 29. Nxc5 Rxd1 30. Rxd1 Re8 31. e4 Re7 32. a4 Rc7 33. Ne6 Bxe6 34. fxe6 b5 35. axb5 axb5 36. c5 Rxc5+ 37. Kb4 Rc7 38. Rd5 Re7 39. Rxe5 Kd8 40. Rd5+ Ke8 41. Rd6 Rc7 42. Kxb5 Ke7 43. Ba3 Ke8 44. Ra6 Rc8 45. Ra7 Rd8 46. Rxg7 Rb8+ 47. Kc6 Rc8+ 48. Kd5 Ra8 49. Bc5 Rd8+ 50. Ke5 Rd3 51. Rxh7 Kd8 52. Rxh6 Kc8 53. Rh7 Rd8 54. g5 Kb8 55. g6 Rg8 56. Kf6 Kc8 57. g7 Kc7 58. Rh8 Rxg7 59. Kxg7 Kc6 60. e7 Kxc5 61. e8=Q Kd4 62. Qc6 Ke3 63. Rf8 Kd3 64. Rf3+ Kd2 65. Qc3+ Ke2 66. Qd3+ Ke1 67. Rf1#
Engine was playing black, and Stockfish says it blundered with move 26. It currently has very few features, just simple alpha-beta, very simple evaluation function and simple move ordering (most valuable victim, least valuable attacker i.e. MVV-LVA). I’ll be adding a transposition table & better evaluation next.
I implemented “perft” to check the move generation and legality checks. It does about 3.9M nodes per second compared to 50M to 100M or more for engines in C++ or Rust. With some tuning, I may be able to get it up to 5M, or so which should be enough for my purposes.
Search is only 500K to 1M nodes per second at the moment, but with the alpha-beta and move ordering, it can go pretty deep sometimes.
- … Nh6 is a bit odd
I don’t have a piece square table yet (which reminds me, that’s before transposition & evaluation!), so my dumb evaluation can make for some interesting moves. In this case, I have a small bonus for just moving the knights & bishops initially - it didn’t care where it moves, as long as there’s no material loss.
Interesting game. https://lichess.org/study/XvYUgXin The first mistake according to Stockfish is 26. … Be5+ . I think, Stockfish is unhappy about giving up the bishop pair for nothing. But that’s hard for a simple engine.
The poor knight stays on h6 until it is taken :slightly_smiling_face:
I just pushed the <https://github.com/lojic/RacketChess/tree/development|development branch> if you want to try it out. rlwrap racket chess.rkt
then enter w or b to tell the computer to play as white or black. When you enter a move, you can also add a think time :) For example: d5 30
will move the pawn to d5 and set the think time to 30 seconds. It will stay at 30 seconds per move until changed.
How does it work with piece square tables? Different values for each piece and move number?
As I understand it, a piece square table has a value for each square on the board for each piece, so an array for each piece. The value of the piece ends up being the normal value plus whatever is in that square. This will encourage certain behaviors such as getting the king active in the middle during the end game, etc.
I think there is usually one set of tables for opening/middle & another set for end game. That’s enough to find mate in the endgame without having to have an endgame table.
Sounds pretty simple - but deriving the numbers…
It will needlessly get into stalemate now, and sometimes it will perpetually check.
Yes, some people do supervised/reinforcment learning for the numbers, but I think in my case, I’ll start with a generic set of tables from another engine before getting fancy.
Makes sense.
I give <https://github.com/lojic/RacketChess/blob/8a2f6b5f0bde74ca463c43ee7e4a6fe81be66742/src/evaluation.rkt#L37|a bonus> for the king being on a castled square, but since I don’t deal with the rook, I saw the engine move the king to the castled square in two steps for one game while leaving the rook in the original spot which trapped the rook :)
I just hacked up a few ideas in that evaluate function so I could try it out.
So far, the biggest change was adding quiescent search - that was huge re: making it stronger!
When I get far enough, I’m going to start a series of blog posts that will create a Racket chess engine bit by bit, I’m really looking forward to that!
What’s “quiescent search”?
I’ll definitely look forward to a blog series!
I am confused about the entering the time:
OK looking at the actual, I don’t think it does
*file
Hmm.. let me try…
I think you entered too soon - wait for the “Enter move:” prompt
Shoot - I think I broke something when hurriedly pushing the last commit. Hold on…
I see that prompt, but it disappears after entering d4 30
Wait. I am using 7.5. I should probably use a newer Racket. What are you on?
Ok, I pushed a fix, it’s playing again. The code in chess.rkt and the top level search function are hacky at the moment. I’m running Racket v7.9 [cs]
badkins@create src % rlwrap racket chess.rkt
Computer plays white or black? (w or b):
b
Enter move: d4 30
---------------------------------
\| r \| n \| b \| q \| k \| b \| n \| r \| 8
---------------------------------
\| p \| p \| p \| p \| p \| p \| p \| p \| 7
---------------------------------
\| \| \| \| \| \| \| \| \| 6
---------------------------------
\| \| \| \| \| \| \| \| \| 5
---------------------------------
\| \| \| \| P \| \| \| \| \| 4
---------------------------------
\| \| \| \| \| \| \| \| \| 3
---------------------------------
\| P \| P \| P \| \| P \| P \| P \| P \| 2
---------------------------------
\| R \| N \| B \| Q \| K \| B \| N \| R \| 1
---------------------------------
\| a \| b \| c \| d \| e \| f \| g \| h \|
Black's move
Depth: 1. Move-i: 1. EP Square: d3. White king pos: e1, Black king pos: e8 Castling: KQkq
Best move (1): pd7-d5
Best move (2): pd7-d5
Best move (3): pd7-d5
Best move (4): pd7-d5
Best move (5): pd7-d5
Best move (6): pd7-d5
570026.6666666666 nodes per second
---------------------------------
\| r \| n \| b \| q \| k \| b \| n \| r \| 8
---------------------------------
\| p \| p \| p \| \| p \| p \| p \| p \| 7
---------------------------------
\| \| \| \| \| \| \| \| \| 6
---------------------------------
\| \| \| \| p \| \| \| \| \| 5
---------------------------------
\| \| \| \| P \| \| \| \| \| 4
---------------------------------
\| \| \| \| \| \| \| \| \| 3
---------------------------------
\| P \| P \| P \| \| P \| P \| P \| P \| 2
---------------------------------
\| R \| N \| B \| Q \| K \| B \| N \| R \| 1
---------------------------------
\| a \| b \| c \| d \| e \| f \| g \| h \|
White's move
Depth: 1. Move-i: 2. EP Square: d6. White king pos: e1, Black king pos: e8 Castling: KQkq
pd7-d5
Score: 0.01
Enter move:
I think it should work just fine on 7.5
I’m running MacOS
@soegaard2 quiescent search works like this - you do an alpha-beta search to a given depth i.e. 3, then, instead of simply returning the evaluation of the board, you enter a quiescent search which only searches capture moves until there are no more, then it returns a static evaluation of the board. That is to prevent you taking a pawn with your Queen, and thinking everything is ok :) It plays out all the captures & recaptures until the board is “quiet” before giving an evaluation. It’s important to have some reasonable move ordering in place first otherwise queens would just go on a rampage gobbling up pawns and the search would combinatorially explode. By having decent move ordering, the alpha beta will prune very well, and it will “quiesce” quickly enough.
I really need to get to the point where I can “plug in” my engine to an app along with another engine and let them play a bunch of games. This way I can make a change, and see if it beats the older version.
Stockfish is amazing - I made it sacrifice its queen, and it still beat my engine !!!
Ah! Figured it out. I thought, I were on the development branch, but I wasn’t. Now it works.
Thanks for the explanation of quiescent search.
Hmm.. that’s even worse if the main branch was failing! I’ll probably merge dev into master soon.
Once I get the UCI stuff coded, you can plug in the engine to a GUI - that’s probably the way to go vs. developing the GUI part.
It’s playing well so far.
To play the engine now, I start my Stockfish app which has a nice board where you can move the pieces, the console display of the board is not good :)
If you give it 30s per move, it’s pretty tough depending on your skill level.
Is the score measured in pawns?
Basically. I use centipawns where a pawn = 100 centipawns, but when I display the score in the console, I divide by 100, so it’s back to pawns.
How do you enter castles? Ke1-g1 is in the list of generated moves, but I get: Move not in generated list
Ke1-g1
pgn-move: no pattern found for move
O-O or O-O-O
Of course. I tried o-o, but I guess that’s for black.
O-O for kingside, O-O-O for queenside - maybe it needs capital O’s ?
I don’t remember if I upcase it or not
@soegaard2 can you give me the lichess link to watch? :)
are you white or black?
I am white. I already lost a pawn :disappointed:
It knows nothing about king safety :)
You have a nice pawn shield for your king.
I’m no expert, but you have all your pieces in play, and one black rook is useless now.
Yes - I am however mostly playing bullet, and bullet tricks doesn’t work here.
Yes. The engine isn’t too smart, but it’s reasonably tactical - it shouldn’t make any obvious blunders like losing a piece unless it’s just outplayed positionally.
lol - you must be doing well when it just moves its rook back like that! :)
Yeah, that were an odd move.
Whenever I see an engine waste a move, I get encouraged :)
That was fast - are you giving it less think time? :)
Still 30
Somehow I need replace my knight.
Hmm.. that move was 16 seconds. I think you changed the think time - maybe accidentally.
It should still play ok though.
Or maybe lichess has lags
It plays fine. A but passive, but no mistakes.
weird, I expected it to move Nd6
Ditto
surely it won’t allow the fork :)
I’m more interested in this game, than in watching the world champion play :)
More rook moves.
Its end game is weak, so if you can survive long enough, you should be able to outplay it in the endgame.
The score now says 999.8, so there must be something here …
oh, you have mate then!
Wrong move. Score dropped to 9.
Okay. No reason to play further. You are spot on about surviving the opening/middle game.
Calculating a move or two further might have saved the situation in this game.
Good job :)
I botched the opening though.
I think, it was a good decision to get the queens of the board.
(from my perspective)
I agree. I always try and simplify since it’s so good at looking 3 to 5 moves ahead :)
It has no opening intelligence. I simply gave a small bonus to 1) pawns on a center square, and 2) knights/bishops that have been moved, and that seems to make it play a “reasonable” opening.
Adding some good piece square tables will make it pretty formidable I think.
Yeah - that’s why I am annoyed it played better than me in the opening :slightly_smiling_face:
Have you seen Hikarus game against the engine Rybka?
The problem for the engine is again the long term strategy.
No, I’ll check it out. Thanks for trying out the engine by the way, that was fun :) I’ll make more progess over the weekend.
It’s a cool project. I am impressed by how well it plays already.
Me too actually :) I’ll walk through that Hikaru vs. Rybka game w/ Stockfish and see how often Stockfish suggests the move.
Hmm. Hadn’t thought about it. Someone much have entered the pgn somewhere.
lol all those bishop & knight promotions!
Wow, that was 3 minute no increment ?!?!
Oh!
Hikaru is scary fast. I watched him solve puzzles. He plays the solution faster, than I can see, where all the pieces.
are
I would love to see that in real time.
@soegaard2 I added the piece square tables (PST) functionality. Accidentally pushed to master, but it seems to be working well. Much better than my goofy evaluation heuristics :)
I didn’t have time to properly determine when “end game” starts, so it just switches from middle to end game after 50 ply. Close enough for now.
The only piece that changes is the King - end game table encourages moving it to the center. Middle game encourages castling and staying behind the pawn shield.
I just followed this pretty much verbatim: https://www.chessprogramming.org/Simplified_Evaluation_Function but I made an important change to the Rook table to encourage castling after it failed to do so with the original Rook table.