Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fcv — Florent Code Visualiser

A local 2D replay viewer, behaviour analyser and annotation tool for Florent Code League.

Watch your bots, click what went wrong, and export a Markdown review an AI can actually act on — no screenshots.

tests python dependencies licence

fcv overview


Why

The usual bot-debugging loop is: watch a replay, screenshot it, paste it into a chat, and try to describe what looked wrong. Images cost a lot of tokens, cannot be diffed, and the model still has to guess coordinates off them.

fcv replaces that. You click the unit that misbehaved, type one sentence, and press export. Out comes Markdown containing your note plus the exact board, the unit's exact state, what it did that turn, and the team's shared memory — in text, with coordinates.

┌───────────────────────────┐     ┌─────────────────────────┐     ┌──────────────────┐
│  run bots  /  open replay │ ──▶ │  click · annotate · tag │ ──▶ │  review.md → AI  │
└───────────────────────────┘     └─────────────────────────┘     └──────────────────┘

Install

No dependencies. Python 3.10 or newer.

git clone https://github.com/DataAthleteChamp/florent-code-visualiser.git
cd florent-code-visualiser
pip install -e .          # optional — gives you the `fcv` command

Without installing, use python3 -m fcv.cli in place of fcv everywhere below.

Try it immediately, with no game or bots required:

fcv demo                  # writes a synthetic replay
fcv watch demo.replay26

Point it at your project

fcv needs to know three things: where your bots are, where your maps are, and where to put reviews. Set them once:

fcv init                  # writes fcv.toml
[fcv]
bots_dir    = "/path/to/your-bot-repo/bots"    # each bot is a folder with main.py
maps_dir    = "/path/to/your-bot-repo/maps"    # the .map26 pool
notes_dir   = "notes"
reviews_dir = "reviews"
work_dir    = "matches"
port        = 8420

Then just:

fcv watch

Flags always beat the config file, so you can override anything ad hoc:

fcv watch replay.replay26 --bots-dir ~/mybot/bots --maps-dir ~/mybot/maps

Environment variables work too (FCV_BOTS_DIR, FCV_MAPS_DIR, …), which is handy in CI.

What counts as a bot?

Exactly what the engine requires: a directory containing main.py with a top-level Player class. fcv scans bots_dir one level deep and lists every folder that has one — no naming convention, no registration.

bots/
├── champion/
│   ├── main.py        ← required
│   └── grid.py        ← extra modules are fine
└── experiment/
    └── main.py
Where do maps and replays come from?

Maps ship with the game — fcode maps sync downloads the pool into maps/. Replays are produced by fcode run … --replay out.replay26, by the RUN MATCH panel, or by fcv run. Any .replay26 works, including ones downloaded from the platform with fcode match replay.

fcv cannot find fcode

Only needed for running matches, not for viewing replays. fcv looks in FCODE_BIN, ./.venv/bin/fcode, then PATH. Set it explicitly if needed:

fcode = "/path/to/.venv/bin/fcode"

Run matches without leaving the tool

run a match

Press RUN MATCH (or M), pick Blue, Orange, a map and a seed. When the match finishes the viewer switches to it and re-runs the detectors.

SWAP SIDES replays the same pairing with the colours reversed. That matters: team A acts first every round, so a single game measures which side you were on as much as which bot is better.

fcv shells out to the official fcode binary — it never simulates the game.

Read the board

legend

Every glyph, colour and overlay is explained in the sidebar, along with the rules that make them matter:

C Core — 2×2, 500 hp, never passable
B Builder Bot — the only mobile unit, cardinal moves only
G S L Gunner / Sentinel / Launcher — a gunner's ray stops at the first occupant; a sentinel pierces
H Harvester — earns nothing unless its chain reaches a cardinal neighbour of its Core
Y X Conveyor / Splitter / Barrier — conveyors are walkable by both teams
. # o empty · wall · titanium ore

