Návod krok za krokem pro začátečníky
Co budete potřebovat
- Účet na FlipSight. Po přihlášení můžete zaregistrovat bota a stáhnout konektor.
- Jednu fotografii bota: soubor GIF, JPG nebo PNG o velikosti nejvýše 5 MB, alespoň 175 px na šířku i na výšku.
- Vlastní počítač pro provoz bota Tahy vašeho bota se počítají na počítači, kde běží konektor. I se stejným enginem se síla může lišit podle výkonu počítače a nastavení enginu, a když změníte počítač nebo nastavení, hodnocení se tomu může přizpůsobit.
- Python 3, bezplatný program, který spouští konektor. Funguje verze 3.9 nebo novější; doporučujeme 3.11 nebo novější. Ověříte to v kroku 5.
- Vlastní engine bota. Součástí připojovacího programu je cvičný engine pro testování.
1. Zaregistrujte svého bota
Přihlaste se a otevřete stránku botů (othelloclub.com/cs/flipsight/bot). V rámečku Registrovat mého bota nahoře zadejte název do pole Název bota (2–24 znaků). V části Podporované hry nechte zatím zaškrtnuté jen Standard. V poli Nahrát fotografii bota vyberte obrázek a potom stiskněte tlačítko Registrovat mého bota.
Úspěch vypadá takto: objeví se rámeček Přístupový token (zobrazen jen jednou) s dlouhým textem začínajícím na fsbm_.
2. Uschovejte token
Tento dlouhý text je token, heslo jen pro vašeho bota. Konektor jím serveru dokazuje, že mluví za tohoto bota. Zkopírujte ho celý a dočasně ho někam vložte, například do textového editoru. Po zavření rámečku už stejný token znovu neuvidíte.
Zacházejte s tokenem jako s heslem. Nikomu ho neukazujte a nezveřejňujte jeho snímek obrazovky. Pokud ho ztratíte nebo ho někdo uvidí, otevřete v seznamu botů Spravovat a stiskněte Vydat přístupový token. Jakmile dostanete nový token, starý okamžitě přestane fungovat.
3. Stáhněte konektor
Stisknutím Stáhnout příklady Python stáhněte soubor ZIP a rozbalte ho. Rozbalené složce tento návod říká „složka bota“. Umístěte ji tam, kde ji snadno najdete, například na plochu.
Úspěch vypadá takto: ve složce bota je soubor README.md a složka tools.
4. Otevřete terminál ve složce bota
Terminál je okno, do kterého se píší příkazy. Otevřete ho podle svého systému takto.
macOS: stiskněte Command+Space, napište Terminal a otevřete ho. Napište cd a jednu mezeru, přetáhněte složku bota do okna a stiskněte Enter.
Linux: ve správci souborů klikněte pravým tlačítkem na složku bota a zvolte Otevřít v terminálu. Název položky se může v různých distribucích lišit. Pokud taková položka chybí, otevřete terminál a použijte cd jako v macOS.
Windows: otevřete složku bota v Průzkumníku souborů. Do adresního řádku nahoře napište powershell a stiskněte Enter. V této složce se otevře okno PowerShell.
Kontrola: napište níže uvedený příkaz a stiskněte Enter. Úspěch znamená, že se v seznamu objeví README.md a tools. Tento příkaz je stejný v macOS, Linuxu i ve Windows PowerShell.
ls5. Ověřte Python
Ve stejném okně zkopírujte jeden příkaz pro svůj systém, vložte ho a stiskněte Enter. Můžete použít tlačítko kopírování vpravo nahoře v každém rámečku s kódem.
macOS / Linux
python3 --versionWindows PowerShell
py --versionÚspěch vypadá takto: verze začínající číslem 3, například Python 3.11.9. Funguje verze 3.9 nebo novější. Pokud příkaz nebyl nalezen, nainstalujte Python podle pokynů níže, zavřete okno a začněte znovu od kroku 4.
macOS: stáhněte z python.org instalátor pro macOS (.pkg) a nainstalujte ho. Potom jednou spusťte Install Certificates.command ze složky Python v Aplikacích. Tím se připraví certifikáty potřebné pro zabezpečená připojení (HTTPS).
Linux: nainstalujte python3 pomocí správce softwaru nebo správce balíčků své distribuce.
Windows: nainstalujte z python.org Python install manager.
6. Zadejte token
Uložte token do proměnné prostředí. Je to dočasné nastavení, které existuje jen v tomto okně terminálu. Jmenuje se FS_BOT_TOKEN. Níže zkopírujte jednořádkový příkaz pro svůj systém, vložte ho a jednou stiskněte Enter. Pak se zobrazí Bot token a čeká na váš vstup. Teď vložte token zkopírovaný v kroku 2 a stiskněte Enter.
macOS / Linux
printf 'Bot token: '; read -r -s FS_BOT_TOKEN; printf '\n'; export FS_BOT_TOKENWindows PowerShell
$env:FS_BOT_TOKEN = [Net.NetworkCredential]::new('', (Read-Host 'Bot token' -AsSecureString)).PasswordV macOS a Linuxu se vložený token nezobrazí a ve Windows místo jeho znaků uvidíte *. Obojí je normální. Protože token není zapsán v příkazu, nezůstane v historii příkazů. Nastavení platí jen v tomto okně. Pokud jste ho zavřeli nebo otevřeli nové okno, zopakujte kroky 4 a 6.
Windows: v PowerShell vkládejte pomocí Ctrl+V nebo kliknutím pravým tlačítkem. Tento jednořádkový příkaz používá zápis, který funguje ve Windows PowerShell 5.1 dodávaném s Windows i v novějším PowerShell 7. Na skutečném počítači s Windows ale zatím nebyl vyzkoušen.
Úspěch vypadá takto: výzva k zadání dalšího příkazu se vrátí bez chyby.
7. Spusťte konektor
Ve stejném okně zkopírujte níže uvedený příkaz beze změny, vložte ho a stiskněte Enter. Názvy souborů v něm neměňte. Váš bot se připojí s cvičným enginem. Je to velmi slabý engine, který hraje na legální pole s nejnižším číslem.
macOS / Linux
python3 tools/bot_match_client.py --engine 'python3 tools/bot_match_example_engine.py'Windows PowerShell
py tools/bot_match_client.py --engine 'py tools/bot_match_example_engine.py'Je normální, že příkaz za běhu nevypisuje text a nevrací se k příkazovému řádku. Chcete-li udržet bota online, nechte toto okno otevřené s běžícím připojovacím programem a enginem a zabraňte uspání počítače. Zavřením okna nebo stisknutím Ctrl+C ukončíte oba programy a bot bude offline. Znovu ho připojíte opětovným spuštěním příkazu výše. Připojovací program automaticky přijímá výzvy ostatních botů.
Dokud bot běží s cvičným enginem, nedostává automatické zápasy a jeho zápasy s jinými boty, i ty, o které požádáte sami, se nezapočítávají do hodnocení. Spusťte ho znovu s vlastním enginem (krok 10) a od té chvíle se páruje a hodnotí.
Úspěch vypadá takto: v seznamu Registrovaní boti na stránce botů je tečka vpravo od názvu vašeho bota zelená. Po najetí myší se zobrazí Online. Pokud je šedá (Offline), stiskněte Obnovit.
8. Vyzkoušejte první zápas
Na stránce botů vyberte svého bota v poli Můj hrající bot. V seznamu Boty FlipSight zvolte libovolného bota, stiskněte Vyzvat a potvrďte v okně, které se otevře. Ve všech druzích her počítá tahy botů FlipSight server, takže herní místnost nemusíte nechávat otevřenou. Při zapnutém automatickém párování (výchozí) web také domlouvá zápasy botů, dokud konektor běží.
Úspěch vypadá takto: herní místnost ukazuje dvě desky a na každém tahu vašeho bota se položí kámen. Cvičný engine je velmi slabý, takže prohra je normální.
9. Běh i po zavření okna (volitelné)
Místo kroku 7 spusťte níže uvedený příkaz a bot poběží i po zavření okna. Spusťte ho ve stejném okně, kde jste zadali token (krok 6). Zastaví se, pokud se počítač vypne nebo přejde do režimu spánku. Zastavení během zápasu znamená prohru tohoto zápasu na čas nebo odpojením.
macOS / Linux
macOS / Linux: po zadání příkazu pro spuštění můžete okno zavřít. Zprávy se zapisují do souboru bot.log ve složce bota. Chcete-li bota zastavit, otevřete nový terminál a zadejte příkaz pro zastavení. Kopírujte jen rámeček, který potřebujete: když vložíte oba rámečky najednou, bot se hned po spuštění zastaví.
Spuštění
nohup python3 tools/bot_match_client.py --engine 'python3 tools/bot_match_example_engine.py' > bot.log 2>&1 &Zastavení
pkill -f bot_match_client.pyWindows PowerShell
Windows: na hlavním panelu se objeví minimalizované okno, ve kterém bot běží. Aktuální okno PowerShell můžete zavřít. Chcete-li bota zastavit, otevřete minimalizované okno a stiskněte Ctrl+C, nebo toto okno zavřete.
Spuštění
Start-Process py -ArgumentList 'tools/bot_match_client.py --engine "py tools/bot_match_example_engine.py"' -WorkingDirectory $PWD -WindowStyle Minimized10. Přejděte na vlastní engine
Po ověření spojení přepněte na vlastní engine: speciální volby níže pro Edax nebo Egaroucid, --nboard pro jiné kompatibilní enginy, JSON pro vlastní engine. Token zůstává stejný.
Variantu hry (Anti, 10x10, Octagon, klasické Reversi) v Podporované hry zaškrtněte jen tehdy, když váš engine zná její pravidla. Ostatní boti i lidé mohou vašeho bota vyzvat v každé zaškrtnuté hře. Chcete-li to změnit, otevřete v seznamu botů Spravovat, upravte Podporované hry a stiskněte Uložit. Během probíhajícího zápasu nebo čekající výzvy to změnit nelze.
Příklady požadavků na engine podle hry
Připojení enginu s podporou NBoard, například Edax
NBoard je grafická aplikace pro Othello, kterou lze používat s NTest. Protokol NBoard je její veřejný formát komunikace s enginy a funguje s enginy, které ho podporují, například NTest a Edax.
Níže uvedené příklady instalace a spuštění používají Edax 4.6 a Egaroucid for Console 7.8.2.
Edax 4.6 není součástí připojovacího programu. Stáhněte soubor pro svůj systém z Assets na Edax 4.6, vložte jej do složky bota a rozbalte přípravnými příkazy níže. Příkazy pro Windows a Linux jsou pro x86-64.
Windows PowerShell (x86-64): edax-4.6-MS-windows-x86.zip
Příprava (jen poprvé)
Expand-Archive -LiteralPath edax-4.6-MS-windows-x86.zip -DestinationPath edax-4.6Spuštění
py tools/bot_match_client.py --edax "edax-4.6\wEdax-x86-64.exe"Linux (x86-64): edax-4.6-linux-x86.tar.gz
Příprava (jen poprvé)
mkdir -p edax-4.6
tar -xzf edax-4.6-linux-x86.tar.gz -C edax-4.6
chmod +x edax-4.6/lEdax-x86-64Spuštění
python3 tools/bot_match_client.py --edax "edax-4.6/lEdax-x86-64"macOS: edax-reversi-4.6.tar.gz
Oficiální spustitelný soubor Edax 4.6 pro macOS není k dispozici. Stáhněte Source code (tar.gz) a edax-4.6-linux-x86.tar.gz ze stejného vydání. Mac program sestavte ze zdrojového kódu; z linuxového archivu vyjměte pouze společný hodnoticí soubor data/eval.dat. Jsou potřeba nástroje Xcode Command Line Tools. Přípravné příkazy kompilují zdrojový kód.
Příprava (jen poprvé)
xcode-select --installtar -xzf edax-reversi-4.6.tar.gz
mv edax-reversi-4.6 edax-4.6
mkdir -p edax-4.6/bin
tar -xzf edax-4.6-linux-x86.tar.gz -C edax-4.6 data/eval.dat
(cd edax-4.6/src && make build ARCH=native OS=osx)Spuštění
python3 tools/bot_match_client.py --edax "edax-4.6/bin/mEdax-native"Edax lze připojit také pouhou cestou ke spustitelnému souboru pomocí --edax níže. Samostatný adaptér není potřeba; konektor rozděluje čas na tahy podle zbývajícího času partie.
Nahraďte cestu v příkazu úplnou cestou k vašemu programu. Cesty s mezerami uzavřete do dvojitých uvozovek. Pokud se soubor hodnocení nenajde automaticky, zadejte eval.dat pomocí --edax-eval.
Připojení přes NBoard hraje jen standardní partie 8×8. V Podporované hry nechte u tohoto bota zaškrtnuté jen Standard. Pokud přijde varianta hry, konektor se enginu nezeptá, žádný tah se nezahraje a partie se přeruší nebo prohraje na čas.
Egaroucid: --egaroucid
Připravte Egaroucid for Console. Zvolte konzolový program a ponechte složku resources na místě. Na macOS a Linuxu použijte program sestavený podle oficiálního návodu. Pozice a zbývající čas se posílají přes setboard, settime a go bez adaptéru.
Windows PowerShell
py tools/bot_match_client.py --egaroucid "egaroucid\Egaroucid_for_console.exe"macOS / Linux
python3 tools/bot_match_client.py --egaroucid "egaroucid/bin/Egaroucid_for_Console.out"Oba režimy podporují jen standardní 8×8, s jedním enginem pro každou souběžnou partii. --max-think-seconds je bezpečnostní limit na tah (výchozí 10 sekund). Pokud ho Egaroucid dosáhne, hledání se zastaví, zahraje se legální náhradní tah a engine se při dalším tahu znovu spustí.
Vlastní engine: připojení přes JSON
Tuto část čtěte jen tehdy, pokud chcete připojit engine, který jste sami napsali. Pokud používáte Edax, Egaroucid nebo jiný engine NBoard, máte už hotovo výše a můžete ji přeskočit.
Pokaždé, když je váš bot na tahu, konektor jednou spustí my_adapter.py. Předá mu aktuální pozici jako jeden řádek (kde leží kameny, kdo je na tahu, která pole jsou legální, kolik času zbývá), my_adapter.py odpoví jedním polem jako jedním řádkem a konektor tento tah zahraje na serveru. my_adapter.py je malý soubor v jazyce Python, který stojí mezi nimi a žádá váš engine o tah. O připojení k serveru, token a odesílání tahů se stará konektor, takže tento soubor se jimi nikdy nezabývá.
bot_match_client.py→ my_adapter.pybot_match_client.py← my_adapter.pyJakého druhu je váš engine?
- Engine, který vybírá tah v kódu Python: použijte příklad A níže.
- Soubor programu, který při každém spuštění vypíše jeden řádek odpovědi a skončí: použijte příklad B níže.
- Spustitelný soubor, který zůstává otevřený a průběžně si vyměňuje příkazy: pokud podporuje NBoard, připojte ho výše přes --nboard. U jiného protokolu upravte příklad B pro daný protokol a nechte my_adapter.py držet engine otevřený a vyměňovat po jednom řádku ve smyčce.
Krok 1: soubor, který jen ověří připojení (my_adapter.py)
Ve složce bota (kde jsou README.md a tools) vytvořte soubor s názvem my_adapter.py, vložte do něj níže uvedený kód beze změny a uložte ho. Tento soubor nepoužívá žádný engine a jednoduše odpoví prvním legálním polem. Slouží k ověření, že připojení funguje, ještě než připojíte svůj engine.
import json
import sys
def choose_move(position):
legal = position["legal_moves"]
return legal[0] if legal else -1
line = sys.stdin.readline()
position = json.loads(line)
move = choose_move(position)
print(json.dumps({"move": move}), flush=True)
Ověřte vstup a výstup před připojením
Příkaz v rámečku 1 pošle ukázkovou pozici přímo do adaptéru. Měl by vypsat jeden řádek: {"move": 19}. Jakmile tento řádek uvidíte, příkaz v rámečku 2 spustí konektor s tímto adaptérem, a to v okně, kde je token z kroku 6. Oba rámečky spouštějte zvlášť. Pokud váš engine čte tento formát JSON sám, můžete jeho příkaz zadat přímo za --engine.
1. Vyzkoušejte samotný adaptér
macOS / Linux
printf '%s\n' '{"board":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"color":1,"legal_moves":[19,26,37,44],"game_mode":"std","board_size":8,"blocked_squares":[],"placed":{"1":0,"2":0},"remaining_ms":60000,"match_id":123,"leg":0,"revision":0,"history":[],"round_no":null,"room_created_at":null,"budget_ms":10000}' | python3 my_adapter.pyWindows PowerShell
'{"board":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"color":1,"legal_moves":[19,26,37,44],"game_mode":"std","board_size":8,"blocked_squares":[],"placed":{"1":0,"2":0},"remaining_ms":60000,"match_id":123,"leg":0,"revision":0,"history":[],"round_no":null,"room_created_at":null,"budget_ms":10000}' | py my_adapter.py2. Spusťte konektor s adaptérem
macOS / Linux
python3 tools/bot_match_client.py --engine 'python3 my_adapter.py'Windows PowerShell
py tools/bot_match_client.py --engine 'py my_adapter.py'Krok 2: přechod na soubor, který volá váš engine
Jakmile krok 1 funguje, nahraďte celý obsah souboru my_adapter.py příkladem A nebo B níže. Změňte jen řádky označené EDIT a zbytek nechte tak, jak je. Potom znovu spusťte testovací příkaz 1 výše a zkontrolujte, že se objeví jeden řádek odpovědi. Tah vybral váš engine, takže jiné pole než 19 je v pořádku.
Příklad A: engine, který vybírá tah funkcí v jazyce Python
Tento příklad předpokládá soubor s názvem my_engine.py s funkcí search, která vybírá tah. U EDIT 1 změňte název souboru a název funkce a u EDIT 2 předávané hodnoty tak, aby odpovídaly vašemu enginu. Funkce má vrátit číslo pole (0 až 63) nebo souřadnici, například d3.
import json
import sys
from pathlib import Path
from my_engine import search # EDIT 1: your engine file and function name
def think_ms(position):
# Time for this move: share the remaining clock over my remaining moves.
my_moves_left = position["board"].count(0) // 2 + 1
share = position["remaining_ms"] / my_moves_left
return int(min(share, position["budget_ms"]) * 0.8)
def choose_move(position):
if not position["legal_moves"]:
return -1
# EDIT 2: pass what your function needs
return search(
board=position["board"],
color=position["color"],
legal_moves=position["legal_moves"],
time_limit_ms=think_ms(position),
)
position = json.loads(sys.stdin.readline())
legal = position["legal_moves"]
try:
move = choose_move(position)
except Exception as error:
# The engine failed: note why in my_adapter.log and play the first legal square.
with open(Path(__file__).with_name("my_adapter.log"), "a") as log:
log.write(repr(error) + "\n")
move = legal[0] if legal else -1
print(json.dumps({"move": move}), flush=True)Příklad B: engine jako soubor programu
Tento příklad předpokládá, že program přečte jeden řádek s 64 znaky desky, stranou na tahu a časem na přemýšlení v milisekundách a odpoví jedním řádkem se souřadnicí, například D3 (. = prázdné, X = černý, O = bílý). Každý engine má vlastní formát, proto u EDIT 1 změňte název souboru programu, u EDIT 2 řádek posílaný enginu a u EDIT 3 část, která čte odpověď, aby vše odpovídalo vašemu enginu.
my_adapter.py → my_engine
...........................OX......XO........................... X 1777my_engine → my_adapter.py
D3import json
import subprocess
import sys
from pathlib import Path
# EDIT 1: your engine program file (kept in the same folder as my_adapter.py)
ENGINE = Path(__file__).with_name("my_engine.exe" if sys.platform == "win32" else "my_engine")
def think_ms(position):
# Time for this move: share the remaining clock over my remaining moves.
my_moves_left = position["board"].count(0) // 2 + 1
share = position["remaining_ms"] / my_moves_left
return int(min(share, position["budget_ms"]) * 0.8)
def choose_move(position):
if not position["legal_moves"]:
return -1
# EDIT 2: build the line your engine reads
board = "".join(".XO"[cell] for cell in position["board"])
color = "X" if position["color"] == 1 else "O"
reply = subprocess.run(
[str(ENGINE)],
input=f"{board} {color} {think_ms(position)}\n",
text=True,
capture_output=True,
check=True,
timeout=position["budget_ms"] / 1000 * 0.9,
)
# EDIT 3: take the square out of your engine's answer
return reply.stdout.strip()
position = json.loads(sys.stdin.readline())
legal = position["legal_moves"]
try:
move = choose_move(position)
except Exception as error:
# The engine failed: note why in my_adapter.log and play the first legal square.
with open(Path(__file__).with_name("my_adapter.log"), "a") as log:
log.write(repr(error) + "\n")
move = legal[0] if legal else -1
print(json.dumps({"move": move}), flush=True)Čas na přemýšlení: v partiích botů má každý bot 1 minutu na každou partii, takže dlouhé přemýšlení nad jedním tahem rychle vede k prohře na čas. think_ms v příkladech dělí zbývající čas (remaining_ms) počtem tahů, které vám zbývají, a tak určí čas pro tento tah. budget_ms není čas, který se má na tento tah spotřebovat. Je to nejdelší doba, po kterou konektor čeká na odpověď (výchozí hodnota je 10 sekund). Pokud odpověď přijde později, bot nemůže v této partii pokračovat a prohraje ji na čas, proto svému enginu předávejte think_ms, ne budget_ms.
Když se něco pokazí: pokud volání enginu selže, ukázkové soubory zapíšou důvod do my_adapter.log a místo toho zahrají první legální pole. Pokud tahy vypadají divně, otevřete nejdřív tento soubor. Konektor chybové zprávy adaptéru na obrazovce nezobrazuje. Funkci print používejte jen pro jeden řádek odpovědi a ladicí text zapisujte do souboru.
Rychlost: připojovací program drží my_adapter.py spuštěný pro každou partii a posílá jeden řádek za tah. Soubor, který odpoví jedním řádkem a skončí, jako příklady, se při dalším tahu spustí znovu, takže i váš engine se pokaždé spouští znovu. Pokud se engine spouští pomalu nebo má navázat na práci z předchozích tahů, udělejte z my_adapter.py smyčku: přečti řádek, odpověz řádkem, opakuj. Nečtěte vstup až do konce a po každé odpovědi proveďte flush.
Zbytek této části je referenční materiál o tom, co si konektor a my_adapter.py vyměňují. Pokud použijete ukázkové soubory výše beze změny, nemusíte ho číst.
Vstupní JSON (konektor → adaptér)
Toto jsou ukázková data odeslaná konektorem, nikoli spustitelný kód. Neukládejte je jako soubor Pythonu. Během partie konektor automaticky posílá aktuální data při každém tahu.
Níže je příklad standardní počáteční pozice 8×8. Ve skutečných partiích konektor doplní aktuální pozici. board vypisuje pole od a1 doprava, pak další řádek; 0=prázdné, 1=černé, 2=bílé. color=1 žádá tah černého. legal_moves vypisuje pole, kam smíte táhnout. remaining_ms je zbývající čas na hodinách partie a budget_ms nejdelší doba, po kterou konektor čeká na odpověď pro tento tah, obojí v milisekundách (1000 = 1 sekunda). Pole a příklady pro varianty najdete níže v části o variantách.
{"board":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"color":1,"legal_moves":[19,26,37,44],"game_mode":"std","board_size":8,"blocked_squares":[],"placed":{"1":0,"2":0},"remaining_ms":60000,"match_id":123,"leg":0,"revision":0,"history":[],"round_no":null,"room_created_at":null,"budget_ms":10000}Odpověď JSON (adaptér → konektor)
Zapište jednu z níže uvedených odpovědí jako jeden řádek JSON zakončený novým řádkem. move je číslo pole nebo souřadnice, na velikosti písmen nezáleží. Na desce 8×8 je d3 (3−1)×8+3 = 19. Vždy vybírejte z obdržených legal_moves. Ve dvojicích botů bez legálního tahu pošlete -1 nebo pass; ve hrách proti člověku řeší vynucená vynechání server. Na stdout zapisujte jen řádek odpovědi, žádné jiné zprávy.
{"move":19}{"move":"d3"}{"move":-1}Příklady požadavků na engine podle hry
Příklady prvního požadavku za černého, který konektor posílá enginu --engine, vygenerované kódem pravidel. Skutečné partie všech typů začínají z náhodné pozice s 12 kameny, takže board a legal_moves se v každé partii liší. Aby příklady zůstaly pevné, byly jejich úvodní tahy zvoleny blízko středu desky. Ve skutečných partiích se liší také match_id a hodnoty hodin.
game_mode: Jedna z hodnot std, anti, grand, octagon, reversi. XOT ve hrách proti člověku přichází jako std.board_size: 8, nebo 10 pro grand a octagon.board: Pole délky board_size². Čte se od a1 doprava, pak další řádek. 0=prázdné (včetně blokovaných polí Octagonu), 1=černé, 2=bílé.blocked_squares: Nepoužitelná pole Octagonu. V ostatních hrách prázdné pole.placed: Dosud položené kameny podle barvy včetně náhodných úvodních tahů; klíče jsou řetězce "1" a "2". Slouží pro limit 32 kamenů v klasickém Reversi.
Anti (anti)
Vstupní JSON (konektor → adaptér)
{
"board": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"color": 1,
"legal_moves": [2, 3, 12, 25, 42, 44, 45],
"game_mode": "anti",
"board_size": 8,
"blocked_squares": [],
"placed": {
"1": 4,
"2": 4
},
"remaining_ms": 60000,
"match_id": 123,
"leg": 0,
"revision": 0,
"history": [],
"round_no": null,
"room_created_at": null,
"budget_ms": 10000
}Odpověď JSON (adaptér → konektor)
{"move":2}Grand (10x10) (grand)
Vstupní JSON (konektor → adaptér)
{
"board": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"color": 1,
"legal_moves": [13, 14, 25, 42, 63, 65, 66],
"game_mode": "grand",
"board_size": 10,
"blocked_squares": [],
"placed": {
"1": 4,
"2": 4
},
"remaining_ms": 60000,
"match_id": 123,
"leg": 0,
"revision": 0,
"history": [],
"round_no": null,
"room_created_at": null,
"budget_ms": 10000
}Odpověď JSON (adaptér → konektor)
{"move":13}Octagon (octagon)
Vstupní JSON (konektor → adaptér)
{
"board": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"color": 1,
"legal_moves": [13, 14, 25, 42, 63, 65, 66],
"game_mode": "octagon",
"board_size": 10,
"blocked_squares": [0, 1, 10, 8, 9, 19, 80, 90, 91, 89, 98, 99],
"placed": {
"1": 4,
"2": 4
},
"remaining_ms": 60000,
"match_id": 123,
"leg": 0,
"revision": 0,
"history": [],
"round_no": null,
"room_created_at": null,
"budget_ms": 10000
}Odpověď JSON (adaptér → konektor)
{"move":13}Klasické reversi (reversi)
Vstupní JSON (konektor → adaptér)
{
"board": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, 2, 1, 1, 0, 0, 0, 0, 2, 2, 1, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"color": 1,
"legal_moves": [18, 25, 33, 41, 42, 50, 51],
"game_mode": "reversi",
"board_size": 8,
"blocked_squares": [],
"placed": {
"1": 6,
"2": 6
},
"remaining_ms": 60000,
"match_id": 123,
"leg": 0,
"revision": 0,
"history": [],
"round_no": null,
"room_created_at": null,
"budget_ms": 10000
}Odpověď JSON (adaptér → konektor)
{"move":18}Časté problémy
| Příznak | Příčina a řešení |
|---|---|
| Bot má šedou tečku (offline). | Zkontrolujte, zda okno konektoru stále běží. Zastaví se, když okno zavřete, stisknete Ctrl+C nebo počítač usne. Zopakujte kroky 6 a 7 a potom stiskněte Obnovit. |
Hlásí, že příkaz nebyl nalezen.command not found / is not recognized | Python chybí, nebo jste po instalaci neotevřeli okno znovu. Podívejte se na krok 5, otevřete nové okno a začněte znovu od kroku 4. |
Hlásí, že soubor nelze otevřít.can't open file | Příkaz jste spustili mimo složku bota. Přejděte do složky bota jako v kroku 4 a pomocí ls ověřte, že se zobrazí README.md a tools. |
Hlásí, že chybí token, a ukončí se.FS_BOT_TOKEN is missing or invalid | V tomto okně není token. Pokud jste otevřeli nové okno, zopakujte krok 6. |
Hlásí, že token byl odmítnut, a zastaví se.Bot credential rejected | Token byl vložen chybně, nebo je starý. Po vydání nového tokenu nebo deaktivaci bota starý token přestane fungovat. Začněte znovu od kroku 6 s nejnovějším tokenem. |
Stále se opakuje stejný řádek s chybou.Connection/state error | Zkontrolujte připojení k internetu. V macOS ověřte, že jste spustili Install Certificates.command z kroku 5. Pokud se chyba objevuje jen během partií, server možná odmítá tah vašeho enginu jako nelegální. Konektor posílá stejný tah znovu, takže je potřeba opravit engine. |
| Tlačítko výzvy nefunguje, nebo výzva není přijata. | Oba boti musí mít zelenou tečku. Zkontrolujte, zda je váš bot vybrán v poli Můj hrající bot. Bot, který už hraje jiný zápas, novou výzvu nepřijme. Nepřijatá výzva vyprší po 120 sekundách. |
Bot netáhne a pak prohraje na čas, nebo se zápas přeruší.Engine failed for match | Engine neodpověděl včas, nebo měla jeho odpověď špatný formát. Zkontrolujte příkaz enginu a jeho jednořádkový výstup JSON. Pokud bot neudělá první tah do 30 sekund od začátku, zápas se přeruší. |
| Nepřicházejí výzvy na varianty hry. | Okno výzvy nabízí jen hry, které podporují oba boti. Zkontrolujte, zda je hra zaškrtnutá v Podporované hry vašeho bota a zda ji podporuje i druhý bot. Bot připojený přes NBoard by měl mít zaškrtnuté jen Standard. |
Slovníček pojmů
| Pojem | Význam |
|---|---|
| API | Dohodnutý způsob, jak si programy posílají požadavky a odpovědi. Konektor to zařídí za vás. |
| Token | Heslo jen pro vašeho bota. Konektor jím serveru dokazuje, o kterého bota jde. |
| Terminál | Okno, do kterého se píší příkazy. Ve Windows použijte PowerShell. |
| Proměnná prostředí | Dočasné nastavení předávané programům spuštěným z jednoho okna terminálu. Po zavření okna zmizí. |
| JSON | Formát pro zápis dat jako textu. Příklad: {"move":19} |
| Engine | Program, který posoudí pozici a vybere tah. |
| Adaptér | Malý program, který převádí data mezi vaším enginem a konektorem. |
| NBoard | NBoard je grafická aplikace pro Othello, kterou lze používat s NTest. Protokol NBoard je její veřejný formát komunikace s enginy a funguje s enginy, které ho podporují, například NTest a Edax. |
| Legální tah | Pole, kam právě smíte táhnout. Server je vypisuje v legal_moves. |
Stažení
Ke stažení se přihlaste. .zip obsahuje návod, konektor a ukázkový engine; .json je specifikace API a .md je návod.
Podrobná specifikace
Zbytek této stránky je podrobná referenční dokumentace pro vývojáře, kteří pracují přímo s enginem nebo s API.
Volby konektoru
Volby pro tools/bot_match_client.py. Povinná je právě jedna z voleb --engine a --nboard / --edax / --egaroucid. Token nikdy není volbou; čte se jen z proměnné prostředí FS_BOT_TOKEN. Návratové kódy: 0 běžné ukončení, 2 token odmítnut, 3 dosažen --max-runtime.
| Parametry | Popis |
|---|---|
--engine "COMMAND" | Spustí tento příkaz jednou za partii a za každý tah vymění jeden řádek JSON, aniž by zavřel jeho vstup. Engine, který odpoví jedním řádkem a skončí, se při dalším tahu spustí znovu. |
--nboard "COMMAND" | Ponechá jeden engine NBoard na partii, celkem dva. Jen standardní hra (std). |
--edax PATH | Edax lze připojit také pouhou cestou ke spustitelnému souboru pomocí --edax níže. Samostatný adaptér není potřeba; konektor rozděluje čas na tahy podle zbývajícího času partie. |
--edax-eval PATH | Cesta k Edax eval.dat. Bez zadání se hledá data/eval.dat ve složce programu nebo její nadřazené složce. |
--egaroucid PATH | Připravte Egaroucid for Console. Zvolte konzolový program a ponechte složku resources na místě. Na macOS a Linuxu použijte program sestavený podle oficiálního návodu. Pozice a zbývající čas se posílají přes setboard, settime a go bez adaptéru. |
--nboard-depth N | Pošle enginu NBoard set depth N. Hodnota od 0 do 60; 0 ponechá výchozí nastavení enginu. (--edax: 0 → 60). |
--max-think-seconds S | Nejdelší doba v sekundách, po kterou se na engine čeká u jednoho tahu. Výchozí 10, nejvýše 300. Nikdy delší než zbývající čas na hodinách minus 0,75 sekundy. Jde o bezpečnostní limit konektoru; server žádný časový limit na tah nemá. Hodnota 0 odstraní limit na tah a použije zbývající čas partie. |
--engine-name NAME | Název enginu uváděný v informacích o botovi (nejvýše 120 znaků). Neuvádějte příkazy ani cesty. |
--cpu-name NAME | Název CPU k ohlášení (nejvýše 120 znaků). Pokud ho vynecháte, zjistí se automaticky. |
--once-match ID | Zpracuje jen tento zápas a po jeho skončení se ukončí (dokončen, přerušen, vypršel, odmítnut). Fungují i záporná ID her proti člověku. Pro testy ji kombinujte s --max-runtime. |
--max-runtime S | Po tomto počtu sekund ukončí enginy a skončí (návratový kód 3). |
--no-stream | Nepoužívá proud událostí; jen se pravidelně dotazuje. |
--api URL, --stream-url URL | Adresy API a proudu. Obvykle ponechte výchozí hodnoty. Přijímá se jen HTTPS, a protože se posílá i token, proud musí být na hostiteli API. |
Vlastní implementace API
Výzvy a pozice přijímejte pomocí agent_poll nebo ověřeného proudu událostí. U čekajících výzev, kde jste bot_b, pošlete agent_accept a potom počítejte jen pozice, kde jste na tahu, v každé partii probíhajícího zápasu. Pro každou kombinaci match_id, partie a revision odešlete nejvýše jednou a při změně stavu zastaralý výpočet zahoďte. Hry proti člověku mají záporné match_id a leg=0 a vyžadují round_no a room_created_at přesně tak, jak byly přijaty. V běžných místnostech řeší vynucená vynechání server; vynechání odesílají jen dvojice botů.
agent_poll / bot-stream → pending (bot_b = my_id)
→ agent_accept → running → legs[0] + legs[1]
→ agent_move(match_id, leg, revision, move) → finished / abortedVarianty hry
Bot dostává jen hry zaškrtnuté v Podporované hry. Hru určuje game_mode v požadavcích na engine a ve stavu zápasu. Velikost desky, blokovaná pole i legální pole počítá server, takže engine jen vybírá z obdržených legal_moves. Negenerujte tahy pro 10×10 ani pro klasické Reversi generátorem tahů pro standardní 8×8.
| Hra | game_mode | Deska | Počáteční kameny | Čas (na partii a barvu) | První legal_moves černého |
|---|---|---|---|---|---|
| Standard | std | 8×8 | Náhodných 12 kamenů | 60,000 ms | Závisí na pozici |
| Anti | anti | 8×8 | Náhodných 12 kamenů | 60,000 ms | Závisí na pozici |
| Grand (10x10) | grand | 10×10 | Náhodných 12 kamenů | 60,000 ms | Závisí na pozici |
| Octagon | octagon | 10×10 (88) | Náhodných 12 kamenů | 60,000 ms | Závisí na pozici |
| Klasické reversi | reversi | 8×8 | Náhodných 12 kamenů | 60,000 ms | Závisí na pozici |
Společná pravidla: Černý (1) táhne první. Obě partie začínají ze stejné pozice s prohozenými barvami a běží současně. Pokud ve dvojici botů nemáte legální tah, přijde legal_moves jako prázdné pole a bot musí sám poslat move=-1 (vynechání). Server za vás nevynechá a vaše hodiny běží dál. Vynechat, když existuje legální tah, je nelegální. Partie končí, když nemůže táhnout ani jedna barva. Prázdná pole se nepřidělují nikomu; sčítají se jen skutečné kameny z obou partií a stejné součty znamenají remízu. Všechny partie botů začínají z náhodné pozice s 12 kameny. Ve všech typech hry má každý bot 1 minutu na každou partii.
Anti: pokládání a otáčení funguje jako ve standardním Othellu. Vyhrává strana s menším počtem kamenů v součtu obou partií.
10x10: standardní pravidla na desce 10×10 (100 polí). Počáteční rozestavení tvoří čtyři středová pole; dvojice botů k němu přidávají 8 náhodných tahů a začínají s 12 kameny.
Octagon: stejná deska a výchozí rozestavení jako 10x10, ale tři pole v každém rohu, celkem 12, jsou nepoužitelná (88 hratelných polí). Blokovaná pole přicházejí jako čísla polí v blocked_squares a v board jako 0, stejná hodnota jako prázdné pole. Pole není seřazené podle čísel. Na blokované pole nelze táhnout a řady kamenů se na něm zastaví.
Klasické Reversi: podle pravidel začíná na prázdné desce. Dokud je některé ze čtyř středových polí (27, 28, 35, 36) prázdné, pokládá se jen na ně a nic se neotáčí. Jakmile jsou všechna čtyři obsazená, platí standardní pravidla. Každá barva položí nejvýše 32 kamenů včetně prvních čtyř tahů. placed počítá kameny, které každá barva dosud položila; jeho klíče jsou řetězce "1" (černý) a "2" (bílý). Dvojice botů začínají poté, co server odehrál 12 náhodných položení včetně čtyř středových tahů, takže v prvním požadavku dávají obě hodnoty placed dohromady 12 (obvykle {"1":6,"2":6}). Barva, která položila všech 32, dostává prázdné legal_moves a na každém svém tahu posílá -1.
Příklady požadavků na engine podle hry
Příklady prvního požadavku za černého, který konektor posílá enginu --engine, vygenerované kódem pravidel. Skutečné partie všech typů začínají z náhodné pozice s 12 kameny, takže board a legal_moves se v každé partii liší. Aby příklady zůstaly pevné, byly jejich úvodní tahy zvoleny blízko středu desky. Ve skutečných partiích se liší také match_id a hodnoty hodin.
Anti (anti)
{"board":[0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,2,2,0,0,0,0,0,1,2,2,1,0,0,0,0,2,2,2,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"color":1,"legal_moves":[2,3,12,25,42,44,45],"game_mode":"anti","board_size":8,"blocked_squares":[],"placed":{"1":4,"2":4},"remaining_ms":60000,"match_id":123,"leg":0,"revision":0,"history":[],"round_no":null,"room_created_at":null,"budget_ms":10000}Grand (10x10) (grand)
{"board":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"color":1,"legal_moves":[13,14,25,42,63,65,66],"game_mode":"grand","board_size":10,"blocked_squares":[],"placed":{"1":4,"2":4},"remaining_ms":60000,"match_id":123,"leg":0,"revision":0,"history":[],"round_no":null,"room_created_at":null,"budget_ms":10000}Octagon (octagon)
{"board":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,1,2,2,1,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"color":1,"legal_moves":[13,14,25,42,63,65,66],"game_mode":"octagon","board_size":10,"blocked_squares":[0,1,10,8,9,19,80,90,91,89,98,99],"placed":{"1":4,"2":4},"remaining_ms":60000,"match_id":123,"leg":0,"revision":0,"history":[],"round_no":null,"room_created_at":null,"budget_ms":10000}Klasické reversi (reversi)
{"board":[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,2,2,1,1,0,0,0,0,2,2,1,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"color":1,"legal_moves":[18,25,33,41,42,50,51],"game_mode":"reversi","board_size":8,"blocked_squares":[],"placed":{"1":6,"2":6},"remaining_ms":60000,"match_id":123,"leg":0,"revision":0,"history":[],"round_no":null,"room_created_at":null,"budget_ms":10000}Pole požadavku na engine
| Pole | Typ | Popis |
|---|---|---|
game_mode | string | Jedna z hodnot std, anti, grand, octagon, reversi. XOT ve hrách proti člověku přichází jako std. |
board_size | integer 8 | 10 | 8, nebo 10 pro grand a octagon. |
board | integer[board_size²] | Pole délky board_size². Čte se od a1 doprava, pak další řádek. 0=prázdné (včetně blokovaných polí Octagonu), 1=černé, 2=bílé. |
blocked_squares | integer[] | Nepoužitelná pole Octagonu. V ostatních hrách prázdné pole. |
placed | {"1": integer, "2": integer} | Dosud položené kameny podle barvy včetně náhodných úvodních tahů; klíče jsou řetězce "1" a "2". Slouží pro limit 32 kamenů v klasickém Reversi. |
legal_moves | integer[] | Pole, kam právě smíte táhnout. Pokud je prázdné, odpovězte move=-1. |
color | integer 1 | 2 | Barva na tahu. 1=černý, 2=bílý. |
budget_ms | integer | Čas povolený pro tento tah v milisekundách: menší z hodnot --max-think-seconds a zbývajícího času na hodinách minus 0,75 sekundy. Při překročení konektor engine zastaví. |
remaining_ms | integer | Zbývající čas vaší barvy v této partii v milisekundách, po odečtení času už spotřebovaného v tomto tahu. |
match_id | integer ≠ 0 | Id zápasu. U her proti člověku záporné (-číslo místnosti). |
leg | integer 0 | 1 | Číslo partie 0 nebo 1. U her proti člověku vždy 0. |
revision | integer | Číslo stavu této partie. Pošlete ho beze změny s agent_move. |
history | array | Tahy zahrané v této partii. Rozhodujte podle board. |
round_no | integer | null | Číslo kola hry proti člověku. Pro dvojice botů null. |
room_created_at | string | null | Čas vytvoření místnosti hry proti člověku. Pro dvojice botů null. |
Výzva jiného bota na variantu
Do agent_challenge přidejte game_mode; výchozí je std. Oba boti ji musí mít v supported_games. Hry soupeře zjistíte z bots[].supported_games v odpovědi agent_poll. Pokud některému z botů hra chybí, odpověď je HTTP 409 s níže uvedenou chybou.
{"action":"agent_challenge","target_id":2,"game_mode":"anti"}
{"action":"agent_challenge","target_name":"Opponent","game_mode":"grand"}HTTP 409
{"ok":false,"error":"unsupported game mode","server_now_ms":1800000000000}Změna supported_games
Registrační formulář začíná se zaškrtnutým Standard a starší boti bez uloženého seznamu podporují std. Seznam lze změnit jen v Spravovat na webové stránce botů, nikdy tokenem bota (API). Během probíhajícího zápasu nebo zápasu čekajícího na přijetí se změna odmítne. Prázdný seznam nelze uložit.
NBoard a varianty
Připojení --nboard podporuje jen standardní hru (std). Když přijde varianta hry, konektor se enginu nezeptá a tento tah vzdá (zpráva Engine failed for match). Žádný tah se neodešle, takže se zápas přeruší před prvním tahem, nebo se tato partie později prohraje na čas. Botovi s NBoard nastavte jako jedinou podporovanou hru standardní hru.
Lidé proti registrovaným botům
Lidé mohou vašeho bota vyzvat v kterékoli hře, kterou má registrovanou. Pokud podporuje standardní hru, může přijít STD i XOT. XOT přichází s game_mode std a začíná z desky, na které už bylo zahráno 8 tahů (jsou uvedeny v history). Čas volí vyzyvatel a partie je bez hodnocení. Hra proti člověku má jednu partii (leg 0) a záporné match_id. Když tah není možný, vynechání řeší server, proto neposílejte -1. Registrovaní online boti se zapnutým automatickým párováním se účastní i podporovaných rychlých zápasů STD/XOT. Ty jsou hodnocené pro členy a nehodnocené pro hosty.
Nelegální tahy a čas
Nelegální tah nebo zastaralá revision se odmítne s HTTP 409 (illegal_move, stale_revision, not_your_turn) a deska se nezmění. Hodiny mezitím běží. Konektor posílá stejný tah znovu se stejnou revision, takže pokud engine neopravíte, tato partie se nakonec prohraje na čas. Server nemá limit na tah a kontroluje jen zbývající čas každé barvy. Oněch 10 sekund je výchozí hodnota --max-think-seconds konektoru.
Ověření
Použijte HTTPS, JSON POST a Authorization: Bearer. Token patří jednomu botovi. Nevkládejte jej do URL, veřejného kódu ani logů.
Content-Type: application/json
Authorization: Bearer <FS_BOT_TOKEN>POST https://othelloclub.com/flipsight/bot_match_api.php
Pravidla zápasu
Všechny partie botů začínají z náhodné pozice s 12 kameny. Dvě partie běží současně s prohozenými barvami. Rozhoduje součet kamenů; v Anti vyhrává menší počet.
Všechny partie botů začínají z náhodné pozice s 12 kameny. Ve všech typech hry má každý bot 1 minutu na každou partii.
Pokud bot neudělá první tah do 30 sekund od začátku, zápas se přeruší a nezapočítá se do hodnocení. Pokud je během zápasu spojení jednoho bota přerušeno déle než 30 sekund, zatímco druhý je připojen, vyhrává připojený bot (finish_reason: disconnect) a výsledek se do hodnocení započítá.
Tahy botů FlipSight počítá server ve všech druzích her. Přijímají se výzvy z webu i přes API (agent_challenge) a server zápas hned spustí, takže herní místnost nemusíte nechávat otevřenou. Když se výpočet serveru zastaví na více než 30 sekund, zápas je přerušen (finish_reason: technical) a nezapočítá se do hodnocení. Varianty jsou náročné na výpočet, proto najednou běží nejvýše 2 zápasy variant proti botům FlipSight. Když je plno, dostanete HTTP 409 s site bot variant matches are full; zkuste to za chvíli znovu.
Automatické zápasy botů: web má neustále otevřené dvě místnosti pro zápasy botů. Dokud je registrovaný bot se zapnutým automatickým párováním online a nehraje, dostává výzvu ke standardnímu zápasu botů (std) proti botovi s podobným hodnocením (nejprve jiný registrovaný bot, jinak bot FlipSight) jako bot_b. Konektor výzvu automaticky přijme a zápas začne. Když bot do 120 sekund nepřijme nebo odmítne, je na 10 minut vyřazen z automatického párování. Automatické zápasy se započítávají do hodnocení. Automatické zápasy botů pokračují i během partií v Aréně. Automatické párování je ve výchozím stavu zapnuté a vypnete ho v části Spravovat na stránce botů. Bot spuštěný s cvičným enginem (environment.practice je true) je vyřazen z automatických zápasů a jeho zápasy s jinými boty v té době se nezapočítávají do hodnocení.
Přímá lidská výzva se hraje jako jedna partie v běžné místnosti s podporovanou hrou a časem. Server vždy vynucuje nehodnocený režim přímých výzev. Registrovaní online boti se zapnutým automatickým párováním se účastní i podporovaných rychlých zápasů STD/XOT. Ty jsou hodnocené pro členy a nehodnocené pro hosty. Registrovaný bot přijme jen tolik partií, kolik dovolí nastavení souběžných partií (výchozí 2). Partie s člověkem i zápas botů (dvě desky) se počítají každá jako 1.
pending → running → finished / aborted
pending → rejected / expired
kind=human: id=-room_id, legs[0], round_no, room_created_at
agent_move: move=19 / "d3" / "D3" (8×8)
10×10: "j10" = "J0" = 99
pass: -1 / "pass" / "PA" / "PS" / "--" (paired games only)
index=row*board_size+column (0-based)Průběh zápasu
Všechny partie botů začínají z náhodné pozice s 12 kameny. Dvě partie běží současně s prohozenými barvami. Rozhoduje součet kamenů; v Anti vyhrává menší počet.
Vaše číslo účastníka je 0, pokud jste bot_a, a 1, pokud jste bot_b. Účastník 0 hraje černými (1) v legs[0] a bílými (2) v legs[1]; účastník 1 naopak. Jste na tahu, když má leg.status hodnotu active a leg.turn odpovídá vaší barvě. Na tahu můžete být v obou partiích zároveň.
Pokud nemáte žádný legální tah, musíte sami odeslat move -1 (vynechání); server za vás nevynechá. Ve hrách proti člověku server řeší vynechání stejně jako v běžných herních místnostech.
Na vašem tahu platí: zbývající čas = clocks_ms[vaše barva - 1] - (server_now_ms - turn_started_ms). clocks_ms je v pořadí barev (černý, bílý); totals a winner jsou v pořadí účastníků (winner 0 nebo 1, null při remíze).
Čísla polí
Číslo pole = řádek × 8 + sloupec, takže a1=0, h1=7, a2=8 a h8=63. Například 19 je d3 a 37 je f5. Pole board používá stejné pořadí: 0=prázdné, 1=černé, 2=bílé.
a b c d e f g h
1 0 1 2 3 4 5 6 7
2 8 9 10 11 12 13 14 15
3 16 17 18 19 20 21 22 23
4 24 25 26 27 28 29 30 31
5 32 33 34 35 36 37 38 39
6 40 41 42 43 44 45 46 47
7 48 49 50 51 52 53 54 55
8 56 57 58 59 60 61 62 63Na desce 10×10 (grand, octagon) platí: pole = řádek × 10 + sloupec. Sloupce jsou a až j a desátý řádek se v souřadnicích píše jako 10 nebo 0. a1=0, j1=9, a2=10, j10=99 (totéž jako j0). Stejná souřadnice má na každé velikosti desky jiné číslo: d3 je 19 na 8×8 a 23 na 10×10.
a b c d e f g h i j
1 0 1 2 3 4 5 6 7 8 9
2 10 11 12 13 14 15 16 17 18 19
3 20 21 22 23 24 25 26 27 28 29
4 30 31 32 33 34 35 36 37 38 39
5 40 41 42 43 44 45 46 47 48 49
6 50 51 52 53 54 55 56 57 58 59
7 60 61 62 63 64 65 66 67 68 69
8 70 71 72 73 74 75 76 77 78 79
9 80 81 82 83 84 85 86 87 88 89
10 90 91 92 93 94 95 96 97 98 99Octagon (octagon)
a b c d e f g h i j
1 ## ## 2 3 4 5 6 7 ## ##
2 ## 11 12 13 14 15 16 17 18 ##
3 20 21 22 23 24 25 26 27 28 29
4 30 31 32 33 34 35 36 37 38 39
5 40 41 42 43 44 45 46 47 48 49
6 50 51 52 53 54 55 56 57 58 59
7 60 61 62 63 64 65 66 67 68 69
8 70 71 72 73 74 75 76 77 78 79
9 ## 81 82 83 84 85 86 87 88 ##
10 ## ## 92 93 94 95 96 97 ## ##Proud událostí
Připojte se se stejným tokenem Bearer na GET https://othelloclub.com/flipsight_api/bot-stream a přijímejte jednu událost JSON na řádek (NDJSON): hello potvrzuje spojení, match znamená, že se změnil některý z vašich zápasů (události mezi boty obsahují celý objekt zápasu), a ping každých 10 sekund udržuje spojení. Po přijetí match zavolejte jednou agent_poll. Pokud se spojení přeruší, připojte se znovu s prodlevou rostoucí od 1 do 30 sekund a mezitím se dál dotazujte. Dokud je proud připojen, váš bot se zobrazuje jako online; každý bot může otevřít nejvýše 3 proudy.
Příklady událostí
curl -N 'https://othelloclub.com/flipsight_api/bot-stream' \
-H "Authorization: Bearer $FS_BOT_TOKEN"
{"type":"hello","bot_id":1,"server_now_ms":1800000000000}
{"type":"match","match":{"id":123,"bot_a":1,"bot_b":2,"status":"running","state":{...}},"server_now_ms":1800000000500}
{"type":"match","match":{"id":-45,"kind":"human","room_id":45},"server_now_ms":1800000003000}
{"type":"ping","server_now_ms":1800000010000}Adaptér enginu
Připojovací program botů FlipSight (Python 3.9+, jen standardní knihovna) běží na Windows, macOS a Linuxu a připojuje engine dvěma způsoby. --engine spustí engine jednou za partii, pro každý tah pošle jeden řádek JSON a přečte jeden řádek {"move": pole}. Engine, který odpoví jedním řádkem a skončí, funguje dál, protože se při dalším tahu spustí znovu, ale engine, který čte vstup až do konce (sys.stdin.read() apod.), nikdy neodpoví, proto čtěte po jednom řádku. legal_moves v požadavku uvádí legální pole; je-li seznam prázdný, odpovězte -1. --nboard drží spuštěný engine NBoard, jeden na partii; hloubku nastavíte přes --nboard-depth.
Edax lze připojit také pouhou cestou ke spustitelnému souboru pomocí --edax níže. Samostatný adaptér není potřeba; konektor rozděluje čas na tahy podle zbývajícího času partie.
Připravte Egaroucid for Console. Zvolte konzolový program a ponechte složku resources na místě. Na macOS a Linuxu použijte program sestavený podle oficiálního návodu. Pozice a zbývající čas se posílají přes setboard, settime a go bez adaptéru.
Vstupní JSON (konektor → adaptér)
Toto jsou ukázková data odeslaná konektorem, nikoli spustitelný kód. Neukládejte je jako soubor Pythonu. Během partie konektor automaticky posílá aktuální data při každém tahu.
{"board":[0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,2,2,0,0,0,0,0,1,2,2,1,0,0,0,0,2,2,2,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"color":1,"legal_moves":[2,3,12,25,42,44,45],"game_mode":"std","board_size":8,"blocked_squares":[],"placed":{"1":4,"2":4},"remaining_ms":40000,"match_id":123,"leg":1,"revision":0,"history":[],"round_no":null,"room_created_at":null,"budget_ms":10000}Odpověď JSON (adaptér → konektor)
{"move":2}Synchronizace stavu
Při připojeném proudu volejte agent_poll při každé události a každých 10 sekund. Bez proudu se dotazujte každou sekundu během zápasu a každých 5 sekund v klidu, nikdy však častěji než jednou za sekundu. Síťová komunikace se odečítá z vašeho času. Pokud se ztratí odpověď na tah, načtěte nový stav a stejný tah odešlete znovu jen tehdy, je-li revision stále stejná.
Akce API
Příklady odpovědí ukazují formát. Tlačítko kopírování zkopíruje jen požadavek a nevolá API.
POST agent_poll
Načíst stav
| Parametry | Typ | Povinné | Popis |
|---|---|---|---|
action | string "agent_poll" | Povinné | Název volané akce |
environment | object | Volitelné | CPU, OS a název enginu na počítači s konektorem (každé nejvýše 120 znaků, volitelné) a practice (true nebo false, volitelné). practice true znamená, že bot trénuje: je vyřazen z automatických zápasů a zápasy botů vyžádané nebo přijaté v té době se nezapočítávají do hodnocení. Konektor hodnotu posílá sám, když je spuštěn s cvičným enginem. Server si pamatuje poslední hodnotu, takže po skončení tréninku ji pošlete bez practice nebo s false. |
Příklad požadavku
curl 'https://othelloclub.com/flipsight/bot_match_api.php' \
-H "Authorization: Bearer $FS_BOT_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"action":"agent_poll","environment":{"cpu":"Apple M4","engine":"MyEngine","os":"Darwin"}}'Příklad odpovědi
{
"ok": true,
"bots": [
{
"id": 1,
"name": "Opponent",
"owner_id": "example",
"owner_name": "Example",
"owner": {
"player_id": "example",
"name": "Example",
"photo": "/img/no_profile.gif",
"country_code": "",
"country_name": "",
"country_flag_url": "",
"donor": false,
"verified_player_id": ""
},
"engine_level": null,
"rating": {
"rating": 1200,
"rd": 300,
"official": false,
"official_progress": {
"kind": "provisional",
"current": 0,
"required": 50
},
"color": "pz-rating-unofficial",
"high_rating": 1200,
"wins": 0,
"losses": 0,
"draws": 0,
"streak": 0,
"best_streak": 0
},
"supported_games": ["std"],
"auto_match": true,
"bio": "",
"registered_at": "2026-11-12 18:00:00",
"registered_at_ts": 1794474000,
"environment": {
"cpu": "Apple M4",
"engine": "MyEngine",
"os": "Darwin",
"practice": false
},
"is_builtin": false,
"online": true,
"disabled": false,
"deleted": false,
"photo": "/flipsight/bot_match_api.php?action=photo&bot_id=1&v=1794474000000"
},
{
"id": 2,
"name": "MyBot",
"owner_id": "example",
"owner_name": "Example",
"owner": {
"player_id": "example",
"name": "Example",
"photo": "/img/no_profile.gif",
"country_code": "",
"country_name": "",
"country_flag_url": "",
"donor": false,
"verified_player_id": ""
},
"engine_level": null,
"rating": {
"rating": 1200,
"rd": 300,
"official": false,
"official_progress": {
"kind": "provisional",
"current": 0,
"required": 50
},
"color": "pz-rating-unofficial",
"high_rating": 1200,
"wins": 0,
"losses": 0,
"draws": 0,
"streak": 0,
"best_streak": 0
},
"supported_games": ["std", "anti"],
"auto_match": true,
"bio": "",
"registered_at": "2026-11-12 18:00:00",
"registered_at_ts": 1794474000,
"environment": {
"cpu": "Apple M4",
"engine": "MyEngine",
"os": "Darwin",
"practice": false
},
"is_builtin": false,
"online": true,
"disabled": false,
"deleted": false,
"photo": "/flipsight/bot_match_api.php?action=photo&bot_id=2&v=1794474000000"
}
],
"record_bots": [],
"matches": [
{
"id": 123,
"bot_a": 1,
"bot_b": 2,
"game_mode": "std",
"time_limit_sec": 60,
"status": "running",
"state": {
"version": 1,
"game_mode": "std",
"time_limit_sec": 60,
"status": "active",
"revision": 1,
"started_ms": 1799999980000,
"last_now_ms": 1799999984200,
"finished_ms": null,
"opening_moves": [19, 20, 29, 34, 26, 11, 43, 37],
"opening_history": [
{
"color": 1,
"move": 19
},
{
"color": 2,
"move": 20
},
{
"color": 1,
"move": 29
},
{
"color": 2,
"move": 34
},
{
"color": 1,
"move": 26
},
{
"color": 2,
"move": 11
},
{
"color": 1,
"move": 43
},
{
"color": 2,
"move": 37
}
],
"legs": [
{
"board": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 2, 1, 2, 2, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"turn": 2,
"black_participant": 0,
"placed": {
"1": 5,
"2": 4
},
"clocks_ms": [55800, 60000],
"turn_started_ms": 1799999984200,
"revision": 1,
"history": [
{
"color": 1,
"participant": 0,
"move": 44,
"at_ms": 1799999984200
}
],
"status": "active",
"discs": [5, 8],
"finished_ms": null,
"legal_moves": [18, 21, 22, 25, 30, 33, 38, 42, 50, 51, 52]
},
{
"board": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"turn": 1,
"black_participant": 1,
"placed": {
"1": 4,
"2": 4
},
"clocks_ms": [60000, 60000],
"turn_started_ms": 1799999980000,
"revision": 0,
"history": [],
"status": "active",
"discs": [3, 9],
"finished_ms": null,
"legal_moves": [2, 3, 12, 25, 42, 44, 45]
}
],
"totals": [14, 11],
"winner": null,
"finish_reason": null,
"board_size": 8,
"blocked_squares": []
},
"auto": false,
"expires_ms": 1800000080000,
"ranked": true,
"rating_applied": false,
"rating_before": [null, null],
"rating_after": [null, null],
"created_at": "2026-11-12 19:00:00"
}
],
"me": "example",
"bot": {
"id": 2,
"name": "MyBot",
"owner_id": "example",
"owner_name": "Example",
"owner": {
"player_id": "example",
"name": "Example",
"photo": "/img/no_profile.gif",
"country_code": "",
"country_name": "",
"country_flag_url": "",
"donor": false,
"verified_player_id": ""
},
"engine_level": null,
"rating": {
"rating": 1200,
"rd": 300,
"official": false,
"official_progress": {
"kind": "provisional",
"current": 0,
"required": 50
},
"color": "pz-rating-unofficial",
"high_rating": 1200,
"wins": 0,
"losses": 0,
"draws": 0,
"streak": 0,
"best_streak": 0
},
"supported_games": ["std", "anti"],
"auto_match": true,
"bio": "",
"registered_at": "2026-11-12 18:00:00",
"registered_at_ts": 1794474000,
"environment": {
"cpu": "Apple M4",
"engine": "MyEngine",
"os": "Darwin",
"practice": false
},
"is_builtin": false,
"online": true,
"disabled": false,
"deleted": false,
"photo": "/flipsight/bot_match_api.php?action=photo&bot_id=2&v=1794474000000"
},
"server_now_ms": 1800000000000
}Objekt zápasu (matches[])
| Pole | Typ | Popis |
|---|---|---|
id | integer | ID zápasu (záporné pro hry proti člověku) |
kind | string | human pro hry proti člověku |
bot_a | integer | ID bota účastníka 0 (0 pro člověka) |
bot_b | integer | ID bota účastníka 1 (0 pro člověka) |
game_mode | string | Hra (std, anti, grand, octagon, reversi) |
time_limit_sec | integer | Sekundy na partii a barvu |
status | string | pending, running, finished, aborted, rejected, expired |
expires_ms | integer | Lhůta pro přijetí (milisekundy) |
ranked | boolean | Zda se zápas započítává do hodnocení |
state.status | string | active, finished nebo aborted |
state.game_mode | string | Hra (std, anti, grand, octagon, reversi) |
state.board_size | integer 8 | 10 | Počet polí na stranu, 8 nebo 10 |
state.blocked_squares | integer[] | Nepoužitelná pole (jen Octagon, neseřazená) |
state.legs | array | Partie (2 mezi boty, 1 proti člověku) |
state.totals | [integer, integer] | Součty kamenů z obou partií podle účastníka |
state.winner | integer | null | Vítězný účastník 0 nebo 1, null při remíze |
state.finish_reason | string | null | discs, timeout, double_timeout, disconnect, no_first_move |
Objekt partie (state.legs[])
| Pole | Typ | Popis |
|---|---|---|
board | integer[64 | 100] | 64 / 100 polí, 0=prázdné 1=černé 2=bílé |
turn | integer | Barva na tahu, 1 (černý) nebo 2 (bílý) |
legal_moves | integer[] | Legální pole pro barvu na tahu. U skončených partií prázdné |
placed | {"1": integer, "2": integer} | Položené kameny podle barvy, klíče "1" (černý) a "2" (bílý) |
black_participant | integer | Účastník, který v této partii hraje černými |
clocks_ms | [number, number] | Výchozí hodnoty zbývajícího času v pořadí barev (černý, bílý) |
turn_started_ms | integer | Čas serveru, kdy začal aktuální tah |
revision | integer | Zvýší se o 1 při každé změně této partie |
status | string | active, finished, timeout, stopped |
history | array | Odehrané tahy (color, participant, move, at_ms) |
discs | [integer, integer] | Počty kamenů v pořadí barev (černý, bílý) |
result_discs | [integer, integer] (optional) | Volitelné stanovené skóre [černý, bílý] pro partii prohranou na čas; sčítá se místo kamenů. Skóre pro stranu, které vypršel čas, a pro jejího soupeře: std a reversi 0 a 64, grand 0 a 100, octagon 0 a 88, anti 64 a 0. Druhá partie pokračuje a o vítězi rozhodnou součty po skončení obou partií. Starší dokončené záznamy se nepřepočítávají. |
POST agent_challenge
Vyzvat
Zadejte target_id (číslo bota) nebo target_name (jméno bota), najdete je v seznamu bots v odpovědi agent_poll. Vestavěné boty FlipSight (is_builtin: true) lze přes API vyzvat ve všech druzích her a server zápas hned spustí. Když už probíhají 2 zápasy variant proti vestavěným botům, dostanete HTTP 409 s site bot variant matches are full.
| Parametry | Typ | Povinné | Popis |
|---|---|---|---|
action | string "agent_challenge" | Povinné | Název volané akce |
target_id | integer ≥ 1 | Volitelné | ID vyzývaného bota |
target_name | string | Volitelné | Jméno vyzývaného bota (místo target_id) |
game_mode | string std | anti | octagon | grand | reversi | Volitelné | Hra pro dvojici botů. Výchozí je std. Oba boti ji musí podporovat. Standard / Anti / Octagon / Grand (10x10) / Klasické reversi |
Příklad požadavku
curl 'https://othelloclub.com/flipsight/bot_match_api.php' \
-H "Authorization: Bearer $FS_BOT_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"action":"agent_challenge","target_id":2,"game_mode":"anti"}'Příklad odpovědi
{
"ok": true,
"match_id": 123,
"server_now_ms": 1800000000000
}POST agent_accept
Přijmout
| Parametry | Typ | Povinné | Popis |
|---|---|---|---|
action | string "agent_accept" | Povinné | Název volané akce |
match_id | integer ≥ 1 | Povinné | ID zápasu (záporné pro hry proti člověku) |
Příklad požadavku
curl 'https://othelloclub.com/flipsight/bot_match_api.php' \
-H "Authorization: Bearer $FS_BOT_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"action":"agent_accept","match_id":123}'Příklad odpovědi
{
"ok": true,
"match": {
"id": 123,
"status": "running",
"bot_a": 1,
"bot_b": 2
},
"server_now_ms": 1800000000000
}POST agent_reject
Odmítnout
| Parametry | Typ | Povinné | Popis |
|---|---|---|---|
action | string "agent_reject" | Povinné | Název volané akce |
match_id | integer ≥ 1 | Povinné | ID zápasu (záporné pro hry proti člověku) |
Příklad požadavku
curl 'https://othelloclub.com/flipsight/bot_match_api.php' \
-H "Authorization: Bearer $FS_BOT_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"action":"agent_reject","match_id":123}'Příklad odpovědi
{
"ok": true,
"match": {
"id": 123,
"status": "rejected",
"bot_a": 1,
"bot_b": 2
},
"server_now_ms": 1800000000000
}POST agent_move
Odeslat tah
| Parametry | Typ | Povinné | Popis |
|---|---|---|---|
action | string "agent_move" | Povinné | Název volané akce |
match_id | integer ≠ 0 | Povinné | ID zápasu (záporné pro hry proti člověku) |
round_no | integer ≥ 0 | Volitelné | Jen pro hry proti člověku: round_no z objektu zápasu |
room_created_at | string | Volitelné | Jen pro hry proti člověku: room_created_at z objektu zápasu |
leg | integer 0 | 1 | Povinné | Partie 0 nebo 1 (0 pro hry proti člověku) |
revision | integer ≥ 0 | Povinné | Revision partie, pro kterou byl tah spočítán; 409 stale_revision, pokud se partie změnila |
move | integer -1..99 | string | Povinné | Pole 0-(board_size²−1), -1 pro vynechání / d3 / D3 / pass |
Příklad požadavku
curl 'https://othelloclub.com/flipsight/bot_match_api.php' \
-H "Authorization: Bearer $FS_BOT_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"action":"agent_move","match_id":123,"leg":1,"revision":0,"move":2}'Příklad odpovědi
{
"ok": true,
"match": {
"id": 123,
"status": "running",
"bot_a": 1,
"bot_b": 2
},
"server_now_ms": 1800000000000
}Chyby
Chyby mají tvar {"ok": false, "error": "…"}. Při 401 zastavte a ověřte token. Při 409 načtěte aktuální stav a ověřte, kdo je na tahu, revision a legální tahy. 429 znamená příliš mnoho proudů jednoho bota. Chyby spojení a 503 opakujte s rostoucí prodlevou a nikdy znovu neposílejte nelegální tah.
{"ok":false,"error":"stale_revision","server_now_ms":1800000000000}| HTTP | error |
|---|---|
| 400 | JSON object required / unknown action |
| 401 | invalid bot credential |
| 403 | access denied / invalid csrf |
| 405 | POST required |
| 409 | stale_revision / not_your_turn / illegal_move / leg_finished / match not running / match timed out / stale room revision / invalid move coordinate / move outside board / unsupported game mode / both bots must be connected / bot already has an active match / bot busy / challenge expired or answered / site bot variant matches are full / target bot not found |
| 413 | request too large |
| 429 | too many streams (bot-stream) |
| 503 | bot match storage unavailable |