Shedletsky's Bits: A... Blog?

A random walk through manifold space
Post

Sword Fight. On The Heights. In A Browser.

A bright ring of flames frames a dark center.

SFOTH Ice Dagger

Christmas Eve, 2008. I am sitting at my grandmother's kitchen table, laptop open, absolutely cooking. Sword Fight on the Heights IV is flying together. I built a fantastic arching bridge, Escheresque. I built a secret hidden trap that makes the arena floor vanish into thin air, treacherous! I crafted blades of light and wind, both of which can send you flying, depending on which end you are on. The game is a huge improvement over the tech demo/test level I built the prior year to test the sword weapon we added to Roblox Brick Battle levels (little known fact: at one point all of the multiplayer games on Roblox were made by me, as sort of a side job to my software engineering responsibilities at Roblox). That was the original Sword Fight on the Heights (SFOTH) level, and this new incarnation would become the canonical version for the next 18 years (SFOTH4). Don't ask where SFOTH2 & SFOTH3 are. All I can say is that the numbering scheme made sense at some point.

A SFOTH Screenshot from April 2009

SFOTH Firebrand

These past two weeks I've been revisiting The Heights. Only this time it runs in a web browser. Well it runs there now. It started out with a little plastic block man who walked like he was receiving instructions by mail. But that's fine. I built a career out of telling little plastic block men what to do.

This is the story of how I ported Sword Fight on the Heights IV out of a Roblox place file, spent an unreasonable amount of time making its movement feel good, and then got distracted teaching robots to fight over the swords. A surprise is that rebuilding the game has also taught me things I can bring back to the Roblox version—including a couple of bugs in code I wrote myself, a long time ago.

SFOTH Venomshank

Why do this to myself?

At this year's Roblox Developers Conference (the first one I've been to in a while—wow, production value went stratospheric), I heard about Roblox's work on letting people play games in a browser without installing the client. This is a huge deal. When I managed Roblox's advertising years ago (yes, also a side job), convincing somebody to download and run an executable was the biggest hole in the funnel: the point where people decided to play something easier or less dodgy-feeling. "DO YOU WANT TO INSTALL THIS STRANGE .EXE ONTO YOUR MACHINE?" Nah. A link that takes you straight into a game is a very different proposition, and the distribution possibilities could be game changing for Roblox.

SFOTH Illumina

Around the same time I was playing SFOTH on Roblox again for the first time in a while, helping get it set up for the platform's twentieth-anniversary event. I still love the place. I was less in love with how the sword fights felt. Movement was mushier than I remembered, and a twitchy game involving melee combat needs to covet every frame of latency. Roblox's netcode has evolved a lot since 2008, but SFOTH's use of it has not. It's not exactly bitrot. It's a time capsule. Also there might be some bitrot.

In a moment of titanic hubris, I asked: what would SFOTH feel like if I rebuilt it for one purpose, with control over the simulation and the messages between server and player?

That is a much narrower question than “can I write better networking than Roblox?” Roblox has to make an extraordinary range of creator-built worlds work on an extraordinary range of devices. I only had to make this one sky full of falling people and deadly swords work.

The first 80 percent took half an hour

God I miss user banner ads on Roblox.

I started with an .rbxl file and a prompt to my godlike programming friend Codex GPT6-Astra (xhigh) (he/it might need a nickname): make a high-performance browser version of this game; here is my preferred stack. This is the dangerous phase of AI-assisted coding (we just call it coding now, keep up) when fifteen or thirty minutes of work produces something that looks 80 percent finished. It is not 80 percent finished. It has completed the visually persuasive part of the first 20 percent. The trap is set.

The level appeared. A character could run around it. His head was a little big. We can fix that. We are head experts. Most of the textures and animations made the trip. The skybox took more fussing than I expected. The "ring of fire" vortex teleport texture UVs were wound backwards, resulting in them only drawing when viewed from the bottom. I probably have the opposite bug in the original, come to think of it. But as a single-player walk through a Roblox level, it was astonishingly quick and mostly correct.

