Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,119 +1,77 @@
|
|
1 |
import streamlit as st
|
2 |
import chess
|
|
|
3 |
import os
|
4 |
import git
|
5 |
import sys
|
6 |
import random
|
|
|
|
|
7 |
|
8 |
# Configuration de la page
|
9 |
st.set_page_config(page_title="Échecs contre IA", layout="wide")
|
10 |
|
11 |
def render_chessboard():
|
12 |
-
"""Render un échiquier interactif
|
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 |
-
crossorigin="anonymous">
|
40 |
-
</script>
|
41 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.3/chess.min.js"></script>
|
42 |
-
</head>
|
43 |
-
<body>
|
44 |
-
<div id="board" style="width: 400px; margin: auto;"></div>
|
45 |
-
<script>
|
46 |
-
var board = null;
|
47 |
-
var game = new Chess('{fen}');
|
48 |
-
|
49 |
-
function onDragStart (source, piece, position, orientation) {{
|
50 |
-
// Permettre seulement le déplacement des pièces blanches
|
51 |
-
if (game.turn() === 'b' ||
|
52 |
-
game.game_over() ||
|
53 |
-
(game.turn() === 'w' && piece.search(/^b/) !== -1)) {{
|
54 |
-
return false;
|
55 |
-
}}
|
56 |
-
}}
|
57 |
-
|
58 |
-
function onDrop (source, target) {{
|
59 |
-
var move = game.move({{
|
60 |
-
from: source,
|
61 |
-
to: target,
|
62 |
-
promotion: 'q'
|
63 |
-
}});
|
64 |
-
|
65 |
-
if (move === null) return 'snapback';
|
66 |
-
|
67 |
-
// Envoyer le coup au serveur via Streamlit
|
68 |
-
window.parent.postMessage({{
|
69 |
-
type: 'move',
|
70 |
-
move: move.san
|
71 |
-
}}, '*');
|
72 |
-
}}
|
73 |
|
74 |
-
|
75 |
-
|
76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
board = Chessboard('board', config);
|
88 |
-
|
89 |
-
// Marquer le dernier coup
|
90 |
-
if ('{last_move}') {{
|
91 |
-
var sourceSquare = '{last_move}'.slice(0, 2);
|
92 |
-
var targetSquare = '{last_move}'.slice(2, 4);
|
93 |
-
$("div[data-square='" + sourceSquare + "']").addClass('highlight-square');
|
94 |
-
$("div[data-square='" + targetSquare + "']").addClass('highlight-square');
|
95 |
-
}}
|
96 |
-
</script>
|
97 |
-
<style>
|
98 |
-
.highlight-square {{
|
99 |
-
box-shadow: inset 0 0 3px 3px yellow;
|
100 |
-
}}
|
101 |
-
</style>
|
102 |
-
</body>
|
103 |
-
</html>
|
104 |
-
"""
|
105 |
-
html(board_html, height=450)
|
106 |
|
107 |
def calculate_max_length(move_count):
|
108 |
-
"""
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
max_length = 100 # Plafond maximum
|
114 |
-
|
115 |
-
dynamic_length = base_length + (move_count * increment)
|
116 |
-
return min(dynamic_length, max_length)
|
117 |
|
118 |
# Setup du modèle
|
119 |
@st.cache_resource
|
@@ -131,30 +89,20 @@ def setup_inference():
|
|
131 |
)
|
132 |
return ChessGenerator(config)
|
133 |
|
134 |
-
# Initialisation
|
135 |
try:
|
136 |
generator = setup_inference()
|
137 |
except Exception as e:
|
138 |
st.error("Erreur lors de l'initialisation.")
|
139 |
st.stop()
|
140 |
|
141 |
-
# Initialisation de l'état
|
142 |
-
if 'board' not in st.session_state:
|
143 |
-
st.session_state.board = chess.Board()
|
144 |
-
st.session_state.moves = []
|
145 |
-
st.session_state.last_move = None
|
146 |
-
|
147 |
def get_ai_move(prompt):
|
148 |
-
"""Obtient le coup de l'IA
|
149 |
-
print(f"\n=== Tour de l'IA ===")
|
150 |
moves_count = len(st.session_state.moves)
|
151 |
-
|
152 |
dynamic_max_length = calculate_max_length(moves_count)
|
153 |
generator.config.max_length = dynamic_max_length
|
154 |
-
print(f"max_length actuel: {dynamic_max_length} pour {moves_count} coups joués")
|
155 |
|
156 |
try:
|
157 |
-
if not prompt:
|
158 |
response = generator.generate("1.")
|
159 |
else:
|
160 |
moves = prompt.split()
|
@@ -164,12 +112,12 @@ def get_ai_move(prompt):
|
|
164 |
response = generator.generate(f"{last_moves} {next_move_num}")
|
165 |
else:
|
166 |
response = generator.generate(f"{last_moves}")
|
167 |
-
|
168 |
moves = response[0].split() if isinstance(response, list) else response.split()
|
169 |
next_move = moves[-1]
|
170 |
if '.' in next_move and len(moves) > 1:
|
171 |
next_move = moves[-2]
|
172 |
-
|
173 |
try:
|
174 |
move = st.session_state.board.parse_san(next_move)
|
175 |
if move in st.session_state.board.legal_moves:
|
@@ -177,7 +125,6 @@ def get_ai_move(prompt):
|
|
177 |
except ValueError:
|
178 |
pass
|
179 |
|
180 |
-
# En cas d'échec, jouer un coup légal aléatoire
|
181 |
legal_moves = list(st.session_state.board.legal_moves)
|
182 |
if legal_moves:
|
183 |
random_move = random.choice(legal_moves)
|
@@ -196,9 +143,7 @@ def try_move(move_str):
|
|
196 |
if move in st.session_state.board.legal_moves:
|
197 |
st.session_state.board.push(move)
|
198 |
st.session_state.moves.append(clean_move)
|
199 |
-
st.session_state.last_move = clean_move
|
200 |
return True
|
201 |
-
|
202 |
return False
|
203 |
except ValueError:
|
204 |
return False
|
@@ -219,18 +164,21 @@ st.title("♟️ Échecs contre IA")
|
|
219 |
col1, col2 = st.columns([2, 1])
|
220 |
|
221 |
with col1:
|
222 |
-
#
|
223 |
render_chessboard()
|
224 |
|
225 |
-
# Input texte comme
|
226 |
move = st.text_input(
|
227 |
-
"
|
228 |
key=f"move_input_{len(st.session_state.moves)}",
|
229 |
placeholder="ex: e4, Nf3, O-O"
|
230 |
)
|
231 |
|
|
|
|
|
|
|
232 |
with col2:
|
233 |
-
#
|
234 |
current_max_length = calculate_max_length(len(st.session_state.moves))
|
235 |
st.info(f"Longueur de génération actuelle: {current_max_length} tokens")
|
236 |
|
@@ -243,7 +191,7 @@ with col2:
|
|
243 |
# Instructions
|
244 |
st.markdown("""
|
245 |
**Comment jouer:**
|
246 |
-
-
|
247 |
- Ou utilisez la notation algébrique :
|
248 |
- Pion: e4, d5
|
249 |
- Cavalier: Nf3, Nc6
|
@@ -258,25 +206,44 @@ with col2:
|
|
258 |
if st.button("Nouvelle partie"):
|
259 |
st.session_state.board = chess.Board()
|
260 |
st.session_state.moves = []
|
261 |
-
st.session_state.
|
262 |
st.rerun()
|
263 |
|
264 |
-
# Gestion des
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
if
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
|
281 |
# Gestion des coups via input texte
|
282 |
if move:
|
|
|
1 |
import streamlit as st
|
2 |
import chess
|
3 |
+
import chess.svg
|
4 |
import os
|
5 |
import git
|
6 |
import sys
|
7 |
import random
|
8 |
+
import base64
|
9 |
+
from streamlit.components.v1 import html
|
10 |
|
11 |
# Configuration de la page
|
12 |
st.set_page_config(page_title="Échecs contre IA", layout="wide")
|
13 |
|
14 |
def render_chessboard():
|
15 |
+
"""Render un échiquier interactif en SVG"""
|
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 |
+
squares=get_legal_moves_squares() if st.session_state.show_hints else None,
|
21 |
+
)
|
22 |
|
23 |
+
# Style pour centrer l'échiquier et ajouter des styles supplémentaires
|
24 |
+
html_content = f"""
|
25 |
+
<div style="display: flex; justify-content: center; margin-bottom: 20px;">
|
26 |
+
<style>
|
27 |
+
.chess-board {{
|
28 |
+
cursor: pointer;
|
29 |
+
}}
|
30 |
+
.square.legal-move {{
|
31 |
+
fill: #afa !important;
|
32 |
+
opacity: 0.5;
|
33 |
+
}}
|
34 |
+
.square.selected {{
|
35 |
+
fill: #0af !important;
|
36 |
+
opacity: 0.5;
|
37 |
+
}}
|
38 |
+
.highlight {{
|
39 |
+
fill: #ff0 !important;
|
40 |
+
opacity: 0.5;
|
41 |
+
}}
|
42 |
+
</style>
|
43 |
+
{board_svg}
|
44 |
+
</div>
|
45 |
+
"""
|
46 |
+
html(html_content, height=450)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
|
48 |
+
def get_legal_moves_squares():
|
49 |
+
"""Obtenir les cases des coups légaux pour la pièce sélectionnée"""
|
50 |
+
squares = []
|
51 |
+
if st.session_state.selected_square:
|
52 |
+
legal_moves = st.session_state.board.legal_moves
|
53 |
+
from_square = chess.parse_square(st.session_state.selected_square)
|
54 |
+
for move in legal_moves:
|
55 |
+
if move.from_square == from_square:
|
56 |
+
squares.append(chess.square_name(move.to_square))
|
57 |
+
return squares
|
58 |
|
59 |
+
# Initialisation des variables de session
|
60 |
+
if 'board' not in st.session_state:
|
61 |
+
st.session_state.board = chess.Board()
|
62 |
+
if 'moves' not in st.session_state:
|
63 |
+
st.session_state.moves = []
|
64 |
+
if 'selected_square' not in st.session_state:
|
65 |
+
st.session_state.selected_square = None
|
66 |
+
if 'show_hints' not in st.session_state:
|
67 |
+
st.session_state.show_hints = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
def calculate_max_length(move_count):
|
70 |
+
"""Calcule le max_length optimal"""
|
71 |
+
base_length = 10
|
72 |
+
increment = 1
|
73 |
+
max_length = 100
|
74 |
+
return min(base_length + (move_count * increment), max_length)
|
|
|
|
|
|
|
|
|
75 |
|
76 |
# Setup du modèle
|
77 |
@st.cache_resource
|
|
|
89 |
)
|
90 |
return ChessGenerator(config)
|
91 |
|
|
|
92 |
try:
|
93 |
generator = setup_inference()
|
94 |
except Exception as e:
|
95 |
st.error("Erreur lors de l'initialisation.")
|
96 |
st.stop()
|
97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
def get_ai_move(prompt):
|
99 |
+
"""Obtient le coup de l'IA"""
|
|
|
100 |
moves_count = len(st.session_state.moves)
|
|
|
101 |
dynamic_max_length = calculate_max_length(moves_count)
|
102 |
generator.config.max_length = dynamic_max_length
|
|
|
103 |
|
104 |
try:
|
105 |
+
if not prompt:
|
106 |
response = generator.generate("1.")
|
107 |
else:
|
108 |
moves = prompt.split()
|
|
|
112 |
response = generator.generate(f"{last_moves} {next_move_num}")
|
113 |
else:
|
114 |
response = generator.generate(f"{last_moves}")
|
115 |
+
|
116 |
moves = response[0].split() if isinstance(response, list) else response.split()
|
117 |
next_move = moves[-1]
|
118 |
if '.' in next_move and len(moves) > 1:
|
119 |
next_move = moves[-2]
|
120 |
+
|
121 |
try:
|
122 |
move = st.session_state.board.parse_san(next_move)
|
123 |
if move in st.session_state.board.legal_moves:
|
|
|
125 |
except ValueError:
|
126 |
pass
|
127 |
|
|
|
128 |
legal_moves = list(st.session_state.board.legal_moves)
|
129 |
if legal_moves:
|
130 |
random_move = random.choice(legal_moves)
|
|
|
143 |
if move in st.session_state.board.legal_moves:
|
144 |
st.session_state.board.push(move)
|
145 |
st.session_state.moves.append(clean_move)
|
|
|
146 |
return True
|
|
|
147 |
return False
|
148 |
except ValueError:
|
149 |
return False
|
|
|
164 |
col1, col2 = st.columns([2, 1])
|
165 |
|
166 |
with col1:
|
167 |
+
# Affichage de l'échiquier
|
168 |
render_chessboard()
|
169 |
|
170 |
+
# Input texte comme méthode alternative
|
171 |
move = st.text_input(
|
172 |
+
"Saisissez votre coup",
|
173 |
key=f"move_input_{len(st.session_state.moves)}",
|
174 |
placeholder="ex: e4, Nf3, O-O"
|
175 |
)
|
176 |
|
177 |
+
# Option pour afficher les coups légaux
|
178 |
+
st.checkbox("Afficher les coups possibles", value=True, key="show_hints")
|
179 |
+
|
180 |
with col2:
|
181 |
+
# Informations sur la partie
|
182 |
current_max_length = calculate_max_length(len(st.session_state.moves))
|
183 |
st.info(f"Longueur de génération actuelle: {current_max_length} tokens")
|
184 |
|
|
|
191 |
# Instructions
|
192 |
st.markdown("""
|
193 |
**Comment jouer:**
|
194 |
+
- Cliquez sur une pièce puis sur la case de destination
|
195 |
- Ou utilisez la notation algébrique :
|
196 |
- Pion: e4, d5
|
197 |
- Cavalier: Nf3, Nc6
|
|
|
206 |
if st.button("Nouvelle partie"):
|
207 |
st.session_state.board = chess.Board()
|
208 |
st.session_state.moves = []
|
209 |
+
st.session_state.selected_square = None
|
210 |
st.rerun()
|
211 |
|
212 |
+
# Gestion des clics sur l'échiquier (à implémenter avec st.experimental_get_query_params)
|
213 |
+
params = st.experimental_get_query_params()
|
214 |
+
if 'square' in params:
|
215 |
+
clicked_square = params['square'][0]
|
216 |
+
if st.session_state.selected_square is None:
|
217 |
+
# Premier clic - sélection de la pièce
|
218 |
+
piece = st.session_state.board.piece_at(chess.parse_square(clicked_square))
|
219 |
+
if piece and piece.color == st.session_state.board.turn:
|
220 |
+
st.session_state.selected_square = clicked_square
|
221 |
+
else:
|
222 |
+
# Deuxième clic - déplacement
|
223 |
+
from_square = chess.parse_square(st.session_state.selected_square)
|
224 |
+
to_square = chess.parse_square(clicked_square)
|
225 |
+
move = chess.Move(from_square, to_square)
|
226 |
+
|
227 |
+
if move in st.session_state.board.legal_moves:
|
228 |
+
# Appliquer le coup
|
229 |
+
st.session_state.board.push(move)
|
230 |
+
st.session_state.moves.append(st.session_state.board.san(move))
|
231 |
+
st.session_state.selected_square = None
|
232 |
+
|
233 |
+
# Tour de l'IA
|
234 |
+
with st.spinner("L'IA réfléchit..."):
|
235 |
+
ai_move = get_ai_move(get_game_string())
|
236 |
+
if ai_move and try_move(ai_move):
|
237 |
+
if st.session_state.board.is_checkmate():
|
238 |
+
st.success("Échec et mat!")
|
239 |
+
elif st.session_state.board.is_game_over():
|
240 |
+
st.info("Partie terminée!")
|
241 |
+
else:
|
242 |
+
st.error("Problème avec le coup de l'IA")
|
243 |
+
st.rerun()
|
244 |
+
else:
|
245 |
+
st.session_state.selected_square = None
|
246 |
+
st.rerun()
|
247 |
|
248 |
# Gestion des coups via input texte
|
249 |
if move:
|