The game

In-game footage. The melee was set up by script, including when the guns fire; the ships, shot, explosion and fort fire are the game's own.
POLYSEA is a naval strategy game set in the Caribbean of 1650. England and Spain are at war, pirates raid the sea lanes and a skeleton fleet roams at night. Serve a navy or raise the black flag: lead your fleet through broadside battles, board enemy ships with grappling hooks, recruit officers and post them to their stations, build up ports, open trade routes, and search the islands and wrecks for treasure.
| Item | Details |
|---|---|
| Genre | Naval strategy |
| Play | Single player / online over Steam |
| Platform | Windows |
| Language | Chinese (more languages planned) |
| Playable sides | England, Spain, four pirate clans |
| Engine | Unity 6 |
| Developer | TechNeko |
| Status | In development, test build v0.8.50 |
Features
Naval combat
Every gun loads and fires on its own. There are six kinds of gun and four kinds of shot, and wind, field of fire, weather and sight all shape a fight. Command the whole fleet from the chart view, or switch to the captain's view and take the helm yourself.
Boarding
Grappling hooks pull the ships together, gangplanks go across and the fight moves to the deck in real time. There are 29 boarding tactics, and every sailor is a person of their own on the field, blade in hand.

Crew
The captain and officers each have their own abilities and skills. Post them to the right cabins and give them weapons and treasures. Officers gain experience from the work at their posts and from their deeds.
Ports and trade
Six port buildings take a town from tents to palaces and forts. Merchant convoys earn along their routes, and in the harbour hall you appoint a governor and a garrison.

The world
Four powers, fog of war, weather and day and night, islands to explore, treasure to find and nets to cast. Unexplored sea is a hand-drawn parchment chart with every island drawn as it really is.

