from flask import Flask, render_template, request, jsonify, session import chess import chess.svg from stockfish import Stockfish, StockfishException import os app = Flask(__name__) app.secret_key = os.urandom(24) # Nécessaire pour les sessions # Configuration du chemin vers l'exécutable de Stockfish # Essayez de le trouver automatiquement, sinon spécifiez le chemin STOCKFISH_PATH = "stockfish" # Ou le chemin complet, ex: "/usr/games/stockfish" if not os.path.exists(STOCKFISH_PATH): # Tente quelques chemins communs si non trouvé directement common_paths = ["/usr/games/stockfish", "/usr/local/bin/stockfish", "/opt/homebrew/bin/stockfish"] for p in common_paths: if os.path.exists(p) and os.access(p, os.X_OK): STOCKFISH_PATH = p break else: print(f"AVERTISSEMENT: Stockfish non trouvé à '{STOCKFISH_PATH}' ou dans les chemins communs. " "L'IA ne fonctionnera pas. Veuillez installer Stockfish et/ou configurer STOCKFISH_PATH.") def get_stockfish_engine(): """Initialise et retourne une instance de Stockfish.""" try: # Utilise les paramètres par défaut du wrapper, mais on peut les ajuster # par ex. augmenter la Hash (RAM) et les Threads si besoin. engine = Stockfish(path=STOCKFISH_PATH, depth=15, parameters={"Threads": 2, "Hash": 128}) # Vérifier si l'engine est bien chargé if not engine._stockfish.poll() is None: # Si le process s'est terminé raise StockfishException("Stockfish process terminated unexpectedly on init.") engine.get_best_move() # Test rapide pour voir si ça fonctionne engine.set_fen_position(chess.STARTING_FEN) # Reset return engine except Exception as e: print(f"Erreur lors de l'initialisation de Stockfish: {e}") print(f"Vérifiez que Stockfish est installé et que STOCKFISH_PATH ('{STOCKFISH_PATH}') est correct.") return None @app.route('/') def index(): if 'board_fen' not in session: session['board_fen'] = chess.Board().fen() session['game_mode'] = 'pvp' # 'pvp' ou 'ai' session['player_color'] = 'white' # Si AI, le joueur est blanc par défaut board = chess.Board(session['board_fen']) return render_template('index.html', initial_fen=session['board_fen'], board_svg=chess.svg.board(board=board, size=400), game_mode=session['game_mode'], player_color=session.get('player_color', 'white'), is_game_over=board.is_game_over(), outcome=get_outcome_message(board) if board.is_game_over() else "") def get_outcome_message(board): if board.is_checkmate(): winner = "Blancs" if board.turn == chess.BLACK else "Noirs" return f"Échec et mat ! {winner} gagnent." if board.is_stalemate(): return "Pat ! Partie nulle." if board.is_insufficient_material(): return "Matériel insuffisant. Partie nulle." if board.is_seventyfive_moves(): return "Règle des 75 coups. Partie nulle." if board.is_fivefold_repetition(): return "Répétition (5 fois). Partie nulle." return "" @app.route('/make_move', methods=['POST']) def make_move(): board = chess.Board(session['board_fen']) if board.is_game_over(): return jsonify({'error': 'La partie est terminée.', 'fen': board.fen(), 'game_over': True, 'outcome': get_outcome_message(board)}) move_uci = request.json.get('move') if not move_uci: return jsonify({'error': 'Mouvement non fourni.', 'fen': board.fen(), 'game_over': board.is_game_over()}) ai_move_made = None ai_move_uci = None try: move = board.parse_uci(move_uci) except ValueError: try: # Essayer de parser en SAN si l'UCI échoue (pour tests manuels par exemple) move = board.parse_san(move_uci) except ValueError: return jsonify({'error': f'Mouvement invalide: {move_uci}', 'fen': board.fen(), 'game_over': board.is_game_over()}) if move in board.legal_moves: board.push(move) session['board_fen'] = board.fen() # Si mode AI et ce n'est pas la fin du jeu pour le joueur if session.get('game_mode') == 'ai' and not board.is_game_over(): stockfish_engine = get_stockfish_engine() if stockfish_engine: stockfish_engine.set_fen_position(board.fen()) # Augmenter le temps de réflexion pour une meilleure IA # best_move_ai = stockfish_engine.get_best_move_time(1000) # 1 seconde best_move_ai = stockfish_engine.get_best_move() # Utilise la profondeur configurée if best_move_ai: ai_move_obj = board.parse_uci(best_move_ai) board.push(ai_move_obj) session['board_fen'] = board.fen() ai_move_made = True ai_move_uci = best_move_ai else: # L'IA ne trouve pas de coup (peut arriver si mat ou pat pour l'IA) pass stockfish_engine.send_quit_command() # Important pour libérer les ressources else: return jsonify({'error': 'Moteur Stockfish non disponible.', 'fen': board.fen(), 'game_over': board.is_game_over()}) else: return jsonify({'error': f'Mouvement illégal: {move_uci}', 'fen': board.fen(), 'game_over': board.is_game_over()}) game_over = board.is_game_over() outcome = get_outcome_message(board) if game_over else "" return jsonify({ 'fen': board.fen(), 'board_svg': chess.svg.board(board=board, size=400, lastmove=move if not ai_move_made else ai_move_obj), 'game_over': game_over, 'outcome': outcome, 'ai_move_uci': ai_move_uci, 'turn': 'white' if board.turn == chess.WHITE else 'black' }) @app.route('/reset_game', methods=['POST']) def reset_game(): session['board_fen'] = chess.Board().fen() # Le mode de jeu et la couleur du joueur sont conservés ou peuvent être réinitialisés ici # Par exemple, pour forcer la réinitialisation du mode : # session['game_mode'] = request.json.get('game_mode', 'pvp') # session['player_color'] = request.json.get('player_color', 'white') # ... ou simplement les effacer pour qu'ils soient redéfinis if 'stockfish_engine' in session: del session['stockfish_engine'] # S'assurer de le recréer return jsonify({'fen': session['board_fen'], 'board_svg': chess.svg.board(board=chess.Board(session['board_fen']), size=400), 'game_mode': session.get('game_mode'), 'player_color': session.get('player_color')}) @app.route('/set_mode', methods=['POST']) def set_mode_route(): mode = request.json.get('game_mode') player_color = request.json.get('player_color', 'white') # Par défaut blanc si non spécifié if mode in ['pvp', 'ai']: session['game_mode'] = mode session['player_color'] = player_color # 'white' or 'black' for AI mode session['board_fen'] = chess.Board().fen() # Reset board on mode change board = chess.Board() initial_ai_move_uci = None # Si l'IA joue en premier (joueur choisit noir) if mode == 'ai' and player_color == 'black': stockfish_engine = get_stockfish_engine() if stockfish_engine: stockfish_engine.set_fen_position(board.fen()) best_move_ai = stockfish_engine.get_best_move() if best_move_ai: ai_move_obj = board.parse_uci(best_move_ai) board.push(ai_move_obj) session['board_fen'] = board.fen() initial_ai_move_uci = best_move_ai stockfish_engine.send_quit_command() else: return jsonify({'error': 'Moteur Stockfish non disponible.'}) return jsonify({ 'message': f'Mode de jeu réglé sur {mode}. Joueur: {player_color if mode == "ai" else "N/A"}.', 'fen': session['board_fen'], 'board_svg': chess.svg.board(board=board, size=400, lastmove=board.move_stack[-1] if initial_ai_move_uci else None), 'game_mode': mode, 'player_color': player_color, 'turn': 'white' if board.turn == chess.WHITE else 'black', 'initial_ai_move_uci': initial_ai_move_uci }) return jsonify({'error': 'Mode invalide'}), 400 if __name__ == '__main__': app.run(debug=True)