SFOTH Touchstone

The two priorities from the beginning: 1) run on my existing server stack and 2) run with high performance. The browser client runs the game simulation in WebAssembly (WASM). The authoritative server is in C# and uses BepuPhysics, which I had zero problems with, but I suspect could be 2-10x faster if it were native and if we built around it correctly. SFOTH is not physically demanding, so it wasn't a bottleneck here. I did not build a general Roblox replacement. I built the collision, moving platforms, conveyors, force PID controllers, ragdolls, swords, and other specific pieces this game needs with incredible ease. The client and server share the relevant simulation rules, which matters when a client tries to predict where its own character will go.

Then I connected two players. The character motion was terrible. My guy visibly stuttered while walking. Stairs made the camera jump around. Even free falling was bad with prediction out of sync with network updates coming in.

Separate local multiplayer captures from a September 19 archive and the September 28 client, both at spawn #50 with the same direction reversals. The archive postdates the first broken prototype; this is a visual comparison, not a direct latency measurement.

Sixty frames per second of bad movement

The browser was drawing plenty of frames. The network round trip on my local test was small. Neither fact meant the player was seeing recent movement. In one early five-minute run, the browser's physics worker took a 204 ms median from receiving work to returning a predicted state. This is a billion years; and the Internet was not causing most of it. This is how you know your code is terrible. The network should always be the bottleneck.

“Jitter” is an annoyingly broad diagnosis and the bane of game engine programmers everywhere. It's like the opening to that Tolstoy novel: every performant piece of code is the same; every janky piece of code is uniquely miserable. Jitter can be caused by a late server packet, a backed-up worker, a camera sampling yesterday's pose, a platform contact that disagrees between client and server, or a new state overwriting a newer one (this is not bad grammar, this is the quintessential headache of client/server code). If you fix the wrong clock, the game can become very efficiently jittery.

SFOTH MedKit

To fix it we added a lot of observability to the pipeline, basically recording everything that happens on the client and the server. We put bookkeepers all along the path from physical input to server execution to prediction to render submission. I've never written code like this before, but I think it is basically required if you want ensure minimal latency through the whole system, which was our stated goal.

My AI got stuck here for a long time, digging around in a local minima. I left it running overnight and it worked for fourteen hours, producing basically nothing. The answer is basic software engineering: break down the problem into smaller (ideally atomic) pieces, add logging, ask the right questions, repeat.

SFOTH Darkheart

The eventual solution was a stack of smaller decisions:

  • Don't replay the entire arena to predict one person. The local client needs nearby bodies, relevant geometry, moving supports, tools, and portal exits. It does not need to resimulate two distant opponents bumping into each other before it can draw my next step.
  • Don't make remote players wait for my prediction worker. Their authoritative updates have a separate presentation path. Opponents can look smooth even while my own physics is doing a correction.
  • Don't queue old news. If a newer server snapshot arrives while the worker is busy, replace the waiting snapshot with the newest one. Keep every ordered input and reliable action; discard replaceable state that has gone stale.
  • Let a sword look and sound like it swung when I click. The client can start the authored pose and sound immediately. Only the server decides whether an attack was accepted or somebody was hit. Prediction is allowed to be convincing; it is not allowed to award itself a kill.
  • Don't use the physics engine for prediction. This might be specific to my implementation, but motion prediction with the physics engine we chose was taking ~14ms P95, pretty much half of the latency period I was trying to conceal. I replaced it with some dumb linear algebra in TypeScript. This is the piece I would design the whole engine around in the future, if I were making a game that required more physics.

SFOTH Windforce

A schematic of the measured worker backlog and the later scoped prediction path. The two runs had different workloads; these are worker turnaround times, not input-to-photon latency.

WebRTC is really cool

Back in my day, you couldn't send or receive unreliable, unordered game messages from a web browser. Now you can, using WebRTC data channels, and this unlocks realtime games. Basically, messages can use reliable or unreliable delivery, and they can be ordered or unordered.

