# app.py - SIMCIUDAD ARGENTINA FUNCIONAL COMPLETA import gradio as gr import time import math import random import os from PIL import Image, ImageDraw import json # Groq AI Integration (mejorada) try: from groq import Groq GROQ_AVAILABLE = True groq_client = Groq(api_key=os.getenv("GROQ_API_KEY")) if os.getenv("GROQ_API_KEY") else None except ImportError: GROQ_AVAILABLE = False groq_client = None class AIUrbanPlanner: """IA para planificación urbana argentina con Groq""" def __init__(self): self.enabled = GROQ_AVAILABLE and groq_client is not None def generate_city_advice(self, city_state): """Genera consejos de planificación urbana inteligentes""" if not self.enabled: fallback_advice = [ "Construí más plazas para aumentar la felicidad vecinal.", "Las parrillas mejoran la cultura y la moral del barrio.", "Balanceá viviendas con servicios públicos.", "Las canchas de fútbol son clave para la identidad argentina." ] return random.choice(fallback_advice) try: prompt = f""" Sos un urbanista argentino experto. Analizá esta ciudad y dá consejos breves: Población: {city_state.get('population', 0)} habitantes Felicidad: {city_state.get('happiness', 0)}% Presupuesto: ${city_state.get('budget', 0):,} Edificios: {city_state.get('buildings', 0)} Identidad Cultural: {city_state.get('culture', 80)}% Dá UN consejo específico y argentino en máximo 2 líneas. Usá jerga porteña cuando sea apropiado. """ response = groq_client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model="llama-3.1-8b-instant", temperature=0.8, max_tokens=100 ) return response.choices[0].message.content.strip() except Exception as e: print(f"Error Groq: {e}") return "La ciudad necesita más espacios verdes y culturales para prosperar." class ArgentineCityBuilder: """SimCity Argentino con mecánicas revolucionarias""" def __init__(self): self.width, self.height = 1000, 700 self.grid_size = 20 self.city_grid = {} # Coordenadas -> tipo de edificio self.population = 100 self.happiness = 75 self.economy = 1000 self.inflation = 1.0 self.cultural_identity = 90 # Sistemas dinámicos self.neighborhoods = {} self.transport_network = {} self.cultural_centers = {} self.businesses = {} # Recursos complejos (CORREGIDO - todos los recursos necesarios) self.resources = { 'presupuesto': 50000, 'energia': 100, 'agua': 100, 'seguridad': 70, 'educacion': 60, 'salud': 65, 'cultura': 80, 'empleo': 75, 'vivienda': 50, 'transport': 60, # AGREGADO 'pollution': 0 # AGREGADO } # Edificios disponibles (CORREGIDOS - todos los efectos) self.building_types = { 'casa': {'cost': 1000, 'pop': 4, 'happiness': 2, 'vivienda': 5}, 'villa': {'cost': 500, 'pop': 8, 'happiness': -2, 'solidarity': 15, 'vivienda': 8}, 'edificio': {'cost': 5000, 'pop': 20, 'happiness': 1, 'vivienda': 15}, 'escuela': {'cost': 8000, 'educacion': 20, 'happiness': 10}, 'hospital': {'cost': 15000, 'salud': 30, 'happiness': 15}, 'cancha': {'cost': 3000, 'happiness': 25, 'cultura': 10}, 'parrilla': {'cost': 2000, 'happiness': 20, 'cultura': 15}, 'centro_cultural': {'cost': 10000, 'cultura': 40, 'educacion': 10}, 'fabrica': {'cost': 12000, 'empleo': 30, 'pollution': -10, 'happiness': -5}, 'plaza': {'cost': 1500, 'happiness': 15, 'cultura': 5}, 'subte': {'cost': 20000, 'transport': 50, 'happiness': 10}, 'comisaria': {'cost': 7000, 'seguridad': 25, 'happiness': -5}, 'kiosco': {'cost': 800, 'happiness': 5, 'empleo': 2} } # Inicializar ciudad base self._initialize_base_city() def _initialize_base_city(self): """Crea la ciudad inicial con algunos edificios""" # Plaza central self.city_grid[(25, 17)] = 'plaza' # Algunas casas iniciales initial_houses = [(20, 15), (30, 15), (25, 12), (25, 22)] for pos in initial_houses: self.city_grid[pos] = 'casa' # Una escuela inicial self.city_grid[(15, 20)] = 'escuela' class AdvancedCityRenderer: """Renderizador avanzado con gráficos isométricos COMPLETO""" def __init__(self): self.animation_time = 0 self.tile_size = 20 def render_city_frame(self, city_builder) -> Image.Image: """Renderiza la ciudad con vista isométrica mejorada""" img = Image.new('RGB', (city_builder.width, city_builder.height), (20, 40, 20)) draw = ImageDraw.Draw(img) self.animation_time += 0.1 # Fondo: Cielo dinámico según felicidad self._draw_dynamic_sky(draw, city_builder) # Grid base de la ciudad self._draw_city_grid(draw, city_builder) # Edificios con perspectiva isométrica self._draw_isometric_buildings(draw, city_builder) # Sistemas dinámicos (tráfico, personas, etc.) self._draw_city_life(draw, city_builder) # UI avanzada self._draw_advanced_ui(draw, city_builder) # Efectos especiales self._draw_special_effects(draw, city_builder) return img def _draw_dynamic_sky(self, draw, city): """Cielo que cambia según el estado de la ciudad""" happiness = city.happiness # Color base del cielo if happiness > 80: sky_color = (135, 206, 250) # Azul cielo feliz elif happiness > 60: sky_color = (176, 196, 222) # Azul grisáceo elif happiness > 40: sky_color = (119, 136, 153) # Gris claro else: sky_color = (105, 105, 105) # Gris oscuro # Gradiente de cielo for y in range(200): alpha = 1.0 - (y / 400) color = tuple(int(c * alpha + 20 * (1-alpha)) for c in sky_color) draw.rectangle([0, y, city.width, y+2], fill=color) # Nubes dinámicas cloud_offset = int(self.animation_time * 20) % city.width for i in range(3): x = (i * 300 + cloud_offset) % (city.width + 100) y = 50 + i * 30 self._draw_cloud(draw, x, y, happiness) def _draw_cloud(self, draw, x, y, happiness): """Dibuja nube con forma según felicidad""" cloud_color = (255, 255, 255) if happiness > 50 else (200, 200, 200) # Forma de nube for i in range(5): offset_x = i * 15 + random.randint(-5, 5) offset_y = random.randint(-8, 8) size = 20 + random.randint(-5, 10) draw.ellipse([x + offset_x - size//2, y + offset_y - size//2, x + offset_x + size//2, y + offset_y + size//2], fill=cloud_color) def _draw_city_grid(self, draw, city): """Grid isométrico de la ciudad""" grid_color = (60, 80, 60) if city.cultural_identity > 50 else (80, 80, 80) # Grid horizontal for y in range(0, city.height, self.tile_size): draw.line([0, y, city.width, y], fill=grid_color, width=1) # Grid vertical for x in range(0, city.width, self.tile_size): draw.line([x, 0, x, city.height], fill=grid_color, width=1) def _draw_isometric_buildings(self, draw, city): """Edificios con perspectiva isométrica - TODOS LOS TIPOS""" for (grid_x, grid_y), building_type in city.city_grid.items(): screen_x = grid_x * self.tile_size screen_y = grid_y * self.tile_size # TODOS los tipos de edificios implementados if building_type == 'casa': self._draw_house_isometric(draw, screen_x, screen_y, city.happiness) elif building_type == 'villa': self._draw_villa_house(draw, screen_x, screen_y, city.cultural_identity) elif building_type == 'edificio': self._draw_apartment_building(draw, screen_x, screen_y, city.economy) elif building_type == 'escuela': self._draw_school(draw, screen_x, screen_y, city.resources['educacion']) elif building_type == 'hospital': self._draw_hospital(draw, screen_x, screen_y, city.resources['salud']) elif building_type == 'cancha': self._draw_soccer_field(draw, screen_x, screen_y, city.happiness) elif building_type == 'parrilla': self._draw_parrilla(draw, screen_x, screen_y, city.cultural_identity) elif building_type == 'plaza': self._draw_plaza(draw, screen_x, screen_y, city.happiness) elif building_type == 'fabrica': self._draw_factory(draw, screen_x, screen_y, city.resources['empleo']) elif building_type == 'centro_cultural': self._draw_cultural_center(draw, screen_x, screen_y, city.resources['cultura']) elif building_type == 'kiosco': self._draw_kiosco(draw, screen_x, screen_y, city.economy) elif building_type == 'subte': self._draw_subway(draw, screen_x, screen_y, city.resources['transport']) elif building_type == 'comisaria': self._draw_police_station(draw, screen_x, screen_y, city.resources['seguridad']) # TODOS los métodos de dibujo de edificios (implementados completamente) def _draw_house_isometric(self, draw, x, y, happiness): """Casa con perspectiva isométrica""" house_color = (139, 69, 19) if happiness > 60 else (101, 67, 33) # Paredes wall_points = [(x + 2, y + 15), (x + 18, y + 15), (x + 18, y + 2), (x + 2, y + 2)] draw.polygon(wall_points, fill=house_color, outline=(80, 40, 0), width=1) # Techo isométrico roof_color = (160, 82, 45) if happiness > 70 else (120, 60, 30) roof_points = [(x, y), (x + 10, y - 5), (x + 20, y), (x + 10, y + 5)] draw.polygon(roof_points, fill=roof_color, outline=(100, 50, 25)) # Puerta draw.rectangle([x + 8, y + 10, x + 12, y + 15], fill=(101, 67, 33)) # Ventanas con luz según felicidad window_color = (255, 255, 200) if happiness > 50 else (150, 150, 150) draw.rectangle([x + 4, y + 8, x + 7, y + 11], fill=window_color, outline=(0, 0, 0)) draw.rectangle([x + 13, y + 8, x + 16, y + 11], fill=window_color, outline=(0, 0, 0)) # Jardincito si hay alta felicidad if happiness > 80: for i in range(3): flower_x = x + 2 + i * 4 flower_y = y + 16 draw.ellipse([flower_x, flower_y, flower_x + 2, flower_y + 2], fill=(255, 100, 150)) def _draw_villa_house(self, draw, x, y, cultural_identity): """Casa de villa con colores vibrantes""" villa_colors = [(255, 100, 100), (100, 255, 100), (100, 100, 255), (255, 255, 100), (255, 100, 255)] color = random.choice(villa_colors) # Casa principal draw.rectangle([x, y + 5, x + 20, y + 18], fill=color, outline=(0, 0, 0), width=2) # Techo de chapa draw.polygon([(x-2, y+5), (x+10, y-2), (x+22, y+5)], fill=(150, 150, 150)) # Extensión autoconstruida if cultural_identity > 60: draw.rectangle([x + 16, y + 12, x + 25, y + 18], fill=color, outline=(0, 0, 0)) # Antena parabólica draw.ellipse([x + 15, y - 1, x + 18, y + 2], outline=(200, 200, 200), width=2) # Tendedero con ropa draw.line([(x + 2, y + 3), (x + 18, y + 3)], fill=(100, 100, 100), width=1) for i in range(3): cloth_x = x + 4 + i * 5 draw.rectangle([cloth_x, y + 3, cloth_x + 3, y + 8], fill=random.choice([(255, 0, 0), (0, 255, 0), (0, 0, 255)])) def _draw_apartment_building(self, draw, x, y, economy): """Edificio de departamentos""" building_color = (120, 120, 160) if economy > 5000 else (100, 100, 140) # Estructura principal más alta draw.rectangle([x + 1, y - 10, x + 23, y + 18], fill=building_color, outline=(0, 0, 0), width=1) # Ventanas en pisos for floor in range(4): floor_y = y - 5 + floor * 6 for window in range(3): window_x = x + 4 + window * 6 light_on = economy > 3000 and random.random() < 0.7 window_color = (255, 255, 200) if light_on else (100, 100, 120) draw.rectangle([window_x, floor_y, window_x + 3, floor_y + 3], fill=window_color, outline=(0, 0, 0)) def _draw_school(self, draw, x, y, education_level): """Escuela con bandera argentina""" school_color = (255, 255, 100) if education_level > 70 else (200, 200, 80) # Edificio principal draw.rectangle([x + 1, y + 8, x + 23, y + 18], fill=school_color, outline=(0, 0, 0), width=2) # Bandera argentina draw.rectangle([x + 20, y + 2, x + 22, y + 8], fill=(100, 150, 255)) # Azul draw.rectangle([x + 20, y + 4, x + 22, y + 6], fill=(255, 255, 255)) # Blanco # Asta draw.line([(x + 22, y + 8), (x + 22, y + 2)], fill=(139, 69, 19), width=2) # Patio escolar draw.rectangle([x + 5, y + 12, x + 19, y + 16], fill=(200, 200, 200), outline=(150, 150, 150)) # Niños en el patio si alta educación if education_level > 60: for i in range(3): child_x = x + 7 + i * 4 child_y = y + 13 draw.ellipse([child_x, child_y, child_x + 2, child_y + 3], fill=(180, 140, 100)) def _draw_hospital(self, draw, x, y, health_level): """Hospital con cruz roja""" hospital_color = (255, 100, 100) if health_level > 70 else (200, 80, 80) # Edificio draw.rectangle([x + 1, y + 5, x + 23, y + 18], fill=hospital_color, outline=(0, 0, 0), width=2) # Cruz roja draw.rectangle([x + 10, y + 8, x + 14, y + 15], fill=(255, 0, 0)) draw.rectangle([x + 7, y + 10, x + 17, y + 13], fill=(255, 0, 0)) # Ambulancia si hospital activo if health_level > 50: draw.rectangle([x + 2, y + 15, x + 10, y + 18], fill=(255, 255, 255), outline=(0, 0, 0)) draw.ellipse([x + 3, y + 16, x + 5, y + 18], fill=(0, 0, 0)) draw.ellipse([x + 7, y + 16, x + 9, y + 18], fill=(0, 0, 0)) def _draw_soccer_field(self, draw, x, y, happiness): """Cancha de fútbol con actividad""" # Césped grass_color = (50, 150, 50) if happiness > 60 else (80, 120, 80) draw.rectangle([x, y, x + 40, y + 25], fill=grass_color, outline=(255, 255, 255), width=2) # Líneas de la cancha draw.line([(x + 20, y), (x + 20, y + 25)], fill=(255, 255, 255), width=2) draw.ellipse([x + 15, y + 10, x + 25, y + 15], outline=(255, 255, 255), width=2) # Arcos draw.rectangle([x, y + 8, x + 3, y + 17], outline=(255, 255, 255), width=2) draw.rectangle([x + 37, y + 8, x + 40, y + 17], outline=(255, 255, 255), width=2) # Jugadores animados if happiness > 70: for i in range(6): player_x = x + 5 + i * 6 + int(3 * math.sin(self.animation_time + i)) player_y = y + 8 + int(2 * math.cos(self.animation_time + i * 1.5)) draw.ellipse([player_x, player_y, player_x + 3, player_y + 5], fill=(180, 140, 100)) # Pelota animada ball_x = x + 20 + int(10 * math.sin(self.animation_time * 2)) ball_y = y + 12 + int(5 * math.cos(self.animation_time * 2)) draw.ellipse([ball_x, ball_y, ball_x + 3, ball_y + 3], fill=(255, 255, 255), outline=(0, 0, 0)) def _draw_parrilla(self, draw, x, y, cultural_identity): """Parrilla argentina con humo""" # Base de la parrilla draw.rectangle([x + 5, y + 10, x + 15, y + 18], fill=(50, 50, 50), outline=(100, 100, 100)) # Parrilla for i in range(3): draw.line([(x + 6 + i * 3, y + 12), (x + 6 + i * 3, y + 16)], fill=(150, 150, 150), width=2) # Carne en la parrilla if cultural_identity > 70: for i in range(2): meat_x = x + 7 + i * 4 meat_y = y + 13 draw.ellipse([meat_x, meat_y, meat_x + 3, meat_y + 2], fill=(139, 69, 19)) # Humo animado for i in range(4): smoke_x = x + 10 + random.randint(-3, 3) smoke_y = y + 8 - i * 4 + int(2 * math.sin(self.animation_time * 2 + i)) size = 3 + i draw.ellipse([smoke_x - size//2, smoke_y - size//2, smoke_x + size//2, smoke_y + size//2], fill=(200, 200, 200)) # Gente alrededor if cultural_identity > 60: for i in range(3): person_x = x + 2 + i * 8 person_y = y + 15 draw.ellipse([person_x, person_y, person_x + 4, person_y + 8], fill=(180, 140, 100)) def _draw_plaza(self, draw, x, y, happiness): """Plaza con actividades""" # Base verde de la plaza plaza_color = (100, 180, 100) if happiness > 50 else (120, 140, 120) draw.rectangle([x, y, x + 30, y + 30], fill=plaza_color, outline=(80, 120, 80)) # Senderos draw.line([(x, y + 15), (x + 30, y + 15)], fill=(160, 140, 120), width=3) draw.line([(x + 15, y), (x + 15, y + 30)], fill=(160, 140, 120), width=3) # Monumento central draw.rectangle([x + 12, y + 12, x + 18, y + 18], fill=(180, 180, 180), outline=(120, 120, 120)) # Árboles tree_positions = [(x + 5, y + 5), (x + 25, y + 5), (x + 5, y + 25), (x + 25, y + 25)] for tree_x, tree_y in tree_positions: # Tronco draw.rectangle([tree_x, tree_y + 3, tree_x + 2, tree_y + 8], fill=(101, 67, 33)) # Copa draw.ellipse([tree_x - 2, tree_y, tree_x + 4, tree_y + 6], fill=(50, 150, 50)) # Actividades según felicidad if happiness > 70: # Ronda de mate for i in range(4): angle = i * (math.pi / 2) person_x = x + 15 + int(8 * math.cos(angle)) person_y = y + 15 + int(8 * math.sin(angle)) draw.ellipse([person_x, person_y, person_x + 3, person_y + 5], fill=(180, 140, 100)) def _draw_factory(self, draw, x, y, employment_level): """Fábrica con chimeneas""" factory_color = (150, 100, 100) if employment_level > 60 else (120, 80, 80) # Edificio principal draw.rectangle([x + 1, y + 8, x + 23, y + 18], fill=factory_color, outline=(0, 0, 0), width=2) # Chimeneas for chimney_x in [x + 6, x + 12, x + 18]: draw.rectangle([chimney_x, y + 2, chimney_x + 3, chimney_x + 8], fill=(80, 80, 80)) # Humo smoke_y = y + 2 - int(3 * math.sin(self.animation_time)) draw.ellipse([chimney_x, smoke_y - 2, chimney_x + 3, smoke_y + 1], fill=(150, 150, 150)) # Ventanas industriales for window_x in range(x + 4, x + 20, 4): draw.rectangle([window_x, y + 12, window_x + 2, window_x + 15], fill=(255, 200, 100), outline=(0, 0, 0)) def _draw_cultural_center(self, draw, x, y, culture_level): """Centro cultural""" center_color = (255, 140, 0) if culture_level > 70 else (200, 100, 0) # Edificio principal draw.rectangle([x + 1, y + 5, x + 23, y + 18], fill=center_color, outline=(0, 0, 0), width=2) # Cartelera cultural draw.rectangle([x + 5, y + 8, x + 19, y + 15], fill=(255, 255, 255), outline=(0, 0, 0)) # Máscaras de teatro draw.ellipse([x + 7, y + 10, x + 11, y + 13], fill=(255, 255, 255), outline=(0, 0, 0)) draw.ellipse([x + 13, y + 10, x + 17, y + 13], fill=(255, 255, 255), outline=(0, 0, 0)) def _draw_kiosco(self, draw, x, y, economy): """Kiosco de barrio""" kiosco_color = (200, 150, 100) if economy > 2000 else (150, 100, 80) # Estructura pequeña draw.rectangle([x + 5, y + 10, x + 15, y + 18], fill=kiosco_color, outline=(0, 0, 0), width=2) # Cartel draw.rectangle([x + 7, y + 8, x + 13, y + 10], fill=(255, 0, 0)) # Productos en ventana for i in range(3): product_x = x + 6 + i * 3 draw.rectangle([product_x, y + 12, product_x + 2, y + 15], fill=random.choice([(255, 0, 0), (0, 255, 0), (0, 0, 255)])) def _draw_subway(self, draw, x, y, transport_level): """Estación de subte""" # Entrada subterránea draw.rectangle([x + 5, y + 10, x + 15, y + 18], fill=(100, 100, 100), outline=(0, 0, 0), width=2) # Cartel de subte draw.rectangle([x + 7, y + 5, x + 13, y + 8], fill=(0, 100, 200)) # Escaleras for i in range(3): step_y = y + 12 + i * 2 draw.line([(x + 6, step_y), (x + 14, step_y)], fill=(150, 150, 150), width=1) def _draw_police_station(self, draw, x, y, security_level): """Comisaría""" station_color = (100, 100, 150) if security_level > 70 else (80, 80, 120) # Edificio draw.rectangle([x + 1, y + 5, x + 23, y + 18], fill=station_color, outline=(0, 0, 0), width=2) # Escudo policial draw.ellipse([x + 10, y + 8, x + 14, y + 12], fill=(255, 255, 255), outline=(0, 0, 0)) # Patrullero if security_level > 50: draw.rectangle([x + 16, y + 15, x + 22, y + 18], fill=(255, 255, 255), outline=(0, 0, 0)) draw.ellipse([x + 17, y + 17, x + 19, y + 19], fill=(0, 0, 0)) draw.ellipse([x + 20, y + 17, x + 22, y + 19], fill=(0, 0, 0)) def _draw_city_life(self, draw, city): """Sistemas dinámicos de vida urbana""" # Tráfico animado en calles principales if city.resources['transport'] > 30: self._draw_animated_traffic(draw, city) # Personas caminando if city.happiness > 40: self._draw_pedestrians(draw, city) # Efectos de contaminación if city.resources.get('pollution', 0) < -20: self._draw_pollution_effects(draw, city) def _draw_animated_traffic(self, draw, city): """Tráfico animado en las calles""" # Colectivos argentinos for i in range(3): bus_x = (100 + i * 300 + int(self.animation_time * 50)) % city.width bus_y = 200 + i * 100 # Colectivo draw.rectangle([bus_x, bus_y, bus_x + 25, bus_y + 12], fill=(255, 200, 0), outline=(0, 0, 0), width=2) # Ventanas for window in range(3): draw.rectangle([bus_x + 3 + window * 6, bus_y + 2, bus_x + 8 + window * 6, bus_y + 7], fill=(150, 200, 255)) # Ruedas draw.ellipse([bus_x + 2, bus_y + 10, bus_x + 6, bus_y + 14], fill=(50, 50, 50)) draw.ellipse([bus_x + 19, bus_y + 10, bus_x + 23, bus_y + 14], fill=(50, 50, 50)) def _draw_pedestrians(self, draw, city): """Peatones animados""" for i in range(8): person_x = (50 + i * 120 + int(self.animation_time * 30 + i * 50)) % city.width person_y = 300 + i * 40 + int(5 * math.sin(self.animation_time + i)) # Persona simple draw.ellipse([person_x, person_y, person_x + 4, person_y + 8], fill=(180, 140, 100)) # Mate en la mano si alta cultura if city.cultural_identity > 70 and i % 3 == 0: draw.ellipse([person_x + 3, person_y + 2, person_x + 6, person_y + 5], fill=(100, 50, 0)) def _draw_pollution_effects(self, draw, city): """Efectos de contaminación""" pollution_level = abs(city.resources.get('pollution', 0)) if pollution_level > 20: # Smog gris for i in range(int(pollution_level / 5)): smog_x = random.randint(0, city.width) smog_y = random.randint(100, 300) size = random.randint(10, 30) draw.ellipse([smog_x - size, smog_y - size, smog_x + size, smog_y + size], fill=(120, 120, 120)) def _draw_advanced_ui(self, draw, city): """UI avanzada con gráficos informativos""" # Panel principal con transparencia panel_width = 300 panel_height = 200 # Panel de fondo con gradiente for i in range(panel_height): alpha = int(180 - i) color = (0, 0, 0) if alpha > 0 else (20, 20, 20) draw.rectangle([10, 10 + i, 10 + panel_width, 11 + i], fill=color) # Borde elegante draw.rectangle([10, 10, 10 + panel_width, 10 + panel_height], outline=(100, 150, 255), width=3) # Información de la ciudad (texto simplificado por limitaciones de PIL) info_y = 25 city_info = [ f"Población: {city.population:,}", f"Felicidad: {city.happiness}%", f"Economía: ${city.economy:,}", f"Identidad: {city.cultural_identity}%", f"Inflación: {city.inflation:.1f}x" ] # Barras de recursos en el lado derecho self._draw_resource_bars(draw, city, city.width - 250, 20) # Mini mapa en esquina inferior derecha self._draw_mini_map(draw, city, city.width - 150, city.height - 120) def _draw_resource_bars(self, draw, city, start_x, start_y): """Barras de recursos con efectos visuales""" resources = city.resources bar_width = 200 bar_height = 15 resource_colors = { 'presupuesto': (255, 215, 0), 'energia': (255, 255, 0), 'agua': (0, 191, 255), 'seguridad': (255, 99, 71), 'educacion': (147, 112, 219), 'salud': (255, 20, 147), 'cultura': (255, 140, 0), 'empleo': (50, 205, 50), 'vivienda': (139, 69, 19), 'transport': (100, 149, 237), 'pollution': (120, 120, 120) } y_offset = start_y for resource, value in resources.items(): if resource == 'presupuesto': # Presupuesto se muestra diferente y_offset += 25 continue # Normalizar valor para barra (0-100) if resource == 'pollution': normalized_value = max(0, min(100, abs(value))) else: normalized_value = max(0, min(100, value)) if isinstance(value, (int, float)) else 50 # Barra de fondo draw.rectangle([start_x, y_offset, start_x + bar_width, y_offset + bar_height], fill=(40, 40, 40), outline=(100, 100, 100)) # Barra de valor con gradiente fill_width = int((normalized_value / 100) * (bar_width - 4)) color = resource_colors.get(resource, (150, 150, 150)) # Efecto de brillo for i in range(fill_width): brightness = 0.7 + 0.3 * math.sin(self.animation_time + i/10) bright_color = tuple(int(c * brightness) for c in color) draw.rectangle([start_x + 2 + i, y_offset + 2, start_x + 3 + i, y_offset + bar_height - 2], fill=bright_color) y_offset += 22 def _draw_mini_map(self, draw, city, start_x, start_y): """Mini mapa de la ciudad""" map_width, map_height = 120, 100 # Fondo del mini mapa draw.rectangle([start_x, start_y, start_x + map_width, start_y + map_height], fill=(20, 20, 20), outline=(150, 150, 150), width=2) # Edificios en el mini mapa scale = 2 for (grid_x, grid_y), building_type in city.city_grid.items(): mini_x = start_x + (grid_x * scale) % map_width mini_y = start_y + (grid_y * scale) % map_height building_colors = { 'casa': (100, 150, 100), 'villa': (255, 150, 100), 'edificio': (150, 150, 200), 'escuela': (200, 200, 100), 'hospital': (255, 100, 100), 'cancha': (100, 255, 100), 'plaza': (150, 255, 150), 'fabrica': (150, 100, 100), 'centro_cultural': (255, 140, 0), 'kiosco': (200, 150, 100), 'subte': (100, 149, 237), 'comisaria': (100, 100, 150), 'parrilla': (200, 100, 50) } color = building_colors.get(building_type, (100, 100, 100)) draw.rectangle([mini_x, mini_y, mini_x + scale, mini_y + scale], fill=color) def _draw_special_effects(self, draw, city): """Efectos especiales adicionales""" # Efectos de festivales si alta cultura if city.resources['cultura'] > 80: for i in range(5): firework_x = random.randint(100, city.width - 100) firework_y = random.randint(50, 150) size = random.randint(3, 8) color = random.choice([(255, 100, 100), (100, 255, 100), (100, 100, 255), (255, 255, 100)]) draw.ellipse([firework_x - size, firework_y - size, firework_x + size, firework_y + size], fill=color) class EnhancedCityGame: """Juego de ciudad argentino mejorado COMPLETO""" def __init__(self): self.city = ArgentineCityBuilder() self.renderer = AdvancedCityRenderer() self.ai_planner = AIUrbanPlanner() self.selected_building = None self.game_speed = 1 self.events_log = [] self.day = 1 def place_building(self, building_type, grid_x, grid_y): """Coloca un edificio en la ciudad - CORREGIDO""" try: grid_x, grid_y = int(grid_x), int(grid_y) if building_type not in self.city.building_types: return "Tipo de edificio inválido", self.get_city_status(), "Error: Tipo de edificio no válido" if (grid_x, grid_y) in self.city.city_grid: return "Ya hay un edificio ahí", self.get_city_status(), "Error: Posición ocupada" building_info = self.city.building_types[building_type] cost = building_info['cost'] if self.city.resources['presupuesto'] < cost: return f"No tenés suficiente presupuesto (${cost:,})", self.get_city_status(), "Error: Sin presupuesto" # Construir edificio self.city.resources['presupuesto'] -= cost self.city.city_grid[(grid_x, grid_y)] = building_type # Aplicar efectos CORREGIDOS self._apply_building_effects(building_type, building_info) # Actualizar estadísticas self._update_city_stats() # Generar consejo IA advice = self.ai_planner.generate_city_advice({ 'population': self.city.population, 'happiness': self.city.happiness, 'budget': self.city.resources['presupuesto'], 'buildings': len(self.city.city_grid), 'culture': self.city.cultural_identity }) frame = self.renderer.render_city_frame(self.city) status = self.get_city_status() message = f"✅ {building_type.replace('_', ' ').title()} construido por ${cost:,}" return frame, status, f"{message}\n\n🤖 IA Urbana: {advice}" except Exception as e: print(f"Error en construcción: {e}") return self.renderer.render_city_frame(self.city), self.get_city_status(), f"Error: {str(e)}" def _apply_building_effects(self, building_type, building_info): """Aplica los efectos de un edificio a la ciudad - CORREGIDO""" for effect, value in building_info.items(): if effect == 'cost': continue elif effect == 'pop': self.city.population += value elif effect == 'happiness': self.city.happiness = min(100, max(0, self.city.happiness + value)) elif effect == 'solidarity': self.city.cultural_identity = min(100, max(0, self.city.cultural_identity + value)) elif effect in self.city.resources: if effect == 'pollution': # Pollution es negativa, así que sumamos el valor negativo self.city.resources[effect] += value else: current_value = self.city.resources[effect] self.city.resources[effect] = min(100, max(0, current_value + value)) def _update_city_stats(self): """Actualiza estadísticas generales de la ciudad""" # Calcular economía basada en edificios economy_buildings = ['fabrica', 'kiosco', 'centro_cultural', 'subte'] economy_bonus = sum(5000 for pos, building in self.city.city_grid.items() if building in economy_buildings) self.city.economy = 1000 + economy_bonus + self.city.population * 30 # Inflación aumenta con el tiempo y población self.city.inflation += 0.005 + (len(self.city.city_grid) * 0.001) # Felicidad general basada en múltiples factores happiness_factors = [ self.city.resources.get('cultura', 50), self.city.resources.get('seguridad', 50), self.city.resources.get('empleo', 50), self.city.resources.get('salud', 50), self.city.resources.get('educacion', 50), self.city.cultural_identity ] self.city.happiness = sum(happiness_factors) // len(happiness_factors) # Limitar felicidad self.city.happiness = max(0, min(100, self.city.happiness)) def simulate_day(self): """Simula un día en la ciudad""" try: self.day += 1 # Ingresos diarios base_income = self.city.population * 12 economy_bonus = self.city.economy * 0.008 daily_income = int(base_income + economy_bonus) self.city.resources['presupuesto'] += daily_income # Gastos de mantenimiento maintenance_cost = len(self.city.city_grid) * 80 self.city.resources['presupuesto'] -= maintenance_cost # Efectos de la inflación (más realista) inflation_impact = int(self.city.resources['presupuesto'] * 0.01) self.city.resources['presupuesto'] -= inflation_impact # Eventos aleatorios event_message = "" if random.random() < 0.25: event_message = self._trigger_city_event() # Actualizar todo self._update_city_stats() # Generar análisis IA del día ai_analysis = self.ai_planner.generate_city_advice({ 'population': self.city.population, 'happiness': self.city.happiness, 'budget': self.city.resources['presupuesto'], 'buildings': len(self.city.city_grid), 'culture': self.city.cultural_identity }) frame = self.renderer.render_city_frame(self.city) status = self.get_city_status() message = f"📅 Día {self.day} completado\n💰 Ingresos: ${daily_income:,}\n💸 Gastos: ${maintenance_cost:,}" if event_message: message += f"\n📰 {event_message}" message += f"\n\n🤖 Análisis IA: {ai_analysis}" return frame, status, message except Exception as e: print(f"Error en simulación: {e}") return self.renderer.render_city_frame(self.city), self.get_city_status(), f"Error: {str(e)}" def _trigger_city_event(self): """Eventos aleatorios de la ciudad - MEJORADOS""" events = [ ("🎉 Festival de Tango en San Telmo", "cultura", 15, "happiness", 20), ("⚽ Superclásico River vs Boca", "happiness", 30, "cultura", 10), ("🏭 Inversión extranjera en fábrica", "empleo", 20, "presupuesto", 8000), ("🌧️ Lluvia beneficiosa para la ciudad", "agua", 25, "energia", -5), ("📚 Programa nacional de alfabetización", "educacion", 18, "cultura", 12), ("🏥 Campaña de vacunación masiva", "salud", 25, "happiness", 15), ("💸 Crisis económica menor", "presupuesto", -5000, "happiness", -8), ("🎭 Inauguración de centro cultural", "cultura", 30, "educacion", 15), ("🚌 Mejoras en transporte público", "transport", 20, "happiness", 12), ("🛡️ Operativo de seguridad exitoso", "seguridad", 15, "happiness", 8), ("🏠 Programa de vivienda social", "vivienda", 25, "happiness", 18), ("🔥 Festival del Asado Argentino", "cultura", 20, "happiness", 25) ] event_name, res1, val1, res2, val2 = random.choice(events) # Aplicar efectos if res1 == 'presupuesto': self.city.resources[res1] += val1 elif res1 == 'happiness': self.city.happiness = min(100, max(0, self.city.happiness + val1)) elif res1 in self.city.resources: self.city.resources[res1] = min(100, max(0, self.city.resources[res1] + val1)) if res2 == 'presupuesto': self.city.resources[res2] += val2 elif res2 == 'happiness': self.city.happiness = min(100, max(0, self.city.happiness + val2)) elif res2 in self.city.resources: self.city.resources[res2] = min(100, max(0, self.city.resources[res2] + val2)) self.events_log.append(event_name) return event_name def get_city_status(self): """Status completo de la ciudad""" try: building_count = len(self.city.city_grid) return { "🏙️ Población": f"{self.city.population:,} habitantes", "😊 Felicidad": f"{self.city.happiness}%", "💰 Presupuesto": f"${self.city.resources['presupuesto']:,}", "📊 Economía": f"${self.city.economy:,}", "🇦🇷 Identidad": f"{self.city.cultural_identity}%", "📈 Inflación": f"{self.city.inflation:.2f}x", "🏗️ Edificios": f"{building_count} construidos", "📅 Día": self.day, "⚡ Energía": f"{self.city.resources['energia']}%", "💧 Agua": f"{self.city.resources['agua']}%", "🎓 Educación": f"{self.city.resources['educacion']}%", "🏥 Salud": f"{self.city.resources['salud']}%", "🎭 Cultura": f"{self.city.resources['cultura']}%", "💼 Empleo": f"{self.city.resources['empleo']}%" } except Exception as e: return {"Error": str(e)} def create_simcity_interface(): """Interfaz del SimCity Argentino COMPLETA""" game = EnhancedCityGame() with gr.Blocks( title="🇦🇷 SIMCIUDAD ARGENTINA - Construcción Épica", theme=gr.themes.Glass(), css=""" .building-btn { font-size: 1.0em; padding: 8px; margin: 2px; } .city-header { background: linear-gradient(45deg, #74b9ff, #0984e3, #00b894); } .status-panel { background: #2d3436; border-radius: 10px; } """ ) as interface: gr.HTML("""

