File size: 3,976 Bytes
96f9c6a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import os
import subprocess
import threading
import json
import requests
import gradio as gr

LICHESS_TOKEN = os.environ.get("LICHESS_TOKEN")
ENGINE_PATH = "./veloct"

def compile_engine():
    print("Compiling engine locally inside Hugging Face runtime...")
    try:
        if os.path.exists(ENGINE_PATH):
            return
        
        compile_cmd = [
            "g++", "-O3", "-std=c++17", "-DNDEBUG", "-DUSE_PTHREADS", 
            "-pthread", "veloct.cpp", "-o", "veloct", "-latomic"
        ]
        result = subprocess.run(compile_cmd, capture_output=True, text=True)
        if result.returncode == 0:
            print("Engine compiled successfully!")
            os.chmod(ENGINE_PATH, 0o755)
        else:
            print(f"Compilation failed:\n{result.stderr}")
    except Exception as e:
        print(f"Compilation error: {e}")

def send_move(game_id, move):
    url = f"https://lichess.org/api/bot/game/{game_id}/move/{move}"
    headers = {"Authorization": f"Bearer {LICHESS_TOKEN}"}
    requests.post(url, headers=headers)

def engine_think(game_id, fen, moves_string):
    try:
        proc = subprocess.Popen(
            [ENGINE_PATH], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True, bufsize=1
        )
        proc.stdin.write("uci\n")
        proc.stdin.write("isready\n")
        
        pos_cmd = f"position fen {fen}" if fen else "position startpos"
        if moves_string:
            pos_cmd += f" moves {moves_string}"
        
        proc.stdin.write(f"{pos_cmd}\n")
        proc.stdin.write("go movetime 1500\n")
        proc.stdin.flush()

        while True:
            line = proc.stdout.readline()
            if not line:
                break
            if line.startswith("bestmove"):
                best_move = line.split()[1]
                if best_move != "(none)":
                    send_move(game_id, best_move)
                break
        proc.terminate()
    except Exception as e:
        print(f"Engine execution error: {e}")

def stream_game(game_id):
    url = f"https://lichess.org/api/bot/game/stream/{game_id}"
    headers = {"Authorization": f"Bearer {LICHESS_TOKEN}"}
    response = requests.get(url, headers=headers, stream=True)
    
    fen = None
    for line in response.iter_lines():
        if line:
            event = json.loads(line.decode('utf-8'))
            if event.get("type") == "gameFull":
                fen = event.get("initialFen")
                state = event.get("state")
            else:
                state = event
            
            moves = state.get("moves", "")
            moves_list = moves.split() if moves else []
            
            if len(moves_list) % 2 == 0:
                threading.Thread(target=engine_think, args=(game_id, fen, moves)).start()

def listen_events():
    url = "https://lichess.org/api/stream/event"
    headers = {"Authorization": f"Bearer {LICHESS_TOKEN}"}
    print("Connecting to Lichess Bot Stream API...")
    
    try:
        response = requests.get(url, headers=headers, stream=True)
        for line in response.iter_lines():
            if line:
                event = json.loads(line.decode('utf-8'))
                if event.get("type") == "challenge":
                    challenge_id = event["challenge"]["id"]
                    requests.post(f"https://lichess.org/api/challenge/{challenge_id}/accept", headers=headers)
                elif event.get("type") == "gameStart":
                    game_id = event["game"]["id"]
                    threading.Thread(target=stream_game, args=(game_id,)).start()
    except Exception as e:
        print(f"Stream error: {e}")

if LICHESS_TOKEN:
    compile_engine()
    threading.Thread(target=listen_events, daemon=True).start()
else:
    print("Warning: LICHESS_TOKEN missing.")

with gr.Blocks() as demo:
    gr.Markdown("# 🤖 VeloCT Engine Lichess Bot")
    gr.Markdown("Status: Running execution loop in background runtime container.")

demo.launch()