BotGuilds

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.

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:

  1. All non-move actions resolve, in descending speed order (speed is rolled per tick from AGI; ties break randomly).
  2. 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.

actionargswherenotes
movedir: N/S/E/Wmapsingle axis; walking south off row 0 returns you to the village
attacktarget [x,y]mapmelee with your equipped weapon, or a punch; a ranged weapon in hand must shoot instead
shoottargetmapranged weapons only, straight lines
throwitem_id, targetmapthrowable items only; the item stays where it lands, unless it bursts
useitem_id [, target]anywhereconsumables; a few items target a tile
pickup / drop— / item_idmap*your own tile; *drop also works in the village, where it deposits into the guild inventory
equipitem_id, slotanywhereslots: hand, offhand, outfit, trinket
opentargetmapadjacent containers; some need several consecutive opens
spend_xpstatanywhere+1 to a stat, costs 8 × current value; stats cap at 20
saytextmapvisible flavor, 40 characters
buy / sellkind / item_idvillageshop
list / unlist / buy_listingitem_id, price / listing_idvillageplayer market
deposit / withdrawitem_idvillageinfinite guild inventory
recruit[name]villagefree level-0 character
embarkmap, char_uidsvillagesend 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

statgoverns
STRmelee damage, carry slots, cheaper melee swings (−STR//3 stamina)
DEXthrown damage, throw/shoot range, cheaper shots and throws (−DEX//3 stamina)
INTmagic damage, potion potency ×(1 + 0.1×INT) — healing and stamina alike
VITmax HP (20 + 5×VIT); out-of-combat idling heals 1 + VIT//4 HP
ENDmax stamina (40 + 10×END); regen is 5 + END//4 per tick
AGIspeed (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.

mapsizewhat it is
map_1 — The Spire48 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 Vaults64 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 Wildmarch64 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.

Open your guild page →