🏙️ SIMCIUDAD ARGENTINA 🇦🇷

Construí la Ciudad Argentina de tus Sueños con IA

Gráficos Isométricos • Mecánicas Realistas • Cultura Auténtica • Powered by Groq AI

""") with gr.Row(): with gr.Column(scale=2): city_display = gr.Image( label="🌆 Tu Ciudad Argentina", width=1000, height=700, value=game.renderer.render_city_frame(game.city) ) gr.Markdown("### 🏗️ HERRAMIENTAS DE CONSTRUCCIÓN") with gr.Row(): grid_x_input = gr.Number(label="Coordenada X", value=20, minimum=0, maximum=45) grid_y_input = gr.Number(label="Coordenada Y", value=20, minimum=0, maximum=30) # Edificios residenciales gr.Markdown("#### 🏠 RESIDENCIALES") with gr.Row(): house_btn = gr.Button("🏠 CASA ($1K)", variant="secondary", elem_classes="building-btn") villa_btn = gr.Button("🏘️ VILLA ($500)", variant="secondary", elem_classes="building-btn") building_btn = gr.Button("🏢 EDIFICIO ($5K)", variant="primary", elem_classes="building-btn") kiosco_btn = gr.Button("🏪 KIOSCO ($800)", variant="secondary", elem_classes="building-btn") # Servicios públicos gr.Markdown("#### 🏛️ SERVICIOS PÚBLICOS") with gr.Row(): school_btn = gr.Button("🏫 ESCUELA ($8K)", variant="primary", elem_classes="building-btn") hospital_btn = gr.Button("🏥 HOSPITAL ($15K)", variant="stop", elem_classes="building-btn") police_btn = gr.Button("🛡️ COMISARÍA ($7K)", variant="stop", elem_classes="building-btn") subway_btn = gr.Button("🚇 SUBTE ($20K)", variant="primary", elem_classes="building-btn") # Cultura y entretenimiento gr.Markdown("#### 🎭 CULTURA Y ENTRETENIMIENTO") with gr.Row(): field_btn = gr.Button("⚽ CANCHA ($3K)", variant="secondary", elem_classes="building-btn") grill_btn = gr.Button("🔥 PARRILLA ($2K)", variant="stop", elem_classes="building-btn") cultural_btn = gr.Button("🎭 C.CULTURAL ($10K)", variant="primary", elem_classes="building-btn") plaza_btn = gr.Button("🌳 PLAZA ($1.5K)", variant="secondary", elem_classes="building-btn") # Industria gr.Markdown("#### 🏭 INDUSTRIA") with gr.Row(): factory_btn = gr.Button("🏭 FÁBRICA ($12K)", variant="primary", elem_classes="building-btn") # Simulación with gr.Row(): simulate_btn = gr.Button("⏭️ SIMULAR DÍA", variant="primary", size="lg") with gr.Column(scale=1): gr.Markdown("### 📊 ESTADO DE LA CIUDAD") city_status = gr.JSON( label="📈 Estadísticas Completas", value=game.get_city_status() ) gr.Markdown("### 📰 NOTICIAS Y EVENTOS") events_display = gr.Textbox( value="🎮 ¡Bienvenido al SimCity Argentino con IA! Construí tu ciudad con identidad nacional. La IA te dará consejos de planificación urbana en tiempo real.", label="📺 Canal de Noticias Urbanas", lines=6, interactive=False ) gr.Markdown("### 💰 GUÍA DE CONSTRUCCIÓN") gr.HTML("""

💵 Precios y Efectos:

""") # Funciones de construcción mejoradas def build_structure(building_type, x, y): return game.place_building(building_type, x, y) # Conectar TODOS los botones house_btn.click(fn=lambda x, y: build_structure("casa", x, y), inputs=[grid_x_input, grid_y_input], )