Historically, browsers mostly used HTTP over TCP, though HTTP/3 now uses QUIC over UDP. TCP delivers data in order. This sounds great, but in a realtime game it has a cost: when a packet is lost, newer data in that stream waits while the missing data is retransmitted. Even if that newer data describes where a player is now, it gets stuck behind news about where they were. This jacks up latency through the pipe and can make realtime games play poorly.

On a UDP route, an unreliable WebRTC channel is kind of like spray and pray. Messages go out. Maybe they get there. But we don't wait for a missing update to be retransmitted. So for things like tracking moving objects in a videogame, we get newer updates as soon as they are available, and then on top of the lossy part we try to build a universe that feels consistent.

Network programming is a swamp of domain knowledge that will break your code, and there are a lot of caveats. Some networks block SFOTH's direct UDP path, in which case WebRTC can use what is called a TURN relay. The relay may still use UDP; if UDP is unavailable, it may use TCP instead. If you see the degraded-connection message when you try to play the new SFOTH, you are getting a less responsive experience Cry.

SFOTH ShieldSphere

We run the main server loop at 60Hz. We send lossy network updates at this rate also, which I think is pretty high for a game server. Remote presentation can consume those updates independently while local restore-and-replay remains capped at 30 Hz. A faster state feed would have been silly if each update meant sending an entire room-sized snapshot forever, so the server can send a lossless change against a frame the client has acknowledged fully receiving. In one controlled test with twenty moving players, a 4,193-byte full frame became a 1,463-byte delta. Multiply the savings by number of players in your server. It's a big deal. Past 60 Hz I think is solidly in the realm of diminishing returns, but I might experiment with high frequencies later. I wonder about the state of the art here and whether any game goes full asynchronous with no indexed frames at all.

Our game server is lean and mean. It runs on a shared machine that hosts other games, a noisy database, and a bunch of websites. We've stress tested it up to 600 concurrent users. With a small amount of effort 1000 players/server is probably possible.

SFOTH Battle Armor

Combat gets its own little fast path too: a short notice can report an accepted attack or actual damage before the next full snapshot has been assembled and decoded. The full authoritative state still wins. At the other end, we tested delayed and missing state packets rather than assuming my nice local network was representative of anybody else's.

A schematic player and packet timeline: reliable swing, acknowledged full frame #81, dropped #82, and #83 decoded from #81. Byte sizes come from a controlled serialization fixture.

This is the part where I would once have written that Roblox cannot do these things. That would be unfair, and by 2026 it would also be wrong. Roblox's Server Authority work includes server-owned simulation, client prediction, rollback, and resimulation. Roblox creators also have unreliable remote events for transient data. The interesting difference is that a standalone game lets me choose the exact snapshot format, cadence, buffering policy, and simulation scope for this one game. Roblox is solving a far larger platform problem. I get to cheat by specializing. (Not at sword fighting. The server checks that.)

SFOTH Ghostwalker

I think the browser version of SFOTH feels sharper than the Roblox version in close fights. I do not have a fair, same-device, same-connection comparison proving that its netcode is universally “better than Roblox.” My server is in one region; Roblox's infrastructure is not. If you live more than several thousand miles from Los Angeles, California, your play experience might be much worse. My character also still handles some stairs and ladders less faithfully than the original. It's a hard detail to get right.

I added bots, and then the bots took over the project (all your base are belong to us etc)

They have a plan. It mostly involves stabbing you.

I needed opponents to test the networked game. I also suspected an empty SFOTH server would be a poor welcome for the first human visitor. So I added bots, and building them became its own project. It's like I'm running Boston Dynamics but with swords.

The bots do not cheat. They connect as actual game clients, sending ordinary inputs and receiving player-visible snapshots over WebRTC. They use the same controls and follow the same health, combat, inventory, and respawn rules as people; they cannot reach into the arena state to teleport, collect a sword, or declare a hit. They do have authored knowledge of the map, and because the bot service runs close to the game server, their connection is much quicker than a distant human's.

