# MACRO v10 -- Zerg. v9 plus anti-air when Mutalisks (or the Spire) are seen.
#
# PROMOTED 2026-10-05: vs fourpool.txt 8-0 (all eliminations); vs v9 6-2; gauntlet
# 15-7 (68%), the best so far.
#
# vs AILien (2026-10-05) six Mutalisks arrived at 7:20 against three Hydralisks and
# no Spore Colonies -- the engine had no anti-air command until `spore N`.
#
# MACRO v9 -- Zerg. v7 with the larva fallback at 350 minerals instead of 700.
#
# PROMOTED 2026-10-05: vs v7 10-2 (p=0.019); vs fourpool.txt 8-0, all eliminations.
#
# MACRO v7 -- Zerg. v4 plus a LARVA FALLBACK when the natural cannot be taken.
#
# PROMOTED 2026-10-02: vs fourpool.txt 6-2 with six real eliminations (v4 under the
# same engine: 0-8); vs v4 head to head 9-7 over 16 games (no regression).
#
# Against the 4-Pool with expansion safety on (2026-10-02), v4 kept its workers but
# sat on ONE Hatchery all game -- the Zerglings never left the natural, so every
# expansion was held -- with 1,300-4,000 minerals banked and no army: one Hatchery is
# one larva every ~11s. Money it cannot turn into units. 0-8 on score.
#
# MACRO v4 -- Zerg. The previous macro-zerg.txt plus SPENDING (Lair, Hydralisk
# range/speed, Evolution Chamber upgrades, bank-driven Zerglings, more Hydralisks at
# three bases).
#
# PROMOTED 2026-10-02: beat the previous version 7-1 head to head, sides swapped,
# every game a real elimination. Against fourpool.txt it is 3-5 (one 4:42 loss, the
# rest capped at 20:00 and judged on score): it survives the rush but cannot kill it.
#
# Measured 2026-10-02 on macro-zerg mirrors with the engine fixes in: every side
# reached two bases and ~24 workers by 10:00 and then banked ~1,900 minerals and
# ~1,300 gas with a small army -- it ran out of things to buy once it had 18
# Hydralisks. An economic lead that is never spent is not a lead. v4 adds sinks.
#
# (Original header follows.)
#
# MACRO -- Zerg. The contender to the 4-Pool that ships in the jar.
#
# The end state for this project is a bot that plays unattended, with no LLM behind
# it, and wins by keeping an economic advantage. 4-Pool is the opposite bet: it wins
# by ending the game before economy matters, and the README's verdict on extending
# that game is blunt -- "everything that extends the game: 0%" against the built-in
# AI, which out-masses us given time.
#
# That verdict is about the BUILT-IN AI, and it is the wrong opponent for this
# question. A macro build is a bet that our economy beats THEIRS, and the built-in AI
# cheats at economy. The honest test is script vs script:
#
#   ./local/play-selfplay.ps1 -Games 5 `
#       -ScriptA local/scripts/macro-zerg.txt `
#       -ScriptB src/main/resources/scripts/zerg-default.txt
#
# Do not promote this over zerg-default.txt until that number exists.
#
# ---------------------------------------------------------------------------
# STRUCTURE. Every trap below has cost this project a game (docs/ETHOS.md):
#
#   * Buildings go through `task build` and `expand`, which are AUTO-PLACED and
#     NON-BLOCKING. `make <building>` goes through the ordered production queue,
#     where one unplaceable building blocks everything behind it -- that jam has
#     twice cost a whole tech tier and the army with it (#3).
#   * Units use `keep` (a standing TARGET that converges and replaces losses), never
#     `every ... -> make` (a repeated ACTION that integrates). The same rule with
#     `every` built 12 Hatcheries and 98 Drones (#13).
#   * Order is stated as DEPENDENCY (`when have pool`), not as position (#2).
# ---------------------------------------------------------------------------

# --- Opening: economy, but it has to survive to spend it -----------------------
# MEASURED, v1 lost 0-2 to the 4-Pool at 5:39 and 4:27, and the ECON line said why:
# it died holding 603 GAS and 47 MINERALS. `task build extractor` ran at the top of
# the script, so `gas 3` pulled three of ~seven Drones onto gas before the Pool was
# even finished -- a 30% cut to the only income that mattered. NOTHING this build
# needs early costs gas: Drones, Pool, Hatchery, Sunkens and Zerglings are all
# minerals. The Den is the first gas sink and v1 never lived to build one.
#
# So gas waits for the second base. See the bottom of the file for where it goes.
gas 0
make drone 5
make pool 1
# v16: LOOK AT THE ENEMY. A drone at 9 supply reaches their base around 1:40 and sees
# the Spawning Pool; a Pool finished before 1:51 is a 4/5-Pool, and the engine (not this
# script) then holds the natural, cancels for our Pool, puts up a Sunken and six
# Zerglings. Steamhammer scouts at the same point (docs/STEAMHAMMER-LESSONS.md).
when supply >= 9 -> scout drone
make drone 3