Press R to see exactly which tiles each turret threatens.

Find the problems

Nine detectors run automatically and mark the timeline, so a 1000-turn match tells you where to look before you scrub:

detector what it catches
crash a unit removed at full health — in this engine an uncaught exception permanently destroys it
timeout / slow_turn units at or over the 10 ms budget
bot_error tracebacks printed by a bot
friendly_fire a turret that shot its own side
wasted_shot ammo spent with nothing damaged
orphan_harvester a mine whose chain never reaches the Core — it earns exactly zero
oscillation a unit ping-ponging between tiles
idle a unit doing nothing for many turns
hoarding titanium piling up unspent
rebuild_churn the same tile rebuilt over and over at rising cost scale

Write your own in a dozen lines — see docs/custom-detectors.md.

Statistics the platform does not publish

The Florent API gives you a rating and a match count. Everything that explains why a game went a certain way is reconstructed here from the replay:

  • connected harvesters vs total — the gap between the two curves is wasted titanium
  • first earning turn, turns with an orphan harvester
  • cost scale, and what each item costs right now
  • damage dealt / taken, shot accuracy, ammo starvation
  • unit-turn utilisation, CPU p50 / p95 / max, timeouts

⚠️ CPU figures read 0 on some platforms — the engine uses a platform-specific clock. A clean local run is not evidence that a bot fits the budget. See docs/stats.md.

Export a review

export

Choose full report or brief, set the folder, then save, copy or download. The dialog previews first and only writes when you press SAVE FILE; it then shows the exact path with show in folder and copy path next to it. Default location is reviews_dir from fcv.toml.

Each note carries its own local board, so the reader needs nothing else:

### 1. [bug] turn 10 — Gunner #7 (5,1)

Fired east into our own builder.

**State**: `hp 40/40` `team A` `facing east` `action-cd 1`

**What it did this turn**
- damage 10 -> Builder Bot #3 (A)
- FIRE (5,1) -> (6,1)  <-- FRIENDLY FIRE

```text
   012345678901
 0 ............
 1 .CC..GB.b...
 2 .CC-H.......
```

Notes can be edited or deleted at any time from the NOTES tab.

Maps

fcv maps --maps-dir maps
fcv maps --show vase      # ASCII, with the analysis
map             size         sym  reg  wall  ore  sep  ore@
bridge      21x8      horizontal    3   28%   10   24     6
duel        12x12     rotational    1    1%    6   12     2
vase        11x16     horizontal    3   21%   11   12     6

2 map(s) have unreachable ground: bridge, vase

Two facts here change how bots behave and neither is in the official map list:

Symmetry. A bot that guesses the enemy Core by mirroring its own gets the wrong answer on a map whose symmetry it assumed. Eight of the twenty-one official maps are not rotational.

Reachable regions. More than one means part of the board cannot be walked to. Navigation that aims at those tiles jitters in place for the rest of the match.

The maps tab in the viewer shows the same thing as thumbnails.

Leagues

A campaign compares two bots. A league keeps a standing estimate of every bot and spends each new game where it resolves the most uncertainty.

fcv league run --games 40                 # play, using the ladder matchmaker
fcv league run --kind vs --target champion  # benchmark one bot against the field
fcv league run --kind rr                  # complete the crosstable
fcv league table                          # standings
fcv league matrix                         # head-to-head crosstable
  #  bot                  skill       ±  games   win  95% interval
  1  dirseal              33.95   11.12      4  100%  ..............####################
  2  champion             31.11   12.82      2  100%  ..........########################
  5  home2                26.58   11.88      2   50%  .......######################.....

Ratings are TrueSkill, implemented in the standard library and verified against the reference package to four decimal places. Two properties matter in practice:

  • A rating carries its own uncertainty, so the ladder can tell "genuinely third" from "barely tested", and can prioritise the games that settle it.
  • Editing a bot raises its uncertainty without discarding its mean. Bot sources are hashed, so a rating earned by different code is re-tested rather than trusted.

