import threading import time import random import tkinter as tk from tkinter import messagebox # Definición de los recursos (Zonas de la Pista) NUM_LOCKS = 5 # Crea una lista de RLock que representan las zonas de la pista (recursos) TRACK_LOCKS = [threading.RLock() for _ in range(NUM_LOCKS)] class Camel(threading.Thread): def __init__(self, name, track_length, root_app, status_label, emoji): super().__init__() self.name = name self.track_length = track_length self.distance = 0 self.is_running = True self.root_app = root_app self.status_label = status_label self.emoji = emoji # No necesitamos self.winner, usamos root_app.winner_name def run(self): # La simulación de la carrera while self.distance < self.track_length and self.is_running: # --- Lógica de Sincronización (Adquisición Ordenada de Locks) --- # El camello necesita dos zonas adyacentes para avanzar lock_id_1 = self.distance % NUM_LOCKS lock_id_2 = (self.distance + 1) % NUM_LOCKS # Regla para PREVENIR DEADLOCK: Adquirir el lock con el ID más bajo primero. lock_a = TRACK_LOCKS[min(lock_id_1, lock_id_2)] lock_b = TRACK_LOCKS[max(lock_id_1, lock_id_2)] # Intento de adquisición ordenada de los recursos with lock_a: self.update_ui(f"{self.name} asegurando zona {min(lock_id_1, lock_id_2)}...", message_only=True) time.sleep(random.uniform(0.01, 0.05)) with lock_b: # Movimiento: incrementar la distancia (simula el avance) move_distance = random.randint(1, 3) self.distance += move_distance if self.distance > self.track_length: self.distance = self.track_length # Comprobar si ha ganado DENTRO de la sección crítica if self.distance >= self.track_length and self.root_app.winner_name is None: # El primero en llegar establece el ganador y detiene a todos self.root_app.winner_name = self.name self.is_running = False self.root_app.after(0, lambda: self.root_app.show_winner(self.name)) # Detener el resto de hilos for thread in self.root_app.camel_threads: if thread.name != self.name: thread.is_running = False # Actualizar la UI con la nueva posición después de liberarse de los locks self.update_ui(f"{self.name} avanzando.") time.sleep(random.uniform(0.1, 0.5)) # --- Lógica al finalizar el hilo --- if self.distance >= self.track_length: self.update_ui(f"¡{self.name} ha cruzado la meta!", final=True) else: self.update_ui(f"{self.name} detenido.", final=True) def update_ui(self, message, final=False, message_only=False): """Actualiza la interfaz de usuario de forma segura.""" # 1. Crear la representación visual de la pista # 40 espacios representan la pista visual en la etiqueta track_display_length = 40 # Calcular la posición del camello en la pista virtual (0 a track_display_length) position_on_display = int((self.distance / self.track_length) * track_display_length) # Asegurarse de que no se salga del inicio if position_on_display < 0: position_on_display = 0 # Construir la cadena de la pista # Espacios de pista antes del camello pre_camel_track = "░" * position_on_display # Espacios de pista después del camello post_camel_length = track_display_length - len(pre_camel_track) - len(self.emoji) if post_camel_length < 0: post_camel_length = 0 post_camel_track = "░" * post_camel_length # Ensamblar la pista visual track_str = pre_camel_track + self.emoji + post_camel_track # Si el camello ha ganado, el emoji siempre debe estar al final if final and self.distance >= self.track_length: track_str = "░" * (track_display_length - len(self.emoji)) + self.emoji # 2. Actualizar el mensaje de actividad general (Estado 1) self.root_app.after(0, self.root_app.update_activity_status, message) if message_only: return # 3. Mensaje a mostrar en la etiqueta de su camello display_message = f"{self.name}\n[{track_str}]" if final and self.distance >= self.track_length: display_message = f"🎉 ¡HA GANADO {self.name}! 🎉\n[{track_str}]" elif final: display_message = f"🛑 {self.name} detenido antes de meta." # 4. Actualizar la UI self.root_app.after(0, self.status_label.config, {'text': display_message}) def start_camel_game(root_app, track_length=50): """Inicializa y comienza la carrera de camellos.""" camel_data = [ {"name": "Sahara Runner", "emoji": "🐪"}, {"name": "Dune Master", "emoji": "🐫"}, {"name": "Oasis King", "emoji": "👑"}, {"name": "Mirage Express", "emoji": "🏎️"} ] root_app.camel_threads = [] root_app.winner_name = None root_app.clear_camel_game_area() root_app.track_length = track_length # Etiqueta que define el inicio y la meta tk.Label( root_app.frame_game, text=f"INICIO \t\t\t\t\t\t\t\t\t\t META", font=("Consolas", 12, "bold"), fg='#006400', bg="#D4EDDA" ).pack(fill='x', padx=10, pady=(5, 0)) # Crear las etiquetas de estado para cada camello en la interfaz for data in camel_data: # Usamos tk.Label con anchor 'w' para que se alineen bien status_label = tk.Label( root_app.frame_game, text=f"{data['name']} | 🏁 Iniciando...", anchor='w', justify=tk.LEFT, font=("Consolas", 12), bg="#F0F0F0", padx=10, pady=5, relief=tk.RAISED ) status_label.pack(fill='x', padx=10, pady=5) camel = Camel(data['name'], track_length, root_app, status_label, data['emoji']) root_app.camel_threads.append(camel) # Iniciar todos los hilos (camellos) for camel in root_app.camel_threads: camel.start() root_app.after(0, root_app.update_activity_status, "¡LA CARRERA HA COMENZADO! Hilos competidores activos.")