"""
Chess AI - Hugging Face Spaces
Entry point: app.py
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import streamlit as st
import chess
import chess.svg
import base64
st.set_page_config(
page_title="Chess AI",
page_icon="♟️",
layout="centered",
initial_sidebar_state="expanded",
)
st.markdown("""
""", unsafe_allow_html=True)
# ─── Cached resource loading ─────────────────────────────────────────────────
@st.cache_resource(show_spinner="Đang tải Chess Engine...")
def load_engine():
from src import config, engine
return config, engine
@st.cache_resource(show_spinner="Đang tải ML Model...")
def load_ml_agent():
try:
from src.ai_battle import MLAgent
return MLAgent(), None
except Exception as e:
return None, str(e)
# ─── Session state ────────────────────────────────────────────────────────────
def init_state():
defaults = {
"board": chess.Board(),
"move_log": [],
"game_over": False,
"status_msg": "Lượt của bạn ♙",
"depth": 2,
"game_mode": "human_vs_minimax",
"input_key": 0,
}
for k, v in defaults.items():
if k not in st.session_state:
st.session_state[k] = v
init_state()
# ─── Helpers ─────────────────────────────────────────────────────────────────
def board_to_html(board: chess.Board, last_move: chess.Move = None) -> str:
arrows = []
if last_move:
arrows.append(chess.svg.Arrow(last_move.from_square, last_move.to_square, color="#f0c06088"))
svg = chess.svg.board(
board, size=460, arrows=arrows,
colors={
"square light": "#ede0c8",
"square dark": "#b58863",
"square light lastmove": "#cdd16e",
"square dark lastmove": "#aaa23a",
}
)
b64 = base64.b64encode(svg.encode()).decode()
return f''
def get_last_move(board):
try:
return board.peek()
except IndexError:
return None
def reset_game():
st.session_state.board = chess.Board()
st.session_state.move_log = []
st.session_state.game_over = False
st.session_state.status_msg = "Lượt của bạn ♙"
st.session_state.input_key = 0
def check_game_over(board: chess.Board) -> bool:
if board.is_checkmate():
winner = "Đen" if board.turn == chess.WHITE else "Trắng"
st.session_state.status_msg = f"♚ Chiếu hết! {winner} thắng!"
st.session_state.game_over = True
elif board.is_stalemate():
st.session_state.status_msg = "🤝 Hòa (Stalemate)"
st.session_state.game_over = True
elif board.is_insufficient_material():
st.session_state.status_msg = "🤝 Hòa (thiếu quân)"
st.session_state.game_over = True
elif board.is_seventyfive_moves():
st.session_state.status_msg = "🤝 Hòa (75 nước)"
st.session_state.game_over = True
return st.session_state.game_over
def do_minimax_move(board: chess.Board):
config, engine = load_engine()
move = engine.find_best_move(board, config.STANDARD_WEIGHTS, st.session_state.depth)
if move and move in board.legal_moves:
st.session_state.move_log.append(f"Minimax (d{st.session_state.depth}): {move.uci()}")
board.push(move)
if not check_game_over(board):
st.session_state.status_msg = "Lượt của bạn ♙"
def do_ml_move(board: chess.Board):
ml_agent, err = load_ml_agent()
if err:
st.error(f"ML Agent lỗi: {err}")
return
move = ml_agent.get_move(board, time_limit=15.0)
if move and move in board.legal_moves:
st.session_state.move_log.append(f"ML Agent: {move.uci()}")
board.push(move)
if not check_game_over(board):
st.session_state.status_msg = "Lượt của bạn ♙"
else:
import random
legal = list(board.legal_moves)
if legal:
fb = random.choice(legal)
board.push(fb)
st.session_state.move_log.append(f"ML Agent (fallback): {fb.uci()}")
check_game_over(board)
# ─── Sidebar ──────────────────────────────────────────────────────────────────
with st.sidebar:
st.markdown("## ♟️ Chess AI")
st.markdown("---")
new_mode = st.radio(
"Chế độ chơi",
["Người vs Minimax", "Người vs ML Agent"],
)
new_mode_key = "human_vs_minimax" if "Minimax" in new_mode else "human_vs_ml"
# Reset tự động nếu đổi chế độ
if new_mode_key != st.session_state.game_mode:
st.session_state.game_mode = new_mode_key
reset_game()
if new_mode_key == "human_vs_minimax":
st.session_state.depth = st.slider("Độ sâu Minimax", 1, 4, 2)
st.caption("Depth 1-2: nhanh · Depth 3-4: mạnh hơn nhưng chậm")
st.markdown("---")
if st.button("🔄 Ván mới"):
reset_game()
st.rerun()
st.markdown("---")
st.markdown("**Hướng dẫn**")
st.markdown("Chọn ô nguồn → ô đích → nhấn **Đi** \nPhong cấp tự động lên Hậu")
st.markdown("---")
st.markdown(
'📂 GitHub',
unsafe_allow_html=True,
)
# ─── Main ─────────────────────────────────────────────────────────────────────
st.markdown("# Chess AI")
board = st.session_state.board
col_board, col_ctrl = st.columns([3, 2], gap="medium")
with col_board:
st.markdown(board_to_html(board, get_last_move(board)), unsafe_allow_html=True)
st.markdown(
f'