SFOTH ShadowSphere

There are Five Models (creepy Cylon music)

There are five levels (tiers) of SFOTH-bot: I to V. I use roman numerals because they look cooler. The tiers differ in reaction time, targeting, equipment decisions, and movement ability. Tier I bots are awful and if you die to one you should feel bad about yourself. Tier V bots represent the best bot that I've been able to program so far. Experienced SFOTH players probably won't have that much difficulty with them, but they might occasionally surprise you with some crazy tactic and can be very dangerous when geared up. Tier V bots are expected to consider loot, healing, hazards, sword combinations, and whether a fight is worth having. Expected is doing some work in that sentence.

A yellow Roblox avatar with a sword faces Clockwork Omega beside the circular fire hazard on the original Heights map.
We had the craziest bugs with bot navigation

The first hard problem was getting anywhere. SFOTH is an obstacle course pretending to be a sword arena. There are conveyors, jump pads, disappearing floors, spinning blades, teleports, narrow landings, and bridges made of wobbling platforms. A static navigation mesh can say two spots are connected. It cannot tell you whether the bridge will be mostly horizontal when a bot's feet get there.

Recorded bot-lab replays: the level bridge, then the harder uphill stairs. Both crossings succeeded using the game’s shared physics; these are not live WebRTC captures.

The wobblies (you know, the wooden platforms that tilt when you stand on them, near the center of the map) were especially effective bot disposal units. Early attempts sent bots over them at almost any angle, which went badly often enough that I stopped trusting a static “crossable” label. The useful insight was to begin with the boring case: cross when the bridge is near its resting orientation. Then widen the allowed tilt only where actual trials show the bot can still board, balance, and leave. A path is sometimes available and sometimes not. That sounds obvious until you have watched a robot repeatedly insist that a plank is a road while it rotates under him.

Schematic side view of the bridge orientation and crossing window. The recorded crossings below show the actual arena geometry.

The new SFOTH leaderboard tracks player strength (and bot strength) essentially by KDA ratio. So dying is bad. Also in SFOTH, loot is very powerful and well-looted players will usually dominate. So if you are trying to create a strong bot, you need to be able to: 1) not die and 2) get loot ASAP (while not dying).

A dark top-down map of the Heights with blue circles marking where Tier V bots were last grounded before falling; larger circles show more falls.
The death map from that diagnostic run: 108 falls across 855 solo navigation trials. Blue circles mark the last grounded position, sized by count.

One problem with my early bots was that even just navigating the map without falling off was a challenge. I started keeping a death map: for each fall, plot the last bit of ground the bot was standing on, and make the dot bigger where failures pile up. This made it easy to see which parts of the map we need to work on with regards to navigation. One Tier V navigation run made 855 solo spawn-to-item-stand trials, each 90 simulated seconds long, and logged 108 falls. We eventually got it down to 0. Then we got greedy and further accelerated Tier V bot looting with Illumina lunges and Ghostwalker/Jump Pad shenanigans, allowing Tier V bots to zip around the map at low risk of being murdered.

Conceptual candidate trajectories and landing checks. The following video is a recorded bot-lab flight, not a capture of the search.

These two swords break the rules of how the map can be traversed. Ghostwalker lets its holder float. Illumina can launch a character like a missile. A bot can sometimes use those abilities to cross gaps or reach loot that walking cannot. For selected routes, a background planner runs short branches of the real game physics, varies launch timing and direction, rejects fragile landings, and checks the chosen plan again against a fresh snapshot before sending ordinary inputs.

The wireframe and game view trace the same recorded bot flight, from the western platform to Touchstone. This is a shared-physics lab replay, not a live WebRTC run.

Some routes now work, including a particularly absurd sequence that swaps between Illumina and Ghostwalker in flight. But some searches find no stable landing, some launch timings break under delay, and opponents can still interfere. An Illumina jump/lunge executed on a narrow beam has about .005 radians of tolerance if you'd like a safe landing. The failures are at least interesting to watch.