Online
Join friends over Steam and fight side by side on the same sea.
Development retrospective
The rest of this page is the game's development record, focused on the technical approach and how problems were solved.
The project began in the small hours of 23 September 2026. The first commit landed at 04:05, and by 04:58 there was a prototype in which you could sail a ship, trade broadsides, board and enter port. Over the following month Claude Code wrote the C# code and the shaders, while the human side set the direction, playtested and signed off, picked the art assets and vetted the sound.
The date on this article marks the day the project started; the content runs up to v0.8.50 on 24 October 2026.
| Item | Value |
|---|---|
| Development period | 2026-09-23 to 2026-10-24, 32 days, with commits on 30 of them |
| Git commits | 663 |
| Game code | C#, 259 files, 87,031 lines |
| Editor tools | C#, 32 files, 9,748 lines |
| Custom shaders | 12 |
| Sound | 410 recorded files, 191 sound slots |
| Network protocol | Version 24 |
The short version
- Every change needs a baseline. The sea went through 36 commits in under 23 hours and was then reverted wholesale. The rule afterwards: change one layer at a time, with a before-and-after comparison at every step.
- The agent can't see or hear, so the means of verification set the quality. Visuals rely on screenshots and frame-by-frame comparison, sound on loudness measurement, combat on automated rehearsal. A problem that can't be measured can't be properly fixed.
- Quantify what you can, but mind the sample size. Boarding battle balance ran through over a hundred simulated battles, and a conclusion drawn from 16 of them reversed with a different random seed.
- Passing in the Editor doesn't count as passing. Some classes of rendering problem appear only after packaging, so a self-test for the packaged build was added later.
- Expensive effects go where they are needed. Real waves failed twice under the top-down camera and only worked once confined to the low-angle captain's view.
The setup
| Item | Detail |
|---|---|
| Agent | Claude Code (desktop app), with the whole project as one continuous session |
| Model | Mainly Claude Opus 5.5, Claude Sonnet 5.5 for some stretches |
| Engine | Unity 6000.3.24f1, URP 17.3 (Forward+) |
| Networking | Netcode for GameObjects 2.13.3, Steamworks.NET, a custom Steam transport |
| Water | Stylized Water 3, plus custom shaders |
| Art | Synty POLYGON model series, Classic RPG GUI interface assets, Human Mega animation pack |
| The human's job | Set direction, playtest and sign off, pick assets, audition and review sound, make the final calls |
The whole UI is built in code (uGUI). The UI directory has 19,076 lines, making it the second-largest module after the world logic in World (23,328 lines). An agent changes code more reliably than it drags controls around in the Editor, and every change can be read line by line in git.
Timeline
| Date | Milestone |
|---|---|
| 09-23 | First commit, playable prototype 53 minutes later; close-up boarding and landing battles added the same day |
| 09-25 | Factions, seeded maps, quests; the sea switched to Stylized Water 3 |
| 09-26 | v0.2 sent to friends (73 MB); sound effects replaced with an asset pack |
| 10-01 | Tried real waves, reverted 13 minutes after the commit |
| 10-02 | Multiplayer feasibility audit: 44,084 lines of code, 8 major obstacles |
| 10-03 | The six phases of the multiplayer rework and the Steam transport, 53 commits that day |
| 10-05 | Sound requirements document: 185 sound effects, 13 music tracks |
| 10-09 to 10-10 | Four rounds of recordings wired in, music and ambience systems |
| 10-12 to 10-13 | KWS realistic water and storm seas, all reverted after 36 commits |
| 10-15 to 10-17 | Boarding battles rebuilt as real-time tactics |
| 10-19 to 10-20 | Parchment fog of war; one person in battle is one figure |
| 10-21 | Solved the race conditions on shared accounts in multiplayer |
| 10-22 to 10-23 | Captain's view, swell, cumulus weather, rain and fog |
| 10-24 | v0.8.50: pirate bounty jobs and tavern intelligence |
The sea: built four times, back to the start, then onward
Water is the most reworked part of this project. The default game camera sits about 340 m above the sea. That one premise decided almost every trade-off that followed, but we only really understood it after trial and error.
First version: drop the LOD mesh, the sea is one big quad
When Stylized Water 3 went in on 25 September, we first used the plugin's own ocean LOD mesh. Each LOD ring snaps to its own grid spacing, and adjacent rings overlap at the seams. The sea is translucent, so the water in the overlap was drawn twice, which put an extra horizontal band of colour over the shallows that also moved as the camera zoomed. On top of that, our sea uses unlit toon shading, so the relief the mesh provided was barely visible.
So the sea became one big quad, and the expressive work moved up into a custom SeaStrokes shader on a layer above it, which gradually gained the following:
- Ripples: two normal maps sampled at multiple scales and drifting in different directions, with toon bands of light and dark and fine bright lines computed from the sun direction.
- Mask: large-scale noise makes one patch of sea rougher than another, which hides the tiling.
- Glints: small bright points flashing on wave faces turned toward the sun, which disappear under overcast skies, in ship shadows and when the camera is too high.
- Shadows: the sea itself takes no light, so the shadows of ships, sails and islands are drawn onto the water by this layer.
- Depth fade: water depth is read from the depth texture and the whole layer fades out in the shallows, leaving the sand bottom and the shore foam visible.
Second pass: real waves, reverted 13 minutes after the commit
On 1 October we tried real waves: an 800 m square water mesh following the camera, with the same set of Gerstner waves computed on the CPU so that ships rose and fell with them. It looked good up close, but there were three problems:
- Short waves aliased on the 3 to 4 m grid and turned into regular patterns when zoomed out.
- The waves themselves were not very noticeable, yet the ships' rocking was quick and jittery.
- When the water surface dropped, the foam ring along the shore occasionally exposed the reef.
The producer's judgement was that this kind of problem would turn out to be more common than expected, and that it should wait until there was a really good approach. The code was reverted and remains only in the git history.
Third pass: realistic water, all reverted 23 hours later
On 12 October we imported KWS Water System to try it, and tried TENKOKU Dynamic Sky at the same time. The latter supports only the built-in pipeline; under URP it drew nothing and threw null references, so it was ruled out on the spot. The KWS numbers looked very good: measured in the Editor at 2560×1440 on an RTX 4070 Ti, the GPU cost only 0.3 to 1.7 ms more than our sea, VRAM use was 155 MB higher, and the CPU was unchanged. It was merged into the main branch that night, with a realistic/toon switch added to the settings.