# --- SCOUT. Nothing economic is knowable without it ---------------------------
# `workerlead` is false until an enemy WORKER has actually been seen, so a bot that
# never looks can never act on a lead. Measured: this vocabulary sat on
# "next: when workerlead >= 6" for an entire game with 98 workers and no army,
# because UNSCOUTED is not a lead (#5). Scouting is what turns economy into
# information, and this build spends the whole game acting on that information.
when have pool -> scout on

# --- Survive the mirror, THEN macro -------------------------------------------
# A 4-Pool has its Pool at 0:34 and six Zerglings at your door by ~1:55, and then
# makes them forever. A 9-Pool cannot out-Zergling that: the Pool alone lands ~40s
# later. v1's six Zerglings met twenty-two.
#
# Sunkens are the answer a macro build can afford, because they cost no larva -- the
# larva stay on Drones, which is the entire point of the build. They are ordered
# SPECULATIVELY, not in reaction: a Creep Colony plus a Sunken is ~40 seconds, and
# by the time `enemy zergling` is true the Zerglings are already inside.
when have pool -> keep ling 8
when have pool -> sunken 2

# REPLACE WORKERS, ALWAYS. v2 lost fifty game-minutes to the lack of this line.
#
# Every Zerg building EATS ITS DRONE. Four to start plus eight made is twelve, minus
# the Pool, minus the Hatchery, minus two Creep Colonies for those Sunkens, is eight
# -- before the 4-Pool kills any. `keep drone 24` existed but was gated on the second
# base, so nothing replaced a worker until after an expansion that could not happen.
# A standing target is exactly right here: it converges and it replaces losses, which
# is the whole difference between `keep` and a fixed `make` (docs/ETHOS.md #13).
when have pool -> keep drone 14

# --- Grow. This is the whole build. -------------------------------------------
# The second base is the economic advantage; everything below is spending it.
# KEEP, NOT `task build`. `task build hatch` is a one-shot: it puts a Hatchery down
# and never notices when one dies. Nothing in this bot rebuilt ANYTHING -- a Pool, a
# Den or a Hatchery that was destroyed stayed destroyed for the rest of the game --
# which is why it wins 100% against Protoss bots that leave it alone and 25% against
# Zerg bots that come and break things. A macro build cannot absorb that.
#
# NOT `keep hatch 2`. That line used to be here, and it was the macro Hatchery seen in
# live games: `keep` on a building rebuilds through `task build`, which places beside
# the main, so it put a second Hatchery in the main long before ten workers -- and the
# old base count counted it as base two, so `bases 2` was already "met" and nothing
# walked to the natural. Expansions are held by `bases 2` below; a dead MAIN Hatchery
# is a lost game whatever this line says. (scriptCheck now warns on `keep hatch 2`.)
when have pool -> keep pool 1

# TEN, not twelve, and reachable now that workers are replaced. v2 asked for twelve
# from a build whose ceiling was eight, so this line was false for the entire game --
# and it is load-bearing: EIGHT rules below gate on `bases >= 2`, so one unreachable
# condition silently disabled the whole mid-game. The state line said so for fifty
# minutes ("STALLED 50m49s"); nothing was reading it.
when workers >= 10 -> expand
# AND HOLD IT. `expand` fires once; `bases 2` is the standing target that retakes an
# expansion that dies. Two thresholds, same as the army stance: expand to get there,
# `bases` to stay there.
when workers >= 10 -> bases 2
when bases >= 2 -> keep drone 24
# GAS ONLY NOW. Two bases means the mineral line is saturated enough that three
# Drones on gas are surplus rather than a tax, and the Den below is finally
# something to spend it on.
# GATED ON WORKERS, NOT ON THE EXPANSION. Chaining every later rule off `bases >= 2`
# made the expansion a single point of failure for the entire build. Gas still waits
# for a real economy -- ten workers, not the seven that v1 crippled itself with -- but
# it no longer waits on something that may never happen.
when workers >= 10 -> task build extractor
when have extractor -> gas 3
when workers >= 20 -> gas 6

# Third base once the second is actually working, not on a timer. Two saturated
# bases is roughly 24 workers; asking for a third before that buys nothing and
# spends the 300 minerals the army needs.
when workers >= 24 -> expand
when workers >= 24 -> bases 3
when bases >= 3 -> keep drone 32