SFOTH Illumina SFOTH Illumina SFOTH Illumina SFOTH Illumina

I think it might be possible to create a superhuman Illumina bot that flies around the map, murdering people with the holy sword of an avenging god. The current one isn't there yet. I have some ideas, and I might return to it.

SFOTH Illumina SFOTH Illumina SFOTH Illumina SFOTH Illumina

What made a bot better? Experiments (yay science Cool)

It is easy to watch one duel, announce that a strategy is genius , and then discover that it spends the next five-minute match falling off the map while looking for a hat (this is a lie. there are no hats in SFOTH. who told you about the hats). We built a repeatable match lab using the same simulation, varied one policy at a time across matched random seeds, and recorded replays so a score could be investigated. It runs at around 10x real time in 16 parallel copies, giving us a lot of match experience to learn from. Across arena matches, navigation trials, and sword duels, we've now logged more than 2,700 simulated hours of SFOTH—about 113 days of continuous game time. Count each bot's time separately, and that's over 36,000 hours of bot play—about 4.1 years of stabbing each other and falling off things. This would be hard to do in Roblox. Or at least, I have no idea where to start.

Some ideas survived. A higher-level bot had been so concerned about finding equipment that it sometimes declined a fight even when it had no reachable gear goal. Requiring an actual committed route before suppressing pursuit helped. Pressing an opponent with a sustained sword attack also worked better for some tiers than their more evasive style.

The more theatrical successes include a Tier V bot diving you with an activated Ice Dagger from a height you can't see, or chaining multiple sword attacks in a row, ending with a Ghostwalker kill to level that sword up.

Other ideas were less cooperative. A Shadow Sphere ambush could dominate an isolated first-life duel, then fail to show a dependable benefit in a full fifteen-bot match with normal loot. An extra lunge produced dramatic wins in controlled sword duels but a much smaller, noisier improvement in the full arena. Sometimes a policy is good and the bot almost never finds the item needed to use it. A zero on the scoreboard cannot tell those stories apart.

What the experiments taught us

The bigger experiment ran from September 25 to 27: 3,220 thirty-minute matches, 15 bots per match, normal arena loot, and 67 randomized settings. That is 1,610 simulated arena-hours and 48,300 bot-match records. We tested general behavior in 1,312 matches, preparation/planning in 1,072, and utility decisions in 836. The table includes all 23 settings with evidence after correcting for the 67 tests, plus four useful inconclusive results. The movement, memory, and item-strategy rows add eight focused comparisons.

The first 27 rows report adjusted changes on the 0-100 leaderboard scale, with 95% intervals. Toggles compare on versus off; ranges compare the stated endpoints. The three randomized groups had different fixed prerequisites, so their effects are not additive. The final eight rows are separate experiments with their own builds and conditions; their intervals are exploratory and were not part of the 67-setting correction.

