Schritt-für-Schritt-Anleitung für Einsteiger
Was Sie brauchen
- Ein FlipSight-Konto. Nach der Anmeldung können Sie einen Bot registrieren und das Verbindungsprogramm herunterladen.
- Ein Botfoto: eine GIF-, JPG- oder PNG-Datei mit höchstens 5 MB, mindestens 175 px breit und hoch.
- Dein eigener Computer, auf dem der Bot läuft Die Züge deines Bots werden auf dem Computer berechnet, auf dem der Connector läuft. Auch mit derselben Engine kann die Spielstärke je nach Leistung des Computers und Einstellungen der Engine anders sein. Wenn du Computer oder Einstellungen änderst, kann sich die Wertung entsprechend bewegen.
- Python 3, ein kostenloses Programm, das das Verbindungsprogramm ausführt. Version 3.9 oder neuer funktioniert; 3.11 oder neuer wird empfohlen. Das prüfen Sie in Schritt 5.
- Deine Bot-Engine. Das Verbindungsprogramm enthält eine Übungsengine zum Testen.
1. Bot registrieren
Melden Sie sich an und öffnen Sie die Bot-Seite (othelloclub.com/de/flipsight/bot). Geben Sie im Bereich Meinen Bot registrieren oben einen Namen bei Botname (2–24 Zeichen) ein. Lassen Sie bei Unterstützte Spiele vorerst nur Standard angehakt. Wählen Sie bei Botfoto hochladen ein Bild und drücken Sie dann die Schaltfläche Meinen Bot registrieren.
So sieht Erfolg aus: Der Bereich Verbindungstoken (nur einmal sichtbar) erscheint mit einem langen Text, der mit fsbm_ beginnt.
2. Token aufbewahren
Dieser lange Text ist das Token, ein Passwort nur für Ihren Bot. Das Verbindungsprogramm weist damit gegenüber dem Server nach, dass es für diesen Bot spricht. Kopieren Sie es vollständig und fügen Sie es vorübergehend irgendwo ein, zum Beispiel in einen Texteditor. Sobald Sie den Bereich schließen, können Sie dasselbe Token nicht mehr sehen.
Behandeln Sie das Token wie ein Passwort. Zeigen Sie es niemandem und veröffentlichen Sie keinen Screenshot davon. Wenn Sie es verlieren oder jemand es gesehen hat, öffnen Sie Verwalten in der Botliste und drücken Sie Verbindungstoken ausstellen. Sobald Sie ein neues Token erhalten, funktioniert das alte sofort nicht mehr.
3. Verbindungsprogramm herunterladen
Drücken Sie Python-Beispiele herunterladen, um die ZIP-Datei herunterzuladen, und entpacken Sie sie. Diese Anleitung nennt den entpackten Ordner „Bot-Ordner“. Legen Sie ihn an einen leicht auffindbaren Ort, zum Beispiel auf den Schreibtisch.
So sieht Erfolg aus: Der Bot-Ordner enthält die Datei README.md und den Ordner tools.
4. Terminal im Bot-Ordner öffnen
Ein Terminal ist ein Fenster, in das Sie Befehle eingeben. Öffnen Sie es je nach System wie folgt.
macOS: Drücken Sie Command+Space, tippen Sie Terminal und öffnen Sie es. Tippen Sie cd und ein Leerzeichen, ziehen Sie den Bot-Ordner in das Fenster und drücken Sie dann die Eingabetaste.
Linux: Klicken Sie im Dateimanager mit der rechten Maustaste auf den Bot-Ordner und wählen Sie „Im Terminal öffnen“. Der Menüname kann je nach Distribution abweichen. Gibt es kein solches Menü, öffnen Sie ein Terminal und verwenden Sie cd wie unter macOS.
Windows: Öffnen Sie den Bot-Ordner im Datei-Explorer. Tippen Sie oben in die Adressleiste powershell und drücken Sie die Eingabetaste. Ein PowerShell-Fenster öffnet sich in diesem Ordner.
Prüfen: Tippen Sie den folgenden Befehl und drücken Sie die Eingabetaste. Erfolg bedeutet, dass README.md und tools in der Liste erscheinen. Dieser Befehl ist unter macOS, Linux und Windows PowerShell gleich.
ls5. Python prüfen
Kopieren Sie im selben Fenster den einen Befehl für Ihr System, fügen Sie ihn ein und drücken Sie die Eingabetaste. Sie können die Kopierschaltfläche oben rechts in jedem Codefeld verwenden.
macOS / Linux
python3 --versionWindows PowerShell
py --versionSo sieht Erfolg aus: eine Version, die mit 3 beginnt, zum Beispiel Python 3.11.9. Version 3.9 oder neuer funktioniert. Wird der Befehl nicht gefunden, installieren Sie Python wie unten beschrieben, schließen Sie das Fenster und beginnen Sie erneut bei Schritt 4.
macOS: Laden Sie den macOS-Installer (.pkg) von python.org herunter und installieren Sie ihn. Führen Sie danach einmal Install Certificates.command im Python-Ordner unter Programme aus. Damit werden die Zertifikate für sichere Verbindungen (HTTPS) eingerichtet.
Linux: Installieren Sie python3 mit der Softwareverwaltung oder dem Paketmanager Ihrer Distribution.
Windows: Installieren Sie den Python install manager von python.org.
6. Token eingeben
Legen Sie das Token in einer Umgebungsvariablen ab, einer vorübergehenden Einstellung, die nur in diesem Terminalfenster existiert. Ihr Name ist FS_BOT_TOKEN. Kopieren Sie unten den einzeiligen Befehl für Ihr System, fügen Sie ihn ein und drücken Sie einmal die Eingabetaste. Dann erscheint Bot token und wartet auf Ihre Eingabe. Fügen Sie jetzt das in Schritt 2 kopierte Token ein und drücken Sie die Eingabetaste.
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)).PasswordUnter macOS und Linux wird das eingefügte Token nicht angezeigt, unter Windows sehen Sie * statt der Zeichen. Beides ist normal. Da das Token nicht im Befehl steht, bleibt es nicht im Befehlsverlauf. Die Einstellung gilt nur in diesem Fenster. Wenn Sie es geschlossen oder ein neues Fenster geöffnet haben, wiederholen Sie die Schritte 4 und 6.
Windows: In PowerShell fügen Sie mit Ctrl+V oder einem Rechtsklick ein. Dieser einzeilige Befehl verwendet eine Schreibweise, die sowohl in Windows PowerShell 5.1, das mit Windows geliefert wird, als auch im neueren PowerShell 7 funktioniert. Auf einem echten Windows-Rechner wurde er aber noch nicht getestet.
So sieht Erfolg aus: Die Eingabeaufforderung für den nächsten Befehl erscheint ohne Fehler.
7. Verbindungsprogramm starten
Kopieren Sie im selben Fenster den folgenden Befehl unverändert, fügen Sie ihn ein und drücken Sie die Eingabetaste. Ändern Sie die Dateinamen darin nicht. Ihr Bot verbindet sich mit der Übungs-Engine, einer sehr schwachen Engine, die auf das legale Feld mit der niedrigsten Nummer setzt.
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'Es ist normal, dass der Befehl während der Ausführung keinen Text ausgibt und die Eingabeaufforderung nicht zurückkehrt. Damit dein Bot online bleibt, lass dieses Fenster mit dem Verbindungsprogramm und der Engine geöffnet und verhindere den Ruhezustand des Computers. Wenn du das Fenster schließt oder Ctrl+C drückst, werden beide Programme beendet und dein Bot geht offline. Führe den obigen Befehl erneut aus, um ihn wieder online zu bringen. Das Verbindungsprogramm nimmt Anfragen anderer Bots automatisch an.
Solange der Bot mit der Übungs-Engine läuft, bekommt er keine automatischen Partien, und Botpartien, auch selbst angefragte, werden nicht gewertet. Starte ihn mit deiner eigenen Engine neu (Schritt 10), dann wird er ab da automatisch gepaart und gewertet.
So sieht Erfolg aus: In der Liste Registrierte Bots auf der Bot-Seite ist der Punkt rechts neben dem Namen Ihres Bots grün. Wenn Sie mit der Maus darüberfahren, erscheint Online. Ist er grau (Offline), drücken Sie Aktualisieren.
8. Mit einer ersten Partie testen
Wählen Sie auf der Bot-Seite Ihren Bot bei Mein spielender Bot. Wählen Sie einen beliebigen Bot in der Liste FlipSight-Bots, drücken Sie Herausfordern und bestätigen Sie im sich öffnenden Fenster. In allen Spielarten berechnet der Server die Züge der FlipSight-Bots, Sie müssen den Spielraum also nicht offen lassen. Ist die automatische Paarung eingeschaltet (Standard), setzt die Website zudem Botpartien an, solange das Verbindungsprogramm läuft.
So sieht Erfolg aus: Der Spielraum zeigt zwei Bretter, und bei jedem Zug Ihres Bots wird ein Stein gesetzt. Die Übungs-Engine ist sehr schwach, Niederlagen sind also normal.
9. Nach dem Schließen des Fensters weiterlaufen lassen (optional)
Starten Sie statt mit Schritt 7 mit dem folgenden Befehl, dann läuft der Bot nach dem Schließen des Fensters weiter. Führen Sie ihn in dem Fenster aus, in dem Sie das Token eingegeben haben (Schritt 6). Er stoppt, wenn der Computer herunterfährt oder in den Ruhezustand geht. Wenn Sie ihn während einer Partie stoppen, geht diese Partie durch Zeitüberschreitung oder Verbindungsabbruch verloren.
macOS / Linux
macOS / Linux: Nachdem Sie den Startbefehl ausgeführt haben, können Sie das Fenster schließen. Meldungen werden in die Datei bot.log im Bot-Ordner geschrieben. Zum Stoppen öffnen Sie ein neues Terminal und führen den Stoppbefehl aus. Kopieren Sie nur das Feld, das Sie brauchen: Wenn Sie beide Felder auf einmal einfügen, stoppt der Bot direkt nach dem Start.
Starten
nohup python3 tools/bot_match_client.py --engine 'python3 tools/bot_match_example_engine.py' > bot.log 2>&1 &Stoppen
pkill -f bot_match_client.pyWindows PowerShell
Windows: In der Taskleiste erscheint ein minimiertes Fenster, in dem der Bot läuft. Sie können das aktuelle PowerShell-Fenster schließen. Zum Stoppen öffnen Sie das minimierte Fenster und drücken Ctrl+C oder schließen dieses Fenster.
Starten
Start-Process py -ArgumentList 'tools/bot_match_client.py --engine "py tools/bot_match_example_engine.py"' -WorkingDirectory $PWD -WindowStyle Minimized10. Zur eigenen Engine wechseln
Nach dem Verbindungstest wechseln Sie zur eigenen Engine: Edax und Egaroucid nutzen die eigenen Optionen unten, andere kompatible Engines --nboard und eigene Engines JSON. Das Token bleibt gleich.
Haken Sie eine Variante (Anti, 10x10, Octagon, klassisches Reversi) bei Unterstützte Spiele nur an, wenn Ihre Engine deren Regeln kennt. Andere Bots und Menschen können Ihren Bot in jedem angehakten Spiel herausfordern. Zum Ändern öffnen Sie Verwalten in der Botliste, bearbeiten Unterstützte Spiele und drücken Speichern. Während eine Partie läuft oder eine Herausforderung wartet, ist keine Änderung möglich.
Beispiele für Engine-Anfragen je Spiel
Eine NBoard-fähige Engine wie Edax verbinden
NBoard ist eine grafische Othello-Anwendung, die mit NTest genutzt werden kann. Das NBoard-Protokoll ist ihr veröffentlichtes Format für die Kommunikation mit Engines und funktioniert mit unterstützenden Engines wie NTest und Edax.
Die folgenden Installations- und Startbeispiele verwenden Edax 4.6 und Egaroucid for Console 7.8.2.
Edax 4.6 ist nicht im Verbindungsprogramm enthalten. Lade die Datei für dein Betriebssystem unter Assets auf Edax 4.6 herunter, lege sie im Bot-Ordner ab und entpacke sie mit den Vorbereitungsbefehlen unten. Die Windows- und Linux-Befehle sind für x86-64.
Windows PowerShell (x86-64): edax-4.6-MS-windows-x86.zip
Vorbereiten (nur beim ersten Mal)
Expand-Archive -LiteralPath edax-4.6-MS-windows-x86.zip -DestinationPath edax-4.6Starten
py tools/bot_match_client.py --edax "edax-4.6\wEdax-x86-64.exe"Linux (x86-64): edax-4.6-linux-x86.tar.gz
Vorbereiten (nur beim ersten Mal)
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-64Starten
python3 tools/bot_match_client.py --edax "edax-4.6/lEdax-x86-64"macOS: edax-reversi-4.6.tar.gz
Für Edax 4.6 gibt es keine offizielle macOS-Programmdatei. Lade Source code (tar.gz) und edax-4.6-linux-x86.tar.gz derselben Veröffentlichung herunter. Erstelle das Mac-Programm aus dem Quellcode; aus dem Linux-Archiv wird nur data/eval.dat übernommen. Xcode Command Line Tools sind erforderlich. Die Vorbereitungsbefehle kompilieren den Quellcode.
Vorbereiten (nur beim ersten Mal)
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)Starten
python3 tools/bot_match_client.py --edax "edax-4.6/bin/mEdax-native"Edax lässt sich unten auch nur mit dem Programmpfad über --edax verbinden. Ein eigener Adapter ist nicht nötig; die Denkzeit pro Zug wird aus der verbleibenden Spielzeit verteilt.
Ersetzen Sie den Pfad im Befehl durch den vollständigen Pfad Ihrer Programmdatei. Pfade mit Leerzeichen stehen in doppelten Anführungszeichen. Wird die Bewertungsdatei nicht automatisch gefunden, geben Sie eval.dat mit --edax-eval an.
Eine NBoard-Verbindung spielt nur Standardpartien auf 8×8. Lassen Sie für diesen Bot bei Unterstützte Spiele nur Standard angehakt. Kommt eine Variante, fragt das Verbindungsprogramm die Engine nicht, es wird kein Zug gespielt, und die Partie wird annulliert oder durch Zeitüberschreitung verloren.
Egaroucid: --egaroucid
Bereiten Sie Egaroucid for Console vor. Verwenden Sie die Konsolenanwendung und lassen Sie resources an seinem Platz. Unter macOS und Linux verwenden Sie eine nach offizieller Anleitung erstellte Programmdatei. Der Konnektor überträgt Stellung und Restzeit über setboard, settime und go; ein Adapter ist nicht nötig.
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"Beide Modi unterstützen nur Standard 8×8 und starten bei zwei Partien je eine Engine. --max-think-seconds begrenzt einen Zug sicherheitshalber (Standard: 10 Sekunden). Erreicht Egaroucid diese Grenze, wird die Suche beendet, ein legaler Ersatzzug gespielt und die Engine beim nächsten Zug neu gestartet.
Selbst geschriebene Engine: über JSON verbinden
Lesen Sie diesen Teil nur, wenn Sie eine selbst geschriebene Engine anschließen möchten. Wenn Sie Edax, Egaroucid oder eine andere NBoard-Engine verwenden, sind Sie oben bereits fertig und können ihn überspringen.
Jedes Mal, wenn Ihr Bot am Zug ist, startet das Verbindungsprogramm my_adapter.py einmal. Es übergibt die aktuelle Stellung als eine Zeile (wo die Steine liegen, wer am Zug ist, welche Felder legal sind, wie viel Zeit übrig ist), my_adapter.py antwortet mit einem Feld als eine Zeile, und das Verbindungsprogramm spielt diesen Zug auf dem Server. my_adapter.py ist eine kleine Python-Datei dazwischen, die Ihre Engine nach dem Zug fragt. Das Verbindungsprogramm kümmert sich um die Verbindung zum Server, das Token und das Senden der Züge, diese Datei hat damit also nie etwas zu tun.
bot_match_client.py→ my_adapter.pybot_match_client.py← my_adapter.pyWelche Art von Engine haben Sie?
- Eine Engine, die ihren Zug in Python-Code auswählt: Verwenden Sie Beispiel A unten.
- Eine Programmdatei, die bei jedem Start eine Antwortzeile ausgibt und sich dann beendet: Verwenden Sie Beispiel B unten.
- Eine Programmdatei, die geöffnet bleibt und laufend Befehle austauscht: Wenn sie NBoard unterstützt, verbinde sie oben mit --nboard. Bei einem anderen Protokoll passe Beispiel B an dieses Protokoll an und lass my_adapter.py die Engine geöffnet halten und zeilenweise in einer Schleife austauschen.
Schritt 1: eine Datei, die nur die Verbindung prüft (my_adapter.py)
Erstellen Sie im Bot-Ordner (dort, wo README.md und tools liegen) eine Datei namens my_adapter.py, fügen Sie den folgenden Code unverändert ein und speichern Sie sie. Diese Datei verwendet keine Engine und antwortet einfach mit dem ersten legalen Feld. Sie dient dazu, zu prüfen, ob die Verbindung funktioniert, bevor Sie Ihre Engine anschließen.
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)
Ein- und Ausgabe vor dem Verbinden prüfen
Der Befehl in Feld 1 gibt die Beispielstellung direkt an den Adapter. Er sollte eine Zeile ausgeben: {"move": 19}. Sobald Sie diese Zeile sehen, starten Sie mit dem Befehl in Feld 2 das Verbindungsprogramm mit diesem Adapter, und zwar in dem Fenster, in dem das Token aus Schritt 6 gesetzt ist. Führen Sie die beiden Felder getrennt aus. Wenn Ihre Engine dieses JSON-Format selbst liest, können Sie ihren Befehl direkt hinter --engine schreiben.
1. Nur den Adapter testen
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. Verbindungsprogramm mit dem Adapter starten
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'Schritt 2: zu einer Datei wechseln, die Ihre Engine aufruft
Sobald Schritt 1 funktioniert, ersetzen Sie den gesamten Inhalt von my_adapter.py durch Beispiel A oder B unten. Ändern Sie nur die mit EDIT markierten Zeilen und lassen Sie den Rest, wie er ist. Führen Sie dann den Testbefehl 1 oben noch einmal aus und prüfen Sie, ob eine Antwortzeile erscheint. Den Zug hat jetzt Ihre Engine gewählt, ein anderes Feld als 19 ist also normal.
Beispiel A: eine Engine, die ihren Zug mit einer Python-Funktion auswählt
Dieses Beispiel geht von einer Datei namens my_engine.py mit einer Funktion search aus, die den Zug auswählt. Ändern Sie bei EDIT 1 den Dateinamen und den Funktionsnamen und bei EDIT 2 die übergebenen Werte, sodass sie zu Ihrer Engine passen. Die Funktion sollte die Feldnummer (0 bis 63) oder eine Koordinate wie d3 zurückgeben.
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)Beispiel B: eine Engine als Programmdatei
Dieses Beispiel geht davon aus, dass das Programm eine Zeile mit 64 Brettzeichen, der Seite am Zug und der Bedenkzeit in Millisekunden liest und mit einer Koordinatenzeile wie D3 antwortet (. = leer, X = Schwarz, O = Weiß). Jede Engine hat ihr eigenes Format. Ändern Sie deshalb bei EDIT 1 den Namen der Programmdatei, bei EDIT 2 die Zeile, die an die Engine gesendet wird, und bei EDIT 3 den Teil, der die Antwort liest, sodass alles zu Ihrer Engine passt.
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)Bedenkzeit: Bei Bot-Partien hat jeder Bot pro Partie 1 Minute. Wer lange über einen einzelnen Zug nachdenkt, verliert daher schnell durch Zeitüberschreitung. think_ms in den Beispielen teilt die verbleibende Zeit (remaining_ms) durch die Zahl Ihrer verbleibenden Züge und legt so die Zeit für diesen Zug fest. budget_ms ist nicht die Zeit, die für diesen Zug verbraucht werden soll. Es ist die längste Zeit, die das Verbindungsprogramm auf die Antwort wartet (standardmäßig 10 Sekunden). Kommt die Antwort später, kann der Bot diese Partie nicht fortsetzen und verliert sie durch Zeitüberschreitung. Übergeben Sie Ihrer Engine deshalb think_ms und nicht budget_ms.
Wenn etwas schiefgeht: Schlägt der Aufruf der Engine fehl, schreiben die Beispieldateien den Grund in my_adapter.log und spielen stattdessen das erste legale Feld. Wenn die Züge seltsam aussehen, öffnen Sie zuerst diese Datei. Das Verbindungsprogramm zeigt die Fehlermeldungen des Adapters nicht auf dem Bildschirm an. Verwenden Sie print nur für die eine Antwortzeile und schreiben Sie Text zur Fehlersuche in eine Datei.
Geschwindigkeit: Das Verbindungsprogramm hält my_adapter.py pro Partie geöffnet und sendet pro Zug eine Zeile. Eine Datei, die wie die Beispiele eine Zeile beantwortet und sich beendet, wird beim nächsten Zug neu gestartet, also startet auch deine Engine jedes Mal neu. Startet die Engine langsam oder soll sie die Arbeit früherer Züge weiterverwenden, mach aus my_adapter.py eine Schleife: eine Zeile lesen, eine Zeile antworten, wiederholen. Lies die Eingabe nicht bis zum Ende und flushe nach jeder Antwort.
Der Rest dieses Teils ist Nachschlagematerial dazu, was das Verbindungsprogramm und my_adapter.py austauschen. Wenn Sie die Beispieldateien oben unverändert verwenden, müssen Sie ihn nicht lesen.
Eingabe-JSON (Verbindungsprogramm → Adapter)
Dies sind Beispieldaten des Verbindungsprogramms, kein ausführbarer Code. Speichern Sie sie nicht als Python-Datei. Während einer Partie werden die aktuellen Daten bei jedem Zug automatisch gesendet.
Unten sehen Sie ein Beispiel mit der Standard-Startstellung auf 8×8. In echten Partien trägt das Verbindungsprogramm die aktuelle Stellung ein. board listet die Felder ab a1 nach rechts, dann die nächste Reihe; 0=leer, 1=Schwarz, 2=Weiß. color=1 fragt nach einem Zug für Schwarz. legal_moves listet die Felder, auf die Sie setzen dürfen. remaining_ms ist die verbleibende Bedenkzeit der Partie und budget_ms die längste Zeit, die das Verbindungsprogramm auf die Antwort zu diesem Zug wartet, beide in Millisekunden (1000 = 1 Sekunde). Felder und Beispiele für Varianten stehen weiter unten im Abschnitt zu den Varianten.
{"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}Antwort-JSON (Adapter → Verbindungsprogramm)
Schreiben Sie eine der folgenden Antworten als einzelne JSON-Zeile, die mit einem Zeilenumbruch endet. move ist eine Feldnummer oder eine Koordinate, Groß- und Kleinschreibung spielt keine Rolle. Auf 8×8 ist d3 gleich (3−1)×8+3 = 19. Wählen Sie immer aus den erhaltenen legal_moves. Haben Sie in Bot-Paarungen keinen legalen Zug, senden Sie -1 oder pass; in Partien gegen Menschen erledigt der Server erzwungenes Passen. Schreiben Sie nur die Antwortzeile nach stdout, keine anderen Meldungen.
{"move":19}{"move":"d3"}{"move":-1}Beispiele für Engine-Anfragen je Spiel
Beispiele für die erste Anfrage für Schwarz, die das Verbindungsprogramm an eine Engine mit --engine sendet, erzeugt mit dem Regelcode. Echte Partien aller Spielarten beginnen mit einer zufälligen Stellung mit 12 Steinen, daher unterscheiden sich board und legal_moves in jeder Partie. Damit diese Beispiele gleich bleiben, wurden ihre Eröffnungszüge nahe der Brettmitte gewählt. Auch match_id und die Zeitwerte unterscheiden sich in echten Partien.
game_mode: Eines von std, anti, grand, octagon, reversi. XOT in Partien gegen Menschen kommt als std.board_size: 8, oder 10 bei grand und octagon.board: Array der Länge board_size². Gelesen ab a1 nach rechts, dann die nächste Reihe. 0=leer (einschließlich der gesperrten Felder bei Octagon), 1=Schwarz, 2=Weiß.blocked_squares: Gesperrte Felder bei Octagon. In anderen Spielen ein leeres Array.placed: Bisher gesetzte Steine je Farbe, einschließlich der zufälligen Eröffnungszüge; die Schlüssel sind die Strings "1" und "2". Wird für das 32-Steine-Limit von klassischem Reversi verwendet.
Anti (anti)
Eingabe-JSON (Verbindungsprogramm → Adapter)
{
"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
}Antwort-JSON (Adapter → Verbindungsprogramm)
{"move":2}Grand (10x10) (grand)
Eingabe-JSON (Verbindungsprogramm → Adapter)
{
"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
}Antwort-JSON (Adapter → Verbindungsprogramm)
{"move":13}Octagon (octagon)
Eingabe-JSON (Verbindungsprogramm → Adapter)
{
"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
}Antwort-JSON (Adapter → Verbindungsprogramm)
{"move":13}Klassisches Reversi (reversi)
Eingabe-JSON (Verbindungsprogramm → Adapter)
{
"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
}Antwort-JSON (Adapter → Verbindungsprogramm)
{"move":18}Häufige Probleme
| Symptom | Ursache und Lösung |
|---|---|
| Der Bot zeigt einen grauen Punkt (offline). | Prüfen Sie, ob das Fenster des Verbindungsprogramms noch läuft. Es stoppt, wenn Sie das Fenster schließen, Ctrl+C drücken oder der Computer in den Ruhezustand geht. Wiederholen Sie die Schritte 6 und 7 und drücken Sie dann Aktualisieren. |
Es heißt, der Befehl wurde nicht gefunden.command not found / is not recognized | Python fehlt, oder das Fenster wurde nach der Installation nicht neu geöffnet. Lesen Sie Schritt 5, öffnen Sie ein neues Fenster und beginnen Sie erneut bei Schritt 4. |
Es heißt, die Datei kann nicht geöffnet werden.can't open file | Sie haben den Befehl außerhalb des Bot-Ordners ausgeführt. Wechseln Sie wie in Schritt 4 in den Bot-Ordner und prüfen Sie mit ls, ob README.md und tools aufgelistet werden. |
Es heißt, das Token fehlt, und das Programm beendet sich.FS_BOT_TOKEN is missing or invalid | In diesem Fenster ist kein Token gesetzt. Wenn Sie ein neues Fenster geöffnet haben, wiederholen Sie Schritt 6. |
Es heißt, das Token wurde abgelehnt, und das Programm stoppt.Bot credential rejected | Das Token wurde falsch eingefügt oder ist veraltet. Nachdem Sie ein neues Token ausgestellt oder den Bot deaktiviert haben, funktioniert das alte Token nicht mehr. Beginnen Sie mit dem neuesten Token erneut bei Schritt 6. |
Dieselbe Fehlerzeile erscheint immer wieder.Connection/state error | Prüfen Sie die Internetverbindung. Prüfen Sie unter macOS, ob Sie Install Certificates.command aus Schritt 5 ausgeführt haben. Erscheint die Zeile nur während Partien, lehnt der Server möglicherweise den Zug Ihrer Engine als illegal ab. Das Verbindungsprogramm sendet denselben Zug erneut, daher muss die Engine korrigiert werden. |
| Die Schaltfläche zum Herausfordern funktioniert nicht, oder die Herausforderung wird nicht angenommen. | Beide Bots brauchen einen grünen Punkt. Prüfen Sie, ob Ihr Bot bei Mein spielender Bot ausgewählt ist. Ein Bot, der schon in einer anderen Partie spielt, nimmt keine neue Herausforderung an. Eine nicht angenommene Herausforderung läuft nach 120 Sekunden ab. |
Der Bot macht keinen Zug und verliert dann durch Zeitüberschreitung, oder die Partie wird annulliert.Engine failed for match | Die Engine hat nicht rechtzeitig geantwortet, oder ihre Antwort hatte das falsche Format. Prüfen Sie den Engine-Befehl und seine einzeilige JSON-Ausgabe. Macht ein Bot innerhalb von 30 Sekunden nach dem Start keinen ersten Zug, wird die Partie annulliert. |
| Es kommen keine Herausforderungen zu Varianten an. | Das Herausforderungsfenster listet nur Spiele, die beide Bots unterstützen. Prüfen Sie, ob das Spiel bei Unterstützte Spiele Ihres Bots angehakt ist und ob der andere Bot es ebenfalls unterstützt. Ein über NBoard verbundener Bot sollte nur Standard angehakt haben. |
Glossar
| Begriff | Bedeutung |
|---|---|
| API | Eine vereinbarte Art, wie Programme Anfragen und Antworten austauschen. Das Verbindungsprogramm erledigt das für Sie. |
| Token | Ein Passwort nur für Ihren Bot. Das Verbindungsprogramm weist damit gegenüber dem Server nach, welcher Bot es ist. |
| Terminal | Ein Fenster, in das Sie Befehle eingeben. Unter Windows verwenden Sie PowerShell. |
| Umgebungsvariable | Eine vorübergehende Einstellung, die an Programme übergeben wird, die aus einem Terminalfenster gestartet werden. Sie verschwindet, wenn das Fenster geschlossen wird. |
| JSON | Ein Format, um Daten als Text zu schreiben. Beispiel: {"move":19} |
| Engine | Ein Programm, das eine Stellung betrachtet und einen Zug auswählt. |
| Adapter | Ein kleines Programm, das Daten zwischen Ihrer Engine und dem Verbindungsprogramm umwandelt. |
| NBoard | NBoard ist eine grafische Othello-Anwendung, die mit NTest genutzt werden kann. Das NBoard-Protokoll ist ihr veröffentlichtes Format für die Kommunikation mit Engines und funktioniert mit unterstützenden Engines wie NTest und Edax. |
| Legaler Zug | Ein Feld, auf das Sie jetzt setzen dürfen. Der Server listet diese Felder in legal_moves. |
Downloads
Python-Beispiele herunterladen
Zum Herunterladen anmelden. .zip enthält Anleitung, Connector und Beispiel-Engine; .json ist die API-Spezifikation und .md die Anleitung.
Ausführliche Spezifikation
Der Rest dieser Seite ist eine ausführliche Referenz für Entwickler, die direkt mit einer Engine oder der API arbeiten.
Optionen des Verbindungsprogramms
Optionen für tools/bot_match_client.py. Genau eine der Optionen --engine und --nboard / --edax / --egaroucid ist erforderlich. Das Token ist nie eine Option; es wird nur aus der Umgebungsvariablen FS_BOT_TOKEN gelesen. Exit-Codes: 0 normales Beenden, 2 Token abgelehnt, 3 --max-runtime erreicht.
| Parameter | Beschreibung |
|---|---|
--engine "COMMAND" | Startet diesen Befehl einmal pro Partie und tauscht pro Zug eine JSON-Zeile aus, ohne die Eingabe zu schließen. Eine Engine, die eine Zeile beantwortet und sich beendet, wird beim nächsten Zug neu gestartet. |
--nboard "COMMAND" | Hält eine NBoard-Engine pro Partie, insgesamt zwei. Nur Standard (std). |
--edax PATH | Edax lässt sich unten auch nur mit dem Programmpfad über --edax verbinden. Ein eigener Adapter ist nicht nötig; die Denkzeit pro Zug wird aus der verbleibenden Spielzeit verteilt. |
--edax-eval PATH | Pfad zu Edax eval.dat. Ohne Angabe wird data/eval.dat im Programmordner oder dessen übergeordnetem Ordner gesucht. |
--egaroucid PATH | Bereiten Sie Egaroucid for Console vor. Verwenden Sie die Konsolenanwendung und lassen Sie resources an seinem Platz. Unter macOS und Linux verwenden Sie eine nach offizieller Anleitung erstellte Programmdatei. Der Konnektor überträgt Stellung und Restzeit über setboard, settime und go; ein Adapter ist nicht nötig. |
--nboard-depth N | Sendet set depth N an die NBoard-Engine. 0 bis 60; 0 behält die Voreinstellung der Engine. (--edax: 0 → 60). |
--max-think-seconds S | Maximale Wartezeit auf die Engine pro Zug in Sekunden. Standard 10, Maximum 300. Nie länger als die verbleibende Bedenkzeit minus 0,75 Sekunden. Dies ist eine Sicherheitsgrenze des Verbindungsprogramms; der Server hat kein Zeitlimit pro Zug. Mit 0 entfällt die zusätzliche Zuggrenze; es gilt die verbleibende Spielzeit. |
--engine-name NAME | Engine-Name, der in den Bot-Informationen angezeigt wird (höchstens 120 Zeichen). Keine Befehle oder Pfade angeben. |
--cpu-name NAME | Anzuzeigender CPU-Name (höchstens 120 Zeichen). Wird ohne Angabe automatisch erkannt. |
--once-match ID | Bearbeitet nur dieses Match und beendet sich, wenn es endet (beendet, annulliert, abgelaufen, abgelehnt). Negative IDs von Partien gegen Menschen funktionieren auch. Für Tests mit --max-runtime kombinieren. |
--max-runtime S | Räumt die Engines auf und beendet sich nach so vielen Sekunden (Exit-Code 3). |
--no-stream | Verwendet den Ereignis-Stream nicht; fragt nur per Polling ab. |
--api URL, --stream-url URL | Adressen der API und des Streams. Normalerweise die Standardwerte beibehalten. Nur HTTPS wird akzeptiert, und weil das Token mitgesendet wird, muss der Stream auf dem API-Host liegen. |
Die API selbst implementieren
Empfangen Sie Herausforderungen und Stellungen mit agent_poll oder dem authentifizierten Ereignis-Stream. Senden Sie agent_accept für wartende Herausforderungen, bei denen Sie bot_b sind, und berechnen Sie dann in jeder Partie eines laufenden Matches nur Stellungen, in denen Sie am Zug sind. Senden Sie höchstens einmal pro match_id, Partie (leg) und revision, und verwerfen Sie veraltete Berechnungen, wenn sich der Status ändert. Partien gegen Menschen haben eine negative match_id und leg=0 und benötigen round_no und room_created_at genau so, wie sie empfangen wurden. In normalen Räumen erledigt der Server erzwungenes Passen; nur Bot-Paarungen senden das Passen selbst.
agent_poll / bot-stream → pending (bot_b = my_id)
→ agent_accept → running → legs[0] + legs[1]
→ agent_move(match_id, leg, revision, move) → finished / abortedVarianten
Ein Bot erhält nur die Spiele, die bei Unterstützte Spiele angehakt sind. game_mode in Engine-Anfragen und im Match-Status nennt das Spiel. Der Server berechnet Brettgröße, gesperrte Felder und legale Felder, die Engine muss also nur aus den erhaltenen legal_moves wählen. Erzeugen Sie Züge für 10×10 oder klassisches Reversi nicht mit der Zuggenerierung für Standard-8×8.
| Spiel | game_mode | Brett | Startsteine | Bedenkzeit (pro Partie, pro Farbe) | Erste legal_moves von Schwarz |
|---|---|---|---|---|---|
| Standard | std | 8×8 | Zufällige 12 Steine | 60,000 ms | Hängt von der Stellung ab |
| Anti | anti | 8×8 | Zufällige 12 Steine | 60,000 ms | Hängt von der Stellung ab |
| Grand (10x10) | grand | 10×10 | Zufällige 12 Steine | 60,000 ms | Hängt von der Stellung ab |
| Octagon | octagon | 10×10 (88) | Zufällige 12 Steine | 60,000 ms | Hängt von der Stellung ab |
| Klassisches Reversi | reversi | 8×8 | Zufällige 12 Steine | 60,000 ms | Hängt von der Stellung ab |
Gemeinsame Regeln: Schwarz (1) zieht zuerst. Beide Partien beginnen mit derselben Stellung und vertauschten Farben und laufen gleichzeitig. Haben Sie in Bot-Paarungen keinen legalen Zug, kommt legal_moves als leeres Array, und der Bot muss selbst move=-1 (Passen) senden. Der Server passt nicht für Sie, und Ihre Uhr läuft weiter. Passen, obwohl ein legaler Zug existiert, ist illegal. Eine Partie endet, wenn keine Farbe mehr ziehen kann. Leere Felder werden niemandem zugerechnet; nur tatsächliche Steine werden über beide Partien summiert, und gleiche Summen sind ein Remis. Alle Bot-Partien beginnen mit einer zufälligen Stellung mit 12 Steinen. In jeder Spielart hat jeder Bot pro Partie 1 Minute.
Anti: Setzen und Umdrehen funktionieren wie im Standard-Othello. Die Seite mit weniger Steinen über beide Partien gewinnt.
10x10: Standardregeln auf einem 10×10-Brett (100 Felder). Die Startsteine stehen auf den vier mittleren Feldern; Bot-Paarungen fügen 8 zufällige Züge hinzu und beginnen mit 12 Steinen.
Octagon: Dasselbe Brett und dieselbe Startaufstellung wie bei 10x10, aber in jeder Ecke sind drei Felder gesperrt, insgesamt 12 (88 spielbare Felder). Gesperrte Felder kommen als Feldnummern in blocked_squares und in board als 0, derselbe Wert wie leer. Das Array ist nicht numerisch sortiert. Auf ein gesperrtes Feld kann nicht gesetzt werden, und Steinreihen enden dort.
Klassisches Reversi: beginnt nach den Regeln mit einem leeren Brett. Solange eines der vier mittleren Felder (27, 28, 35, 36) leer ist, setzt man nur dort, und es wird nichts umgedreht. Sind alle vier besetzt, gelten die Standardregeln. Jede Farbe setzt höchstens 32 Steine, einschließlich der ersten vier Züge. placed zählt die bisher von jeder Farbe gesetzten Steine; die Schlüssel sind die Strings "1" (Schwarz) und "2" (Weiß). Bot-Paarungen beginnen, nachdem der Server 12 zufällige Steine gesetzt hat, einschließlich der vier Züge in der Mitte; in der ersten Anfrage ergeben die beiden placed-Werte daher zusammen 12 (meist {"1":6,"2":6}). Eine Farbe, die alle 32 gesetzt hat, erhält leere legal_moves und sendet bei jedem ihrer Züge -1.
Beispiele für Engine-Anfragen je Spiel
Beispiele für die erste Anfrage für Schwarz, die das Verbindungsprogramm an eine Engine mit --engine sendet, erzeugt mit dem Regelcode. Echte Partien aller Spielarten beginnen mit einer zufälligen Stellung mit 12 Steinen, daher unterscheiden sich board und legal_moves in jeder Partie. Damit diese Beispiele gleich bleiben, wurden ihre Eröffnungszüge nahe der Brettmitte gewählt. Auch match_id und die Zeitwerte unterscheiden sich in echten Partien.
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}Klassisches 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}Felder der Engine-Anfrage
| Feld | Typ | Beschreibung |
|---|---|---|
game_mode | string | Eines von std, anti, grand, octagon, reversi. XOT in Partien gegen Menschen kommt als std. |
board_size | integer 8 | 10 | 8, oder 10 bei grand und octagon. |
board | integer[board_size²] | Array der Länge board_size². Gelesen ab a1 nach rechts, dann die nächste Reihe. 0=leer (einschließlich der gesperrten Felder bei Octagon), 1=Schwarz, 2=Weiß. |
blocked_squares | integer[] | Gesperrte Felder bei Octagon. In anderen Spielen ein leeres Array. |
placed | {"1": integer, "2": integer} | Bisher gesetzte Steine je Farbe, einschließlich der zufälligen Eröffnungszüge; die Schlüssel sind die Strings "1" und "2". Wird für das 32-Steine-Limit von klassischem Reversi verwendet. |
legal_moves | integer[] | Felder, auf die Sie jetzt setzen dürfen. Wenn leer, antworten Sie mit move=-1. |
color | integer 1 | 2 | Farbe am Zug. 1=Schwarz, 2=Weiß. |
budget_ms | integer | Für diesen Zug erlaubte Zeit in Millisekunden: der kleinere Wert aus --max-think-seconds und der verbleibenden Bedenkzeit minus 0,75 Sekunden. Bei Überschreitung stoppt das Verbindungsprogramm die Engine. |
remaining_ms | integer | Verbleibende Bedenkzeit Ihrer Farbe in dieser Partie in Millisekunden, nach Abzug der in diesem Zug bereits verbrauchten Zeit. |
match_id | integer ≠ 0 | Match-ID. Negativ (-Raumnummer) bei Partien gegen Menschen. |
leg | integer 0 | 1 | Partienummer 0 oder 1. Bei Partien gegen Menschen immer 0. |
revision | integer | Statusnummer dieser Partie. Unverändert mit agent_move senden. |
history | array | In dieser Partie gespielte Züge. Entscheiden Sie anhand von board. |
round_no | integer | null | Rundennummer einer Partie gegen einen Menschen. null bei Bot-Paarungen. |
room_created_at | string | null | Erstellungszeit des Spielraums einer Partie gegen einen Menschen. null bei Bot-Paarungen. |
Einen anderen Bot zu einer Variante herausfordern
Fügen Sie agent_challenge das Feld game_mode hinzu; der Standardwert ist std. Beide Bots müssen das Spiel in supported_games haben. Die Spiele des Gegners lesen Sie aus bots[].supported_games in der Antwort von agent_poll. Fehlt das Spiel bei einem der Bots, lautet die Antwort HTTP 409 mit dem folgenden Fehler.
{"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}supported_games ändern
Das Registrierungsformular beginnt mit angehaktem Standard, und ältere Bots ohne gespeicherte Liste unterstützen std. Die Liste lässt sich nur über Verwalten auf der Bot-Webseite ändern, nie mit dem Bot-Token (API). Während ein Match läuft oder auf Annahme wartet, wird die Änderung abgelehnt. Eine leere Liste kann nicht gespeichert werden.
NBoard und Varianten
Eine Verbindung mit --nboard unterstützt nur Standard (std). Kommt eine Variante, fragt das Verbindungsprogramm die Engine nicht und gibt diesen Zug auf (Meldung Engine failed for match). Es wird kein Zug gesendet, daher wird das Match vor dem ersten Zug annulliert, oder diese Partie geht später durch Zeitüberschreitung verloren. Geben Sie einem NBoard-Bot nur Standard als unterstütztes Spiel.
Menschen gegen registrierte Bots
Menschen können Ihren Bot in jedem Spiel herausfordern, das für ihn registriert ist. Unterstützt er Standard, können sowohl STD als auch XOT kommen. XOT kommt mit game_mode std und beginnt mit einem Brett, auf dem bereits 8 Züge gespielt wurden (sie sind in history enthalten). Die herausfordernde Person wählt die Bedenkzeit, und die Partie ist ungewertet. Ein Match gegen einen Menschen besteht aus einer Partie (leg 0) und hat eine negative match_id. Der Server übernimmt das Passen, wenn kein Zug möglich ist, senden Sie also nicht -1. Registrierte Online-Bots mit eingeschalteter automatischer Paarung können auch unterstützte STD/XOT-Schnellpartien spielen. Für Mitglieder sind diese gewertet, für Gäste ungewertet.
Illegale Züge und Zeit
Ein illegaler Zug oder eine veraltete revision wird mit HTTP 409 (illegal_move, stale_revision, not_your_turn) abgelehnt, und das Brett ändert sich nicht. Die Uhr läuft währenddessen weiter. Das Verbindungsprogramm sendet denselben Zug mit derselben revision erneut; wird die Engine nicht korrigiert, geht diese Partie also schließlich durch Zeitüberschreitung verloren. Der Server hat kein Limit pro Zug und prüft nur die verbleibende Bedenkzeit jeder Farbe. Die 10 Sekunden sind der Standardwert von --max-think-seconds im Verbindungsprogramm.
Authentifizierung
HTTPS, JSON POST und Authorization: Bearer verwenden. Das Token gilt für einen Bot. Nicht in URLs, öffentlichen Code oder Logs schreiben.
Content-Type: application/json
Authorization: Bearer <FS_BOT_TOKEN>POST https://othelloclub.com/flipsight/bot_match_api.php
Spielregeln
Alle Bot-Partien beginnen mit einer zufälligen Stellung mit 12 Steinen. Zwei Partien laufen gleichzeitig mit vertauschten Farben. Die gesamte Steinzahl entscheidet; bei Anti gewinnt die kleinere Zahl.
Alle Bot-Partien beginnen mit einer zufälligen Stellung mit 12 Steinen. In jeder Spielart hat jeder Bot pro Partie 1 Minute.
Hat ein Bot innerhalb von 30 Sekunden nach dem Start keinen ersten Zug gemacht, wird das Match annulliert und nicht gewertet. Ist während des Matches die Verbindung eines Bots länger als 30 Sekunden unterbrochen, während der andere verbunden ist, gewinnt der verbundene Bot (finish_reason: disconnect) und das Ergebnis wird gewertet.
Der Server berechnet die Züge der FlipSight-Bots in allen Spielarten. Herausforderungen über das Web und über die API (agent_challenge) werden angenommen, und der Server startet die Partie sofort, Sie müssen den Spielraum also nicht offen lassen. Steht die Berechnung des Servers länger als 30 Sekunden still, wird die Partie annulliert (finish_reason: technical) und nicht gewertet. Varianten sind teuer zu berechnen, daher laufen höchstens 2 Variantenpartien gegen FlipSight-Bots gleichzeitig. Sind alle Plätze belegt, kommt HTTP 409 mit site bot variant matches are full; versuchen Sie es kurz darauf erneut.
Automatische Botpartien: Die Website hält ständig zwei Räume für Botpartien offen. Solange ein registrierter Bot mit eingeschalteter automatischer Paarung online ist und nicht spielt, erhält er eine Herausforderung zu einer Standard-Botpartie (std) gegen einen Bot mit ähnlicher Wertung (zuerst ein anderer registrierter Bot, sonst ein FlipSight-Bot), als bot_b. Das Verbindungsprogramm nimmt sie automatisch an, und die Partie beginnt. Nimmt der Bot nicht innerhalb von 120 Sekunden an oder lehnt ab, wird er 10 Minuten lang nicht automatisch gepaart. Automatische Partien werden gewertet. Automatische Bot-Partien laufen auch während Arena-Partien weiter. Die automatische Paarung ist standardmäßig eingeschaltet und lässt sich auf der Bot-Seite unter Verwalten ausschalten. Ein Bot, der mit der Übungs-Engine gestartet wurde (environment.practice ist true), wird nicht automatisch gepaart, und seine Botpartien in dieser Zeit werden nicht gewertet.
Eine direkte Herausforderung durch einen Menschen spielt eine Partie im normalen Raum mit unterstütztem Spiel und Zeit. Direkte Herausforderungen sind serverseitig immer ungewertet. Registrierte Online-Bots mit eingeschalteter automatischer Paarung können auch unterstützte STD/XOT-Schnellpartien spielen. Für Mitglieder sind diese gewertet, für Gäste ungewertet. Ein registrierter Bot nimmt nur so viele Partien an, wie seine Einstellung für gleichzeitige Partien erlaubt (Standard 2). Eine Partie gegen einen Menschen und ein Bot-Match (zwei Bretter) zählen jeweils 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)Ablauf eines Matches
Alle Bot-Partien beginnen mit einer zufälligen Stellung mit 12 Steinen. Zwei Partien laufen gleichzeitig mit vertauschten Farben. Die gesamte Steinzahl entscheidet; bei Anti gewinnt die kleinere Zahl.
Ihre Teilnehmernummer ist 0 als bot_a und 1 als bot_b. Teilnehmer 0 spielt in legs[0] Schwarz (1) und in legs[1] Weiß (2); bei Teilnehmer 1 ist es umgekehrt. Sie sind am Zug, wenn leg.status active ist und leg.turn Ihrer Farbe entspricht. Sie können in beiden Partien gleichzeitig am Zug sein.
Haben Sie keinen legalen Zug, müssen Sie selbst move -1 (Passen) senden; der Server passt nicht für Sie. In Partien gegen einen Menschen behandelt der Server das Passen wie in normalen Spielräumen.
Restzeit am Zug = clocks_ms[Ihre Farbe - 1] - (server_now_ms - turn_started_ms). clocks_ms ist nach Farben geordnet (Schwarz, Weiß); totals und winner sind nach Teilnehmern geordnet (winner 0 oder 1, null bei Remis).
Feldnummern
Feldnummer = Reihe × 8 + Spalte, also a1=0, h1=7, a2=8 und h8=63. Zum Beispiel ist 19 d3 und 37 f5. Das board-Array verwendet dieselbe Reihenfolge mit 0=leer, 1=Schwarz, 2=Weiß.
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 63Auf 10×10 (grand, octagon) gilt: Feld = Reihe × 10 + Spalte. Die Spalten sind a bis j, und die zehnte Reihe wird in Koordinaten als 10 oder 0 geschrieben. a1=0, j1=9, a2=10, j10=99 (gleich j0). Dieselbe Koordinate hat auf jeder Brettgröße eine andere Nummer: d3 ist auf 8×8 die 19 und auf 10×10 die 23.
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 ## ##Ereignis-Stream
Verbinden Sie sich mit demselben Bearer-Token per GET https://othelloclub.com/flipsight_api/bot-stream, um pro Zeile ein JSON-Ereignis zu erhalten (NDJSON): hello bestätigt die Verbindung, match bedeutet, dass sich eines Ihrer Matches geändert hat (Ereignisse zwischen Bots enthalten das vollständige Match-Objekt), und ping ist ein Keep-alive alle 10 Sekunden. Rufen Sie bei match einmal agent_poll auf. Bricht die Verbindung ab, verbinden Sie sich mit einer von 1 auf 30 Sekunden wachsenden Verzögerung neu und fragen in der Zwischenzeit weiter ab. Solange der Stream verbunden ist, wird Ihr Bot als online angezeigt; jeder Bot kann bis zu 3 Streams öffnen.
Ereignisbeispiele
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}Engine-Adapter
Das FlipSight-Bot-Verbindungsprogramm (Python 3.9+, nur Standardbibliothek) läuft unter Windows, macOS und Linux und bindet eine Engine auf zwei Arten an. --engine startet die Engine einmal pro Partie, sendet für jeden Zug eine JSON-Zeile und liest eine Zeile {"move": Feld}. Eine Engine, die eine Zeile beantwortet und sich beendet, funktioniert weiter, weil sie beim nächsten Zug neu gestartet wird; eine Engine, die die Eingabe bis zum Ende liest (sys.stdin.read() usw.), antwortet nie, also zeilenweise lesen. legal_moves in der Anfrage listet die legalen Felder; ist die Liste leer, antworte mit -1. --nboard hält eine NBoard-Engine laufend, eine pro Partie; die Suchtiefe setzt --nboard-depth.
Edax lässt sich unten auch nur mit dem Programmpfad über --edax verbinden. Ein eigener Adapter ist nicht nötig; die Denkzeit pro Zug wird aus der verbleibenden Spielzeit verteilt.
Bereiten Sie Egaroucid for Console vor. Verwenden Sie die Konsolenanwendung und lassen Sie resources an seinem Platz. Unter macOS und Linux verwenden Sie eine nach offizieller Anleitung erstellte Programmdatei. Der Konnektor überträgt Stellung und Restzeit über setboard, settime und go; ein Adapter ist nicht nötig.
Eingabe-JSON (Verbindungsprogramm → Adapter)
Dies sind Beispieldaten des Verbindungsprogramms, kein ausführbarer Code. Speichern Sie sie nicht als Python-Datei. Während einer Partie werden die aktuellen Daten bei jedem Zug automatisch gesendet.
{"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}Antwort-JSON (Adapter → Verbindungsprogramm)
{"move":2}Statusabgleich
Mit verbundenem Stream rufen Sie agent_poll bei jedem Ereignis und alle 10 Sekunden auf. Ohne Stream fragen Sie während eines Matches jede Sekunde und im Leerlauf alle 5 Sekunden ab, nie häufiger als einmal pro Sekunde. Netzwerkzeit geht von Ihrer Uhr ab. Geht die Antwort auf einen Zug verloren, laden Sie den neuen Status und senden denselben Zug nur erneut, wenn die revision unverändert ist.
API-Aktionen
Die Antwortbeispiele zeigen das Format. Die Kopierschaltfläche kopiert nur die Anfrage und ruft die API nicht auf.
POST agent_poll
Status abrufen
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
action | string "agent_poll" | Pflicht | Name der aufzurufenden Aktion |
environment | object | Optional | CPU, Betriebssystem und Engine-Name des Connector-PCs (je bis zu 120 Zeichen, optional) sowie practice (true oder false, optional). practice true heißt, dass der Bot übt: Er wird nicht automatisch gepaart, und Botpartien, die in dieser Zeit angefragt oder angenommen werden, werden nicht gewertet. Der Connector sendet den Wert automatisch, wenn er mit der Übungs-Engine gestartet wird. Der Server merkt sich den letzten Wert. Sende am Ende der Übung den Wert ohne practice oder mit false. |
Anfragebeispiel
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"}}'Antwortbeispiel
{
"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
}Match-Objekt (matches[])
| Feld | Typ | Beschreibung |
|---|---|---|
id | integer | Match-ID (negativ bei Partien gegen einen Menschen) |
kind | string | human bei Partien gegen einen Menschen |
bot_a | integer | Bot-ID von Teilnehmer 0 (0 für einen Menschen) |
bot_b | integer | Bot-ID von Teilnehmer 1 (0 für einen Menschen) |
game_mode | string | Spiel (std, anti, grand, octagon, reversi) |
time_limit_sec | integer | Sekunden pro Partie und Farbe |
status | string | pending, running, finished, aborted, rejected, expired |
expires_ms | integer | Annahmefrist (Millisekunden) |
ranked | boolean | Ob das Match gewertet wird |
state.status | string | active, finished oder aborted |
state.game_mode | string | Spiel (std, anti, grand, octagon, reversi) |
state.board_size | integer 8 | 10 | Felder pro Seite, 8 oder 10 |
state.blocked_squares | integer[] | Gesperrte Felder (nur Octagon, nicht sortiert) |
state.legs | array | Partien (2 zwischen Bots, 1 gegen einen Menschen) |
state.totals | [integer, integer] | Steinsummen beider Partien je Teilnehmer |
state.winner | integer | null | Siegender Teilnehmer 0 oder 1, null bei Remis |
state.finish_reason | string | null | discs, timeout, double_timeout, disconnect, no_first_move |
Partie-Objekt (state.legs[])
| Feld | Typ | Beschreibung |
|---|---|---|
board | integer[64 | 100] | 64 / 100 Felder, 0=leer 1=Schwarz 2=Weiß |
turn | integer | Farbe am Zug, 1 (Schwarz) oder 2 (Weiß) |
legal_moves | integer[] | Legale Felder für die Farbe am Zug. Leer bei beendeten Partien |
placed | {"1": integer, "2": integer} | Gesetzte Steine je Farbe, Schlüssel "1" (Schwarz) und "2" (Weiß) |
black_participant | integer | Teilnehmer, der in dieser Partie Schwarz spielt |
clocks_ms | [number, number] | Restzeit-Basiswerte nach Farben (Schwarz, Weiß) |
turn_started_ms | integer | Serverzeit zu Beginn des aktuellen Zugs |
revision | integer | Steigt bei jeder Änderung dieser Partie um 1 |
status | string | active, finished, timeout, stopped |
history | array | Gespielte Züge (color, participant, move, at_ms) |
discs | [integer, integer] | Steinanzahl nach Farben (Schwarz, Weiß) |
result_discs | [integer, integer] (optional) | Optionales festgesetztes Ergebnis [Schwarz, Weiß] für eine durch Zeitüberschreitung verlorene Partie; es wird statt der Steine summiert. Ergebnisse für die Seite mit Zeitüberschreitung und ihren Gegner: std und reversi 0 bis 64, grand 0 bis 100, octagon 0 bis 88, anti 64 bis 0. Die andere Partie läuft weiter, und nach dem Ende beider Partien entscheiden die Summen über den Sieger. Ältere beendete Einträge werden nicht neu berechnet. |
POST agent_challenge
Herausfordern
Geben Sie target_id (Bot-ID) oder target_name (Bot-Name) an; beides steht in der Liste bots der Antwort von agent_poll. Auch eingebaute FlipSight-Bots (is_builtin: true) lassen sich in allen Spielarten über die API herausfordern, und der Server startet die Partie sofort. Laufen bereits 2 Variantenpartien gegen eingebaute Bots, kommt HTTP 409 mit site bot variant matches are full.
Über die Bot-Seite herausfordern
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
action | string "agent_challenge" | Pflicht | Name der aufzurufenden Aktion |
target_id | integer ≥ 1 | Optional | ID des herausgeforderten Bots |
target_name | string | Optional | Name des herausgeforderten Bots (statt target_id) |
game_mode | string std | anti | octagon | grand | reversi | Optional | Spiel einer Bot-Paarung. Standardwert std. Beide Bots müssen es unterstützen. Standard / Anti / Octagon / Grand (10x10) / Klassisches Reversi |
Anfragebeispiel
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"}'Antwortbeispiel
{
"ok": true,
"match_id": 123,
"server_now_ms": 1800000000000
}POST agent_accept
Annehmen
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
action | string "agent_accept" | Pflicht | Name der aufzurufenden Aktion |
match_id | integer ≥ 1 | Pflicht | Match-ID (negativ bei Partien gegen einen Menschen) |
Anfragebeispiel
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}'Antwortbeispiel
{
"ok": true,
"match": {
"id": 123,
"status": "running",
"bot_a": 1,
"bot_b": 2
},
"server_now_ms": 1800000000000
}POST agent_reject
Ablehnen
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
action | string "agent_reject" | Pflicht | Name der aufzurufenden Aktion |
match_id | integer ≥ 1 | Pflicht | Match-ID (negativ bei Partien gegen einen Menschen) |
Anfragebeispiel
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}'Antwortbeispiel
{
"ok": true,
"match": {
"id": 123,
"status": "rejected",
"bot_a": 1,
"bot_b": 2
},
"server_now_ms": 1800000000000
}POST agent_move
Zug senden
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
action | string "agent_move" | Pflicht | Name der aufzurufenden Aktion |
match_id | integer ≠ 0 | Pflicht | Match-ID (negativ bei Partien gegen einen Menschen) |
round_no | integer ≥ 0 | Optional | Nur bei Partien gegen einen Menschen: round_no aus dem Match-Objekt |
room_created_at | string | Optional | Nur bei Partien gegen einen Menschen: room_created_at aus dem Match-Objekt |
leg | integer 0 | 1 | Pflicht | Partie 0 oder 1 (0 bei Partien gegen einen Menschen) |
revision | integer ≥ 0 | Pflicht | revision der Partie, für die der Zug berechnet wurde; 409 stale_revision, wenn sich die Partie geändert hat |
move | integer -1..99 | string | Pflicht | Feld 0-(board_size²−1), -1 für Passen / d3 / D3 / pass |
Anfragebeispiel
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}'Antwortbeispiel
{
"ok": true,
"match": {
"id": 123,
"status": "running",
"bot_a": 1,
"bot_b": 2
},
"server_now_ms": 1800000000000
}Fehler
Fehler haben die Form {"ok": false, "error": "…"}. Bei 401 stoppen und das Token prüfen. Bei 409 den aktuellen Status abfragen und Zugrecht, revision und legale Züge prüfen. 429 bedeutet zu viele Streams für einen Bot. Verbindungsfehler und 503 mit wachsendem Abstand wiederholen und nie einen illegalen Zug erneut senden.
{"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 |