# --- Defend the lead ----------------------------------------------------------
# Reacting to what the OPPONENT is doing, not to a clock. Sunkens cost no larva,
# which is exactly why they are right when the larva are wanted for Drones.
# FOUR, not eight. A 4-Pool commits with SIX. v1 asked for eight, so against the
# one opponent it was actually measured against this rule could never fire -- a
# threshold set by intuition rather than by reading what the opponent does.
when enemy zergling >= 4 -> sunken 4
# v10: AIR. A Spire means Mutalisks within ~90s; a Spore needs an Evolution Chamber, so
# that is kept on sight too (it is also the upgrade building; `keep`, not `task build`,
# which would add a second one). `spore N` SETS the total
# and spreads one per base first, beside the Hatchery where Mutalisks hunt Drones.
when enemy spire >= 1 -> keep evo 1
when enemy mutalisk >= 1 -> keep evo 1
when enemy spire >= 1 -> spore 2
when enemy mutalisk >= 1 -> spore 2
# Terran air too: Wraiths and a Tank push took Iron's games while no Spore existed,
# because only a Spire or Mutalisks triggered anti-air (2026-10-06).
when enemy starport >= 1 -> keep evo 1
when enemy wraith >= 1 -> spore 2
when enemy wraith >= 4 -> keep hydra 30
when enemy mutalisk >= 5 -> spore 4
when enemy mutalisk >= 1 -> keep hydra 30
# (Removed 2026-10-02: `when enemy zealot/marine/hydralisk ... -> sunken 2`. `sunken N`
# SETS the total, so these could only ever LOWER it: vs NuiBot, its Hydralisks cut the
# target from 6 to 2 mid-game and lost Sunkens stopped being replaced. The Pool line
# above already guarantees two. scriptCheck now warns on a lowering `sunken`.)

# --- Convert the lead into an army --------------------------------------------
# An advantage that is never spent is not an advantage. Hydralisks because they are
# the Zerg unit that does not need a tech tier to be useful, and because they beat
# the Zerglings a 4-Pool opponent is made of.
when have extractor -> keep den 1
when have den -> keep ling 12
# GATED ON THE DEN, not on the base count. `keep` feeds the same ordered production
# queue, so a Hydralisk asked for before the Den exists sits at the queue head as
# "no Hydralisk_Den" and blocks the Drones behind it -- the exact jam that cost a
# tech tier twice (#3). `keep <type>` is keyed BY TYPE, so the second `keep ling`
# above simply raises the standing target; that one is safe to repeat.
when have den -> keep hydra 18

# --- SPEND IT (v4) -------------------------------------------------------------
# Gas sinks first, because gas is what was floating hardest: Hydralisk range and
# speed (both need a Lair), then the Evolution Chamber upgrades that every unit we
# make benefits from. All gated on the building that researches them -- `upgrade`
# retries until it can start, but a gate keeps the state line honest.
when have den -> make lair 1
# ZERGLING SPEED. Never researched before -- Steamhammer gets it "if at all sensible"
# (Pool, 9+ Drones, gas, 6+ Zerglings: StrategyBossZerg.cpp:3407). Slow Zerglings lose
# every ZvZ ling fight and cannot catch anything.
when have extractor -> upgrade speed
when have lair -> upgrade range
when have lair -> upgrade hydraspeed
when bases >= 2 -> keep evo 1
when have evo -> upgrade missile
when have evo -> upgrade carapace
# Mineral sinks. A third base raises the Hydralisk target; banked minerals turn into
# Zerglings. `every` is right here for the same reason as the stance rules: `keep`
# SETS a target, so repeating it converges instead of accumulating (ETHOS #13).
when bases >= 3 -> keep hydra 30
every minerals >= 600 -> keep ling 24

# --- LARVA FALLBACK (v7) ------------------------------------------------------------
# `keep hatch N` counts EVERY Hatchery, and `bases` counts mineral lines -- so with the
# natural up, two Hatcheries already exist and the first line does nothing. Only a
# build stuck on one base with money it cannot spend gets the macro Hatchery, which is
# exactly the case it is for. (scriptCheck warns on these; here it is deliberate.)
# v9: 350, not 700. Against the 4-Pool the natural is (rightly) held while Zerglings
# camp it, and one Hatchery sat at 0 larva from 3:00 to 10:00 with 400-560 banked --
# 700 never accumulated, so the fallback never came. The expansion reservation stops
# this stealing the natural's money while a Drone is on its way.
when minerals >= 350 -> keep hatch 2
when minerals >= 800 -> keep hatch 3

# --- Attack when the army is real; go home when it is not ---------------------
# `every`, not `when`, and this is the one shape where that is right. A stance is
# IDEMPOTENT -- setting "attack" twice is setting it once -- so the rule converges
# instead of accumulating, which is the whole test docs/ETHOS.md #13 asks you to
# apply before writing `every`. `when` fires once and can never change its mind, and
# a bot that cannot change its mind loses the game it is already losing.
#
# HYSTERESIS, deliberately wide: commit at 24, retreat at 8. Two thresholds with a
# gap between them cannot oscillate; one threshold would flip the army around every
# five seconds at the boundary and it would spend the game walking.
every army >= 24 -> attack
every army <= 8 -> defend

# The state the script starts in and returns to. Nothing after this line is a
# fallback -- `defend` is where the bot sits for most of the game, and the two rules
# above are what move it out and back.
defend
