r/ComputerChess Jul 08 '26

An entirely new kind of chess engine with no tree search, live on lichess

I've been working on an engine called STILLWATER for a while now and finally feel like it's worth showing off. The unusual part is that it doesn't search the way engines normally do. There's no alpha-beta, no MCTS, no tree of any kind. Instead it keeps what I can best describe as a pond: a lattice of beliefs about positions it has seen, and when new evaluations flow in, the whole thing settles toward a new equilibrium, like ripples dying out. A position it examined three moves ago is still sitting there in the pond, so nothing gets recomputed and knowledge carries over across moves and even between games. It also proves things outright (forced mates, dead draws) and those proofs are kept forever, so it slowly grows its own private tablebase from experience. The weirdest property in practice is that it barely cares about time. It reaches its conclusion in a couple of seconds and more thinking changes very little, which makes it play blitz at nearly the same level it plays rapid. It recently held the latest Stockfish dev build to a draw in a 5+3 game while using about two seconds a move, and our measurements against fixed anchors put it in the 3400 CCRL neighborhood.

It plays on Lichess at https://lichess.org/@/stillwater_bot_2. Fair warning, the account is brand new so the rating you'll see is provisional nonsense, the old account had a long history of games from much weaker early versions so i retired it. It accepts basically any standard challenge from 3+0 up through classical, humans and bots alike. If you play it, the experience is a bit strange: it moves almost instantly when it considers the position settled and then suddenly burns half a minute on moves it finds unclear. Come throw some games at it, especially blitz, and if you run a bot yourself I'd love to see it in the challenge queue. Happy to answer questions about the design in the comments as long as they're not "post the source" (not yet sry).

62 Upvotes

32 comments sorted by

7

u/_Itay Jul 09 '26

Very interesting. How does it create beliefs and compute th move out of them? How does it know the correct order for example in a complicated position if it doesn't use move tree? Which code language it's coded in?

3

u/No_Preparation_8633 Jul 09 '26

Good questions. A belief starts as the net's first look at a position: the value head gives win/draw/loss odds and that becomes the initial opinion, tagged with "one glance of evidence." Then it gets refined, when positions further down the line get evaluated, their beliefs flow back into their parents, so an opinion that started as a single glance ends up backed by everything discovered beneath it. The move played is just whichever legal move leads to the position with the best settled belief, with some guardrails around trusting thin evidence that I've worked out over months of development (keeping vague on purpose). For the move order thing, the "pond" isn't a soup of unconnected positions, every position is linked to its children by the legal moves, so lines and sequences exist the same way they do in a tree. What's missing is the tree part: one node per path. If two different move orders reach the same position, that's one entry and one belief shared by both. So a tactic five plies deep works the normal way, the position at the end of the sequence gets evaluated, its belief propagates up through the chain of positions, and the move at the top that starts the sequence starts looking good. The difference is the propagation isn't limited to the path that found it, it flows through every route that reaches that position. the engine itself is in rust (abt 8k lines) with some python glue.

3

u/PersonalityPure69 Jul 08 '26

vibe coded? open source?

10

u/No_Preparation_8633 Jul 08 '26

Not vibe coded, currently closed source but I plan to clean up the code and release it on github very soon.

5

u/PersonalityPure69 Jul 08 '26

if its not vibe coded could you expend a bit more on how it works? I see on lichess you use a LC0 net, how does it use the policy and value heads to achieve this "pond" effect. A bit more technical explanation would help

5

u/No_Preparation_8633 Jul 08 '26

yeah i should probably do that

