Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,122 +1,33 @@
|
|
1 |
import streamlit as st
|
2 |
import chess
|
|
|
3 |
import os
|
4 |
import git
|
5 |
import sys
|
6 |
import random
|
7 |
-
from streamlit.components.v1 import html
|
8 |
|
9 |
# Configuration de la page
|
10 |
st.set_page_config(page_title="Échecs contre IA", layout="wide")
|
11 |
|
12 |
-
def
|
13 |
-
"""
|
14 |
-
#
|
15 |
-
|
|
|
16 |
|
17 |
-
|
18 |
-
|
19 |
-
<!DOCTYPE html>
|
20 |
-
<html>
|
21 |
-
<head>
|
22 |
-
<meta charset="UTF-8">
|
23 |
-
<link
|
24 |
-
rel="stylesheet"
|
25 |
-
href="https://cdnjs.cloudflare.com/ajax/libs/chessboard-js/1.0.0/chessboard-1.0.0.min.css"
|
26 |
-
integrity="sha512-TU/clvRaSqKB43MX6dvJPgxm0ytgRrg/6tkbTPCFwOXG6Ej70nGCkcFkatrbcPHqR0mlPCy3WuYNg936BGvNOg=="
|
27 |
-
crossorigin="anonymous"
|
28 |
-
/>
|
29 |
-
<script
|
30 |
-
src="https://code.jquery.com/jquery-3.6.0.min.js"
|
31 |
-
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
|
32 |
-
crossorigin="anonymous">
|
33 |
-
</script>
|
34 |
-
<script
|
35 |
-
src="https://cdnjs.cloudflare.com/ajax/libs/chessboard-js/1.0.0/chessboard-1.0.0.min.js"
|
36 |
-
integrity="sha512-WfASs5HtTgTL/eZs0Td0JVJZ+8tNxF/MDNU99TzJI8D4jXrZdQZRO9G7EPIVQhvmb7yUEEH+AKY9PfGi4pqrw=="
|
37 |
-
crossorigin="anonymous">
|
38 |
-
</script>
|
39 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.3/chess.min.js"></script>
|
40 |
-
</head>
|
41 |
-
<body>
|
42 |
-
<div id="board" style="width: 400px; margin: auto;"></div>
|
43 |
-
<script>
|
44 |
-
// Initialisation du jeu
|
45 |
-
var board = null;
|
46 |
-
var game = new Chess('{fen}');
|
47 |
-
|
48 |
-
function onDragStart (source, piece, position, orientation) {{
|
49 |
-
// Autoriser uniquement les pièces blanches à être déplacées
|
50 |
-
if (game.turn() === 'b' ||
|
51 |
-
game.game_over() ||
|
52 |
-
(game.turn() === 'w' && piece.search(/^b/) !== -1)) {{
|
53 |
-
return false;
|
54 |
-
}}
|
55 |
-
}}
|
56 |
-
|
57 |
-
function onDrop (source, target) {{
|
58 |
-
// Vérifier si le coup est légal
|
59 |
-
var move = game.move({{
|
60 |
-
from: source,
|
61 |
-
to: target,
|
62 |
-
promotion: 'q'
|
63 |
-
}});
|
64 |
-
|
65 |
-
// Coup illégal
|
66 |
-
if (move === null) return 'snapback';
|
67 |
-
|
68 |
-
// Envoyer le coup à Streamlit
|
69 |
-
window.parent.postMessage({{
|
70 |
-
type: 'move',
|
71 |
-
move: move.san
|
72 |
-
}}, '*');
|
73 |
-
}}
|
74 |
-
|
75 |
-
function onSnapEnd () {{
|
76 |
-
board.position(game.fen());
|
77 |
-
}}
|
78 |
-
|
79 |
-
// Configuration de l'échiquier
|
80 |
-
var config = {{
|
81 |
-
position: '{fen}',
|
82 |
-
draggable: true,
|
83 |
-
onDragStart: onDragStart,
|
84 |
-
onDrop: onDrop,
|
85 |
-
onSnapEnd: onSnapEnd,
|
86 |
-
pieceTheme: 'https://cdnjs.cloudflare.com/ajax/libs/chessboard-js/1.0.0/img/chesspieces/wikipedia/{{piece}}.png'
|
87 |
-
}};
|
88 |
-
|
89 |
-
// Initialisation de l'échiquier
|
90 |
-
board = Chessboard('board', config);
|
91 |
-
|
92 |
-
// S'assurer que l'échiquier est responsive
|
93 |
-
$(window).resize(board.resize);
|
94 |
-
|
95 |
-
// Gestion des messages de Streamlit
|
96 |
-
window.addEventListener('message', function(e) {{
|
97 |
-
if (e.data.type === 'update') {{
|
98 |
-
board.position(e.data.fen);
|
99 |
-
game.load(e.data.fen);
|
100 |
-
}}
|
101 |
-
}});
|
102 |
-
</script>
|
103 |
-
<style>
|
104 |
-
.highlight-square {{
|
105 |
-
box-shadow: inset 0 0 3px 3px yellow;
|
106 |
-
}}
|
107 |
-
</style>
|
108 |
-
</body>
|
109 |
-
</html>
|
110 |
-
"""
|
111 |
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
|
|
|
|
120 |
|
121 |
# Setup du modèle
|
122 |
@st.cache_resource
|
@@ -134,11 +45,11 @@ def setup_inference():
|
|
134 |
)
|
135 |
return ChessGenerator(config)
|
136 |
|
137 |
-
# Initialisation
|
138 |
try:
|
139 |
generator = setup_inference()
|
140 |
except Exception as e:
|
141 |
-
st.error("Erreur lors de l'initialisation
|
142 |
st.stop()
|
143 |
|
144 |
# Initialisation de l'état
|
@@ -147,6 +58,13 @@ if 'board' not in st.session_state:
|
|
147 |
if 'moves' not in st.session_state:
|
148 |
st.session_state.moves = []
|
149 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
def get_ai_move(prompt):
|
151 |
"""Obtient le coup de l'IA"""
|
152 |
moves_count = len(st.session_state.moves)
|
@@ -159,10 +77,10 @@ def get_ai_move(prompt):
|
|
159 |
else:
|
160 |
moves = prompt.split()
|
161 |
last_moves = " ".join(moves[-4:])
|
162 |
-
if len(moves) % 2 == 0:
|
163 |
next_move_num = f"{(len(moves)//2 + 1)}."
|
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()
|
@@ -182,9 +100,8 @@ def get_ai_move(prompt):
|
|
182 |
if legal_moves:
|
183 |
random_move = random.choice(legal_moves)
|
184 |
return st.session_state.board.san(random_move)
|
185 |
-
|
186 |
except Exception as e:
|
187 |
-
|
188 |
return None
|
189 |
|
190 |
def try_move(move_str):
|
@@ -211,24 +128,34 @@ def get_game_string():
|
|
211 |
result.append(st.session_state.moves[i+1])
|
212 |
return " ".join(result)
|
213 |
|
|
|
|
|
|
|
|
|
|
|
|
|
214 |
# Interface utilisateur
|
215 |
st.title("♟️ Échecs contre IA")
|
216 |
|
217 |
col1, col2 = st.columns([2, 1])
|
218 |
|
219 |
with col1:
|
220 |
-
# Échiquier
|
221 |
-
|
222 |
|
223 |
-
# Input
|
224 |
move = st.text_input(
|
225 |
-
"
|
226 |
key=f"move_input_{len(st.session_state.moves)}",
|
227 |
placeholder="ex: e4, Nf3, O-O"
|
228 |
)
|
|
|
|
|
|
|
|
|
229 |
|
230 |
with col2:
|
231 |
-
#
|
232 |
st.subheader("Partie en cours")
|
233 |
game_str = get_game_string()
|
234 |
if game_str:
|
@@ -237,15 +164,13 @@ with col2:
|
|
237 |
# Instructions
|
238 |
st.markdown("""
|
239 |
**Comment jouer:**
|
240 |
-
-
|
241 |
-
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
- Roi: Ke2, Kg8
|
248 |
-
- Roque: O-O ou O-O-O
|
249 |
""")
|
250 |
|
251 |
# Nouvelle partie
|
@@ -254,24 +179,7 @@ with col2:
|
|
254 |
st.session_state.moves = []
|
255 |
st.rerun()
|
256 |
|
257 |
-
#
|
258 |
-
if 'js_move' in st.session_state:
|
259 |
-
move = st.session_state.js_move
|
260 |
-
st.session_state.js_move = None
|
261 |
-
if try_move(move):
|
262 |
-
game_str = get_game_string()
|
263 |
-
with st.spinner("L'IA réfléchit..."):
|
264 |
-
ai_move = get_ai_move(game_str)
|
265 |
-
if ai_move and try_move(ai_move):
|
266 |
-
if st.session_state.board.is_checkmate():
|
267 |
-
st.success("Échec et mat!")
|
268 |
-
elif st.session_state.board.is_game_over():
|
269 |
-
st.info("Partie terminée!")
|
270 |
-
else:
|
271 |
-
st.error("Problème avec le coup de l'IA")
|
272 |
-
st.rerun()
|
273 |
-
|
274 |
-
# Gestion des coups via input texte
|
275 |
if move:
|
276 |
if try_move(move):
|
277 |
game_str = get_game_string()
|
|
|
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 |
|
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 en SVG avec mise en évidence des coups légaux"""
|
14 |
+
# Déterminer les cases à mettre en évidence
|
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=last_move,
|
26 |
+
check=st.session_state.board.king(st.session_state.board.turn) if st.session_state.board.is_check() else None
|
27 |
+
)
|
28 |
+
|
29 |
+
# On utilise unsafe_allow_html=True car on sait que le SVG est sûr (généré par python-chess)
|
30 |
+
st.write(board_svg, unsafe_allow_html=True)
|
31 |
|
32 |
# Setup du modèle
|
33 |
@st.cache_resource
|
|
|
45 |
)
|
46 |
return ChessGenerator(config)
|
47 |
|
48 |
+
# Initialisation
|
49 |
try:
|
50 |
generator = setup_inference()
|
51 |
except Exception as e:
|
52 |
+
st.error(f"Erreur lors de l'initialisation: {str(e)}")
|
53 |
st.stop()
|
54 |
|
55 |
# Initialisation de l'état
|
|
|
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"""
|
70 |
moves_count = len(st.session_state.moves)
|
|
|
77 |
else:
|
78 |
moves = prompt.split()
|
79 |
last_moves = " ".join(moves[-4:])
|
80 |
+
if len(moves) % 2 == 0:
|
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()
|
|
|
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 de l'IA: {str(e)}")
|
105 |
return None
|
106 |
|
107 |
def try_move(move_str):
|
|
|
128 |
result.append(st.session_state.moves[i+1])
|
129 |
return " ".join(result)
|
130 |
|
131 |
+
def display_legal_moves():
|
132 |
+
"""Affiche les coups légaux possibles"""
|
133 |
+
legal_moves = [st.session_state.board.san(move) for move in st.session_state.board.legal_moves]
|
134 |
+
st.text("Coups légaux possibles:")
|
135 |
+
st.code(", ".join(legal_moves))
|
136 |
+
|
137 |
# Interface utilisateur
|
138 |
st.title("♟️ Échecs contre IA")
|
139 |
|
140 |
col1, col2 = st.columns([2, 1])
|
141 |
|
142 |
with col1:
|
143 |
+
# Échiquier
|
144 |
+
render_board()
|
145 |
|
146 |
+
# Input du coup
|
147 |
move = st.text_input(
|
148 |
+
"Votre coup",
|
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 |
+
# Historique et état de la partie
|
159 |
st.subheader("Partie en cours")
|
160 |
game_str = get_game_string()
|
161 |
if game_str:
|
|
|
164 |
# Instructions
|
165 |
st.markdown("""
|
166 |
**Comment jouer:**
|
167 |
+
- Pion: e4, d5
|
168 |
+
- Cavalier: Nf3, Nc6
|
169 |
+
- Fou: Bc4, Be7
|
170 |
+
- Tour: Ra1, Rd8
|
171 |
+
- Dame: Qd1, Qh4
|
172 |
+
- Roi: Ke2, Kg8
|
173 |
+
- Roque: O-O ou O-O-O
|
|
|
|
|
174 |
""")
|
175 |
|
176 |
# Nouvelle partie
|
|
|
179 |
st.session_state.moves = []
|
180 |
st.rerun()
|
181 |
|
182 |
+
# Logique du jeu
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
if move:
|
184 |
if try_move(move):
|
185 |
game_str = get_game_string()
|