Experiment / comparison Result What we tested and learned
Loot preparation, 0 to 1 +10.52 (+9.93 to +11.12) Higher greed emphasized gear and safer loot decisions. Score rose across the tested values, most sharply from 0.75 to 1.
Noticing falls with Touchstone, 0 to 1 +6.58 (+5.97 to +7.19) Varied the chance of noticing a fall and attempting a Touchstone rescue. More reliable noticing helped.
Reflexes, 0 to 1 +4.97 (+4.39 to +5.56) Compared slower and faster reaction/attack presets. Most of the gain was already present at 0.75.
Seeking BattleArmor +4.72 (+4.33 to +5.12) Enabled deliberate armor pursuit. This was one of the strongest beneficial utility decisions.
Preparing equipment before fighting +4.45 (+4.16 to +4.73) Enabled preparation in the zero-greed test group. Gearing up improved score in that context.
Escaping with Touchstone +3.08 (+2.68 to +3.49) Enabled the threatened-escape decision. Touchstone use improved score in the tested utility mix.
Seeking Touchstone +3.06 (+2.66 to +3.47) Enabled deliberate Touchstone acquisition. Seeking it helped, separately from the decision to activate it.
Seeking healing +2.95 (+2.58 to +3.32) Enabled healing pursuit. Bots died less often and achieved a clear score gain.
Footwork, 0 to 1 +2.11 (+1.52 to +2.70) Compared movement skill settings. The gain appeared at 1; the intermediate settings showed little score change.
Lunge chance, 0 to 1 +1.83 (+1.23 to +2.43) Varied how often the bot attempted a lunge. Higher chance generally helped; this did not resolve the best lunge policy.
Targeting, 0 to 1 +1.73 (+1.14 to +2.33) Varied target awareness, prediction, and stickiness together. The useful gains appeared mostly at 0.75 and 1.
Retreating from a losing duel +1.17 (+0.78 to +1.56) Enabled retreat when a duel looked unfavorable. KOs fell, but deaths fell more, improving score.
Using MedKit to heal +0.69 (+0.28 to +1.11) Enabled an additional MedKit-use decision. It produced a modest benefit; seeking a kit is a separate behavior.
Advanced drop routes +0.66 (+0.38 to +0.94) Allowed advanced downward routes. The benefit was modest and measured in the zero-greed test group.
Chaining attacks across swords +0.47 (+0.10 to +0.83) Enabled sword switching to chain attacks. The overall gain was small and did not repeat clearly in both chronological halves.
Automatic backstepping after attacks −18.56 (−18.92 to −18.19) Enabled the current backstep controller. It roughly halved mean KOs and increased deaths: a large loss.
Unconditional ShieldSphere pursuit −3.39 (−3.79 to −2.99) Enabled deliberate shield chasing. More utility pickups accompanied fewer KOs and more deaths; pursuit had a substantial cost.
Avoiding loot near a threat −3.30 (−3.67 to −2.93) Blocked looting when a foe was within 25 studs. The broad guard suppressed pickups and hurt score.
Coordinated goal planner −1.92 (−2.21 to −1.64) Swapped the whole planner at zero greed. The bundle lost score; this does not identify which planning decision was responsible.
Jump-based combat evasion −1.21 (−1.60 to −0.83) Enabled the current jump reflex. It hurt score; this result applies to that implementation, not every possible dodge.
Flourish trigger range, 7 to 11 studs −1.04 (−1.61 to −0.48) Expanded the distance that triggered flourishing. Values 7-10 were similar; the widest tested range was worse.
Defensive ShadowSphere decision −0.87 (−1.28 to −0.46) Enabled cloak defense, but the flag also skipped ordinary utility fallback. The loss cannot be assigned solely to cloaking.
Retreating while holding an active shield −0.58 (−0.94 to −0.22) Enabled the shield-held retreat refinement. Its small loss is not a verdict on shield acquisition or every shield use.
ShadowSphere ambush +0.19 (−0.18 to +0.55) Unresolved: enabled cloak-and-strike. Ordinary arena matches did not establish a reliable score benefit.
Shielding against a crowd −0.34 (−0.73 to +0.05) Unresolved: enabled crowd-triggered shielding. An early negative estimate did not survive the completed study.
Illumina travel +0.03 (−0.35 to +0.41) Unresolved: enabled the travel pilot. The core group recorded 3,741 flights, but lacked destination-arrival and travel-payoff measurements.
Ghostwalker escape routes +0.24 (−0.14 to +0.62) Unresolved: enabled the low-health escape pilot. Zero launches occurred, so this screen could not judge the actual escape maneuver.
Navigation fall fixes Falls 41 to 1 in 93 paired five-minute solo trials; 2 falls in 93 fresh follow-up trials. Compared old and corrected navigation across all 31 spawns, without opponents. Falls dropped while distance traveled increased; this measures navigation, not combat strength.
Wobbling bridge controller 189 of 192 canonical crossings completed; 0 falls, 3 timeouts. Tested four crossing directions with live input cadence. Normal crossings became reliable, but uphill balancing could still stall; extreme tilt remained harder.
Strict equipment preparation Early armor 3/24 to 8/24; paired score change −1.98 (−10.34 to +6.38). Eight paired ten-minute matches per policy. The room-wide preparation change improved early gearing and survival, but reduced fighting and did not establish a score gain.
Opponent inventory memory −0.50 points (−5.83 to +4.87) across 16 paired fifteen-minute seeds. Toggled only Tier V memory in normal-loot matches. Item use changed, but the broader memory system showed no reliable leaderboard lift.
Focused Windforce/Touchstone memory rule 200 wins each in 400 mirrored duels; also identical outcomes in 16 paired ten-minute arena seeds. Retested the narrower eight-point Windforce discount on a later build. It changed a valuation score but no measured action or outcome in these scenarios.
Ice Dagger strategy, Shadow Sphere on −0.29 points (−8.59 to +8.02); 5/8 seeds helped. Eight paired fifteen-minute matches with normal loot: both policies scored 67.53 versus 67.82 for Shadow only. 41 dagger pickups and 249 attacks did not establish a gain.
Shadow Sphere strategy, Ice Dagger on +2.66 points (−1.78 to +7.10); 6/8 seeds helped. The same eight pairs: both policies scored 67.53 versus 64.87 for Ice only. 12 sphere pickups and 111 activations accompanied an unresolved favorable trend.
Both item strategies versus neither −2.08 points (−10.12 to +5.96); 4/8 seeds helped. The same four-condition test: neither policy scored 69.61 versus 67.53 with both. The combined strategy did not establish a score benefit.

