Normal BattleTabs ends when one fleet is gone, so the last few turns are often a slow hunt for the final, best-hidden ship. First to Three turns it into a race: the first captain to sink three enemy ships wins. The fleet is the Kayak, Sailboat, Raft and Mini Sub.
The player's request:
Make a BattleTabs mod called First to Three: it's a race, the first player to sink 3 enemy ships wins.
We built it from scratch with an AI agent and the public mod tool, starting from the tool's own docs and no game code. It took 18 calls to the mod tool, two versions and one typecheck error, and no balance tuning.
All in one go: Paste this into your agent with the battletabs-mod tool connected, and it runs the whole loop by itself: build, test, tune.
Make a BattleTabs mod called First to Three: it's a race, the first player to sink 3 enemy ships wins.
Read the battletabs-mod ship-mods docs first, and only use ships that are allowed in the Arcade.
Typecheck it, then play a few test games on different seeds and check the rule works.
Run 500 bot games and compare the win split and game length with normal rules. If one side wins much more often, or games run much longer, change it and test again.
Save it as case-first-to-three, then tell me what you changed along the way and why.Or step by step, as we did it: Going one prompt at a time lets you look at each result and steer, the way the decisions on this page were made. Some prompts react to what came back, so read the result before you send the next one.
Make a BattleTabs mod called First to Three: it's a race, the first player to sink 3 enemy ships wins.Use only ships I could publish to the Arcade. Keep it simple: a Kayak, a Sailboat, a Raft and a Mini Sub.Typecheck it and fix any errors.Play one game on seeds 1, 2 and 3 and show me who won, how many turns it took and how many ships each side sank.Run 200 bot games and tell me the win split and how long games last compared with normal BattleTabs.When someone is one ship away from winning, warn both players. Add it to the rules card too.Run 500 games to double-check the balance.Show me a picture of the board when someone has sunk 2 ships.Save it as case-first-to-three.Prompt 1
Make a BattleTabs mod called First to Three: it's a race, the first player to sink 3 enemy ships wins.get_docs (ship-mods)onSink hook, and ctx has end(outcome). The guide never shows what an outcome looks like.onSink hook that counts each player's sinks and ends the match at three.Prompt 2
Use only ships I could publish to the Arcade. Keep it simple: a Kayak, a Sailboat, a Raft and a Mini Sub.list_ships (arcade_only)Prompt 3
Typecheck it and fix any errors.ctx.end({ winner: sink.by }).save_mod, typecheck, get_docs (types), save_mod, typecheckOutcome = { winners: Seat[] } | { draw: true }. It's a list because the same API also serves team games.ctx.end({ winners: [sink.by] }), plus a "three ships sunk" message and an endedByRace counter. Typecheck passed, including the publish text rules.Prompt 4
Play one game on seeds 1, 2 and 3 and show me who won, how many turns it took and how many ships each side sank.play_once (seeds 1, 2 and 3), render_stateendedByRace was 1.Prompt 5
Run 200 bot games and tell me the win split and how long games last compared with normal BattleTabs.simulate_n (200 games)Prompt 6
When someone is one ship away from winning, warn both players. Add it to the rules card too.save_mod, typecheck, play_once (seed 4){"sunk":[3,2],"matchPoints":2,"endedByRace":1}. The warning fired on turn 16 and the win came on turn 19. matchPoints was 2 because both players reached match point, one warning each.Prompts 7 to 9
Run 500 games to double-check the balance.Show me a picture of the board when someone has sunk 2 ships.Save it as case-first-to-three.simulate_n (500 games), render_state (twice)The guide doesn't show what ctx.end takes, so the first draft guessed { winner: sink.by }. Typecheck caught it before any game ran.
Fix: When a guess fails, read the types instead of guessing again. get_docs (types) is one call.
The Mini Sub's sonar sank nothing in 500 bot games, in either mode. That comes from the normal ship, not from this mod.
Fix: Compare against normal play before you blame your own rule. The ship was left alone.
A finished game doesn't tell you which rule ended it.
Fix: Put a counter in ctx.data for every hook path. endedByRace: 1 proved the race rule ended the game, and matchPoints: 2 showed the warning fired once per player.
500 bot games on the final version, seeds 1 to 500, against normal rules on the same fleets and seeds.
| First to Three | Normal rules | |
|---|---|---|
| Wins, seat 0 / seat 1 | 248 / 252 | 256 / 244 |
| First mover won | 268 | 268 |
| Average turns played | 19.9 | 28.0 |
| Draws / errors | 0 / 0 | 0 / 0 |
Every version along the way:
| Version | Change | Result | Kept? |
|---|---|---|---|
| v1 | First to sink 3 ships wins | 93 / 107, 19.5 turns (normal 27.9), 200 games | Yes |
| v2 | Adds the match-point warning | 248 / 252, 19.9 turns (normal 28.0), 500 games | Final |
TODO: in-game screenshot of this game in the BattleTabs Arcade.
The mod tool's render_state draws the boards as 2D pictures. Dark red cells are a sunk ship.



The whole mod, as saved by save_mod. Copy it into your own mods folder to start from it.
import { defineShipMod } from "@battletabs/mod-api";
// First to Three: a race. The first captain to sink 3 enemy ships wins,
// so you never have to hunt down the last, best-hidden ship.
export default defineShipMod({
id: "case-first-to-three",
apiVersion: 1,
classicVersion: "classic-2",
rules: {
pitch: "A race: the first captain to sink 3 enemy ships wins.",
bullets: [
"Sink 3 enemy ships to win",
"You do not need to sink the whole fleet",
"Both captains are warned when someone is one ship away",
],
},
maxTurns: 200,
fleet: ["kayak", "sailboat", "raft", "subsmall"],
ships: [],
// sunk[seat] = enemy ships that seat has sunk. matchPoints counts the warning toast.
data: { sunk: [0, 0], matchPoints: 0, endedByRace: 0 },
hooks: {
onSink(sink, ctx) {
ctx.data.sunk[sink.by] += 1;
const count = ctx.data.sunk[sink.by];
if (count >= 3) {
ctx.data.endedByRace += 1;
ctx.emit({ type: "show-message", audience: "all", template: "three-ships-sunk" });
ctx.end({ winners: [sink.by] });
} else if (count === 2) {
ctx.data.matchPoints += 1;
ctx.emit({ type: "show-message", audience: "all", template: "one-ship-from-victory" });
}
},
},
});
Install the mod tools and connect your agent with the Arcade guide. Then paste the prompts above into your agent, all in one go or one at a time. To play against the bot yourself, ask your agent to open the play page.