212 lines
8.0 KiB
Python
212 lines
8.0 KiB
Python
# Módulo: vista/ventana_buscaMinas.py
|
|
|
|
import tkinter as tk
|
|
from tkinter import ttk, messagebox
|
|
# 🔑 Importamos la lógica del minijuego
|
|
from logica.T2.buscaMinas import BuscaMinas
|
|
# 🔑 Importamos la configuración de estilos
|
|
from vista.config import *
|
|
|
|
|
|
class VentanaBuscaMinas(tk.Toplevel):
|
|
"""
|
|
Ventana secundaria (tk.Toplevel) para el Buscaminas.
|
|
Es no-modal y permite interactuar con la VentanaPrincipal.
|
|
"""
|
|
|
|
def __init__(self, parent):
|
|
# Inicializar como ventana secundaria
|
|
super().__init__(parent)
|
|
self.title("💣 Buscaminas - App3")
|
|
self.geometry("500x600")
|
|
self.resizable(False, False)
|
|
|
|
# 1. Inicializar la lógica del juego (16x16, 40 minas)
|
|
self.game = BuscaMinas(rows=16, cols=16, mines=40)
|
|
self.buttons = {} # Almacena las referencias a los botones (celdas)
|
|
|
|
# Variables de control de UI
|
|
self.feedback_var = tk.StringVar(value="Haz clic para empezar...")
|
|
|
|
# 2. Configurar el layout principal
|
|
self.grid_columnconfigure(0, weight=1)
|
|
self.grid_rowconfigure(1, weight=1)
|
|
|
|
self.create_widgets()
|
|
self.protocol("WM_DELETE_WINDOW", self.on_close) # Manejar el cierre
|
|
|
|
def create_widgets(self):
|
|
"""Crea el marco superior de control y el marco del tablero."""
|
|
|
|
# --- Marco Superior de Control ---
|
|
control_frame = ttk.Frame(self, padding=10)
|
|
control_frame.grid(row=0, column=0, sticky="ew")
|
|
control_frame.grid_columnconfigure(0, weight=1)
|
|
control_frame.grid_columnconfigure(1, weight=1)
|
|
|
|
# Botón Nuevo Juego (Usamos ttk para botones fuera del tablero)
|
|
ttk.Button(control_frame, text="🔄 Nuevo Juego", command=self.reset_game, style='Action.TButton').grid(
|
|
row=0, column=0, padx=(0, 5), sticky="w")
|
|
|
|
# Feedback (Mensajes de éxito/derrota)
|
|
ttk.Label(control_frame, textvariable=self.feedback_var, font=FUENTE_NEGOCIOS).grid(
|
|
row=0, column=1, padx=(5, 0), sticky="e")
|
|
|
|
# --- Marco del Tablero ---
|
|
self.board_frame = ttk.Frame(self, padding=5, style='TFrame')
|
|
self.board_frame.grid(row=1, column=0, sticky="nsew")
|
|
|
|
self.draw_board()
|
|
|
|
def draw_board(self):
|
|
"""Crea la matriz de botones que representan las celdas del buscaminas."""
|
|
|
|
# Limpiar y reconfigurar el frame
|
|
for widget in self.board_frame.winfo_children():
|
|
widget.destroy()
|
|
self.buttons = {}
|
|
|
|
# Asegurar que los botones se expandan uniformemente
|
|
for i in range(self.game.rows):
|
|
self.board_frame.grid_rowconfigure(i, weight=1)
|
|
for j in range(self.game.cols):
|
|
self.board_frame.grid_columnconfigure(j, weight=1)
|
|
|
|
for r in range(self.game.rows):
|
|
for c in range(self.game.cols):
|
|
# USANDO tk.Button ESTÁNDAR para la configuración dinámica de colores
|
|
btn = tk.Button(
|
|
self.board_frame,
|
|
text="",
|
|
width=2,
|
|
height=1,
|
|
relief=tk.RAISED,
|
|
background='gray70',
|
|
activebackground='gray50',
|
|
# Clic Izquierdo (Button-1): revelar (usando 'command')
|
|
command=lambda row=r, col=c: self.handle_click(row, col)
|
|
)
|
|
btn.grid(row=r, column=c, sticky="nsew", padx=1, pady=1)
|
|
self.buttons[(r, c)] = btn
|
|
|
|
# VINCULAR CLIC DERECHO (Button-3) a handle_flag
|
|
btn.bind("<Button-3>", lambda event, row=r, col=c: self.handle_flag(row, col))
|
|
|
|
def handle_click(self, r, c):
|
|
"""Maneja el clic Izquierdo para revelar."""
|
|
|
|
if self.game.game_over:
|
|
return
|
|
|
|
message, game_over = self.game.reveal_cell(r, c)
|
|
|
|
# 1. Actualizar el tablero con el nuevo estado
|
|
self.update_ui_board()
|
|
|
|
# 2. Manejar el estado final del juego
|
|
if game_over:
|
|
self.feedback_var.set(message)
|
|
self.show_end_game()
|
|
|
|
else:
|
|
self.feedback_var.set("¡Cuidado! Minando...")
|
|
# Mostrar mensaje si la lógica impidió la revelación (ej: por bandera)
|
|
if message:
|
|
self.feedback_var.set(message)
|
|
|
|
def handle_flag(self, r, c):
|
|
"""
|
|
Maneja el clic Derecho para colocar/quitar la bandera.
|
|
"""
|
|
if self.game.game_over:
|
|
return
|
|
|
|
# Llama a la lógica para alternar la bandera
|
|
if self.game.toggle_flag(r, c):
|
|
self.update_ui_board() # Solo actualizamos si el estado de la bandera cambió
|
|
|
|
def update_ui_board(self):
|
|
"""Recorre el estado lógico y actualiza el texto, color y estado de los botones."""
|
|
state = self.game.get_board_state()
|
|
revealed_bg = 'lightgray'
|
|
|
|
for r in range(self.game.rows):
|
|
for c in range(self.game.cols):
|
|
text = state[r][c]
|
|
btn = self.buttons[(r, c)]
|
|
|
|
# Estado por defecto (oculto, levantado)
|
|
btn.config(text="", background='gray70', foreground='black', relief=tk.RAISED, state=tk.NORMAL)
|
|
|
|
if text == 'F':
|
|
# Bandera (juego en curso)
|
|
btn.config(text="🚩", foreground='blue', relief=tk.RAISED, state=tk.NORMAL)
|
|
|
|
# 🔑 NUEVO: Manejo de estado final de banderas
|
|
elif self.game.game_over:
|
|
btn.config(state=tk.DISABLED) # Deshabilitar todos los botones
|
|
|
|
if text == 'F+':
|
|
# Bandera Correcta (Mina marcada)
|
|
btn.config(text="✅", background='green', foreground='white', relief=tk.RAISED)
|
|
elif text == 'F-':
|
|
# Bandera Incorrecta (Celda segura marcada)
|
|
btn.config(text="❌", background='orange', foreground='white', relief=tk.RAISED)
|
|
elif text == 'M':
|
|
# Mina sin marcar (se revela al final)
|
|
btn.config(text="💣", background='black', foreground='white', relief=tk.SUNKEN)
|
|
# El resto de celdas se manejan a continuación si están reveladas.
|
|
|
|
if self.game.revealed[r][c]:
|
|
# Celda revelada
|
|
btn.config(relief=tk.SUNKEN, state=tk.DISABLED)
|
|
|
|
if text == 'M':
|
|
# Mina clickeada (rojo, mina detonadora)
|
|
btn.config(text="💣", background='red', foreground='white')
|
|
|
|
elif text != '0':
|
|
# Número
|
|
btn.config(
|
|
text=text,
|
|
background=revealed_bg,
|
|
foreground=self._get_number_color(int(text))
|
|
)
|
|
else:
|
|
# Cero
|
|
btn.config(text="", background=revealed_bg)
|
|
|
|
def show_end_game(self):
|
|
"""Muestra el estado final, revela el tablero completo y deshabilita clics."""
|
|
|
|
self.update_ui_board()
|
|
|
|
# Asegurar el deshabilitado final (redundante pero seguro)
|
|
for btn in self.buttons.values():
|
|
btn.config(state=tk.DISABLED)
|
|
|
|
def reset_game(self):
|
|
"""Reinicia el juego lógico y recrea el tablero visual."""
|
|
self.game.setup_board()
|
|
self.feedback_var.set("¡A jugar! Encontrando minas en 16x16...")
|
|
self.draw_board()
|
|
self.update_ui_board()
|
|
|
|
def _get_number_color(self, number):
|
|
"""Retorna un color basado en el número de la mina."""
|
|
colors = {
|
|
1: 'blue',
|
|
2: 'green',
|
|
3: 'red',
|
|
4: 'purple',
|
|
5: 'maroon',
|
|
6: 'turquoise',
|
|
7: 'black',
|
|
8: 'gray'
|
|
}
|
|
return colors.get(number, 'black')
|
|
|
|
def on_close(self):
|
|
"""Maneja el evento de cierre de la ventana Toplevel."""
|
|
print("[Minigame] Cerrando ventana Buscaminas.")
|
|
self.destroy() |