Steg-för-steg-guide för nybörjare
Det här behöver du
- Ett konto på FlipSight. När du har loggat in kan du registrera en bot och hämta anslutningsprogrammet.
- En botbild: en GIF-, JPG- eller PNG-fil på högst 5 MB, minst 175 px bred och hög.
- Din egen dator för att köra boten Botens drag beräknas på datorn där anslutningsprogrammet körs. Även med samma motor kan styrkan variera med datorns prestanda och motorns inställningar, och om du byter dator eller inställningar kan ratingen röra sig därefter.
- Python 3, ett gratisprogram som kör anslutningsprogrammet. Version 3.9 eller senare fungerar; 3.11 eller senare rekommenderas. Du kontrollerar det i steg 5.
- Din bots motor. Anslutningsprogrammet innehåller en övningsmotor för testning.
1. Registrera din bot
Logga in och öppna botsidan (othelloclub.com/sv/flipsight/bot). I rutan Registrera min bot högst upp skriver du ett namn i Botnamn (2–24 tecken). Låt tills vidare bara Standard vara markerat under Spel som stöds. Välj en bild under Ladda upp botfoto och tryck sedan på knappen Registrera min bot.
Så här ser det ut när det lyckas: rutan Anslutningstoken (visas bara en gång) visas med en lång text som börjar med fsbm_.
2. Spara tokenen
Den långa texten är tokenen, ett lösenord bara för din bot. Anslutningsprogrammet använder den för att bevisa för servern att det talar för den här boten. Kopiera hela texten och klistra in den någonstans tillfälligt, till exempel i en textredigerare. När du har stängt rutan kan du inte se samma token igen.
Behandla tokenen som ett lösenord. Visa den inte för någon och lägg inte upp skärmbilder av den. Om du tappar bort den eller någon annan ser den öppnar du Hantera i botlistan och trycker på Skapa anslutningstoken. När du får en ny token slutar den gamla att fungera direkt.
3. Hämta anslutningsprogrammet
Tryck på Hämta Python-exempel för att hämta ZIP-filen och packa upp den. I den här guiden kallas den uppackade mappen för "botmappen". Lägg den på ett ställe där den är lätt att hitta, till exempel på skrivbordet.
Så här ser det ut när det lyckas: botmappen innehåller filen README.md och mappen tools.
4. Öppna en terminal i botmappen
En terminal är ett fönster där du skriver kommandon. Öppna den så här för ditt system.
macOS: tryck på Command+Space, skriv Terminal och öppna programmet. Skriv cd och ett mellanslag, dra botmappen till fönstret och tryck på Enter.
Linux: högerklicka på botmappen i filhanteraren och välj Öppna i terminal. Namnet på menyvalet kan skilja sig mellan distributioner. Om menyvalet saknas öppnar du en terminal och använder cd som på macOS.
Windows: öppna botmappen i Utforskaren. Skriv powershell i adressfältet högst upp och tryck på Enter. Ett PowerShell-fönster öppnas i den mappen.
Kontroll: skriv kommandot nedan och tryck på Enter. Det har lyckats om README.md och tools finns i listan. Kommandot är detsamma på macOS, Linux och i Windows PowerShell.
ls5. Kontrollera Python
I samma fönster kopierar du det kommando som gäller ditt system, klistrar in det och trycker på Enter. Du kan använda kopieringsknappen uppe till höger i varje kodruta.
macOS / Linux
python3 --versionWindows PowerShell
py --versionSå här ser det ut när det lyckas: en version som börjar med 3, till exempel Python 3.11.9. Version 3.9 eller senare fungerar. Om kommandot inte hittas installerar du Python enligt nedan, stänger fönstret och börjar om från steg 4.
macOS: hämta installationsprogrammet för macOS (.pkg) från python.org och installera det. Kör sedan Install Certificates.command en gång från Python-mappen i Program. Det förbereder de certifikat som behövs för säkra anslutningar (HTTPS).
Linux: installera python3 med din distributions programcenter eller pakethanterare.
Windows: installera Python install manager från python.org.
6. Ange tokenen
Lägg tokenen i en miljövariabel, en tillfällig inställning som bara finns i det här terminalfönstret. Den heter FS_BOT_TOKEN. Kopiera enradskommandot för ditt system nedan, klistra in det och tryck på Enter en gång. Då visas Bot token och väntar på att du skriver något. Klistra nu in tokenen du kopierade i steg 2 och tryck på 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)).PasswordPå macOS och Linux visas inte den inklistrade tokenen, och på Windows ser du * i stället för tecknen. Båda är normalt. Eftersom tokenen inte skrivs in i kommandot hamnar den inte i kommandohistoriken. Inställningen gäller bara i det här fönstret. Om du har stängt det eller öppnat ett nytt fönster upprepar du steg 4 och 6.
Windows: i PowerShell klistrar du in med Ctrl+V eller ett högerklick. Det här enradskommandot använder en syntax som fungerar både i Windows PowerShell 5.1, som följer med Windows, och i nyare PowerShell 7, men det har inte testats på en riktig Windows-dator än.
Så här ser det ut när det lyckas: prompten för nästa kommando kommer tillbaka utan fel.
7. Starta anslutningsprogrammet
I samma fönster kopierar du kommandot nedan som det är, klistrar in det och trycker på Enter. Ändra inte filnamnen i det. Din bot ansluter med övningsmotorn, en mycket svag motor som spelar den lagliga ruta som har lägst nummer.
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'Det är normalt att kommandot inte visar någon text eller återgår till kommandoprompten medan det körs. Håll det här fönstret öppet med anslutningsprogrammet och motorn igång och förhindra att datorn går i viloläge för att hålla din bot online. Om du stänger fönstret eller trycker Ctrl+C avslutas båda programmen och boten blir offline. Kör kommandot ovan igen för att ansluta den på nytt. Anslutningsprogrammet accepterar automatiskt utmaningar från andra bottar.
Så länge boten körs med övningsmotorn får den inga automatiska matcher, och dess matcher mot andra botar, även de du själv begär, är inte rankade. Starta om den med din egen motor (steg 10), så lottas och rankas den från och med då.
Så här ser det ut när det lyckas: i listan Registrerade botar på botsidan är pricken till höger om din bots namn grön. När du håller muspekaren över den visas Online. Om den är grå (Offline) trycker du på Uppdatera.
8. Testa med en första match
Välj din bot under Min spelande bot på botsidan. Välj valfri bot i listan FlipSight-botar, tryck på Utmana och bekräfta i fönstret som öppnas. I alla speltyper beräknar servern dragen för FlipSight-botarna, så du behöver inte hålla spelrummet öppet. Med automatisk lottning på (standard) ordnar webbplatsen också botmatcher medan anslutningsprogrammet körs.
Så här ser det ut när det lyckas: spelrummet visar två brädor och en bricka läggs varje gång det är din bots tur. Övningsmotorn är mycket svag, så det är normalt att förlora.
9. Låt den köra när fönstret är stängt (valfritt)
Starta med kommandot nedan i stället för steg 7, så fortsätter boten att köra när du stänger fönstret. Kör det i samma fönster där du angav tokenen (steg 6). Den stoppas om datorn stängs av eller går i viloläge. Om du stoppar den under en match förloras matchen på tid eller på grund av frånkoppling.
macOS / Linux
macOS / Linux: när du har kört startkommandot kan du stänga fönstret. Meddelanden skrivs till filen bot.log i botmappen. För att stoppa den öppnar du en ny terminal och kör stoppkommandot. Kopiera bara den ruta du behöver: klistrar du in båda rutorna på en gång stoppas boten direkt efter att den har startat.
Starta
nohup python3 tools/bot_match_client.py --engine 'python3 tools/bot_match_example_engine.py' > bot.log 2>&1 &Stoppa
pkill -f bot_match_client.pyWindows PowerShell
Windows: ett minimerat fönster visas i aktivitetsfältet och boten körs i det. Du kan stänga det nuvarande PowerShell-fönstret. För att stoppa den öppnar du det minimerade fönstret och trycker på Ctrl+C, eller stänger det fönstret.
Starta
Start-Process py -ArgumentList 'tools/bot_match_client.py --engine "py tools/bot_match_example_engine.py"' -WorkingDirectory $PWD -WindowStyle Minimized10. Byt till din egen motor
Efter anslutningstestet byter du till din egen motor: särskilda alternativ nedan för Edax eller Egaroucid, --nboard för andra kompatibla motorer, JSON för en egen motor. Behåll samma token.
Markera en variant (Anti, 10x10, Octagon, klassisk Reversi) under Spel som stöds bara om din motor kan reglerna för den. Andra botar och människor kan utmana din bot i alla markerade spel. För att ändra det öppnar du Hantera i botlistan, redigerar Spel som stöds och trycker på Spara. Du kan inte ändra det medan en match pågår eller en utmaning väntar.
Exempel på motorförfrågningar per spel
Anslut en NBoard-motor som Edax
NBoard är ett grafiskt Othello-program som kan användas med NTest. NBoard-protokollet är programmets offentliga kommunikationsformat för motorer och fungerar med motorer som stöder det, som NTest och Edax.
Installations- och startexemplen nedan använder Edax 4.6 och Egaroucid for Console 7.8.2.
Edax 4.6 ingår inte i anslutningsprogrammet. Hämta filen för ditt operativsystem under Assets på Edax 4.6, lägg den i botmappen och packa upp den med förberedelsekommandona nedan. Windows- och Linux-kommandona gäller x86-64.
Windows PowerShell (x86-64): edax-4.6-MS-windows-x86.zip
Förbered (bara första gången)
Expand-Archive -LiteralPath edax-4.6-MS-windows-x86.zip -DestinationPath edax-4.6Starta
py tools/bot_match_client.py --edax "edax-4.6\wEdax-x86-64.exe"Linux (x86-64): edax-4.6-linux-x86.tar.gz
Förbered (bara första gången)
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-64Starta
python3 tools/bot_match_client.py --edax "edax-4.6/lEdax-x86-64"macOS: edax-reversi-4.6.tar.gz
Det finns ingen officiell macOS-programfil för Edax 4.6. Hämta Source code (tar.gz) och edax-4.6-linux-x86.tar.gz från samma utgåva. Bygg Mac-programmet från källkoden; ta bara den gemensamma utvärderingsfilen data/eval.dat från Linux-arkivet. Xcode Command Line Tools krävs. Förberedelsekommandona kompilerar källkoden.
Förbered (bara första gången)
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)Starta
python3 tools/bot_match_client.py --edax "edax-4.6/bin/mEdax-native"Edax kan också anslutas med bara programsökvägen via --edax nedan. Ingen separat adapter behövs; anslutningsprogrammet fördelar betänketiden per drag från den återstående speltiden.
Ersätt sökvägen i kommandot med hela sökvägen till din programfil. Sätt sökvägar med mellanslag inom dubbla citattecken. Om utvärderingsfilen inte hittas automatiskt, ange eval.dat med --edax-eval.
En NBoard-anslutning spelar bara standardmatcher på 8×8. Låt bara Standard vara markerat under Spel som stöds för den här boten. Om en variant kommer frågar anslutningsprogrammet inte motorn, inget drag spelas och matchen annulleras eller förloras på tid.
Egaroucid: --egaroucid
Förbered Egaroucid for Console. Välj konsolprogrammet och behåll resources-mappen på sin plats. På macOS och Linux används en programfil byggd enligt den officiella anvisningen. Ställning och återstående tid skickas via setboard, settime och go utan adapter.
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"Båda lägena stöder bara standard 8×8, med en motor per samtidigt parti. --max-think-seconds är säkerhetsgränsen per drag (10 sekunder som standard). Om Egaroucid når gränsen stoppas sökningen, ett lagligt reservdrag spelas och motorn startas om vid nästa tur.
Egen motor: anslut via JSON
Läs den här delen bara om du vill ansluta en motor som du har skrivit själv. Om du använder Edax, Egaroucid eller en annan NBoard-motor är du redan klar ovan och kan hoppa över den.
Varje gång det är din bots tur kör anslutningsprogrammet my_adapter.py en gång. Det lämnar över den aktuella ställningen som en rad (var brickorna ligger, vems tur det är, vilka rutor som är lagliga, hur mycket tid som är kvar), my_adapter.py svarar med en ruta som en rad, och anslutningsprogrammet spelar det draget på servern. my_adapter.py är en liten Python-fil däremellan som ber din motor om draget. Anslutningsprogrammet sköter anslutningen till servern, tokenen och inskickningen av drag, så den här filen behöver aldrig hantera det.
bot_match_client.py→ my_adapter.pybot_match_client.py← my_adapter.pyVilken sorts motor har du?
- En motor som väljer sitt drag i Python-kod: använd exempel A nedan.
- En programfil som skriver ut en svarsrad och avslutas varje gång den körs: använd exempel B nedan.
- En programfil som förblir öppen och utbyter kommandon löpande: stöder den NBoard, anslut den med --nboard ovan. Med ett annat protokoll anpassar du exempel B till protokollet och låter my_adapter.py hålla motorn öppen och utbyta en rad i taget i en slinga.
Steg 1: en fil som bara kontrollerar anslutningen (my_adapter.py)
Skapa en fil som heter my_adapter.py i botmappen (där README.md och tools finns), klistra in koden nedan som den är och spara filen. Den här filen använder ingen motor utan svarar bara med den första lagliga rutan. Den finns till för att bekräfta att anslutningen fungerar innan du kopplar in din motor.
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)
Kontrollera in- och utdata innan du ansluter
Kommandot i ruta 1 skickar exempelställningen direkt in i adaptern. Det ska skriva ut en rad: {"move": 19}. När du ser den raden startar kommandot i ruta 2 anslutningsprogrammet med denna adapter, i fönstret som har tokenen från steg 6. Kör de två rutorna var för sig. Om din motor själv läser detta JSON-format kan du skriva dess kommando direkt efter --engine.
1. Testa bara adaptern
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. Starta anslutningsprogrammet med adaptern
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'Steg 2: byt till en fil som anropar din motor
När steg 1 fungerar ersätter du hela innehållet i my_adapter.py med exempel A eller B nedan. Ändra bara raderna som är märkta EDIT och låt resten vara som det är. Kör sedan testkommando 1 ovan igen och kontrollera att en svarsrad visas. Det är din motor som har valt draget, så en annan ruta än 19 är helt normalt.
Exempel A: en motor som väljer sitt drag med en Python-funktion
Det här exemplet förutsätter en fil som heter my_engine.py med en funktion search som väljer draget. Ändra filnamnet och funktionsnamnet vid EDIT 1 och värdena som skickas in vid EDIT 2 så att de passar din motor. Funktionen ska returnera rutans nummer (0 till 63) eller en koordinat som 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)Exempel B: en motor som är en programfil
Det här exemplet förutsätter att programmet läser en rad med 64 brädtecken, sidan som är vid draget och betänketiden i millisekunder, och svarar med en koordinatrad som D3 (. = tom, X = svart, O = vit). Varje motor har sitt eget format, så ändra programfilens namn vid EDIT 1, raden som skickas till motorn vid EDIT 2 och delen som läser svaret vid EDIT 3 så att det passar din motor.
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)Betänketid: i botpartier har varje bot 1 minut per parti, så lång betänketid på ett enda drag leder snabbt till förlust på tid. think_ms i exemplen delar den återstående tiden (remaining_ms) med antalet drag du har kvar och bestämmer på så sätt tiden för det här draget. budget_ms är inte tiden som ska användas på det här draget. Det är den längsta tid som anslutningsprogrammet väntar på svaret (10 sekunder som standard). Om svaret kommer senare än så kan boten inte fortsätta i det partiet och förlorar det på tid, så ge din motor think_ms och inte budget_ms.
När något går fel: om anropet till motorn misslyckas skriver exempelfilerna orsaken till my_adapter.log och spelar den första lagliga rutan i stället. Om dragen ser konstiga ut öppnar du den filen först. Anslutningsprogrammet visar inte adapterns felmeddelanden på skärmen. Använd print bara för den enda svarsraden och skriv all felsökningstext till en fil.
Hastighet: anslutningsprogrammet håller my_adapter.py igång för varje parti och skickar en rad per drag. En fil som svarar en rad och avslutas, som exemplen, startas om vid nästa drag, så din motor startar också om varje gång. Om motorn startar långsamt eller ska behålla arbetet från tidigare drag, gör my_adapter.py till en slinga: läs en rad, svara en rad, upprepa. Läs inte indata till slutet och gör flush efter varje svar.
Resten av den här delen är referensmaterial om vad anslutningsprogrammet och my_adapter.py utbyter. Om du använder exempelfilerna ovan som de är behöver du inte läsa det.
Indata-JSON (anslutningsprogram → adapter)
Detta är exempeldata från anslutningsprogrammet, inte körbar kod. Spara dem inte som en Python-fil. Under partiet skickas aktuella data automatiskt vid varje drag.
Nedan finns ett exempel på standardstartställningen på 8×8. I riktiga partier fyller anslutningsprogrammet i den aktuella ställningen. board listar rutorna från a1 åt höger och sedan nästa rad; 0=tom, 1=svart, 2=vit. color=1 ber om ett drag för svart. legal_moves listar rutorna du får spela på. remaining_ms är återstående tid på klockan och budget_ms är den längsta tid som anslutningsprogrammet väntar på svaret för detta drag, båda i millisekunder (1000 = 1 sekund). Fält och exempel för varianter finns i avsnittet om varianter nedan.
{"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}Svars-JSON (adapter → anslutningsprogram)
Skriv ett av svaren nedan som en enda JSON-rad som slutar med en radbrytning. move är ett rutnummer eller en koordinat, oavsett stora eller små bokstäver. På 8×8 är d3 (3−1)×8+3 = 19. Välj alltid bland de legal_moves du har tagit emot. I botpar utan lagligt drag skickar du -1 eller pass; i matcher mot människor avgör servern tvingade pass. Skriv bara svarsraden till stdout, utan andra meddelanden.
{"move":19}{"move":"d3"}{"move":-1}Exempel på motorförfrågningar per spel
Exempel på den första förfrågan för svart som anslutningsprogrammet skickar till en motor med --engine, genererade med regelkoden. Riktiga matcher av alla typer startar från en slumpmässig ställning med 12 brickor, så board och legal_moves skiljer sig i varje match. För att exemplen ska vara fasta valdes deras öppningsdrag nära brädets mitt. match_id och klockvärdena skiljer sig också i riktiga matcher.
game_mode: Ett av std, anti, grand, octagon, reversi. XOT i matcher mot människor kommer som std.board_size: 8, eller 10 för grand och octagon.board: Array med längden board_size². Läses från a1 åt höger och sedan nästa rad. 0=tom (inklusive Octagons blockerade rutor), 1=svart, 2=vit.blocked_squares: Oanvändbara rutor i Octagon. En tom array i andra spel.placed: Lagda brickor hittills per färg, inklusive de slumpmässiga öppningsdragen; nycklarna är strängarna "1" och "2". Används för gränsen på 32 brickor i klassisk Reversi.
Anti (anti)
Indata-JSON (anslutningsprogram → 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
}Svars-JSON (adapter → anslutningsprogram)
{"move":2}Grand (10x10) (grand)
Indata-JSON (anslutningsprogram → 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
}Svars-JSON (adapter → anslutningsprogram)
{"move":13}Octagon (octagon)
Indata-JSON (anslutningsprogram → 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
}Svars-JSON (adapter → anslutningsprogram)
{"move":13}Klassisk reversi (reversi)
Indata-JSON (anslutningsprogram → 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
}Svars-JSON (adapter → anslutningsprogram)
{"move":18}Vanliga problem
| Symptom | Orsak och lösning |
|---|---|
| Boten har en grå prick (offline). | Kontrollera att fönstret med anslutningsprogrammet fortfarande körs. Det stoppas när du stänger fönstret, trycker på Ctrl+C eller datorn går i viloläge. Upprepa steg 6 och 7 och tryck sedan på Uppdatera. |
Det står att kommandot inte hittades.command not found / is not recognized | Python saknas, eller så öppnades fönstret inte på nytt efter installationen. Se steg 5, öppna ett nytt fönster och börja om från steg 4. |
Det står att filen inte kan öppnas.can't open file | Du körde kommandot utanför botmappen. Gå till botmappen som i steg 4 och kontrollera med ls att README.md och tools finns i listan. |
Det står att tokenen saknas, och programmet avslutas.FS_BOT_TOKEN is missing or invalid | Det här fönstret har ingen token. Om du har öppnat ett nytt fönster upprepar du steg 6. |
Det står att tokenen avvisades, och programmet stoppas.Bot credential rejected | Tokenen klistrades in fel eller är en gammal token. När du har skapat en ny token eller inaktiverat boten fungerar den gamla tokenen inte längre. Börja om från steg 6 med den senaste tokenen. |
Samma felrad fortsätter att visas.Connection/state error | Kontrollera internetanslutningen. På macOS kontrollerar du att du har kört Install Certificates.command från steg 5. Om felet bara visas under matcher kan servern avvisa din motors drag som olagligt. Anslutningsprogrammet skickar samma drag igen, så motorn måste rättas. |
| Utmaningsknappen fungerar inte, eller så accepteras inte utmaningen. | Båda botarna måste ha en grön prick. Kontrollera att din bot är vald under Min spelande bot. En bot som redan är i en annan match tar inte emot en ny utmaning. En utmaning som inte accepteras löper ut efter 120 sekunder. |
Boten gör inget drag och förlorar sedan på tid, eller så annulleras matchen.Engine failed for match | Motorn svarade inte i tid, eller så hade svaret fel format. Kontrollera motorkommandot och dess JSON-utdata på en rad. Om en bot inte gör sitt första drag inom 30 sekunder efter start annulleras matchen. |
| Inga utmaningar i varianter kommer. | Utmaningsfönstret visar bara spel som båda botarna stöder. Kontrollera att spelet är markerat under Spel som stöds för din bot och att den andra boten också stöder det. En bot som är ansluten via NBoard ska bara ha Standard markerat. |
Ordlista
| Begrepp | Betydelse |
|---|---|
| API | Ett överenskommet sätt för program att skicka förfrågningar och svar. Anslutningsprogrammet sköter det åt dig. |
| Token | Ett lösenord bara för din bot. Anslutningsprogrammet använder den för att bevisa för servern vilken bot det är. |
| Terminal | Ett fönster där du skriver kommandon. På Windows använder du PowerShell. |
| Miljövariabel | En tillfällig inställning som skickas till program som startas från ett terminalfönster. Den försvinner när fönstret stängs. |
| JSON | Ett format för att skriva data som text. Exempel: {"move":19} |
| Motor | Ett program som tittar på en ställning och väljer ett drag. |
| Adapter | Ett litet program som omvandlar data mellan din motor och anslutningsprogrammet. |
| NBoard | NBoard är ett grafiskt Othello-program som kan användas med NTest. NBoard-protokollet är programmets offentliga kommunikationsformat för motorer och fungerar med motorer som stöder det, som NTest och Edax. |
| Lagligt drag | En ruta du får spela på nu. Servern listar dem i legal_moves. |
Nedladdningar
Logga in för att hämta. .zip innehåller guide, anslutningsprogram och exempelmotor; .json är API-specifikationen och .md är guiden.
Detaljerad specifikation
Resten av sidan är en detaljerad referens för utvecklare som arbetar direkt med en motor eller med API:t.
Alternativ för anslutningsprogrammet
Alternativ för tools/bot_match_client.py. Exakt ett av --engine och --nboard / --edax / --egaroucid krävs. Tokenen är aldrig ett alternativ; den läses bara från miljövariabeln FS_BOT_TOKEN. Avslutningskoder: 0 normalt stopp, 2 token avvisad, 3 --max-runtime uppnådd.
| Parametrar | Beskrivning |
|---|---|
--engine "COMMAND" | Startar kommandot en gång per parti och utbyter en JSON-rad per drag utan att stänga dess indata. En motor som svarar en rad och avslutas startas om vid nästa drag. |
--nboard "COMMAND" | Behåller en NBoard-motor per parti, två totalt. Bara standard (std). |
--edax PATH | Edax kan också anslutas med bara programsökvägen via --edax nedan. Ingen separat adapter behövs; anslutningsprogrammet fördelar betänketiden per drag från den återstående speltiden. |
--edax-eval PATH | Sökväg till Edax eval.dat. Om den utelämnas söks data/eval.dat i programmappen eller dess överordnade mapp. |
--egaroucid PATH | Förbered Egaroucid for Console. Välj konsolprogrammet och behåll resources-mappen på sin plats. På macOS och Linux används en programfil byggd enligt den officiella anvisningen. Ställning och återstående tid skickas via setboard, settime och go utan adapter. |
--nboard-depth N | Skickar set depth N till NBoard-motorn. 0 till 60; 0 behåller motorns standardvärde. (--edax: 0 → 60). |
--max-think-seconds S | Högsta antal sekunder att vänta på motorn per drag. Standard 10, högst 300. Aldrig längre än återstående tid på klockan minus 0,75 sekunder. Detta är en säkerhetsgräns i anslutningsprogrammet; servern har ingen tidsgräns per drag. Med 0 tas gränsen per drag bort och den återstående speltiden används. |
--engine-name NAME | Motornamn som anges i botinformationen (högst 120 tecken). Ta inte med kommandon eller sökvägar. |
--cpu-name NAME | CPU-namn som ska anges (högst 120 tecken). Identifieras automatiskt om det utelämnas. |
--once-match ID | Hanterar bara denna match och avslutas när den tar slut (avslutad, annullerad, utgången, avböjd). Negativa ID:n för matcher mot människor fungerar också. Vid tester kan du kombinera det med --max-runtime. |
--max-runtime S | Stänger motorerna och avslutas efter så här många sekunder (avslutningskod 3). |
--no-stream | Använder inte händelseströmmen; frågar bara med jämna mellanrum. |
--api URL, --stream-url URL | Adresser till API och ström. Behåll normalt standardvärdena. Bara HTTPS accepteras, och eftersom tokenen skickas med måste strömmen ligga på API-värden. |
Implementera API:t själv
Ta emot utmaningar och ställningar med agent_poll eller den autentiserade händelseströmmen. Skicka agent_accept för väntande utmaningar där du är bot_b. Beräkna sedan bara ställningar där det är din tur, i varje parti i en pågående match. Skicka högst en gång per match_id, parti och revision, och släpp inaktuellt arbete när tillståndet ändras. Matcher mot människor använder negativt match_id och leg=0, och kräver round_no och room_created_at exakt som de togs emot. I vanliga rum hanterar servern tvingade pass; bara botpar skickar pass.
agent_poll / bot-stream → pending (bot_b = my_id)
→ agent_accept → running → legs[0] + legs[1]
→ agent_move(match_id, leg, revision, move) → finished / abortedVarianter
En bot tar bara emot spelen som är markerade under Spel som stöds. game_mode i motorförfrågningar och matchtillstånd anger vilket spel det är. Servern beräknar brädstorlek, blockerade rutor och lagliga rutor, så motorn behöver bara välja bland de legal_moves den tar emot. Generera inte drag för 10×10 eller klassisk Reversi med draggenerering för standard 8×8.
| Spel | game_mode | Bräde | Startbrickor | Klocka (per parti, per färg) | Svarts första legal_moves |
|---|---|---|---|---|---|
| Standard | std | 8×8 | Slumpmässiga 12 brickor | 60,000 ms | Beror på ställningen |
| Anti | anti | 8×8 | Slumpmässiga 12 brickor | 60,000 ms | Beror på ställningen |
| Grand (10x10) | grand | 10×10 | Slumpmässiga 12 brickor | 60,000 ms | Beror på ställningen |
| Octagon | octagon | 10×10 (88) | Slumpmässiga 12 brickor | 60,000 ms | Beror på ställningen |
| Klassisk reversi | reversi | 8×8 | Slumpmässiga 12 brickor | 60,000 ms | Beror på ställningen |
Gemensamma regler: Svart (1) börjar. Båda partierna startar från samma ställning med ombytta färger och spelas samtidigt. Om du i ett botpar inte har något lagligt drag kommer legal_moves som en tom array, och boten måste själv skicka move=-1 (pass). Servern passar inte åt dig, och din klocka fortsätter att gå. Det är olagligt att passa när ett lagligt drag finns. Ett parti slutar när ingen av färgerna kan dra. Tomma rutor ges inte till någon; bara faktiska brickor räknas ihop över båda partierna, och lika summor är remi. Alla botpartier börjar från en slumpmässig ställning med 12 brickor. I alla speltyper har varje bot 1 minut per parti.
Anti: brickor läggs och vänds som i standard-Othello. Sidan med minst antal brickor sammanlagt över båda partierna vinner.
10x10: standardregler på ett 10×10-bräde (100 rutor). Startbrickorna står på de fyra mittrutorna; botpar lägger till 8 slumpmässiga drag och börjar med 12 brickor.
Octagon: samma bräde och startuppställning som 10x10, men tre rutor i varje hörn, 12 totalt, kan inte användas (88 spelbara rutor). Blockerade rutor kommer som rutnummer i blocked_squares och som 0, samma värde som tom, i board. Arrayen är inte i numerisk ordning. Du kan inte spela på en blockerad ruta, och rader av brickor stannar där.
Klassisk Reversi: börjar enligt reglerna på ett tomt bräde. Så länge någon av de fyra mittrutorna (27, 28, 35, 36) är tom lägger du bara där, och inget vänds. När alla fyra är fyllda gäller standardreglerna. Varje färg lägger högst 32 brickor, inklusive de fyra första dragen. placed räknar brickorna som varje färg har lagt hittills; nycklarna är strängarna "1" (svart) och "2" (vit). Botpar börjar efter att servern har lagt 12 slumpmässiga brickor, inklusive de fyra mittdragen, så i den första förfrågan blir de två placed-värdena tillsammans 12 (oftast {"1":6,"2":6}). En färg som har lagt alla 32 får en tom legal_moves och skickar -1 varje gång det är dess tur.
Exempel på motorförfrågningar per spel
Exempel på den första förfrågan för svart som anslutningsprogrammet skickar till en motor med --engine, genererade med regelkoden. Riktiga matcher av alla typer startar från en slumpmässig ställning med 12 brickor, så board och legal_moves skiljer sig i varje match. För att exemplen ska vara fasta valdes deras öppningsdrag nära brädets mitt. match_id och klockvärdena skiljer sig också i riktiga matcher.
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}Klassisk 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}Fält i motorförfrågan
| Fält | Typ | Beskrivning |
|---|---|---|
game_mode | string | Ett av std, anti, grand, octagon, reversi. XOT i matcher mot människor kommer som std. |
board_size | integer 8 | 10 | 8, eller 10 för grand och octagon. |
board | integer[board_size²] | Array med längden board_size². Läses från a1 åt höger och sedan nästa rad. 0=tom (inklusive Octagons blockerade rutor), 1=svart, 2=vit. |
blocked_squares | integer[] | Oanvändbara rutor i Octagon. En tom array i andra spel. |
placed | {"1": integer, "2": integer} | Lagda brickor hittills per färg, inklusive de slumpmässiga öppningsdragen; nycklarna är strängarna "1" och "2". Används för gränsen på 32 brickor i klassisk Reversi. |
legal_moves | integer[] | Rutor du får spela på nu. Om listan är tom svarar du move=-1. |
color | integer 1 | 2 | Färgen vid draget. 1=svart, 2=vit. |
budget_ms | integer | Tillåten tid för detta drag i millisekunder: det minsta av --max-think-seconds och återstående tid på klockan minus 0,75 sekunder. Anslutningsprogrammet stoppar motorn när tiden överskrids. |
remaining_ms | integer | Din färgs återstående tid i detta parti, i millisekunder, efter att tiden som redan använts under denna tur har dragits av. |
match_id | integer ≠ 0 | Match-ID. Negativt (-rumsnummer) för matcher mot människor. |
leg | integer 0 | 1 | Partinummer 0 eller 1. Alltid 0 för matcher mot människor. |
revision | integer | Tillståndsnummer för detta parti. Skicka det oförändrat med agent_move. |
history | array | Drag som spelats i detta parti. Fatta beslutet utifrån board. |
round_no | integer | null | Rondnummer i en match mot en människa. null för botpar. |
room_created_at | string | null | Tidpunkt då rummet för matchen mot en människa skapades. null för botpar. |
Utmana en annan bot i en variant
Lägg till game_mode i agent_challenge; standard är std. Båda botarna måste ha spelet i supported_games. Läs motståndarens spel från bots[].supported_games i svaret från agent_poll. Om någon av botarna saknar spelet blir svaret HTTP 409 med felet nedan.
{"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}Ändra supported_games
Registreringsformuläret börjar med Standard markerat, och äldre botar utan sparad lista stöder std. Du kan bara ändra listan under Hantera på botsidan på webben, aldrig med botens token (API). Ändringen avvisas medan en match pågår eller väntar på att accepteras. En tom lista kan inte sparas.
NBoard och varianter
En --nboard-anslutning stöder bara standard (std). När en variant kommer frågar anslutningsprogrammet inte motorn och ger upp den turen (meddelandet Engine failed for match). Inget drag skickas, så matchen annulleras före första draget, eller så förloras partiet på tid senare. Ge en NBoard-bot standard som enda spel som stöds.
Människor mot registrerade botar
Människor kan utmana din bot i alla spel den har registrerat. Om den stöder standard kan både STD och XOT komma. XOT kommer med game_mode std och startar från ett bräde där 8 drag redan har spelats (de ingår i history). Den som utmanar väljer klockan, och matchen påverkar inte ratingen. En match mot en människa har ett parti (leg 0) och ett negativt match_id. Servern hanterar pass när inget drag är möjligt, så skicka inte -1. Registrerade bottar online med automatisk matchning aktiverad deltar också i STD/XOT-snabbmatcher som stöds. De är rankade för medlemmar och orankade för gäster.
Olagliga drag och tid
Ett olagligt drag eller en inaktuell revision avvisas med HTTP 409 (illegal_move, stale_revision, not_your_turn) och brädet ändras inte. Klockan fortsätter under tiden att gå. Anslutningsprogrammet skickar samma drag igen med samma revision, så om motorn inte rättas förloras partiet till slut på tid. Servern har ingen gräns per drag och kontrollerar bara varje färgs återstående tid. De 10 sekunderna är anslutningsprogrammets standardvärde för --max-think-seconds.
Autentisering
Använd HTTPS, JSON POST och Authorization: Bearer. Tokenen gäller en bot. Lägg den aldrig i URL:er, offentlig kod eller loggar.
Content-Type: application/json
Authorization: Bearer <FS_BOT_TOKEN>POST https://othelloclub.com/flipsight/bot_match_api.php
Matchregler
Alla botpartier börjar från en slumpmässig ställning med 12 brickor. Två partier spelas samtidigt med ombytta färger. Det totala antalet brickor avgör; i Anti vinner det lägsta antalet.
Alla botpartier börjar från en slumpmässig ställning med 12 brickor. I alla speltyper har varje bot 1 minut per parti.
Om en bot inte har gjort sitt första drag inom 30 sekunder från start annulleras matchen och räknas inte för ratingen. Om en bots anslutning bryts i mer än 30 sekunder under matchen medan den andra är ansluten vinner den anslutna boten (finish_reason: disconnect) och resultatet räknas för ratingen.
Servern beräknar FlipSight-botens drag i alla speltyper. Utmaningar både via webben och via API:et (agent_challenge) tas emot, och servern startar matchen direkt, så du behöver inte hålla spelrummet öppet. Om serverns beräkning står still i mer än 30 sekunder annulleras matchen (finish_reason: technical) och den påverkar inte ratingen. Varianter är tunga att beräkna, så högst 2 variantmatcher mot FlipSight-botar spelas samtidigt. Är platserna fulla får du HTTP 409 med site bot variant matches are full; försök igen om en stund.
Automatiska botmatcher: webbplatsen håller alltid två rum med botmatcher igång. Medan en registrerad bot med automatisk lottning på är online och inte spelar får den en utmaning till en standard-botmatch (std) mot en bot med liknande rating (först en annan registrerad bot, annars en FlipSight-bot) som bot_b. Anslutningsprogrammet accepterar automatiskt och matchen startar. Om boten inte accepterar inom 120 sekunder eller avböjer hålls den utanför automatisk lottning i 10 minuter. Automatiska matcher är rankade. Automatiska botmatcher fortsätter medan Arena-partier pågår. Automatisk lottning är på som standard och kan stängas av under Hantera på botsidan. En bot som startats med övningsmotorn (environment.practice är true) hålls utanför automatiska matcher, och dess matcher mot andra botar under tiden är inte rankade.
En direkt utmaning från en människa spelas som ett parti i ett vanligt rum med spel och tid som stöds. Servern gör alltid direkta utmaningar orankade. Registrerade bottar online med automatisk matchning aktiverad deltar också i STD/XOT-snabbmatcher som stöds. De är rankade för medlemmar och orankade för gäster. En registrerad bot tar bara emot så många partier som inställningen för samtidiga partier tillåter (standard 2). Ett parti mot en människa och en botmatch (två brädor) räknas som 1 vardera.
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)Matchförlopp
Alla botpartier börjar från en slumpmässig ställning med 12 brickor. Två partier spelas samtidigt med ombytta färger. Det totala antalet brickor avgör; i Anti vinner det lägsta antalet.
Ditt deltagarnummer är 0 om du är bot_a och 1 om du är bot_b. Deltagare 0 är svart (1) i legs[0] och vit (2) i legs[1]; för deltagare 1 är det tvärtom. Det är din tur när leg.status är active och leg.turn är din färg. Det kan vara din tur i båda partierna samtidigt.
Om du inte har något lagligt drag måste du själv skicka move -1 (pass); servern passar inte åt dig. I partier mot en människa hanterar servern pass som i vanliga spelrum.
På din tur är återstående tid = clocks_ms[din färg - 1] - (server_now_ms - turn_started_ms). clocks_ms är i färgordning (svart, vit); totals och winner är i deltagarordning (winner 0 eller 1, null vid remi).
Rutnummer
Rutnummer = rad × 8 + kolumn, så a1=0, h1=7, a2=8 och h8=63. Till exempel är 19 d3 och 37 f5. board-arrayen använder samma ordning, med 0=tom, 1=svart, 2=vit.
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 63På 10×10 (grand, octagon) är ruta = rad × 10 + kolumn. Kolumnerna är a till j, och den tionde raden skrivs som 10 eller 0 i koordinater. a1=0, j1=9, a2=10, j10=99 (samma som j0). Samma koordinat har olika nummer på olika brädstorlekar: d3 är 19 på 8×8 och 23 på 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 ## ##Händelseström
Anslut med samma Bearer-token till GET https://othelloclub.com/flipsight_api/bot-stream för att ta emot en JSON-händelse per rad (NDJSON): hello bekräftar anslutningen, match betyder att en av dina matcher har ändrats (händelser mellan botar innehåller hela matchobjektet) och ping är en keep-alive-signal var 10:e sekund. När du tar emot match anropar du agent_poll en gång. Om anslutningen bryts ansluter du igen med en fördröjning som ökar från 1 till 30 sekunder och fortsätter att fråga under tiden. Medan strömmen är ansluten visas din bot som online; varje bot kan öppna upp till 3 strömmar.
Händelseexempel
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}Motoradapter
FlipSights botanslutning (Python 3.9+, endast standardbiblioteket) körs på Windows, macOS och Linux och ansluter en motor på två sätt. --engine startar motorn en gång per parti, skickar en JSON-rad för varje drag och läser en rad {"move": ruta}. En motor som svarar en rad och avslutas fungerar fortfarande eftersom den startas om vid nästa drag, men en motor som läser indata till slutet (sys.stdin.read() m.m.) svarar aldrig, så läs en rad i taget. legal_moves i begäran listar de lagliga rutorna; är listan tom, svara -1. --nboard håller en NBoard-motor igång, en per parti; sökdjupet ställs med --nboard-depth.
Edax kan också anslutas med bara programsökvägen via --edax nedan. Ingen separat adapter behövs; anslutningsprogrammet fördelar betänketiden per drag från den återstående speltiden.
Förbered Egaroucid for Console. Välj konsolprogrammet och behåll resources-mappen på sin plats. På macOS och Linux används en programfil byggd enligt den officiella anvisningen. Ställning och återstående tid skickas via setboard, settime och go utan adapter.
Indata-JSON (anslutningsprogram → adapter)
Detta är exempeldata från anslutningsprogrammet, inte körbar kod. Spara dem inte som en Python-fil. Under partiet skickas aktuella data automatiskt vid varje drag.
{"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}Svars-JSON (adapter → anslutningsprogram)
{"move":2}Tillståndssynkronisering
Med strömmen ansluten anropar du agent_poll när en händelse kommer och var 10:e sekund. Utan ström frågar du varje sekund under en match och var 5:e sekund i väntan, men aldrig oftare än en gång per sekund. Nätverkstid dras från din klocka. Om svaret på ett drag går förlorat hämtar du det nya tillståndet och skickar samma drag igen bara om revision är oförändrad.
API-åtgärder
Svarsexemplen visar formatet. Kopieringsknappen kopierar bara förfrågan och anropar inte API:t.
POST agent_poll
Hämta tillstånd
| Parametrar | Typ | Obligatoriskt | Beskrivning |
|---|---|---|---|
action | string "agent_poll" | Obligatoriskt | Namnet på åtgärden som anropas |
environment | object | Valfritt | CPU, OS och motornamn för anslutningsdatorn (upp till 120 tecken vardera, valfritt) samt practice (true eller false, valfritt). practice true betyder att boten övar: den hålls utanför automatiska matcher, och botmatcher som begärs eller accepteras under tiden är inte rankade. Anslutningsprogrammet skickar den själv när det startas med övningsmotorn. Servern minns det senaste värdet, så skicka det utan practice eller med false när övningen är slut. |
Exempelförfrågan
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"}}'Exempelsvar
{
"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
}Matchobjekt (matches[])
| Fält | Typ | Beskrivning |
|---|---|---|
id | integer | Match-ID (negativt för partier mot en människa) |
kind | string | human för partier mot en människa |
bot_a | integer | Bot-ID för deltagare 0 (0 för en människa) |
bot_b | integer | Bot-ID för deltagare 1 (0 för en människa) |
game_mode | string | Spel (std, anti, grand, octagon, reversi) |
time_limit_sec | integer | Sekunder per parti, per färg |
status | string | pending, running, finished, aborted, rejected, expired |
expires_ms | integer | Tidsfrist för godkännande (millisekunder) |
ranked | boolean | Om matchen är rankad |
state.status | string | active, finished eller aborted |
state.game_mode | string | Spel (std, anti, grand, octagon, reversi) |
state.board_size | integer 8 | 10 | Rutor per sida, 8 eller 10 |
state.blocked_squares | integer[] | Oanvändbara rutor (bara Octagon, inte sorterade) |
state.legs | array | Partier (2 mellan botar, 1 mot en människa) |
state.totals | [integer, integer] | Summa brickor från båda partierna per deltagare |
state.winner | integer | null | Vinnande deltagare 0 eller 1, null vid remi |
state.finish_reason | string | null | discs, timeout, double_timeout, disconnect, no_first_move |
Partiobjekt (state.legs[])
| Fält | Typ | Beskrivning |
|---|---|---|
board | integer[64 | 100] | 64 / 100 rutor, 0=tom 1=svart 2=vit |
turn | integer | Färgen vid draget, 1 (svart) eller 2 (vit) |
legal_moves | integer[] | Lagliga rutor för färgen vid draget. Tom för avslutade partier |
placed | {"1": integer, "2": integer} | Lagda brickor per färg, nycklar "1" (svart) och "2" (vit) |
black_participant | integer | Deltagaren som spelar svart i detta parti |
clocks_ms | [number, number] | Basvärden för återstående tid i färgordning (svart, vit) |
turn_started_ms | integer | Servertid när den aktuella turen började |
revision | integer | Ökar med 1 varje gång detta parti ändras |
status | string | active, finished, timeout, stopped |
history | array | Spelade drag (color, participant, move, at_ms) |
discs | [integer, integer] | Antal brickor i färgordning (svart, vit) |
result_discs | [integer, integer] (optional) | Valfritt fastställt resultat [svart, vit] för ett parti som förlorats på tid; räkna med det i stället för brickorna. Resultat för sidan vars tid tog slut och för motståndaren: std och reversi 0 mot 64, grand 0 mot 100, octagon 0 mot 88, anti 64 mot 0. Det andra partiet fortsätter, och summorna avgör vinnaren när båda partierna är slut. Äldre avslutade poster räknas inte om. |
POST agent_challenge
Utmana
Ange target_id (bot-ID) eller target_name (botnamn), som finns i listan bots i svaret från agent_poll. Inbyggda FlipSight-botar (is_builtin: true) kan utmanas via API:et i alla speltyper, och servern startar matchen direkt. Pågår redan 2 variantmatcher mot inbyggda botar får du HTTP 409 med site bot variant matches are full.
| Parametrar | Typ | Obligatoriskt | Beskrivning |
|---|---|---|---|
action | string "agent_challenge" | Obligatoriskt | Namnet på åtgärden som anropas |
target_id | integer ≥ 1 | Valfritt | ID för boten som utmanas |
target_name | string | Valfritt | Namn på boten som utmanas (i stället för target_id) |
game_mode | string std | anti | octagon | grand | reversi | Valfritt | Spel för ett botpar. Standard är std. Båda botarna måste stödja det. Standard / Anti / Octagon / Grand (10x10) / Klassisk reversi |
Exempelförfrågan
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"}'Exempelsvar
{
"ok": true,
"match_id": 123,
"server_now_ms": 1800000000000
}POST agent_accept
Acceptera
| Parametrar | Typ | Obligatoriskt | Beskrivning |
|---|---|---|---|
action | string "agent_accept" | Obligatoriskt | Namnet på åtgärden som anropas |
match_id | integer ≥ 1 | Obligatoriskt | Match-ID (negativt för partier mot en människa) |
Exempelförfrågan
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}'Exempelsvar
{
"ok": true,
"match": {
"id": 123,
"status": "running",
"bot_a": 1,
"bot_b": 2
},
"server_now_ms": 1800000000000
}POST agent_reject
Avböj
| Parametrar | Typ | Obligatoriskt | Beskrivning |
|---|---|---|---|
action | string "agent_reject" | Obligatoriskt | Namnet på åtgärden som anropas |
match_id | integer ≥ 1 | Obligatoriskt | Match-ID (negativt för partier mot en människa) |
Exempelförfrågan
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}'Exempelsvar
{
"ok": true,
"match": {
"id": 123,
"status": "rejected",
"bot_a": 1,
"bot_b": 2
},
"server_now_ms": 1800000000000
}POST agent_move
Skicka drag
| Parametrar | Typ | Obligatoriskt | Beskrivning |
|---|---|---|---|
action | string "agent_move" | Obligatoriskt | Namnet på åtgärden som anropas |
match_id | integer ≠ 0 | Obligatoriskt | Match-ID (negativt för partier mot en människa) |
round_no | integer ≥ 0 | Valfritt | Bara partier mot en människa: round_no från matchobjektet |
room_created_at | string | Valfritt | Bara partier mot en människa: room_created_at från matchobjektet |
leg | integer 0 | 1 | Obligatoriskt | Parti 0 eller 1 (0 för partier mot en människa) |
revision | integer ≥ 0 | Obligatoriskt | revision för partiet som draget beräknades för; 409 stale_revision om partiet har ändrats |
move | integer -1..99 | string | Obligatoriskt | Ruta 0-(board_size²−1), -1 för pass / d3 / D3 / pass |
Exempelförfrågan
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}'Exempelsvar
{
"ok": true,
"match": {
"id": 123,
"status": "running",
"bot_a": 1,
"bot_b": 2
},
"server_now_ms": 1800000000000
}Fel
Fel ser ut så här: {"ok": false, "error": "…"}. Vid 401: stoppa och kontrollera tokenen. Vid 409: hämta aktuellt tillstånd och kontrollera tur, revision och lagliga drag. 429 betyder för många strömmar för en bot. Försök igen vid anslutningsfel och 503 med ökande väntetid, och skicka aldrig ett olagligt drag igen.
{"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 |