September 17, 2026 - input ordering and blood-spell healing
This update fixes a few cases where the client could lose or repeat an input around a game tick:
- Clicking Attack now replaces a queued manual spell while keeping configured autocast.
- Eating and then equipping a two-handed weapon in the same tick works when the food frees the space needed for the switch.
- Removing two pieces of equipment in one tick keeps both changes and puts each item in its own inventory slot.
- Using an item on another inventory slot no longer also eats or equips the target when the mouse button is released.
Blood-spell healing now uses the equipment worn when the spell was cast. Switching away from Zuriel's staff before the hit lands keeps its healing bonus; switching onto it after casting does not add one.
You can also remove and restore the arena trees. Removing them opens their tiles for movement and updates the minimap. Restoring them waits until both fighters and any dropped items are clear of those tiles.
August 2026 - the Elo league, and why teacher165 earned the spot
The big thing that happened since the last post is that I finally built a proper ranking system. For a long time I was comparing checkpoints in small matched screens, which works but only ever tells you "better than the one it fought". I wanted a real ladder: everyone fights everyone, once, and the numbers come out of that.
So I ran one. Every compatible checkpoint I had plus the scripted opponents, all in one round-robin league: 313 entrants, every pair played one full fight, 48,828 fights in total, classic Elo with a K-factor of 32 and everyone starting at 1500. Nothing fancy - it's the boring kind of Elo - but it turns a pile of "probably fine" checkpoints into an actual ordering.
- 1st - teacher139-no-opponent-prayer-all - 1890.6
- 2nd - teacher111-prayer-vlsordinary-from110-alpha075 - 1833.3
- 3rd - teacher165-ranged-multigear-prayer-from154 - 1797.2
- 4th - teacher116b-safe-ward-from116a - 1792.9
- 5th - lr050-ancestry20-r01 - 1790.4
One thing worth knowing before I quote prayer numbers at you: against a skilled opponent who genuinely mixes styles, correct prayer settles somewhere around 33-35%. You're reacting at least a tick behind, you can't predict everything, and the other side knows it. So 50%-ish is not a bad floor in absolute terms - what makes the top checkpoints stand out is that they sit so far above it. The only reason that's possible is the meta: magic is the dominant damage source, so everyone throws a lot more magic than anything else, and if you can bet on "he's probably casting", you can pray against it far more often. And it cuts the other way too, though less obviously: Zuriel's staff is a 4-tick weapon, just like the DMM version of the staff, and it's naturally strong - a cast often carries enough expected damage that casting through Protect from Magic can still be the right call despite the damage reduction. Protect from Magic reduces damage in PvP; it does not make the spell more likely to splash.
The interesting part is that the league winner is not the one I shipped. First place is teacher139, and its name tells you exactly what's wrong with it: it's an ablation that literally cannot see the opponent's prayer. It still wins fights - it puts out 95.4 expected damage per fight and went 245-67 - but it only reads prayer correctly 52% of the time, which is above the ~33-35% you'd expect against a proper style-mixer but miles below the 80%+ the top checkpoints reach on the same field. It wins on raw output, not on reading. That's not a useful practice opponent, that's a DPS check.
Second place (teacher111, 1833.3) is closer to being good: 79.2% prayer correctness and a 92.1 expected-damage edge per fight. But teacher165, one spot lower on rating, does the things I actually care about better: 84.9% prayer correctness - the best of the top five - with a 91.1 expected-damage edge and the best realized edge of the top three (+94.6). And its attack mix is properly balanced at 66% magic / 13% ranged / 21% melee instead of leaning on one style. Rating-wise the gap between 2nd and 3rd is tiny. On the things that make an opponent worth practicing against, 165 wins. So 165 is the model on the site now.
There's also a practical reason: teacher165 is from the newest training line, and it's the one with the browser contract actually verified against the Java checkpoint - I replayed real Java fight snapshots through both and they agree within 1e-5. A model that ranks 3rd in a simulator but provably behaves identically in the browser beats a model that ranks 1st and might not.
The road to teacher165
Since the last update I've been chasing one main thing: making the DMM bot read the fight properly instead of just outputting damage. The line from teacher148 to teacher165 went through a ranged multi-gear curriculum - teaching the bot to mix ranged pressure and believable staff/VLS gear fakes into its normal three-style openings - and a complete-history prayer head that reads a longer window of recent ticks instead of just the current one.
There were real hiccups along the way, and I'm not going to pretend they didn't happen.
The most humbling one: I trained a focused prayer head that looked amazing offline - 66% accuracy on untouched data, up from 19% - and then it completely failed to transfer to live fights, praying correctly 13.4% of the time. It had memorized the training distribution instead of learning timing. That whole branch got rejected. It taught me to stop trusting offline numbers that aren't backed by live fights.
Another one was quieter and nastier: a normalization repair I'd written earlier had silently zeroed five learned state inputs (attack, strength, ranged, magic ratios and magic deficit). The bot could still fight, but it had lost part of the information it needed to tell a boosted state from a drained one. That explained a confusing regression where a "broad improvement" run raised ranged usage while making the bot strictly worse at everything else.
And the elo league itself caught a degenerate case I'd have shipped otherwise: the #1-ranked checkpoint turns out to ignore opponent prayer entirely. Ratings are great, but they rank fighters, not practice partners. That's why the league became part of the selection process instead of the whole process.
The site also got simpler: I removed the old aim trainer section so the page is just the
NH practice client with the NH stake and DMM setups. And the GPU fight engine that runs
the league is now in the repo too, under fastsim/, so the whole evaluation
path is visible next to the site.
Why it started as a policy and became a neural network
When I first wrote about this project, the plan was a policy: something reactive that adapts to timing, pressure, and the player's rhythm instead of looping through a fixed rotation. That part of the idea never changed. What changed is what the policy actually is - the opponent on the site now is a trained neural network, not the simpler decision logic those early posts described. It reads 114 fight inputs every tick and picks from 86 actions. Where the older sections below still say "learned policy", that's me describing how the idea started, not what it is now.
The training idea is the same one I began with: many paired fights, with a rolling window of recent combat state recorded against each decision. That short memory lets rewards connect an action to future outcomes: a good freeze, a defensive gear swap, a delayed special attack, or a supply decision may only prove useful several ticks later.
What changed with the neural hard mode
Hard mode is now controlled by neural-network models for both setups. The selected setup decides which model loads: NH stake and DMM each use their own current hard fixture. The bot still reads practical NH state instead of private hidden information: prayers, health, supplies, distance, freeze state, weapon timing, special attack state, recent pressure, and visible gear context.
- NH stake Hard and DMM Hard are separate neural fixtures. Exact schema and parameter counts should be read from the current fixture files, not copied from old notes.
- DMM hard uses direct item/slot actions rather than broad style loadouts: weapon equips, shield swaps, selected unequips/re-equips, and explicit Gmaul, Voidwaker, and Vesta's longsword spec actions.
- Legacy compact DMM controllers, generic best-special fallbacks, and EV style overrides are no longer the intended hard-mode path.
- NH stake has the same idea for dragon boots: the model can learn whether taking them off is ever worth it from fight outcomes, not from a hand-written reward.
How I trained this version
The current direction is self-play. I train in loops, usually in groups of four, and feed the next data run with the best available version of the model fighting itself. That is important because I do not want the bot to only copy one fixed opponent or one old opponent. I still keep cohort fights around as pressure tests so the model does not forget common patterns while it learns from itself.
Current DMM rollout data keeps one row per real bot decision. Same-tick direct gear choices are stored as labels on that row, so the model can learn multiple gear actions in one tick without pretending that extra game ticks happened.
The cohorts are scripted styles used for evaluation and coverage: melee camping, repeated AGS spec pressure, mage-range alternation, mage-heavy and range-heavy pressure, staff or crossbow baiting, prayer-camp melee, panic eating, stand-under freeze, long-range crossbow, close fake switches, one-tick and two-tick fakes, and delayed reactions. They are not the final goal. They are checks that catch obvious regressions while the neural model learns a wider fight.
Fixing what the bot was learning from
Some of the early problems were not model size problems. They were measurement problems. One old reward path tried to infer damage from HP changes. That breaks as soon as food is involved, because a player can eat on the same tick they take damage and end up with a similar HP value. The training signal then says the hit did not really happen, even though it did. That had to be fixed by using the actual damage events instead of only looking at the final HP number.
Another issue was timing. If the bot sees a weapon, movement, or prayer change too early, it becomes unfair and also learns the wrong fight. It can pray or switch on the same tick as the player's choice, which makes it feel impossible instead of good. I have been tightening the observation timing so the neural model learns from the information a player could actually react to.
What I am watching next
The next interesting thing for me is whether more DMM training makes the model time vengeance trinket usage better, especially around specs and predicted burst windows. I am also curious whether the neural model starts abusing worn-gear weaknesses more naturally: not because I told it a specific item trick, but because it learns when an equipment penalty or bonus actually changes the outcome of a fight.
Difficulty setup
Older builds exposed several checkpoint difficulties. The current public build is simpler: Hard is the supported opponent, and the NH stake or DMM setup determines which neural weights are loaded.