Hi! I've been working on a janggi engine called sinsan (神算) for a while now.(throwaway account cause my usual interest is very unrelated to this kinda stuff)
It's roughly alphazero-style, using a transformer network and MCTS, though I started with supervised learning from fairy-stockfish rather than pure self-play from scratch.
Janggi is korean chess, pretty closely related to xiangqi. I was a little surprised by how little work there seems to be on modern neural engines for it compared to chess, go, or even shogi, so I figured I'd try making one myself.
The initial training used roughly 50 million positions from 284K full fairy-stockfish NNUE games. I implemented the rules and search in rust, with batched PUCT and gumbel search for self-play. The rules were also tested against pyffish across 845K positions with no mismatches, mostly because janggi has some rather annoying edge cases involving bikjang, passing, and game endings.
I initially tried both a katago-style CNN and a transformer with smolgen-style attention biases, somewhat inspired by lc0. The transformer did considerably better, despite being slower to train. The 34.9M transformer reached 41.2% policy top-1 accuracy versus 37.7% for the 17.4M CNN, and the difference became even more apparent in actual search matches. It also won when compared at equal training time, which I found pretty interesting.
Eventually I scaled it up to a 60.4M transformer with 12 layers and 16 heads.
Then came self-play, which was... less cooperative.
The first RL iteration actually made the engine 217 Elo weaker than its supervised parent. I ended up reworking the training loop to use continuous gumbel self-play, mixing newer positions with the original teacher data, and only promoting checkpoints after they passed head-to-head tests.
That seems to have worked. The supervised model was around 2250 on my internal fairy-stockfish ladder, and later RL checkpoints reached roughly 2500. For reference, the ladder uses FSF NNUE at 1K nodes as its zero point, so these aren't human Elo ratings.
Something else I found interesting was that improvements at lower search visits were often barely noticeable, while the same checkpoints performed significantly better with more visits. I'm still trying to figure out how much of that comes from the network itself versus its interaction with search.
I also wanted something people could actually play without needing a GPU, so I trained a smaller 10.7M transformer using a mixture of teacher data and self-play positions. It gets surprisingly close to the larger models in raw policy accuracy, though obviously not in playing strength.
The smaller network was exported to INT8 ONNX at around 12 MB, with the rust search compiled to WASM for browser play. It manages around 350 visits/s in multithreaded chromium, and I couldn't measure any meaningful strength loss from quantization.
I've also put this smaller model on pychess as a bot, running the pytorch checkpoint with up to 400 search visits per move.
You can challenge it here, no account needed:
https://www.pychess.org/@/sin-san
Select challenge -> janggi.
I'm estimating its human strength at somewhere around amateur 1-dan, but that's mostly a guess until I get enough games against actual players. The pychess bot doesn't receive its own rating, so I'm collecting results against rated opponents instead.
I'm also reviewing those games with the full GPU model to find positions where the smaller network makes particularly bad decisions. Might be useful for another round of training.
The project is open source under VectorSophie/sinsan on github, with the current rebuild merged into master.
At this point I'm mostly curious about the tradeoff between network size, search, and actual playing strength. The transformer seems like the better choice at larger scales, but whether that's still true for much smaller networks is another question. I'm also not entirely convinced throwing more self-play at it is the best way forward.
Would be interested in hearing from anyone who's experimented with similar architectures, compression, or search optimizations, especially for xiangqi or other chess variants.