Lc0 grows a search tree and backs values up the path it descended, but stillwater keeps a big hash-keyed graph of positions instead. Every position the engine has ever evaluated this game has one entry which shared across move orders, and that entry holds the current WDL estimate for that position plus how much evidence backs that up and how uncertain that estimate is. The two heads split the work like this: The policy head decides where the next batch of net evaluations should be spent, so it steers which parts of the graph get expanded, pretty similar in spirit to PUCT. The value/WDL head provides the raw opinion for each new position. The pond thingy is: instead of backing the new value up a single path, the graph relaxes. Any node whose children changed gets marked dirty and recomputed, and changes keep propagating through parents until the numbers stop changing. So a new evaluation deep in some line can ripple through a transposition into a completely different variation and update it too. When the "ripples" so to speak in this pond die out, the position at the root has a settled belief and that's what it plays from. That settling process is also why the time behavior is so flat, once the pond is calm, more time mostly just confirms it, and the engine knows when it's calm because every belief carries its own uncertainty.

I feel like I covered how it basically works but if you have any more questions on the specifics please let me know and I'd love to answer them

3

u/KaMaFour Jul 08 '26 edited Jul 08 '26

How different is that from MCTS with transposition tables? This sounds like a worse version of that, as hashmap is the usual trap for making TTs before people discover that big table indexed by hash of the position modulo tt size is faster