Every pairing is played on both sides, and fcv league recompute rebuilds all ratings from stored history — which is also a check that they are reproducible rather than an artefact of update order.

Campaigns

Play a whole pool and get one report ordered by what to fix first:

fcv run bots/mine bots/theirs --maps all --seeds 1,2 --jobs 6
score   24/24 = 100.0%
95% CI  86.2% .. 100.0%
sides   A 12 / B 12
report  campaigns/latest/campaign-report.md   (714 findings)

The report gives the verdict with a confidence interval, maps worst-first, then behaviours ranked by how many maps they affect — a behaviour on many maps is structural, one on a single map is probably situational. If the interval spans 50%, it says so rather than letting you read a verdict into noise.

Commands

fcv watch [REPLAY]        # the 2D viewer
fcv run BOT_A BOT_B       # play a pool, write a campaign report
fcv info  [REPLAY]        # one-screen summary
fcv show  [REPLAY] --turn 42 --actions --comms --legend
fcv analyse [REPLAY]      # detectors, as text or --json
fcv note  [REPLAY] --turn 42 --entity 7 --text "shot its own builder"
fcv export [REPLAY] --kind report --out reviews/review.md
fcv maps                  # the map pool with terrain analysis
fcv league run|table|matrix|games|recompute
fcv ladder                # public standings, no login required
fcv init                  # write fcv.toml
fcv demo                  # synthetic replay to play with
fcv schema                # the decoded replay schema

Keyboard

key action key action
space play / pause T bot-step mode
turn (shift = 10) R turret ranges
, . one unit action I bot indicators
home end first / last turn F follow selection
19 jump to 10–90% 0 fit to window
+ - double / halve speed M run a match
backspace restart esc deselect

Drag to pan, wheel to zoom, click to select.

How it works

fcv decodes .replay26 protobuf directly. The schema is re-derived from the official visualiser bundle by tools/extract_schema.py, so it is not a guess — it is the same descriptor the official viewer decodes with.

Along the way this documents two fields the official schema omits, including the 16-slot team communication store, recorded every turn for both teams. That is your bot's shared memory, replayable with no instrumentation. See docs/replay-format.md.

fcv/
  proto.py        protobuf codec (decode + encode)
  schema.json     replay schema, re-derived from the official bundle
  replay.py       state reconstruction, sub-step attribution
  analysis.py     behaviour detectors
  metrics.py      derived statistics
  maps.py         map discovery and terrain analysis
  rating.py       TrueSkill, standard library only
  league.py       persistent ladder, matchmakers, matchup matrix
  notes.py        annotations with context capture
  ascii.py        ASCII board rendering
  export.py       Markdown report / brief
  runner.py       plays matches via the fcode CLI
  campaign.py     batch analysis and the campaign report
  server.py       localhost HTTP + JSON API
  cli.py          command line
web/              vanilla JS + canvas, no build step

Tests

python3 -m unittest discover -s tests -v

145 tests, standard library only.

Documentation

replay-format.md the .replay26 format, reverse-engineered
workflow.md the annotate → export → improve loop
ui.md what is on screen and why
stats.md every statistic and where it comes from
custom-detectors.md writing your own detector

Design constraints

  • No dependencies, no build step. A debugging tool that needs a working toolchain breaks exactly when you need it.
  • No engine simulation. fcv only folds the deltas the engine recorded. Rules the format does not carry live in constants.py, labelled as assumptions.
  • No bot strategy in the repository. Fixtures are synthetic.

Contributing

Issues and pull requests are welcome — see CONTRIBUTING.md.

Licence

MIT. Not affiliated with Florent Code League.

About

Local 2D replay viewer, behaviour analyser and annotation tool for Florent Code League. Click what went wrong, export a Markdown review an AI can act on. Zero dependencies.

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages