Update app.py
Browse files
app.py
CHANGED
@@ -1,127 +1,208 @@
|
|
|
|
1 |
from flask import Flask, render_template, request, jsonify, session
|
2 |
-
from stockfish import Stockfish
|
3 |
-
import
|
|
|
|
|
|
|
4 |
|
5 |
app = Flask(__name__)
|
6 |
-
app.secret_key = '
|
7 |
|
8 |
-
#
|
9 |
-
# Assurez-vous que le binaire stockfish est dans votre PATH ou spécifiez le chemin.
|
10 |
try:
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
|
|
|
|
|
|
|
|
17 |
stockfish = None
|
18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
@app.route('/')
|
21 |
def index():
|
22 |
return render_template('index.html')
|
23 |
|
24 |
-
@app.route('/
|
25 |
-
def
|
26 |
-
if not
|
27 |
-
return
|
28 |
-
|
29 |
-
data = request.json
|
30 |
-
mode = data.get('mode', 'human') # 'ai' or 'human'
|
31 |
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
session['
|
36 |
-
session['history'] = []
|
37 |
|
38 |
-
|
39 |
-
|
40 |
-
# python-chess board (optionnel mais recommandé)
|
41 |
-
session['board_pgn'] = chess.Board().fen() # Stocker le FEN pour reconstruire
|
42 |
-
|
43 |
-
return jsonify({
|
44 |
-
"fen": initial_fen,
|
45 |
-
"turn": session['turn'],
|
46 |
-
"message": "Nouvelle partie commencée."
|
47 |
-
})
|
48 |
-
|
49 |
-
@app.route('/move', methods=['POST'])
|
50 |
-
def handle_move():
|
51 |
-
if not stockfish:
|
52 |
-
return jsonify({"error": "Stockfish non initialisé"}), 500
|
53 |
-
|
54 |
-
if 'fen' not in session:
|
55 |
-
return jsonify({"error": "Aucune partie en cours. Commencez une nouvelle partie."}), 400
|
56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
data = request.json
|
58 |
-
|
59 |
-
|
60 |
-
# Reconstruire l'état du board python-chess (si utilisé)
|
61 |
-
board = chess.Board(session['fen'])
|
62 |
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
except ValueError:
|
71 |
-
return jsonify({"error": "Format de coup invalide.", "fen": session['fen'], "turn": session['turn']}), 400
|
72 |
-
|
73 |
-
|
74 |
-
if not stockfish.is_move_correct(move_uci) or move_obj not in board.legal_moves:
|
75 |
-
return jsonify({"error": "Coup illégal.", "fen": session['fen'], "turn": session['turn']}), 400
|
76 |
-
|
77 |
-
# Appliquer le coup humain
|
78 |
-
stockfish.make_moves_from_current_position([move_uci])
|
79 |
-
board.push(move_obj)
|
80 |
-
session['fen'] = stockfish.get_fen_position() # ou board.fen()
|
81 |
-
session['history'].append(move_uci)
|
82 |
-
session['turn'] = 'b' if current_player_color == 'w' else 'w'
|
83 |
-
|
84 |
-
ai_move_uci = None
|
85 |
-
game_status = "En cours"
|
86 |
-
|
87 |
-
if board.is_checkmate():
|
88 |
-
game_status = f"Mat! {'Les Blancs' if board.turn == chess.BLACK else 'Les Noirs'} gagnent."
|
89 |
-
elif board.is_stalemate() or board.is_insufficient_material() or board.is_seventyfive_moves() or board.is_fivefold_repetition():
|
90 |
-
game_status = "Pat!"
|
91 |
-
|
92 |
-
# Mode IA
|
93 |
-
if session['mode'] == 'ai' and game_status == "En cours" and ( (board.turn == chess.BLACK and current_player_color == 'w') or \
|
94 |
-
(board.turn == chess.WHITE and current_player_color == 'b') ) : # Tour de l'IA
|
95 |
-
# S'assurer que stockfish a la bonne position si on utilise board.fen()
|
96 |
-
stockfish.set_fen_position(board.fen())
|
97 |
-
ai_move_uci = stockfish.get_best_move_time(1000) # 1 seconde de réflexion
|
98 |
-
if ai_move_uci:
|
99 |
-
ai_move_obj = board.parse_uci(ai_move_uci)
|
100 |
-
stockfish.make_moves_from_current_position([ai_move_uci]) # Stockfish est déjà à jour
|
101 |
-
board.push(ai_move_obj)
|
102 |
-
session['fen'] = stockfish.get_fen_position() # ou board.fen()
|
103 |
-
session['history'].append(ai_move_uci)
|
104 |
-
session['turn'] = 'w' if session['turn'] == 'b' else 'b'
|
105 |
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
|
126 |
if __name__ == '__main__':
|
127 |
app.run(debug=True)
|
|
|
1 |
+
# app.py
|
2 |
from flask import Flask, render_template, request, jsonify, session
|
3 |
+
from stockfish import Stockfish
|
4 |
+
import uuid
|
5 |
+
import chess
|
6 |
+
import chess.pgn
|
7 |
+
from datetime import datetime
|
8 |
|
9 |
app = Flask(__name__)
|
10 |
+
app.secret_key = 'your-secret-key-change-this'
|
11 |
|
12 |
+
# Configuration Stockfish
|
|
|
13 |
try:
|
14 |
+
stockfish = Stockfish(
|
15 |
+
path="/usr/local/bin/stockfish", # Ajustez selon votre installation
|
16 |
+
parameters={
|
17 |
+
"Threads": 2,
|
18 |
+
"Hash": 256,
|
19 |
+
"Skill Level": 15, # Niveau IA (0-20)
|
20 |
+
"Minimum Thinking Time": 100
|
21 |
+
}
|
22 |
+
)
|
23 |
+
except:
|
24 |
stockfish = None
|
25 |
+
print("Stockfish non trouvé. Mode IA désactivé.")
|
26 |
+
|
27 |
+
class ChessGame:
|
28 |
+
def __init__(self, game_id, mode='human'):
|
29 |
+
self.game_id = game_id
|
30 |
+
self.mode = mode # 'human' ou 'ai'
|
31 |
+
self.board = chess.Board()
|
32 |
+
self.moves_history = []
|
33 |
+
self.current_player = 'white'
|
34 |
+
self.game_status = 'active' # 'active', 'checkmate', 'stalemate', 'draw'
|
35 |
+
self.created_at = datetime.now()
|
36 |
+
|
37 |
+
def make_move(self, move_uci):
|
38 |
+
"""Effectue un coup et retourne le résultat"""
|
39 |
+
try:
|
40 |
+
move = chess.Move.from_uci(move_uci)
|
41 |
+
|
42 |
+
if move in self.board.legal_moves:
|
43 |
+
self.board.push(move)
|
44 |
+
self.moves_history.append(move_uci)
|
45 |
+
|
46 |
+
# Changer de joueur
|
47 |
+
self.current_player = 'black' if self.current_player == 'white' else 'white'
|
48 |
+
|
49 |
+
# Vérifier l'état du jeu
|
50 |
+
self._update_game_status()
|
51 |
+
|
52 |
+
return {
|
53 |
+
'success': True,
|
54 |
+
'move': move_uci,
|
55 |
+
'board_fen': self.board.fen(),
|
56 |
+
'current_player': self.current_player,
|
57 |
+
'game_status': self.game_status,
|
58 |
+
'legal_moves': [move.uci() for move in self.board.legal_moves],
|
59 |
+
'is_check': self.board.is_check()
|
60 |
+
}
|
61 |
+
else:
|
62 |
+
return {'success': False, 'error': 'Coup illégal'}
|
63 |
+
|
64 |
+
except Exception as e:
|
65 |
+
return {'success': False, 'error': str(e)}
|
66 |
+
|
67 |
+
def get_ai_move(self):
|
68 |
+
"""Obtient le coup de l'IA via Stockfish"""
|
69 |
+
if not stockfish or self.mode != 'ai':
|
70 |
+
return None
|
71 |
+
|
72 |
+
try:
|
73 |
+
stockfish.set_fen_position(self.board.fen())
|
74 |
+
ai_move = stockfish.get_best_move()
|
75 |
+
return ai_move
|
76 |
+
except:
|
77 |
+
return None
|
78 |
+
|
79 |
+
def _update_game_status(self):
|
80 |
+
"""Met à jour l'état du jeu"""
|
81 |
+
if self.board.is_checkmate():
|
82 |
+
winner = 'black' if self.current_player == 'white' else 'white'
|
83 |
+
self.game_status = f'checkmate_{winner}'
|
84 |
+
elif self.board.is_stalemate():
|
85 |
+
self.game_status = 'stalemate'
|
86 |
+
elif self.board.is_insufficient_material():
|
87 |
+
self.game_status = 'draw_material'
|
88 |
+
elif self.board.is_seventyfive_moves():
|
89 |
+
self.game_status = 'draw_75moves'
|
90 |
+
elif self.board.is_fivefold_repetition():
|
91 |
+
self.game_status = 'draw_repetition'
|
92 |
+
|
93 |
+
def get_board_state(self):
|
94 |
+
"""Retourne l'état complet du plateau"""
|
95 |
+
return {
|
96 |
+
'fen': self.board.fen(),
|
97 |
+
'current_player': self.current_player,
|
98 |
+
'game_status': self.game_status,
|
99 |
+
'legal_moves': [move.uci() for move in self.board.legal_moves],
|
100 |
+
'is_check': self.board.is_check(),
|
101 |
+
'moves_history': self.moves_history,
|
102 |
+
'move_count': len(self.moves_history)
|
103 |
+
}
|
104 |
+
|
105 |
+
# Stockage des parties en mémoire (remplacez par une base de données en production)
|
106 |
+
games = {}
|
107 |
|
108 |
@app.route('/')
|
109 |
def index():
|
110 |
return render_template('index.html')
|
111 |
|
112 |
+
@app.route('/game/<mode>')
|
113 |
+
def game(mode):
|
114 |
+
if mode not in ['human', 'ai']:
|
115 |
+
return "Mode de jeu invalide", 400
|
|
|
|
|
|
|
116 |
|
117 |
+
# Créer une nouvelle partie
|
118 |
+
game_id = str(uuid.uuid4())
|
119 |
+
games[game_id] = ChessGame(game_id, mode)
|
120 |
+
session['game_id'] = game_id
|
|
|
121 |
|
122 |
+
return render_template('game.html', mode=mode, game_id=game_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
|
124 |
+
@app.route('/api/game/state')
|
125 |
+
def get_game_state():
|
126 |
+
game_id = session.get('game_id')
|
127 |
+
if not game_id or game_id not in games:
|
128 |
+
return jsonify({'error': 'Partie non trouvée'}), 404
|
129 |
+
|
130 |
+
game = games[game_id]
|
131 |
+
return jsonify(game.get_board_state())
|
132 |
+
|
133 |
+
@app.route('/api/game/move', methods=['POST'])
|
134 |
+
def make_move():
|
135 |
+
game_id = session.get('game_id')
|
136 |
+
if not game_id or game_id not in games:
|
137 |
+
return jsonify({'error': 'Partie non trouvée'}), 404
|
138 |
+
|
139 |
data = request.json
|
140 |
+
move = data.get('move')
|
|
|
|
|
|
|
141 |
|
142 |
+
if not move:
|
143 |
+
return jsonify({'error': 'Coup manquant'}), 400
|
144 |
+
|
145 |
+
game = games[game_id]
|
146 |
+
result = game.make_move(move)
|
147 |
+
|
148 |
+
return jsonify(result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
|
150 |
+
@app.route('/api/game/ai-move', methods=['POST'])
|
151 |
+
def get_ai_move():
|
152 |
+
game_id = session.get('game_id')
|
153 |
+
if not game_id or game_id not in games:
|
154 |
+
return jsonify({'error': 'Partie non trouvée'}), 404
|
155 |
+
|
156 |
+
game = games[game_id]
|
157 |
+
|
158 |
+
if game.mode != 'ai':
|
159 |
+
return jsonify({'error': 'Mode IA non activé'}), 400
|
160 |
+
|
161 |
+
if game.current_player != 'black': # IA joue les noirs
|
162 |
+
return jsonify({'error': 'Ce n\'est pas le tour de l\'IA'}), 400
|
163 |
+
|
164 |
+
ai_move = game.get_ai_move()
|
165 |
+
if not ai_move:
|
166 |
+
return jsonify({'error': 'IA ne peut pas jouer'}), 500
|
167 |
+
|
168 |
+
# Effectuer le coup de l'IA
|
169 |
+
result = game.make_move(ai_move)
|
170 |
+
result['ai_move'] = True
|
171 |
+
|
172 |
+
return jsonify(result)
|
173 |
|
174 |
+
@app.route('/api/game/reset', methods=['POST'])
|
175 |
+
def reset_game():
|
176 |
+
game_id = session.get('game_id')
|
177 |
+
if not game_id or game_id not in games:
|
178 |
+
return jsonify({'error': 'Partie non trouvée'}), 404
|
179 |
+
|
180 |
+
game = games[game_id]
|
181 |
+
# Réinitialiser la partie
|
182 |
+
games[game_id] = ChessGame(game_id, game.mode)
|
183 |
+
|
184 |
+
return jsonify({'success': True, 'message': 'Partie réinitialisée'})
|
185 |
|
186 |
+
@app.route('/api/game/legal-moves')
|
187 |
+
def get_legal_moves():
|
188 |
+
game_id = session.get('game_id')
|
189 |
+
if not game_id or game_id not in games:
|
190 |
+
return jsonify({'error': 'Partie non trouvée'}), 404
|
191 |
+
|
192 |
+
data = request.args
|
193 |
+
square = data.get('square')
|
194 |
+
|
195 |
+
if not square:
|
196 |
+
return jsonify({'error': 'Case manquante'}), 400
|
197 |
+
|
198 |
+
game = games[game_id]
|
199 |
+
legal_moves = []
|
200 |
+
|
201 |
+
for move in game.board.legal_moves:
|
202 |
+
if move.from_square == chess.parse_square(square):
|
203 |
+
legal_moves.append(chess.square_name(move.to_square))
|
204 |
+
|
205 |
+
return jsonify({'legal_moves': legal_moves})
|
206 |
|
207 |
if __name__ == '__main__':
|
208 |
app.run(debug=True)
|