We have more than one match to go on. In a separate set of 60 mirrored, first-life duels on the real map, the Tier V Shadow Sphere ambush won 43 fights (one draw), versus 28 wins (four draws) when the bot never used the sphere. That looked pretty convincing.

The extra lunge got a similar reality check in a separate 36-seed, five-minute full-arena test: 697 KOs / 762 deaths with lunges, versus 636 / 772 without. That was a mean gain of 0.65 performance points, positive on 22 of the 36 paired seeds. Much less impressive than the sword duels. The replay below is an earlier, 90-second match with a Shadow Sphere granted at spawn; it shows why we kept asking the question, not why we called the policy a winner.

Paired seed 0, 15 bots, 90 seconds, sphere granted at spawn: level V ambush bots recorded 10 KOs, 5 deaths, and 14 sphere uses, versus 5, 6, and 8 for hold-only. This nine-second excerpt is a separate trial from the eight-seed, 15-minute comparison above.

The bots are a work in progress. That is part of their charm. They are also my most demanding playtesters: tireless, willing to run off a cliff for a data point, and incapable of filing a useful bug report unless I write the telemetry first. I think they are more fun to play with than walking around an empty server waiting for another player to join. Try it and let me know!

You can file a useful bug report when you play. I'm sure there are a million edge cases the bots won't find; human playtesters have a gift for those.

SFOTH Firebrand SFOTH Ice Dagger SFOTH Venomshank SFOTH Windforce SFOTH Ghostwalker SFOTH Illumina SFOTH Darkheart

Oh hey, one more thing.

Making SFOTH bots was so fun that I'm thinking about a bot SDK, so people could write their own and run them on shedletsky.com servers (maybe in a special bot arena with its own leaderboard). With modern programming tools, the loop is pretty fun: dream up a crazy SFOTH strategy, have an AI program it, run matches, inspect the data, and repeat.

I'd like to gauge interest in this.

Poll

What is your interest in developing a SFOTH murder machine?

Log in to vote.

See results

TL;DR:

Once upon a time I made a game with swords in it. Then I came back to it and made it 20% cooler. The feature creep on this project was real.


Early reviews from people who absolutely did not say this

My imaginary marketing department is thrilled.

Fake profile review attributed to Elon.Musk: Can you delete my account? I’ve been playing this game all day and I’m addicted.
Fake profile review attributed to asimo3089: John Shedletsky is a jerk and his games are bad.

SFOTH Sword

Comments

No comments yet.

Log In or Sign Up to comment.