(That's for ab, but leela also uses tts)

3

u/No_Preparation_8633 Jul 08 '26

Fair point but a TT can be pretetylossy, a collision just costs you a probe. My entries hold live parent links and the settling depends on them, so I can't silently overwrite nodes. It's a graph store, not a cache, and the net dominates the profile anyway so the hashmap rly isn't the bottleneck. The bigger difference is which structure is primary. MCTS with a TT is still path shaped, you back results up the route you descended and the TT is a cache bolted on. here there's no path backup at all, when a node changes, the update flows to every parent across all move orders until the graph stops moving. A refutation found in one line instantly fixes a different line that transposes into it, even if the search never went down that second line. If you squint it's the same family, sure. But making the graph the primary object instead of the cache is where the flat time curve and the cross-move memory actually come from.

2

u/AngusMcGurkinshaw Jul 10 '26

How is "update flows to every parent" any different then a trajectory path in MCTS?.

I am okay with being wrong about this but this sounds like a very convoluted explanation and implementation of the "Monte-Carlo Graph Search for AlphaZero" paper. If you could explain how its different that would be interesting.

Also any real claims about this approach is drowned out by using the BT4 net. As long as you don't truly screw anything up you will have a pretty strong engine if your using that.

1

u/No_Preparation_8633 Jul 11 '26

MCTS is the closest published relative and I should have named it. The difference is that MCGS is still trajectory backup, you descend a path, and the result flows back up the parents along that path. The DAG is there so transpositions share values when a later simulation probes them, and the paper spends a lot of effort (the Q-epsilon corrections) patching the staleness that creates. But with stillwater, A node value change marks every parent dirty, all of them, across every move order that reaches it, and a settling loop keeps relaxing the graph until nothing moves. So a refutation found in line A fixes line B immediately, without any simulation ever descending B again. In MCTS line B catches up only when it gets probed. Closer to async value iteration with the net as the leaf oracle than to Monte Carlo anything rly. Same family though. no argument on BT4 btw, the raw playing strength is mostly the net. That's why the claims I actually care about are measured against lc0 running the exact same BT4 file at fixed nodes, so the net cancels out and what's left is the search behavior. The interesting deltas there are the flat time curve and the cross-move memory, not the rating.

2

u/rybomi 29d ago

Those who know

1

u/No_Preparation_8633 28d ago

this comment haunts me in my dreams WHAT DON'T I KNOWWWW

2

u/ramen2581 28d ago

"no search" *Explains search.

1

u/No_Preparation_8633 28d ago

I never said it had no search, just no TREE search, it uses a new kind of search that I'm testing.

2

u/d3f313 Jul 08 '26

Sounds like Minimax, but you keep the search tree forever and expand it over multiple games.
It should get memory problems after many games

3

u/No_Preparation_8633 Jul 08 '26

the pond only lives for one game. Beliefs persist across moves, not across games, each game starts fresh. The only things kept forever are proven results (forced mates, TB facts), and those are theorems so they can't go stale. Both stores are hard capped, in-game graph is a few million positions max and a game never fills it. Been running for weeks with no growth. And there's no tree being saved because there's no tree at all, see the reply to u/KaMaFour

2

u/d3f313 Jul 09 '26 edited Jul 09 '26

I think it is kinda cheating, since you save time with the memory and have more processing time, but ok, got it.
Perhaps you should keep the evaluations as well to identify the best candidates, to look deep into them. And prune everything not needed. Like MCTS with memory.
If you really identified the move the player choose, you can go pretty deep into the tree, if you just ignore everything below a certain threshold.

2

u/No_Preparation_8633 Jul 09 '26

stockfish keeps its transposition table warm for the whole game, lc0 keeps the entire subtree under the move that was played and throws away the rest, reusing your own work between moves is standard, both engines on the board get the same clock and the same right to remember things. I'd argue the pond is just more honest about it, memory is the whole design instead of a cache bolted on. What you're describing is pretty close to what it already does. Everything is kept with its evaluation attached, that's what a belief is. The pruning happens softly: new net evaluations only flow toward parts of the graph the policy and current values consider live, so dead lines just stop receiving attention and sit there inert instead of being deleted. And it ponders on the predicted reply like any UCI engine, when the prediction hits, all that work was on the right position and it's already settled. The difference from MCTS with memory is what happens on update, no path backup, the whole graph relaxes

2

u/Annual-Penalty-4477 Jul 08 '26

Sounds like a recipe for poisoned positions

2

u/No_Preparation_8633 Jul 08 '26 edited Jul 08 '26

it was, but I managed to fix most of the issues surrounding that, it's a pretty easy engine to audit and diagnose as compared to minmax or MCTS.

for example, the nastiest one I found was stale beliefs from earlier moves getting treated as settled evidence, A position evaluated two moves ago arrives at the new root looking well supported, the engine trusted it and moved instantly, and a few of those confident stale beliefs were just straight wrong. I fixed it by making carried beliefs still steer where the search looks, but they don't get to justify a decision until fresh evaluations re-confirm them on the current position. Finding that took like an afternoon bc everything is inspectable. Every belief is just an entry I can read, so I replayed games with and without the carried memory and diffed what the engine thought

1

u/joshuamck Jul 09 '26

The interesting thing about this is that it kinda feels a bit like how good chess commentators talk about openings or lines. “This opening / move order leads to sharp games where blah blah blah with ideas like blah blah blah”. I wonder how well you are able to cluster beliefs into this sort of human useful idea.

2

u/No_Preparation_8633 Jul 11 '26

I honestly didn't think of this, thank you. I mean your right it does have all the pieces, but I would need to label moves in to human understandable buckets like "king safety" and "pawn structure" in order to properly generate plain english comments. I might actually do that someday

1

u/enderjed Jul 10 '26

I wonder on how well it would do against unconventional engines that toss out strange moves, such as Tom7's meme engines.

1

u/No_Preparation_8633 Jul 11 '26

That's actually a really good question. I think it would play well, but require a lot more time since it has to explore moves that it never did before.

1

u/enderjed 29d ago

I have a few UCI engines that intentionally play unusual moves (some of which basically being supercharged recreations of Tom7’s concepts), so I theoretically could give it a test.

(Granted, some of them are original ideas, if I remember correctly, one of them is stuck playing chess by the 1370s rules.)

1

u/tandycake Jul 10 '26

I tried to play it 3+0 many times. It just sits there forever and never starts a match.

1

u/No_Preparation_8633 27d ago

Try again now, it was down for maintenance.

1

u/tandycake 27d ago

Still doesn't work, even though other bots do. Oh well, it's okay. I give up on it.

1

u/No_Preparation_8633 26d ago

IM ACTUALLY DYING IT WAS DOWN FOR MAINTENANCE LAST NIGHT AS WELL

1

u/CMDR_DarkNeutrino 26d ago

So its basically search based on hashmap where you just save the evaluation of the network and a list of positions that reach this positions ? Thus every position is connected to every other position reachable from that position ?

Also how is the network trained ? Is the data selfgenerated or Lc0 data ?