Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -5,33 +5,144 @@ import os
|
|
5 |
import git
|
6 |
import sys
|
7 |
import random
|
|
|
8 |
|
9 |
# Configuration de la page
|
10 |
st.set_page_config(page_title="Échecs contre IA", layout="wide")
|
11 |
|
12 |
def render_board():
|
13 |
-
"""Rendre l'échiquier
|
14 |
-
#
|
15 |
-
squares = None
|
16 |
-
last_move = None
|
17 |
-
|
18 |
-
if st.session_state.board.move_stack:
|
19 |
-
last_move = st.session_state.board.peek()
|
20 |
-
|
21 |
-
# Créer le SVG
|
22 |
board_svg = chess.svg.board(
|
23 |
board=st.session_state.board,
|
24 |
size=400,
|
25 |
-
lastmove=
|
26 |
-
check=st.session_state.board.king(st.session_state.board.turn) if st.session_state.board.is_check() else None
|
27 |
)
|
28 |
|
29 |
-
#
|
30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
|
32 |
-
# Setup du modèle
|
33 |
@st.cache_resource
|
34 |
def setup_inference():
|
|
|
35 |
if not os.path.exists('chess'):
|
36 |
git.Repo.clone_from('https://github.com/l-pommeret/chess.git', 'chess')
|
37 |
if 'chess' not in sys.path:
|
@@ -45,25 +156,12 @@ def setup_inference():
|
|
45 |
)
|
46 |
return ChessGenerator(config)
|
47 |
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
# Initialisation de l'état
|
56 |
-
if 'board' not in st.session_state:
|
57 |
-
st.session_state.board = chess.Board()
|
58 |
-
if 'moves' not in st.session_state:
|
59 |
-
st.session_state.moves = []
|
60 |
-
|
61 |
-
def calculate_max_length(move_count):
|
62 |
-
"""Calcule le max_length optimal"""
|
63 |
-
base_length = 10
|
64 |
-
increment = 1
|
65 |
-
max_length = 100
|
66 |
-
return min(base_length + (move_count * increment), max_length)
|
67 |
|
68 |
def get_ai_move(prompt):
|
69 |
"""Obtient le coup de l'IA"""
|
@@ -76,18 +174,20 @@ def get_ai_move(prompt):
|
|
76 |
response = generator.generate("1.")
|
77 |
else:
|
78 |
moves = prompt.split()
|
79 |
-
last_moves = " ".join(moves[-4:])
|
80 |
-
|
|
|
81 |
next_move_num = f"{(len(moves)//2 + 1)}."
|
82 |
response = generator.generate(f"{last_moves} {next_move_num}")
|
83 |
-
else:
|
84 |
response = generator.generate(f"{last_moves}")
|
85 |
|
86 |
moves = response[0].split() if isinstance(response, list) else response.split()
|
87 |
next_move = moves[-1]
|
88 |
if '.' in next_move and len(moves) > 1:
|
89 |
next_move = moves[-2]
|
90 |
-
|
|
|
91 |
try:
|
92 |
move = st.session_state.board.parse_san(next_move)
|
93 |
if move in st.session_state.board.legal_moves:
|
@@ -95,17 +195,18 @@ def get_ai_move(prompt):
|
|
95 |
except ValueError:
|
96 |
pass
|
97 |
|
98 |
-
#
|
99 |
legal_moves = list(st.session_state.board.legal_moves)
|
100 |
if legal_moves:
|
101 |
random_move = random.choice(legal_moves)
|
102 |
return st.session_state.board.san(random_move)
|
|
|
103 |
except Exception as e:
|
104 |
-
st.error(f"Erreur lors de la génération du coup
|
105 |
return None
|
106 |
|
107 |
def try_move(move_str):
|
108 |
-
"""
|
109 |
try:
|
110 |
clean_move = move_str.split('.')[-1].strip()
|
111 |
move = st.session_state.board.parse_san(clean_move)
|
@@ -119,7 +220,7 @@ def try_move(move_str):
|
|
119 |
return False
|
120 |
|
121 |
def get_game_string():
|
122 |
-
"""
|
123 |
result = []
|
124 |
for i in range(0, len(st.session_state.moves), 2):
|
125 |
move_num = i//2 + 1
|
@@ -128,11 +229,23 @@ def get_game_string():
|
|
128 |
result.append(st.session_state.moves[i+1])
|
129 |
return " ".join(result)
|
130 |
|
131 |
-
def
|
132 |
-
"""
|
133 |
-
|
134 |
-
st.
|
135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
|
137 |
# Interface utilisateur
|
138 |
st.title("♟️ Échecs contre IA")
|
@@ -140,22 +253,18 @@ st.title("♟️ Échecs contre IA")
|
|
140 |
col1, col2 = st.columns([2, 1])
|
141 |
|
142 |
with col1:
|
143 |
-
# Échiquier
|
144 |
render_board()
|
145 |
|
146 |
-
# Input
|
147 |
move = st.text_input(
|
148 |
-
"
|
149 |
key=f"move_input_{len(st.session_state.moves)}",
|
150 |
placeholder="ex: e4, Nf3, O-O"
|
151 |
)
|
152 |
-
|
153 |
-
# Afficher les coups légaux si demandé
|
154 |
-
if st.checkbox("Afficher les coups possibles"):
|
155 |
-
display_legal_moves()
|
156 |
|
157 |
with col2:
|
158 |
-
#
|
159 |
st.subheader("Partie en cours")
|
160 |
game_str = get_game_string()
|
161 |
if game_str:
|
@@ -164,13 +273,15 @@ with col2:
|
|
164 |
# Instructions
|
165 |
st.markdown("""
|
166 |
**Comment jouer:**
|
167 |
-
-
|
168 |
-
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
|
|
|
|
174 |
""")
|
175 |
|
176 |
# Nouvelle partie
|
@@ -179,7 +290,27 @@ with col2:
|
|
179 |
st.session_state.moves = []
|
180 |
st.rerun()
|
181 |
|
182 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
if move:
|
184 |
if try_move(move):
|
185 |
game_str = get_game_string()
|
|
|
5 |
import git
|
6 |
import sys
|
7 |
import random
|
8 |
+
from streamlit.components.v1 import html
|
9 |
|
10 |
# Configuration de la page
|
11 |
st.set_page_config(page_title="Échecs contre IA", layout="wide")
|
12 |
|
13 |
def render_board():
|
14 |
+
"""Rendre l'échiquier interactif"""
|
15 |
+
# Générer le SVG de l'échiquier
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
board_svg = chess.svg.board(
|
17 |
board=st.session_state.board,
|
18 |
size=400,
|
19 |
+
lastmove=st.session_state.board.peek() if st.session_state.board.move_stack else None,
|
|
|
20 |
)
|
21 |
|
22 |
+
# Code HTML/JS pour rendre l'échiquier interactif
|
23 |
+
html_content = f"""
|
24 |
+
<style>
|
25 |
+
#chess-board {{
|
26 |
+
user-select: none;
|
27 |
+
position: relative;
|
28 |
+
width: 400px;
|
29 |
+
margin: auto;
|
30 |
+
}}
|
31 |
+
.dragging {{
|
32 |
+
opacity: 0.8;
|
33 |
+
cursor: grabbing !important;
|
34 |
+
}}
|
35 |
+
.piece {{
|
36 |
+
cursor: grab;
|
37 |
+
}}
|
38 |
+
.square {{
|
39 |
+
cursor: pointer;
|
40 |
+
}}
|
41 |
+
.highlight {{
|
42 |
+
fill: yellow;
|
43 |
+
opacity: 0.5;
|
44 |
+
}}
|
45 |
+
.last-move {{
|
46 |
+
fill: #aaf;
|
47 |
+
opacity: 0.5;
|
48 |
+
}}
|
49 |
+
</style>
|
50 |
+
|
51 |
+
<div id="chess-board">
|
52 |
+
{board_svg}
|
53 |
+
</div>
|
54 |
+
|
55 |
+
<script>
|
56 |
+
(function() {{
|
57 |
+
const board = document.querySelector('#chess-board');
|
58 |
+
let draggedPiece = null;
|
59 |
+
let startSquare = null;
|
60 |
+
let isDragging = false;
|
61 |
+
let dragStartX = 0;
|
62 |
+
let dragStartY = 0;
|
63 |
+
let originalTransform = null;
|
64 |
+
|
65 |
+
// Convertir les coordonnées en notation algébrique
|
66 |
+
function getSquareFromCoords(x, y) {{
|
67 |
+
const boardRect = board.getBoundingClientRect();
|
68 |
+
const squareSize = boardRect.width / 8;
|
69 |
+
const file = Math.floor((x - boardRect.left) / squareSize);
|
70 |
+
const rank = 7 - Math.floor((y - boardRect.top) / squareSize);
|
71 |
+
if (file >= 0 && file < 8 && rank >= 0 && rank < 8) {{
|
72 |
+
return String.fromCharCode(97 + file) + (rank + 1);
|
73 |
+
}}
|
74 |
+
return null;
|
75 |
+
}}
|
76 |
+
|
77 |
+
// Déplacer la pièce
|
78 |
+
function movePiece(piece, x, y) {{
|
79 |
+
const deltaX = x - dragStartX;
|
80 |
+
const deltaY = y - dragStartY;
|
81 |
+
piece.style.transform = `translate(${{deltaX}}px, ${{deltaY}}px)`;
|
82 |
+
}}
|
83 |
+
|
84 |
+
// Début du glissement
|
85 |
+
board.addEventListener('mousedown', function(e) {{
|
86 |
+
const piece = e.target.closest('.piece');
|
87 |
+
if (piece && piece.classList.contains('white')) {{
|
88 |
+
isDragging = true;
|
89 |
+
draggedPiece = piece;
|
90 |
+
dragStartX = e.clientX;
|
91 |
+
dragStartY = e.clientY;
|
92 |
+
startSquare = getSquareFromCoords(e.clientX, e.clientY);
|
93 |
+
originalTransform = window.getComputedStyle(piece).transform;
|
94 |
+
|
95 |
+
piece.classList.add('dragging');
|
96 |
+
e.preventDefault();
|
97 |
+
}}
|
98 |
+
}});
|
99 |
+
|
100 |
+
// Glissement
|
101 |
+
document.addEventListener('mousemove', function(e) {{
|
102 |
+
if (isDragging && draggedPiece) {{
|
103 |
+
movePiece(draggedPiece, e.clientX, e.clientY);
|
104 |
+
}}
|
105 |
+
}});
|
106 |
+
|
107 |
+
// Fin du glissement
|
108 |
+
document.addEventListener('mouseup', function(e) {{
|
109 |
+
if (isDragging && draggedPiece) {{
|
110 |
+
const endSquare = getSquareFromCoords(e.clientX, e.clientY);
|
111 |
+
|
112 |
+
// Réinitialiser la position de la pièce
|
113 |
+
draggedPiece.style.transform = originalTransform;
|
114 |
+
draggedPiece.classList.remove('dragging');
|
115 |
+
|
116 |
+
// Envoyer le coup si valide
|
117 |
+
if (startSquare && endSquare && startSquare !== endSquare) {{
|
118 |
+
window.parent.postMessage({{
|
119 |
+
type: 'chess_move',
|
120 |
+
from: startSquare,
|
121 |
+
to: endSquare
|
122 |
+
}}, '*');
|
123 |
+
}}
|
124 |
+
|
125 |
+
isDragging = false;
|
126 |
+
draggedPiece = null;
|
127 |
+
startSquare = null;
|
128 |
+
}}
|
129 |
+
}});
|
130 |
+
}})();
|
131 |
+
</script>
|
132 |
+
"""
|
133 |
+
|
134 |
+
html(html_content, height=450)
|
135 |
+
|
136 |
+
def calculate_max_length(move_count):
|
137 |
+
"""Calcule le max_length optimal"""
|
138 |
+
base_length = 10
|
139 |
+
increment = 1
|
140 |
+
max_length = 100
|
141 |
+
return min(base_length + (move_count * increment), max_length)
|
142 |
|
|
|
143 |
@st.cache_resource
|
144 |
def setup_inference():
|
145 |
+
"""Initialise le modèle d'IA"""
|
146 |
if not os.path.exists('chess'):
|
147 |
git.Repo.clone_from('https://github.com/l-pommeret/chess.git', 'chess')
|
148 |
if 'chess' not in sys.path:
|
|
|
156 |
)
|
157 |
return ChessGenerator(config)
|
158 |
|
159 |
+
def initialize_state():
|
160 |
+
"""Initialise ou réinitialise l'état du jeu"""
|
161 |
+
if 'board' not in st.session_state:
|
162 |
+
st.session_state.board = chess.Board()
|
163 |
+
if 'moves' not in st.session_state:
|
164 |
+
st.session_state.moves = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
|
166 |
def get_ai_move(prompt):
|
167 |
"""Obtient le coup de l'IA"""
|
|
|
174 |
response = generator.generate("1.")
|
175 |
else:
|
176 |
moves = prompt.split()
|
177 |
+
last_moves = " ".join(moves[-4:]) # Garder seulement les 2 derniers coups
|
178 |
+
|
179 |
+
if len(moves) % 2 == 0: # Tour des blancs
|
180 |
next_move_num = f"{(len(moves)//2 + 1)}."
|
181 |
response = generator.generate(f"{last_moves} {next_move_num}")
|
182 |
+
else: # Tour des noirs
|
183 |
response = generator.generate(f"{last_moves}")
|
184 |
|
185 |
moves = response[0].split() if isinstance(response, list) else response.split()
|
186 |
next_move = moves[-1]
|
187 |
if '.' in next_move and len(moves) > 1:
|
188 |
next_move = moves[-2]
|
189 |
+
|
190 |
+
# Vérifier si le coup est légal
|
191 |
try:
|
192 |
move = st.session_state.board.parse_san(next_move)
|
193 |
if move in st.session_state.board.legal_moves:
|
|
|
195 |
except ValueError:
|
196 |
pass
|
197 |
|
198 |
+
# Si le coup n'est pas légal, jouer un coup aléatoire
|
199 |
legal_moves = list(st.session_state.board.legal_moves)
|
200 |
if legal_moves:
|
201 |
random_move = random.choice(legal_moves)
|
202 |
return st.session_state.board.san(random_move)
|
203 |
+
|
204 |
except Exception as e:
|
205 |
+
st.error(f"Erreur lors de la génération du coup: {str(e)}")
|
206 |
return None
|
207 |
|
208 |
def try_move(move_str):
|
209 |
+
"""Tente d'appliquer un coup"""
|
210 |
try:
|
211 |
clean_move = move_str.split('.')[-1].strip()
|
212 |
move = st.session_state.board.parse_san(clean_move)
|
|
|
220 |
return False
|
221 |
|
222 |
def get_game_string():
|
223 |
+
"""Obtient la notation de la partie"""
|
224 |
result = []
|
225 |
for i in range(0, len(st.session_state.moves), 2):
|
226 |
move_num = i//2 + 1
|
|
|
229 |
result.append(st.session_state.moves[i+1])
|
230 |
return " ".join(result)
|
231 |
|
232 |
+
def handle_move(move_from, move_to):
|
233 |
+
"""Gère un coup joué via l'interface graphique"""
|
234 |
+
move = chess.Move.from_uci(f"{move_from}{move_to}")
|
235 |
+
if move in st.session_state.board.legal_moves:
|
236 |
+
san_move = st.session_state.board.san(move)
|
237 |
+
if try_move(san_move):
|
238 |
+
return True
|
239 |
+
return False
|
240 |
+
|
241 |
+
# Initialisation
|
242 |
+
try:
|
243 |
+
generator = setup_inference()
|
244 |
+
except Exception as e:
|
245 |
+
st.error(f"Erreur d'initialisation: {str(e)}")
|
246 |
+
st.stop()
|
247 |
+
|
248 |
+
initialize_state()
|
249 |
|
250 |
# Interface utilisateur
|
251 |
st.title("♟️ Échecs contre IA")
|
|
|
253 |
col1, col2 = st.columns([2, 1])
|
254 |
|
255 |
with col1:
|
256 |
+
# Échiquier interactif
|
257 |
render_board()
|
258 |
|
259 |
+
# Input texte alternatif
|
260 |
move = st.text_input(
|
261 |
+
"Ou saisissez votre coup ici",
|
262 |
key=f"move_input_{len(st.session_state.moves)}",
|
263 |
placeholder="ex: e4, Nf3, O-O"
|
264 |
)
|
|
|
|
|
|
|
|
|
265 |
|
266 |
with col2:
|
267 |
+
# Informations de la partie
|
268 |
st.subheader("Partie en cours")
|
269 |
game_str = get_game_string()
|
270 |
if game_str:
|
|
|
273 |
# Instructions
|
274 |
st.markdown("""
|
275 |
**Comment jouer:**
|
276 |
+
- Glissez et déposez les pièces blanches
|
277 |
+
- Ou utilisez la notation algébrique :
|
278 |
+
- Pion: e4, d5
|
279 |
+
- Cavalier: Nf3, Nc6
|
280 |
+
- Fou: Bc4, Be7
|
281 |
+
- Tour: Ra1, Rd8
|
282 |
+
- Dame: Qd1, Qh4
|
283 |
+
- Roi: Ke2, Kg8
|
284 |
+
- Roque: O-O ou O-O-O
|
285 |
""")
|
286 |
|
287 |
# Nouvelle partie
|
|
|
290 |
st.session_state.moves = []
|
291 |
st.rerun()
|
292 |
|
293 |
+
# Gestion des coups (glisser-déposer et texte)
|
294 |
+
if st.session_state.get("chess_move"):
|
295 |
+
move_data = st.session_state.chess_move
|
296 |
+
st.session_state.chess_move = None
|
297 |
+
if handle_move(move_data["from"], move_data["to"]):
|
298 |
+
game_str = get_game_string()
|
299 |
+
# Tour de l'IA
|
300 |
+
with st.spinner("L'IA réfléchit..."):
|
301 |
+
ai_move = get_ai_move(game_str)
|
302 |
+
if ai_move and try_move(ai_move):
|
303 |
+
if st.session_state.board.is_checkmate():
|
304 |
+
st.success("Échec et mat!")
|
305 |
+
elif st.session_state.board.is_game_over():
|
306 |
+
st.info("Partie terminée!")
|
307 |
+
else:
|
308 |
+
st.error("Problème avec le coup de l'IA")
|
309 |
+
st.rerun()
|
310 |
+
else:
|
311 |
+
st.error("Coup invalide")
|
312 |
+
|
313 |
+
# Gestion des coups via input texte
|
314 |
if move:
|
315 |
if try_move(move):
|
316 |
game_str = get_game_string()
|