What this is
One persistent world, ticking forever at a fixed rate. You register a guild, run a bot, and your bot plays: it recruits characters, sends them into the dungeon, fights, loots, trades, and brings the gold home. There are no matches and no rounds — if your bot is offline, your characters simply stand still and rest.
You can absolutely write your bot by hand — the whole interface fits on this page — but the game is built for playing through a coding agent: tell it what you want your guild to do, point it at the starter kit (which ships agent-ready docs), and iterate. Your strategy is the game; the Python is negotiable.
1. Get playing in three steps
a. Register your guild. The token is shown once — copy it.
download guild_token.json — drop it next to your bot and you're done.
Already have a guild? Paste your guild_token.json to sign this
browser in:
b. Get the starter kit — a working bot plus the client library. Clone it:
git clone /starter.git my-bot
or download starter_kit.zip if you would rather not
use git. Same files either way; cloning means git pull picks up
updates to the client library. The kit holds two example bots —
starter_bot.py (wander and punch) and farmer_bot.py
(remembers the map, path-finds, retreats and sells) — plus AGENTS.md,
a reference written for your coding agent.
c. Run it:
pip install pyzmq # only for a tcp:// server python starter_bot.py
Your token file carries the server address that works from where you
registered, and the client picks its transport from that URL: tcp://…:5570
speaks ZeroMQ, https://… runs the identical protocol over this web port
and needs nothing installed. If you registered from the public internet you have the
https:// form, because a proxy that only forwards HTTP cannot carry a
ZeroMQ socket. A bot that connects and then hangs holding a valid token is pointed at
a tcp:// address it cannot reach.
The starter bot works unmodified: it recruits a full party, embarks, wanders and
punches whatever it meets. You fork it by rewriting one method, on_frame.
Auth, reconnects and logging are already handled by the library. Then open the
Player tab and watch your party go.
2. The tick
Every tick (default 0.25 s) the server collects one action per character, resolves them, and sends you one frame per world you have characters in. Order within a tick:
- All non-move actions resolve, in descending
speedorder (speed is rolled per tick from AGI; ties break randomly). - Then all moves resolve, in descending speed order.
Because attacks resolve before moves, you cannot dodge by moving away: an attack hits whoever is on the target tile when it resolves. Two characters can kill each other on the same tick. Moving into a solid or occupied tile fails and still costs stamina.
3. The frame
A frame carries your characters in full, plus everything inside the union of their vision squares (Chebyshev radius, no line-of-sight blocking), plus the events that happened where you could see them. Other guilds' characters show name, look, outfit, position, a health fraction and what they last held in hand — never their stats, statuses or inventory.
{"type": "frame", "tick": 10412, "world": "map_1", "bounds": [48, 192],
"next_refresh": {"band": 2, "in_ticks": 1180},
"chars": [{"char_uid": "c_7f", "pos": [14, 87], "hp": 22, "max_hp": 35,
"stamina": 40, "stats": {...}, "xp": 45, "inventory": [...],
"equipment": {"hand": {...}, "offhand": null, "outfit": "..."}}],
"visible": {"tiles": [[12, 85, "floor", 0], ...], "entities": [...],
"items": [...], "gold": [...]},
"events": [{"kind": "attack", "attacker": 311, "target": 902, "dmg": 5, ...}]}
The village frame has no visible; it carries your guild block (gold,
guild inventory, market listings, where your characters are) and the shop stock.
4. Actions
One per character per tick; a second action for the same character in the same tick
replaces the first. Rejected actions come back as action_err with a
reason and cost nothing.
| action | args | where | notes |
|---|---|---|---|
| move | dir: N/S/E/W | map | single axis; walking south off row 0 returns you to the village |
| attack | target [x,y] | map | melee with your equipped weapon, or a punch; a ranged weapon in hand must shoot instead |
| shoot | target | map | ranged weapons only, straight lines |
| throw | item_id, target | map | throwable items only; the item stays where it lands, unless it bursts |
| use | item_id [, target] | anywhere | consumables; a few items target a tile |
| pickup / drop | — / item_id | map* | your own tile; *drop also works in the village, where it deposits into the guild inventory |
| equip | item_id, slot | anywhere | slots: hand, offhand, outfit, trinket |
| open | target | map | adjacent containers; some need several consecutive opens |
| spend_xp | stat | anywhere | +1 to a stat, costs 8 × current value; stats cap at 20 |
| say | text | map | visible flavor, 40 characters |
| buy / sell | kind / item_id | village | shop |
| list / unlist / buy_listing | item_id, price / listing_id | village | player market |
| deposit / withdraw | item_id | village | infinite guild inventory |
| recruit | [name] | village | free level-0 character |
| embark | map, char_uids | village | send a party, up to the per-map cap |
recruit, embark, buy, list,
unlist and buy_listing are guild-level: send them with no
char_uid.
There is no rest action — a character you send nothing rests automatically:
double stamina regen, and it heals a little (more with VIT) once it has gone unhit
for a couple of seconds. Free healing in the field is a finite reserve per
expedition (field_heal_mult × max HP, then resting only restores
stamina): camping forever does not work. Pack or forage food, carry potions, or
walk home — returning to the village refills the reserve. Your own frames carry
field_healed so you can budget it. Idling in the village restores
stamina fully and heals faster. Doing nothing is a real move.
5. Characters
| stat | governs |
|---|---|
| STR | melee damage, carry slots, cheaper melee swings (−STR//3 stamina) |
| DEX | thrown damage, throw/shoot range, cheaper shots and throws (−DEX//3 stamina) |
| INT | magic damage, potion potency ×(1 + 0.1×INT) — healing and stamina alike |
| VIT | max HP (20 + 5×VIT); out-of-combat idling heals 1 + VIT//4 HP |
| END | max stamina (40 + 10×END); regen is 5 + END//4 per tick |
| AGI | speed (AGI×5 + d4, rolled per tick) and cheaper moves (−AGI//2) |
Recruits roll 1–3 in each stat and cost nothing. XP comes from kills (split by
damage dealt) and discoveries; spend it with spend_xp. Each stat caps
at 20. There are no classes. A character's level is a derived shorthand — total stat points above
the 1-per-stat floor — so every purchased point is exactly +1 level.
Death is permanent. A dead character drops its whole inventory and equipment on its tile — for anyone to pick up. Your guild keeps its gold and guild inventory. Recruiting a replacement is free.
6. Stamina
Stamina sets the pace: roughly one meaningful action every four or five ticks. Moves cost 20 (less with AGI), a punch 20 (less with STR), weapons 15–40 depending on how heavy they are (melee less with STR, shots less with DEX), item actions 10 (a throw 15, a few special items more), village actions nothing. No cost drops below 5. You may act only when you can afford the cost; an idle tick regenerates at double rate.
7. Damage
Damage is deterministic — there are no to-hit rolls, so bots can plan:
weapon base + stat bonus − target armor, minimum 1 on a real hit. Armor
is flat reduction. Magic damage ignores armor but some gear and creatures resist it.
Friendly fire is on: attacks hit whatever is on the tile, including your own
guildmates and other guilds' characters. PvP is simply attacking an occupied tile.
Status effects exist (poison, stun, sleep, chill, regen and more). Your own
characters carry a full statuses list with remaining ticks; visible
monsters show only which statuses they have; other guilds' characters show none.
What causes each one is for you to find out.
8. The maps
Two, and you choose which to embark on. Every band of both maps has
several independent ways up, so a rival guild can never plug the only route — expect
company, and expect to be able to go around it. Both run bottom to top: you
spawn in the open band at the bottom, the exit is the bottom edge (move S
from row 0), and enemies, loot and containers get better the further north you push.
The village frame lists them under maps.
| map | size | what it is |
|---|---|---|
map_1 — The Spire | 48 x 192 | rooms and corridors around three parallel north–south spines; a gentle first band, a long climb, and no shortage of doorways to fight in |
map_2 — The Sunken Vaults | 64 x 144 | open flooded caverns threaded by four wandering channels. Shorter, wider, and much less forgiving: nothing here is a starter monster, and something waits at the top that a lone party will not survive |
map_3 — The Wildmarch | 64 x 160 | the overworld: meadows, forests, lakes and farmland. Tall grass hides
you from monsters and rival guilds alike, trails are fast lanes, fences
and bushes can be hacked through or bombed, crops and critters feed a
party living off the land, and standing portals shortcut the climb.
The gentlest start — and its own loot: food, tools of war, and
trinkets worn in the new trinket slot |
Bands refresh. Each map regenerates one horizontal band at a time on a
schedule. Every map frame carries next_refresh, and a
band_refresh_warning event fires a minute ahead. A band with characters
still inside defers its refresh — but only for a while, and then it happens anyway.
Loot left in a refreshed band is gone.
9. Economy
Gold is guild-level. The shop sells a small stock of basics at list price and buys anything back at 20% — which is exactly why the player market exists: list items at your own price, other guilds buy them, and the seller keeps every coin. Listings are public in every village frame. Guild inventory is free and infinite.
10. Connecting
ZeroMQ DEALER to the server's bot port, one JSON object per message. The client library does this for you; if you want to write your own:
-> {"type": "hello", "guild_id": "g_ab12", "token": "…"}
<- {"type": "hello_ok", "tick": 10411, "config": {...}, "guild": {...}}
<- {"type": "hello_err", "reason": "bad_token"} check your guild_token.json
<- {"type": "frame", ...} one per world per tick
-> {"type": "actions", "tick": 10412, "actions": [ ... ]}
<- {"type": "action_err", "char_uid": "…", "reason": "out_of_range"}
<- {"type": "server_pause"} live restart — reconnect shortly
<- {"type": "kick", "reason": "superseded"} another session hello'd as you
-> {"type": "bye"} polite hangup, optional
A new hello for your guild retires the previous session, so you never
run two bots against each other by accident. A slow bot just misses ticks.
11. What isn't written down
This page describes the interaction surface completely. It deliberately does not
list the items, outfits, enemies, containers, traps or boss mechanics — the contents
of the world are content. Your bot's local database
(guild_log.db) records everything you have seen, which is where your map
knowledge, drop tables and bestiary come from. Go find out.