June 19, 2025
tuning is going well (it isnt). its super slow so i made a version that
1. doesn't do quiescence search, only eval
2. 30x less positions.
the error did end up lowering and the gradient descent seemed to converge, but the performance was awful (giving negative values to pawns in some positions). gonna have to actually do it properly probably but that'll take hours.
fixed the strictness bug. still very slow but it doesn't oom when using tunedEval now (which i'm not doing anyway). might start using massiv for multithreading but that honestly feels like the wrong solution, single threaded should be ok judging from other people's attempt even though this is an embarrassingly parallel problem
started parsing pgns for texel-tuner. not sure if they are correct yet. started with string in parsec but it was slow and bad so i switched to text in megaparsec (even though base project uses parsec for uci... maybe switch?)
It's still not blazing fast or anything but its ok. concerningly i found a mystery game in my output trove where one of the recent engine versions crashed. it was only one in a few dozen thousand so that's not great.
also forgot to devlog so here's a new update: validated the parser-ish! played through all the moves and none broke. had to debug for a while because i'm dumb. 14 seconds all told
set up the texel-tuner stack thing. surprisingly annoying. haskell-language-server apparently doesn't play nice with packages: .., and using a cabal hie.yaml to make hls not use stack breaks because cabal isn't aware of the packages: .. in the stack.yaml (only package.yaml is mirrored to trout.cabal). Solution is to create a separate cabal.project just for hls even though i'm not even using cabal.
tested a bunch of stuff...
ended up keeping mobility tuning (tapered mobility) and changing the lmr (late move reductions, searching moves the engine immediately thinks is worse less) reduction formula from hardcoded 1-2 depths depending on type of move to logarithm-based formula based on depth and move index.
Tried to improve eval a little but it just takes too long to sprt test. going to write a texel tuner to 1. tune the piece-square tables and 2. make tuning eval easier when adding more terms
finally got some elo out of razoring with quadratic depth scaling. also messed with reverse futility pruning but not much there.
still not entirely clear on relationship between futility, reverse futility, and razoring? i mean yeah they're slightly different but it feels like they should overlap at some point.
from my logs i think I also tried big delta pruning (don't know what the actual name is, whole nodes in quiescence), aspiration window adjustments, both fail.
tried 7 bitboard storage format, fail. was significnatly slower.
did end up trying tt move ordering in quiescence search but it was maybe a little worse? definitely not better.
added all pieces to mobility calculation, small elo boost. it is a lot slower now because it's doing a lot of movegen in eval but i tried removing mobility completely and it was real bad (see screenshot)
big dubs checking for transposition table cut in quiescence search. 40+ elo increase. might try transposition table move ordering next, but effectiveness might be low because of the capture restriction on quiescence moves. dunno how else i could improve quiescence move ordering because it already has SEE which is pretty powerful and a lot of other ordering is based on quiet moves only.
could try inserting to tt in quiescence? that sounds like a bad idea though
some cleanups, bugfixes, nice background, deploy
fiddled with endscreen and added download button!
basic api interaction, just remembered about dual division events. that's gonna be a whole thing. Yippee!!!
ramer-douglas-peucker smoothing on the shape! pretty happy with how it looks.
spent some time trying to smooth out the shape, but i think i need to reimplement ramer douglas peucker to make it actually look normal.
some miscellaneous polishing, like homescreen improvements and brush overlay. we have real stl rendering now! with synched cameras
what did I do?...
I added a brush size slider, theming, may or may not have fixed some bugs...
Also added a proper homepage with file loading. may pretty it up later
fixed the randomly disappearing benchy layer! turns out it was rdp line simplification being bad because instead of finding distance from line segment to point i did line to point. not good. also found some ghost shapes the slicer found that are just like one point but i honestly have no idea how they are made and they don't affect anything for now because the slicer is only for drawing. very annoying though. could be evidence of a larger issue
use marching squares instead. this should quash a bunch of the weird outlining issues because it outlines between points instead of on them directly. still some issues left: threejs doesn't like rendering holes or some reason and for some reason one layer at 50 layers just doesn't show up
improved rednering a bit, added panning, fixed outstanding bugs. as a result it's now incredibly slow to calculate the outlines per layer, i'll need to fix later. i added a cheat mode so here's a detailed 50 layer benchy
tried to fix scaling and got more bugs as a reward. i hate bugs. insects
hunted down the polygonization issue, the source was in the slicing part; code for intersection between triangle and plane didn't work properly when the triangle was touching the plane exactly but only at one point, and also intersected the plane.
BENCHY TIME BENCHY TIME BENCHY TIME BENCHY TIME BENCHY TIME
added code to polygonize the drawing. its untested so far. probably broken. speaking of broken, slicing! edge case when slicing middle of benchy. will investigate later, this is weird
some progress on polygonization code. why doesnt this exist already i dont wanna write this man
flood fill! scanline algo. also fixed layout more
spent way way way too long trying to figure out how to get the square canvas to fit within width and height simultaneously. at this point I don't think its a thing you can do in css only. ended up having a js function constantly watching window resizes and changing the class of the square depending on whether it needed to be width-limited or height-limited. Also i did the drawing thing and it's path based so no loose circles. Next up is flood fill
tried to get drawing working (good!) then tried to make it path based instead of just spamming circles. its bad. real bad.
wasm done, it can generate layers for arbitrary stls now
implemented ramer douglas peucker with epsilon 1e-4 for the segment simplification. shrunk the point count of the first layer from like 4k+? to less than 900
implemented proper path creation
now that's what i call a benchy! primitive drawing thing to preview what horrible things my slicer has created.
slicer thing done. i use air quotes because it probably doesn't work; gonna need to write a frontend to view it next
got stl parsing done. surprisingly simple format, easy to parse with nom
draw a benchy like a 3d printer
made mobile actually properly responsive (the viewport thing is weird...) shouldn't have taken this long but websites be websiting. you know how it is. Also the board coordinates get messed up i guess because the board is dynamically sized? have to look into that. its not a big deal because they're so hard to see in the first place
https://osrepnay.github.io/trout-web/ added github actions workflow with only like 4 attempts! yay me! also added end screen for losing. also realized chessground doesn't do en passant either.
super stuck now. tried adding improving to lmr and nmp (horrible), more lmr and nmp fiddling (nothing), more fiddling with mobility (nonlinear) (nothing), transposition table replacement strategy (nothing). Got 20 elo from blocked pawns penalty (i guess this is mobility in a way?) but that's kinda it. i think i'll ship, im burning too much time and cpu time on this
fix issue where engine just doesn't want to checkmate. added checkmate distance penalties, but the issue was actually with reverse futility pruning (static eval of -500cp is significantly higher than beta of -99999999cp, so clearly this is fail high!). added requirement for non-pv node, probably should add more. this is turning out to be pretty dangerous optimization
my tuning attempts constantly fail.... why do i somehow always stumble into a local maximum instantly....
did like 20 things with null move pruning and couldn't get any elo out of it
fixed endgame disabling (no elo), enabled it on pv searches (negative elo), disabled pv checking altogether (no elo), put it after tt cut (negative elo/??? why???) made it set pv flag to false because null window (no elo), increased reduction amount (no elo)
also added insufficient material checking to draw detection (30 elo!)
i tricked you. i lied. that gave no elo as well
i saw kevin of pzchessbot added check extensions. i added check extensions. 40 elo
don't know whether to return to tuning old parameters or add new stuff or just ship at this point. theoretically the old parameters are all untuned but i can never improve the untuned versions and it's driving me insane
late move reduction! sizeable improvement
spent a long time fiddling with settings and for some reason the one that was really good was:
1. 1 ply reductions
2. research on fail-high (i think this is a given for lmr but i'm not completely sure)
3. disable on depth < 2
i'll try messing with move ordering, number of moves tried before reducing (rn is 3), variable reduction (i tried a little bit it didn't work very well for some reason) later
Experimented with killer move heuristic from previous plys (failure) and more history heuristic fiddling (failure)
maybe late move reductions now?
also I found a bug in the 50 move rule that accidentally made it the 25 move rule. oops
and the chess engine doesn't really know how to checkmate in K v RK, I would assume PeSTO would prioritize king on the edge more but it doesn't really
get some more elo out of smarter history pruning
history gravity (i think? it's slightly different than the cpwiki version which looks wrong) and history penalties/maluses for uncutting quiet moves
i also tried adding mobility to eval but that just killed elo for some reason like -100
i don't know if it's because of terrible performance (pst-only eval is already a significant fraction of runtime) or just too much/too little fraction of eval score dedicated on mobility
might revisit and retune some parameters from previous optimizations (e.g. null move pruning R, aspiration window settings)
cleaned up tt short circuit/cut code, it's much less nested and tangly now
made a logo (it's very good. it's the banner)
re-add aspiration windows! disabled a long long time ago because of lack of evidence of goodness, but with better (?) settings (starting 25 centipawns, 4x multiplier instead of 50 centipawns, 2x multiplier like previous) and better move ordering managed to squeeze out 20 elo
still some room for tuning on the aspiration window settings however
i think next is history heuristic or smarter eval
might be able to ship soon, the engine should be pretty competent now
Fiddle some more with killer heuristic
got ~20 more elo out of it by implementing 3 slots per ply (fifo with duplicate ignoring) and only allowing non-captures to be killers (i think this prevents interference with SEE ordering?)
next: check if nmp check detection, tt cuts (again) do anything
possibly move on to history heuristic
various fiddling
1. retry a bunch of aspiration window configurations, couldn't get anything reasonable (all under 10 elo increase and even dropped elo sometimes). might need smarter aspiration-aware pvs like cpwiki says that drops searching immediately if in a pv node and aspirating or just give up completely (i dont think aspiration windows+pvs is universal?)
2. attempt wasm compilation with the new ghc backend. some hiccups, like it not supporting threaded runtime so it can't properly implement the uci interface (can't cancel searches mid-search), but it's also like 4x slower on a depth 7 search when i run with wasmtime. the engine is bad enough i dont need wasm to make it seem even worse. probably just gonna throw up a lichess bot acct, i have a server to use
next: possibly killer moves, smarter eval, late moves pruning, futility pruning?
added mtd-f (https://www.chessprogramming.org/MTD(f)) to search
why didn't I do pvs?
there was less code.........
i'll probably compare pvs and mtd-f later. apparently mtd-f is less friendly to some search techniques and is definitely less tested/used
In the process of trying to make transposition table work with the draw rules. Very annoying. Tried to make transposition table faster by not checking for actual equality (only going off of hash), and apparently we already have a hash collision? very not cool.
Also another discovery: IntMap is significantly slower (in this use-case) than HashMap with Int keys. Why??????????????
random site (also projects)
fixing the homepage and formatting for now
This was widely regarded as a great move by everyone.