Top row: the game's default 340 m camera. Bottom row: a low close-up. Left column: the toon sea of the time. Middle column: the KWS default preset. Right column: KWS tuned to a Caribbean palette. KWS is clearly better up close; at the height the game actually uses, the advantage disappears.
A string of patches followed:
- KWS waves are real geometry, so wave crests covered the range circles on the sea. All sea markers switched to one shader that raises vertices along the view ray, by an amount that tracks the current maximum wave height.
- Night was flat and dark. The moon is in the south and the camera faces north, so reflections could never appear; the only option was to darken the water colour and brighten the foam.
- Dynamic wave wakes, at a 260 m range and 4 pixels per metre, looked like ripples on a pond, the scale clearly wrong.
- After switching from KWS2 back to KWS1, the Editor crashed on entering Play, reporting
d3d11: out of range sampler binding point. The cause was a corrupted shader cache; movingLibrary/ShaderCacheaway fixed it.
At the same time storms were being built on the toon sea: whitecaps, breakers, bow waves, spray. After playing it, the producer reached two conclusions: seen from game height, KWS was worse than the original toon sea; and the toon sea had been broken along the way too.
The first revert went back only to just before KWS, so the whitecaps and foam added the same day were still there. What the producer wanted was the sea from before KWS and before all the sea work. Only the second revert, to v0.8.8, finished the job. From merging KWS to reverting everything took under 23 hours and 36 commits. The code is kept in a tag, and only three things stayed: rain that is fine at any zoom, slow weather transitions, and slower light shafts through the clouds.
What also stayed was a set of rules, each one paid for by a rejected approach:
| Rule | Why |
|---|---|
| No cellular textures for foam | The web pattern reads as a texture at a glance |
| Don't time breakers with a smooth spatial phase field | Their births sweep across like a wave front and look like white dots racing along. Each breaker needs its own position, size and lifetime |
| Foam attached to a moving object samples its noise in object space | A mask in the hull's coordinate frame with noise in world coordinates changes its edge every frame and appears to shake |
Integrate the flow offset, don't use t * speed | With the latter, the whole image jumps as soon as the speed changes |
Apply pow(x, 1/2.2) first when reading SW3 noise textures | They are imported as sRGB, so the shader reads a mean of 0.24, not 0.5, and every threshold is off |
| Measure sea motion with a camera following the ship | The "layer rolling past at high speed" the producer kept mentioning could not be seen at all in our fixed-camera tests |
The last one is worth the most. The agent's eyes are a camera, and with the camera in the wrong place no number of screenshots helps. After this, sea problems were checked by recording short clips with the following camera and reading filmstrips of one frame every 1/3 second.
Fourth pass: one layer at a time
Starting again from the v0.8.8 sea, the rule was to change one layer at a time, show a before-and-after comparison, and leave everything else alone.
The first step dealt with what the producer called "the sandy grain of the seabed". Only turning things off layer by layer located it: hiding the seabed, and lowering it 25 m and darkening it, left the image unchanged, so it wasn't the seabed; with SeaStrokes off the grain was still there, so it came from the water's own normal map. That map repeated every 5 m and the second layer's scale was exactly half, which gave a very regular rhythm. It was changed to 12.5 m, with a second-layer scale of 0.37.
The second step was hexagonal tiling. We modified SampleNormals in Stylized Water 3's Normals.hlsl: the plane is divided into hexagons, the texture in each cell gets its own translation and rotation, and the nearest three cells are blended, so no repetition shows and there are no seams. Store assets don't go into git, so the modified file and the original are kept together in the repository's ThirdPartyPatches/, with a note that it must be copied back over after reimporting the plugin.
Captain's view: waves finally find their place
On 22 October we added a captain's view similar to World of Sea Battle: the camera sits close behind the ship and WASD steers it directly. At that low angle a flat sea gives itself away at once, and the producer had always liked the swell on the title screen, so real waves were enabled in this view only:
- A 760 m
WaterGridwith 2 m vertex spacing around the camera, following it. - Farther out it is still the flat sea; the big quad becomes a ring with a square hole in the middle at this point.
- Wave height fades to zero between 140 and 300 m from the camera, so the edge of the mesh meets the flat sea exactly.
- Ships compute their height on the CPU with the plugin's same Gerstner waveform, same clock and same fade curve, then follow it with easing. Game rules are still computed on a flat sea.

