Madras1 commited on
Commit
57c2c0e
·
verified ·
1 Parent(s): 0fb7cb2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +50 -33
app.py CHANGED
@@ -13,13 +13,13 @@ MODEL_PATH = "checkers_master_final.pth"
13
 
14
  st.set_page_config(page_title="AlphaCheckerZero", page_icon="♟️", layout="wide")
15
 
16
- # --- ESTILOS CSS PERSONALIZADOS (A MÁGICA VISUAL) ---
17
  st.markdown("""
18
  <style>
19
  .board-container {
20
  display: grid;
21
- grid-template-columns: 30px repeat(8, 60px); /* Coluna guia + 8 colunas do jogo */
22
- grid-template-rows: 30px repeat(8, 60px); /* Linha guia + 8 linhas do jogo */
23
  gap: 2px;
24
  background-color: #444;
25
  padding: 10px;
@@ -44,8 +44,8 @@ st.markdown("""
44
  font-size: 40px;
45
  cursor: default;
46
  }
47
- .white-square { background-color: #f0d9b5; } /* Cor clara (madeira) */
48
- .black-square { background-color: #b58863; } /* Cor escura (madeira) */
49
 
50
  .piece-white {
51
  color: #fff;
@@ -59,12 +59,11 @@ st.markdown("""
59
  }
60
  .king { border: 2px solid gold; border-radius: 50%; padding: 2px; box-shadow: 0 0 10px gold;}
61
 
62
- /* Ajuste para deixar o selectbox mais bonito */
63
  .stSelectbox label { font-size: 1.2rem; font-weight: bold; }
64
  </style>
65
  """, unsafe_allow_html=True)
66
 
67
- # --- LÓGICA DO JOGO (Inalterada) ---
68
  class Checkers:
69
  def get_initial_board(self):
70
  board = np.zeros((BOARD_SIZE, BOARD_SIZE), dtype=np.int8)
@@ -124,7 +123,13 @@ class Checkers:
124
 
125
  def apply_move(self, board, move):
126
  b_ = np.copy(board)
127
- is_jump_chain = isinstance(move, list) or (isinstance(move, tuple) and isinstance(move[0], tuple) and isinstance(move[0][0], tuple))
 
 
 
 
 
 
128
  sub_moves = move if is_jump_chain else [move]
129
  for (r1, c1), (r2, c2) in sub_moves:
130
  piece = b_[r1, c1]
@@ -206,7 +211,10 @@ class MCTS:
206
  else: start_pos_tuple = move[0]
207
  start_pos_idx = start_pos_tuple[0] * BOARD_SIZE + start_pos_tuple[1]
208
  prior = policy_probs[start_pos_idx]
 
 
209
  key = tuple(move) if isinstance(move, list) else move
 
210
  move_priors[key] = prior; total_prior += prior
211
  if total_prior > 0:
212
  for move_key, prior in move_priors.items(): node.children[move_key] = MCTSNode(parent=node, prior=prior / total_prior)
@@ -216,7 +224,7 @@ class MCTS:
216
  node.children[key] = MCTSNode(parent=node, prior=1.0 / len(valid_moves))
217
  return value
218
 
219
- # --- INTERFACE GRÁFICA MELHORADA ---
220
 
221
  @st.cache_resource
222
  def load_model():
@@ -244,38 +252,48 @@ game = Checkers()
244
  mcts = MCTS(game, model, sims=150)
245
 
246
  def format_move_for_human(move):
247
- """Transforma ((2,1), (3,2)) em algo mais legível"""
248
- if isinstance(move, list): # Pulo múltiplo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  path = " -> ".join([f"({r},{c})" for (r,c), _ in move] + [str(move[-1][1])])
250
- return f"Salto Múltiplo: {path}"
251
  else:
 
252
  (r1, c1), (r2, c2) = move
253
  return f"Mover de ({r1}, {c1}) para ({r2}, {c2})"
254
 
255
  def render_board_html(board):
256
  html = '<div class="board-container">'
257
-
258
- # Cabeçalho das colunas (0-7)
259
- html += '<div class="header-cell"></div>' # Canto vazio
260
- for c in range(8):
261
- html += f'<div class="header-cell">{c}</div>'
262
 
263
  for r in range(8):
264
- # Cabeçalho da linha (0-7)
265
  html += f'<div class="header-cell">{r}</div>'
266
-
267
  for c in range(8):
268
  color_class = "black-square" if (r + c) % 2 == 1 else "white-square"
269
  piece = board[r, c]
270
-
271
  content = ""
272
  if piece == 1: content = '<span class="piece-white">⚪</span>'
273
- elif piece == 2: content = '<span class="piece-white king">👑</span>' # Dama branca
274
  elif piece == -1: content = '<span class="piece-black">⚫</span>'
275
- elif piece == -2: content = '<span class="piece-black king">👑</span>' # Dama preta (coroa dourada no CSS)
276
-
277
  html += f'<div class="square {color_class}">{content}</div>'
278
-
279
  html += '</div>'
280
  return html
281
 
@@ -300,16 +318,13 @@ with col2:
300
  else:
301
  st.info(st.session_state.message)
302
 
303
- # Lógica de Turno
304
  if st.session_state.player == 1:
305
  valid_moves = game.get_valid_moves(st.session_state.board, 1)
306
-
307
  if not valid_moves:
308
  st.session_state.game_over = True
309
  st.session_state.message = "Sem movimentos válidos. Você perdeu. 😔"
310
  st.rerun()
311
 
312
- # Dicionário para mapear a descrição bonita para o objeto de movimento real
313
  move_map = {format_move_for_human(m): m for m in valid_moves}
314
  selected_desc = st.selectbox("Sua vez! Escolha o movimento:", list(move_map.keys()))
315
 
@@ -321,24 +336,26 @@ with col2:
321
  st.rerun()
322
 
323
  else:
324
- # Vez da IA
325
  with st.spinner("A AlphaCheckerZero está pensando..."):
326
- time.sleep(0.2) # UX Feel
327
-
328
  valid_moves, policy = mcts.run(np.copy(st.session_state.board), -1)
329
-
330
  if not valid_moves:
331
  st.session_state.game_over = True
332
  st.session_state.message = "A IA travou! VOCÊ VENCEU! 🎉"
333
  st.rerun()
334
 
335
  move = valid_moves[np.argmax(policy)]
 
 
 
 
 
 
336
  st.session_state.board = game.apply_move(st.session_state.board, move)
337
  st.session_state.player = 1
338
- st.session_state.message = f"IA moveu: {format_move_for_human(move)}. Sua vez!"
339
  st.rerun()
340
 
341
- # Rodapé explicativo
342
  st.markdown("---")
343
  st.caption("**Legenda:** ⚪ Suas Peças | ⚫ Peças da IA | 👑 Dama")
344
  st.caption("Desenvolvido por Gabriel Yogi com auxílio de Berta.")
 
13
 
14
  st.set_page_config(page_title="AlphaCheckerZero", page_icon="♟️", layout="wide")
15
 
16
+ # --- ESTILOS CSS PERSONALIZADOS ---
17
  st.markdown("""
18
  <style>
19
  .board-container {
20
  display: grid;
21
+ grid-template-columns: 30px repeat(8, 60px);
22
+ grid-template-rows: 30px repeat(8, 60px);
23
  gap: 2px;
24
  background-color: #444;
25
  padding: 10px;
 
44
  font-size: 40px;
45
  cursor: default;
46
  }
47
+ .white-square { background-color: #f0d9b5; }
48
+ .black-square { background-color: #b58863; }
49
 
50
  .piece-white {
51
  color: #fff;
 
59
  }
60
  .king { border: 2px solid gold; border-radius: 50%; padding: 2px; box-shadow: 0 0 10px gold;}
61
 
 
62
  .stSelectbox label { font-size: 1.2rem; font-weight: bold; }
63
  </style>
64
  """, unsafe_allow_html=True)
65
 
66
+ # --- LÓGICA DO JOGO ---
67
  class Checkers:
68
  def get_initial_board(self):
69
  board = np.zeros((BOARD_SIZE, BOARD_SIZE), dtype=np.int8)
 
123
 
124
  def apply_move(self, board, move):
125
  b_ = np.copy(board)
126
+ # AQUI TAMBÉM PODERIA DAR ERRO, ENTÃO GARANTIMOS O FORMATO
127
+ is_jump_chain = False
128
+ if isinstance(move, list):
129
+ is_jump_chain = True
130
+ elif isinstance(move, tuple) and len(move) > 0 and isinstance(move[0], tuple) and len(move[0]) > 0 and isinstance(move[0][0], tuple):
131
+ is_jump_chain = True
132
+
133
  sub_moves = move if is_jump_chain else [move]
134
  for (r1, c1), (r2, c2) in sub_moves:
135
  piece = b_[r1, c1]
 
211
  else: start_pos_tuple = move[0]
212
  start_pos_idx = start_pos_tuple[0] * BOARD_SIZE + start_pos_tuple[1]
213
  prior = policy_probs[start_pos_idx]
214
+
215
+ # IMPORTANTE: MCTS converte lista para tupla aqui para usar como chave de dicionário
216
  key = tuple(move) if isinstance(move, list) else move
217
+
218
  move_priors[key] = prior; total_prior += prior
219
  if total_prior > 0:
220
  for move_key, prior in move_priors.items(): node.children[move_key] = MCTSNode(parent=node, prior=prior / total_prior)
 
224
  node.children[key] = MCTSNode(parent=node, prior=1.0 / len(valid_moves))
225
  return value
226
 
227
+ # --- INTERFACE GRÁFICA ---
228
 
229
  @st.cache_resource
230
  def load_model():
 
252
  mcts = MCTS(game, model, sims=150)
253
 
254
  def format_move_for_human(move):
255
+ """
256
+ Formata o movimento para texto legível.
257
+ Lida tanto com listas (pulos originais) quanto tuplas aninhadas (pulos convertidos pelo MCTS).
258
+ """
259
+ is_jump = False
260
+
261
+ # 1. Se for lista, é um pulo
262
+ if isinstance(move, list):
263
+ is_jump = True
264
+
265
+ # 2. Se for tupla, precisamos ver o conteúdo para saber se é pulo ou movimento simples
266
+ elif isinstance(move, tuple):
267
+ # Se o primeiro item da tupla for OUTRA tupla (ex: ((r,c), (r,c))), então é um pulo que foi convertido
268
+ # Um movimento simples teria um int como primeiro sub-item: ((2,3), (3,4)) -> move[0] é (2,3), move[0][0] é 2 (int).
269
+ if len(move) > 0 and isinstance(move[0], tuple) and len(move[0]) > 0 and isinstance(move[0][0], tuple):
270
+ is_jump = True
271
+
272
+ if is_jump:
273
+ # Pulo múltiplo ou captura simples
274
  path = " -> ".join([f"({r},{c})" for (r,c), _ in move] + [str(move[-1][1])])
275
+ return f"Salto/Captura: {path}"
276
  else:
277
+ # Movimento simples
278
  (r1, c1), (r2, c2) = move
279
  return f"Mover de ({r1}, {c1}) para ({r2}, {c2})"
280
 
281
  def render_board_html(board):
282
  html = '<div class="board-container">'
283
+ html += '<div class="header-cell"></div>'
284
+ for c in range(8): html += f'<div class="header-cell">{c}</div>'
 
 
 
285
 
286
  for r in range(8):
 
287
  html += f'<div class="header-cell">{r}</div>'
 
288
  for c in range(8):
289
  color_class = "black-square" if (r + c) % 2 == 1 else "white-square"
290
  piece = board[r, c]
 
291
  content = ""
292
  if piece == 1: content = '<span class="piece-white">⚪</span>'
293
+ elif piece == 2: content = '<span class="piece-white king">👑</span>'
294
  elif piece == -1: content = '<span class="piece-black">⚫</span>'
295
+ elif piece == -2: content = '<span class="piece-black king">👑</span>'
 
296
  html += f'<div class="square {color_class}">{content}</div>'
 
297
  html += '</div>'
298
  return html
299
 
 
318
  else:
319
  st.info(st.session_state.message)
320
 
 
321
  if st.session_state.player == 1:
322
  valid_moves = game.get_valid_moves(st.session_state.board, 1)
 
323
  if not valid_moves:
324
  st.session_state.game_over = True
325
  st.session_state.message = "Sem movimentos válidos. Você perdeu. 😔"
326
  st.rerun()
327
 
 
328
  move_map = {format_move_for_human(m): m for m in valid_moves}
329
  selected_desc = st.selectbox("Sua vez! Escolha o movimento:", list(move_map.keys()))
330
 
 
336
  st.rerun()
337
 
338
  else:
 
339
  with st.spinner("A AlphaCheckerZero está pensando..."):
340
+ time.sleep(0.2)
 
341
  valid_moves, policy = mcts.run(np.copy(st.session_state.board), -1)
 
342
  if not valid_moves:
343
  st.session_state.game_over = True
344
  st.session_state.message = "A IA travou! VOCÊ VENCEU! 🎉"
345
  st.rerun()
346
 
347
  move = valid_moves[np.argmax(policy)]
348
+
349
+ # AQUI É ONDE OCORRIA O ERRO ANTES:
350
+ # A IA retorna o movimento (que pode ser uma tupla de pulo)
351
+ # E agora a função 'format_move_for_human' sabe lidar com isso.
352
+ move_text = format_move_for_human(move)
353
+
354
  st.session_state.board = game.apply_move(st.session_state.board, move)
355
  st.session_state.player = 1
356
+ st.session_state.message = f"IA moveu: {move_text}. Sua vez!"
357
  st.rerun()
358
 
 
359
  st.markdown("---")
360
  st.caption("**Legenda:** ⚪ Suas Peças | ⚫ Peças da IA | 👑 Dama")
361
  st.caption("Desenvolvido por Gabriel Yogi com auxílio de Berta.")