Ocelot
- 10 Devlogs
- 29 Total hours
A simple UCI chess engine.
A simple UCI chess engine.
I’m going to try not to sound like a cringey motivational speaker, so bear with me. I’ve been procrastinating for a while now on one final part of the UCI implementation: the go command. It’s really complicated and I just didn’t want to write a parser for it, so since it was created, the engine hasn’t been able to change its search time based on how much time it has left. This is a problem, because it couldn’t reliably play with non-infinite time controls.
Anyway, after I waited for the willpower to write the parser I just sat down and did it, and it wasn’t actually that hard!
The one issue is that it seems to be weaker than it was before the time controls. That’s definitely not a huge problem I’ll have to fix later!
I’ve been doing some work on less crucial parts of the engine, but mostly trying to entertain myself while I waited for my ship to be reviewed. Today, I can proudly say that it was! However, it was send back because the player could make illegal moves in the TUI. This wasn’t too hard to fix, but there were a few things that I hadn’t known were wrong that work properly now.
I didn’t realize it, but if the player tried to castle when they couldn’t, it would tell them it was illegal… after switching the color of the piece the king was going to replace. I did some sleuthing, and it turns out that it’s because the castle wasn’t being parsed as a castle, but as a move. This move was then being performed and undone. When moves are performed, they record the type of piece they capture so that the pieces can be restored when the moves are undone. Here, though, it was recording that it was capturing a knight but replacing it with a black knight because white can’t legally capture its own pieces.
Anyway, that’s fixed now, so I’ll request another review and keep you posted!
After my first (still pending) ship, I needed something else to focus my energy on other than the fundamentals. I landed on time management: I can make the engine more efficient with its time. And I’ve kind of done that, in a way. Firstly, I made the search algorithm track its time, so if it was iterating on a node while it surpassed its allowed time, it would just return the value it already had. I played around with the time value, and it seemed to work! When I set it to 1, the engine played moves in around 1.1 seconds. When I set it to 15, it played moves in exactly 15 seconds! It doesn’t dynamically adjust depth yet, but this is a good start!
I’d already done most of the work–I just needed a few more minor things done before I could ship. These were:
I’m proud to say that I’ve done all of those! Making the search tree dispatch threads was much easier than I thought it would be, especially considering I’m still pretty new to Rust. The TUI was simply adding a struct field of whether to exit. Publishing was a breeze, and I’ve now published 2 different versions.
All I need to do before I ship now is finish my README, which is just add a section about installing the project.
After finishing most of the engine, I play tested it for a bit. As discussed in my last post, I fixed 3 big bugs. I was getting closer than ever to shipping a finished product!
However, there was one problem: the engine wasn’t really usable on its own. Sure, it played decently, but it still needed the GUI to display the board. This is unfeasible for shipping–I can’t make people download someone else’s GUI just so my engine is playable. Therefore, I needed some sort of inbuilt user interface. My highest-priority design goal was to have no dependencies, so making a GUI wasn’t really an option. That left TUIs. I couldn’t use a library, so that left me 2 options: I could do the easy thing and hard-code the interface, or I could (and I know this sounds absurd) build a miniature TUI framework from the ground up just to make a simple screen.
I picked the latter, because why not? I already had a decent prototype written in Python for another project, so I could just port that to Rust and life would be easy. Right? Well, kind of. I spent some time on it, but before I added yet another mandatory argument to a trait method, I stopped and asked myself if this was really worth it. It wasn’t going to be easy or practical, so why do it in the first place?
And after coming to that realization, I quickly threw together a decent interface using hard-coded methods. No, it’s not extensible. But why would it need to be? It works, and that’s what matters. And it’s pretty nice, too!
This one’s more minor, so I’ll keep it short. There were 3 bugs:
unwrap())Board::turn)To solve them, I did 3 fixes:
I also played the first legal game with this engine! I used CuteChess to make it play itself, and 35 rounds later the game ended by stalemate. No illegal moves were made and no crashes happened. I’ve attached a screenshot of the end of the game. I’m going to take this as a good sign and try to target Monday (6/29/2026) for my first ship.
I did UCI! The engine now is UCI-compatible, so it can play with GUIs like CuteChess. When I first connected it to CuteChess, it crashed on the first move. After enabling debugging, I realized it was because I hadn’t added the moves clause to the position command. I added it and…
It still crashed. It crashed when it was time to make a move over and over and over again. After many ultimately useless println! statements, I finally realized why: I had been applying the user’s move before resetting the entire board, thus overwriting the move. After fixing this, I turned it on again and played for exactly one move before it crashed again. Why now? Once again, I hadn’t properly implemented the position command. It only accepted one move, but needed to take as many as were supplied. This was pretty easy - just a for loop.
After fixing that, I was able to play an almost-complete game with it before it crashed yet again. After some more testing, it seems to crash when it doesn’t have any legal king moves. My next step is to fix this and a few other minor bugs, and then we should be off to the races!
I’ve written a FEN parser. FEN (Forsyth-Edwards Notation) is a way of representing a chess board in one line of ASCII characters.
It was intimidating, and I procrastinated a lot before biting the bullet. However, after a couple hours of work, I can successfully generate a board from a string and cargo test is green again!
I’ve been working on a chess engine for a couple months now - first in Python, then in Rust. The Python implementation had a few problems. It made illegal moves and was buggy in general. But it had one problem that couldn’t be solved by more loss of sleep: It was written in Python. This made it incredibly slow, and you can’t do much about the language you’re writing in. I tried for a couple days to port the code base over to Codon, a compiled implementation of Python, but I had used dynamic typing so much that it wouldn’t have been feasible.
So I came to a crossroads. I could keep working on the Python project, with the limitations of either an incredibly slow environment or an unpopular one, or I could leave my comfort zone, learn something, rewrite it in Rust. I’m now about 2000 lines into the Rust version, and it’s faster and more accurate than the old one could ever dream of being.
I joined Hack Club a couple days ago, and I didn’t know what to expect. I’ve had fun talking to other like-minded “hackers”. I was reading about Stardance, and it seemed like the thing to do. I was even told that I could bring an existing project! I renamed my engine (“Sophisticate”) to Ocelot, and joined.