The top-down sea wasn't touched at all, and the three problems from 1 October each got an answer: this view is low-angle and never pulls up high enough to show the aliasing; the ship's motion is eased, so it no longer twitches; and a check of the shores around the main island showed no exposed reef.
But the first time the captain's view opened, the waves raced along as if on fast-forward. The cause lay between two systems: SeaMood changes the sea material's _Direction every frame so the patterns drift with the wind, while the plugin computes wave phase by multiplying this direction by world position. A few kilometres from the origin, even a tiny change in direction makes the waveform slide; for a ship 2.5 km out that added about 4.5 m/s of extra speed, and the direction component also flattened the waveform by a factor of 1.8. The solution is this rule: on a material with waves enabled, _Direction must stay constant. The CPU height calculation also has to read the global _CustomTime rather than WaterObject.CustomTime, whose getter is always -1.
Fog had a similarly hidden problem. The sea took no fog at all, and the horizon looked cut with a knife. It turned out that the plugin shader's import settings had the fog integration set to SC Post Effects, which the project doesn't have installed; changing it to Unity fog put it right. This folder stays out of git as well, so it went into the notes: it has to be set again after a reimport.
Markers on the sea
One more problem, small but typical. Field-of-fire fans must not get darker where several guns overlap, so the stencil buffer ensures each pixel is tinted only once. The first version used bit 64, and the fans didn't show on the sea at all: SW3 water writes a stencil value of 65 to every pixel, which occupies bit 1 and bit 64. Changing to 128 worked. Later we found that where our field of fire overlapped an enemy's, only the side drawn first appeared, so each side got its own bit: 128 for ours, 32 for the enemy.
Music and sound: from code synthesis to a measuring pipeline
First version: synthesised in code
Less than a day into the project, SfxSynth could synthesise cannon fire, explosions, splashes, thunder and rain loops in code, with no assets at all. The problem was just as plain: the agent can't hear. It can compute a spectrum but can't judge whether something sounds good. On 26 September these were replaced with sound effects from an asset pack, tiered by gun weight. On 2 October, after player feedback that the waves and heavy rain were too loud, four channels (ambience, combat, UI, music) and a master volume were added.
Sound as a trade with its own requirements document
On 5 October we wrote a sound requirements document: 185 sound effects and 13 music tracks, each entry with an ID, trigger, duration, playback mode, variant count, priority (P0 to P2), current status and sound requirements. The delivery spec was fixed as well: WAV, 44.1 or 48 kHz, mono for sound effects, stereo for ambience and music, filenames <id>_<n>.wav, and no intelligible human voices, to leave the way open for multiple languages later.
The sound was produced outside this project, following the document. The first batch delivered 424 WAVs, which a person then auditioned and reviewed one by one, with the results stored as JSON. The agent here doesn't judge audio quality; it only wires in the files that passed review, then changes the matching row in the document to "wired in".
| Round | Date | Newly passed files | Cumulative files | Cumulative sound slots |
|---|---|---|---|---|
| Round 1 | 10-09 | 256 | 256 | 122 |
| Round 2 | 10-10 | 64 | 320 | 152 |
| Round 3 | 10-10 | 53 | 373 | 178 |
| Round 4 | 10-10 | 36 | 409 | 191 |
A later music update added one more track, so the count now stands at 410 files. These sounds were adapted from 519 CC BY, CC0 and public-domain sources, through editing, layering, filtering, and speed and pitch changes. The attribution list is generated by a script from the asset table and ships with the game; it runs to over 3,000 lines. Maintaining it by hand isn't realistic, and CC BY attribution is a hard requirement.
Loudness: BS.1770 in place of ears
The agent can't hear, but it can measure. This was the most useful decision in this section. Tools/Audio/audio_loudness.py measures every file to ITU-R BS.1770, the K-weighted loudness used in broadcasting:
| Type | Measurement |
|---|---|
| One-shot sound effects | The loudest 400 milliseconds (momentary max); anything shorter than 400 milliseconds counts as 400 milliseconds |
| Loops and music | Integrated loudness of the whole piece (integrated LUFS) |
Then a single table records the relative loudness each ID should have in the game, for example naval gun -18, heavy gun -17, swivel gun -21, button click -32, mouse hover -40, waves -34, sailing music -32. From this the script computes each sound slot's volume and the gain of each variant of an ID, and generates RecordedSoundTable.cs.
The whole table is anchored on the cannon: the recorded cannon is placed where the old cannon sat, an overall offset of -4.8 dB, so the old sounds not yet replaced keep their place in the mix. Measurements are cached by each file's sha, so the next round measures only the new files. After that every round settled into the same five steps: copy the files, measure and generate the loudness table, import, add triggers for new IDs, update the document.
Music and ambience
Music.cs has nine moods: title, daytime sailing, night sailing, storm, naval battle, boarding, port, island exploration, ghost ship. Two AudioSources crossfade, over 2.5 seconds normally and 1 second going into battle.
A few small rules keep the music from becoming annoying: after a calm track has played once there are 25 to 70 seconds of silence before the next one, and only tense tracks loop; battle music carries on for 12 seconds after the last shot; a brief change such as passing a port waits 2 seconds before switching, to avoid jumping back and forth; when a naval battle ends a short phrase plays while the background is lowered.
Ambience.cs handles ambient sound, made up of a set of looping layers: wind that changes with the weather, the night sea, voices from the port, surf on the shore, insects and birds on the islands, wind in the sails. Each layer fades in and out according to what the camera sees, and gulls, bells in the fog and gusts turn up now and then. Alert sounds need restraint too: the alert for sighting enemy sails, for example, plays at most once in 25 seconds and not at all in the first 5 seconds of a game.
Small engineering traps
forceToMonoflipped on every other import. The import code chose the setting from the channel count, and a clip mixed down to mono reports 1 channel, so the next decision naturally came out reversed.- A method named
Startthat takes parameters is treated by Unity as a lifecycle function, which givesStart() can not take parameters. Names like this are off limits. - Promotional videos have to be recorded at a fixed frame rate, and the game's real-time sound doesn't line up with that. Besides saving frames,
ClipRecorderwrites each AudioSource's start, volume changes and stop intosound.csvframe by frame, and a script rebuilds the audio track from that log, which gives frame-accurate sync.
Multiplayer: the architecture reworked in a day
The code audit on 2 October listed 8 obstacles, for example: GameManager.player and the static PlayerFaction were referenced in over 200 places; isPlayer appeared in about 150 places and meant both "human-controlled" and "locally controlled"; pausing relied on Time.timeScale; boarding and landing were singletons; there were over 330 uses of unseeded UnityEngine.Random, and ship movement depended on PhysX contacts. Those last two points ruled out lockstep outright, leaving host authority with state snapshots as the only route.
For the stack we chose Netcode for GameObjects 2.x in listen-server mode, with our own transport written on SteamNetworkingSockets. On 3 October we first sent 6 read-only agents off to audit separately, merged their findings into a six-phase rework plan, then finished it phase by phase, with 53 commits that day.
The price of getting it done came afterwards. Some representative problems:
- Editor crashes. The function pointer of a debug callback had been handed to Steam, and after a script reload Steam's thread called into freed memory. It is now registered only in packaged builds started with a specific argument, and cleared before a reload.
- Recompiling during Play emptied the NGO message table. The symptom was clients being refused, with
Allowed Count: 0in the log. The root cause took a long way round to find: Steamworks.NET automatically adds theSTEAMWORKS_NETdefine on every domain reload, and after each test we cleaned up the settings withgit checkout ProjectSettings, which deleted the define, so the next entry into Play added it back and triggered a recompile. Committing the define to the repository solved it. - Races on shared accounts. Two players of the same nation share a purse and a warehouse. A client's grid cell commands carried only coordinates, so right after a teammate had touched the warehouse the wrong item could be sold. Commands now also carry "what I saw"; the host checks it and, on a mismatch, rejects the command and tells the player. Every kind of rejection now has a clear message as well, rather than failing silently.
Testing runs with the Editor as host and a packaged build as client, over local loopback. The client has a set of command-line arguments (such as -polysea-net-test) that carry out buying and selling, appointments, boarding and so on through the real game functions. Tools/NetTest holds nearly twenty scenario scripts. Every change to the network format bumps the protocol version, and the handshake also compares a fingerprint of the value tables, so the two sides can't connect if their values differ.
Boarding battles: automated rehearsal instead of feel
On 15 October boarding battles became real-time tactics with control of individual units. Animation, pathfinding, morale and 29 kinds of tactical items all had to take real effect on deck, which was too much to test by hand, so we wrote BoardingRehearsal: it runs random battles back to back in the Editor and records causes of death, stuck units (with screenshots) and whether items really took effect. Running in place, foot sliding and overlapping figures were all turned into counted metrics. The results over 72 or more battles: running in place 0, sliding 0, overlaps 0 to 2 pairs, errors 0.
Balance started here too. With the same brig and the same numbers of men, our win rates were 75% against England, 94% against Spain, 44% against pirates and 0% against skeletons. Yet the combat strength shown in the UI was about 430 for all four, which meant the combat strength formula left out each faction's weapons and the fact that skeletons don't rout. After correcting that, the combat strength coefficients were calibrated against measured win rates, so that auto-resolve and manual battles agree:
| Opponent | Auto-resolve win rate | Measured win rate |
|---|---|---|
| England | 48% | 47% |
| Spain | 81% | 81% |
| Pirates | 36% | 34% |
| Skeletons | 29% | 31% |
The curve is steep. Taking the skeletons' health coefficient from 0.7 to 0.68 to 0.65 moved our win rate from 25% to 31% to 56%; being four hands short (24 against 28) lost all 32 battles.
The lesson about sample size came from here too. One round of 16 battles gave 34% on attack and 63% on defence, which looks like a huge advantage for the defender; with another random seed it became 56% on attack and 38% on defence. A sample of 16 battles is not enough to judge a difference like this.
On scale, the producer set a rule: one person is one figure, never merged. Each side has at most 64 sailors and 10 soldiers on the field, with the rest in reserve; once losses reach a certain level they come on in batches of 8, with at most 2 spawned per frame to avoid hitches. After optimisation each living person costs about 0.1 ms, corpses stay out of the per-frame loop, and at most 80 remain on the field.
The parchment chart: hand-drawing a random map

