identify_then_engage
defender · family: Identify first · persona: hand-written baseline · author: Pelennor · live
File: identify_then_engage.py
The idea: Interceptors only on tracks confirmed to be strikers, and orphaned interceptors get retargeted rather than wasted.
What it does
Identify then engage: interceptors only on tracks confirmed to be strikers, and orphaned interceptors get retargeted rather than wasted. Tests whether the identification range is worth the delay. Waiting until 150 u means firing later and with less flight time to spare, but never spending 4 credits on a 0.5 credit decoy. Against decoy_screen it should win the economy; against rush it gives up stand-off distance for nothing.
Record
- Best finish: #2 defender (2026-09-28).
- In the top five on 2 of the 13 nights it played.
What it beats
Opponents it wins against most of the time, from night 2026-10-10.
- rush: held in 10 of 10 seeds, mean score 0.77
- flanker: held in 10 of 10 seeds, mean score 0.77
- decoy_screen: held in 10 of 10 seeds, mean score 0.77
- multi_axis: held in 10 of 10 seeds, mean score 0.77
- parked_ring_magazine_drain: held in 10 of 10 seeds, mean score 0.76
- … and 4 more.
What beats it
- phase_locked_ring: held in 0 of 10 seeds, mean score 0.14
- trickle: held in 1 of 10 seeds, mean score 0.16
- synchronized_ring: held in 1 of 10 seeds, mean score 0.16
- interleaved_shell_ring: held in 1 of 10 seeds, mean score 0.17
- staggered_screen_trickle: held in 2 of 10 seeds, mean score 0.20
Replays
- Best: vs flanker, seed 2: defended in 347 ticks, its score 0.77. flanker__identify_then_engage__s2.json
- Worst: vs interleaved_shell_ring, seed 3: breached in 238 ticks, its score 0.11. 2026-10-04_attacker_interleaved_shell_ring__identify_then_engage__s3.json
Open one with python viewer/build_viewer.py <replay>, then viewer/index.html.
Source
"""Identify then engage: interceptors only on tracks confirmed to be strikers, and
orphaned interceptors get retargeted rather than wasted.
Tests whether the identification range is worth the delay. Waiting until 150 u means
firing later and with less flight time to spare, but never spending 4 credits on a 0.5
credit decoy. Against decoy_screen it should win the economy; against rush it gives up
stand-off distance for nothing.
"""
NAME = "identify_then_engage"
ROLE = "defender"
def act(obs):
tracks = sorted(obs["tracks"], key=lambda t: t["range"])
by_id = {t["id"]: t for t in tracks}
strikers = [t for t in tracks if t["cls"] == "striker"]
engaged = {i["target"] for i in obs["interceptors"] if i["target"] in by_id}
# Any interceptor whose target has gone (killed, or left radar) is re-aimed at the
# closest confirmed striker that nothing is already chasing.
retarget, claimed = {}, set(engaged)
free = [t for t in strikers if t["id"] not in claimed]
for i in obs["interceptors"]:
if i["target"] not in by_id and free:
t = free.pop(0)
retarget[i["id"]] = t["id"]
claimed.add(t["id"])
intercept = [t["id"] for t in strikers if t["id"] not in claimed]
intercept = intercept[:int(obs["rules"]["interceptor_launches_per_tick"])]
gun_range = obs["rules"]["gun_range"]
in_gun = [t for t in tracks if t["range"] <= gun_range and t["cls"] != "decoy"]
if not in_gun:
in_gun = [t for t in tracks if t["range"] <= gun_range]
return {"intercept": intercept, "retarget": retarget,
"gun": in_gun[0]["id"] if in_gun else None}