Unexplored waters are shown as a hand-drawn parchment chart. The difficulty is that the map is generated at random from a seed, so neither coastlines nor buildings can be drawn in advance, and every chart has to be generated at runtime:
- Terrain. The land layer is rendered once from above, with all meshes swapped to a mask shader and clipped 1.2 m below the water surface, then rendered again at -8 m to get the shallows. The result is stored as a linear RG16 texture: R is the distance to the coast (±48 m), G is rock height or shallows.
- Strokes. A shader uses this field to draw rock hatching, sandy shore stippling and fine dots for the shallows.
- Pictures. 80 small pictures (30 kinds of building, 14 kinds of terrain, 36 island-name motifs) are painted into an atlas on a background thread by a small SDF brush, in about 1.7 seconds.
- Placement. Meshes in the scene are classified by name, clustered into individual buildings with union-find, then replaced by the matching picture.
Labels at first took about 6 seconds on the main thread, and only moving all of that into a Task solved it. There was a hidden trap as well: data textures must be linear, and with the early R8_SRGB version the islands came out slightly too small all the way round.
Problems that only appear after packaging
| Symptom | Cause | Fix |
|---|---|---|
| Landing battle ground vanishes, nothing but sea | Terrain drawInstanced needs a material with GPU instancing enabled; URP's own doesn't have it on, and the variant is stripped at build time | Use our own terrain material |
| Objects created at runtime turn magenta | CreatePrimitive has no usable material in URP builds | Always assign a material explicitly |
| No fog in the captain's view | The main scene has fog off, so automatic stripping removed all the fog variants | Set stripping to custom and keep linear fog |
None of these can be seen in the Editor. A self-test for the packaged build was added later: POLYSEA.exe -batchmode -polysea-autotest <dir> plays a game, and the landing self-test also renders the battle scene and judges whether the centre of the image is warm ground or blue sea. This self-test has to start with graphics; with -nographics it can't read pixels and falsely reports failure.
Working habits with the agent
- Tests and the player share a save. Tests overwrote the producer's save at least four times. Now, after entering Play,
SaveSystem.OverrideFolderis first pointed at a test folder and read back to confirm before a game starts. - No script edits in Play mode. Recompiling resets static variables, including that same save redirect. Even after this rule was written down, we still broke it.
- Restore project settings after tests. Unity writes the time scale and run-in-background changes made during tests back into
ProjectSettings; they have to be restored after testing and checked before packaging. - Code defaults are not the values in assets. Change a default number in C# and the old value saved in
GameTuning.assetstill applies. - Decisions go into documents. Big systems get a design document first, open items become dropdown options for the producer to choose from, and the document is the spec.
- Traps become notes. Context gets compacted; notes don't. There are about 90 so far, each one a single fact with its cause and what to do about it.
Usage
The session began at 2026-09-23 04:01 (Japan time). The figures here run up to the writing of this article on 2026-11-01 and include the main session and 440 subagent logs.
| Item | Main session | Subagents | Total |
|---|---|---|---|
| Model calls | 23,731 | 15,756 | 39,487 |
| Output tokens | 27.13 million | 10.01 million | 37.14 million |
| Cache-read tokens | 12.76 billion | 2.89 billion | 15.65 billion |
| Cache-write tokens | 119 million | 73 million | 192 million |
| Uncached input tokens | 47,810 | 31,522 | 79,332 |
Converted with this site's earlier method (cache read ×0.1, cache write ×2, output ×5), the total comes to about 2.13 billion tokens. Of the calls, Claude Opus 5.5 made 34,992, Claude Sonnet 5.5 made 4,491 and Claude Haiku 5.5 made 4.
What went right
- Keeping the ability to roll back. Every big experiment left a tag, so overturning a direction cost little.
- A dedicated verification tool for every system. Boarding rehearsal, network scripts, the packaged build self-test, the harbour unjamming test: these tools are worth more than any single feature.
- Measurement instead of guesswork. Loudness, frame time and win rate all have numbers to look at.
- Effects limited to where they are needed. Swell exists only in the captain's view, and the top-down sea stays simple.
What went wrong
- Changing the sea continuously with no baseline, and stopping only when there was no way back.
- Testing motion problems with a fixed camera, so one symptom was reported several times before it was found.
- Drawing conclusions from win rates over 16 battles.
- Tests overwriting the producer's save, and more than once.
- Not confirming exactly which version "before" meant at the first revert, which took one more round of reverting.
Next
- Polish the numbers and models of each ship class in turn.
- Adjust the money curves according to the paper economy model.
- Multiple languages: the UI is Chinese only for now, and new UI already uses language-neutral marks.
- A battle scale setting for low-end machines.
- Real online testing of bounty jobs and tavern intelligence, and testing under poor network conditions. Neither has been done yet.
Written by Claude Opus 5.5 from the project's git history, code and development notes; the usage figures come from counting the session logs directly.