Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,479 +1,438 @@
|
|
| 1 |
-
#
|
| 2 |
import gradio as gr
|
| 3 |
import time
|
| 4 |
import math
|
| 5 |
import random
|
|
|
|
| 6 |
from PIL import Image, ImageDraw
|
| 7 |
-
from
|
|
|
|
| 8 |
|
| 9 |
-
class
|
|
|
|
| 10 |
def __init__(self):
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
'dolares': 50,
|
| 20 |
-
'comida': 80,
|
| 21 |
-
'esperanza': 100,
|
| 22 |
-
'solidaridad': 90,
|
| 23 |
-
'trabajo': 70
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
# Mecánicas únicas argentinas
|
| 27 |
-
self.inflation_rate = 1.0
|
| 28 |
-
self.social_network = []
|
| 29 |
-
self.cultural_events = []
|
| 30 |
-
self.crisis_level = 1
|
| 31 |
-
|
| 32 |
-
# Estados del juego dinámicos
|
| 33 |
-
self.day = 1
|
| 34 |
-
self.season = "verano"
|
| 35 |
-
self.current_crisis = "corralito_2001"
|
| 36 |
-
self.neighborhood_mood = "esperanzado"
|
| 37 |
-
|
| 38 |
-
self.initialize_argentina()
|
| 39 |
|
| 40 |
-
def
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
class ArgentineCharacter:
|
| 47 |
-
def __init__(self):
|
| 48 |
-
self.name = "Carlos"
|
| 49 |
-
self.profession = random.choice(["albañil", "docente", "comerciante", "empleado_publico"])
|
| 50 |
-
self.position = [450, 300] # Centro de CABA
|
| 51 |
-
self.family = {
|
| 52 |
-
'spouse': True,
|
| 53 |
-
'children': random.randint(1, 3),
|
| 54 |
-
'elderly_parents': random.choice([True, False])
|
| 55 |
-
}
|
| 56 |
-
|
| 57 |
-
# Skills argentinos únicos
|
| 58 |
-
self.skills = {
|
| 59 |
-
'rebusque': 70,
|
| 60 |
-
'mate_sharing': 90,
|
| 61 |
-
'asado_expertise': 80,
|
| 62 |
-
'crisis_survival': 60,
|
| 63 |
-
'soccer_knowledge': 95,
|
| 64 |
-
'political_debate': 75
|
| 65 |
-
}
|
| 66 |
-
|
| 67 |
-
self.current_strategy = "supervivencia_diaria"
|
| 68 |
-
self.stress_level = 30
|
| 69 |
-
self.community_reputation = 50
|
| 70 |
-
|
| 71 |
-
class CrisisManager:
|
| 72 |
-
"""Sistema que simula las crisis argentinas reales"""
|
| 73 |
-
def __init__(self):
|
| 74 |
-
self.crisis_timeline = {
|
| 75 |
-
2001: {
|
| 76 |
-
'name': 'Corralito',
|
| 77 |
-
'effects': {'peso_devaluation': 300, 'unemployment': 25, 'social_unrest': 80},
|
| 78 |
-
'opportunities': ['trueque', 'economia_popular', 'solidaridad_vecinal']
|
| 79 |
},
|
| 80 |
-
|
| 81 |
-
'
|
| 82 |
-
'
|
| 83 |
-
'
|
| 84 |
},
|
| 85 |
-
|
| 86 |
-
'
|
| 87 |
-
'
|
| 88 |
-
'
|
| 89 |
},
|
| 90 |
-
|
| 91 |
-
'
|
| 92 |
-
'
|
| 93 |
-
'
|
| 94 |
}
|
| 95 |
}
|
| 96 |
|
| 97 |
-
def
|
| 98 |
-
"""
|
| 99 |
-
if
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
'challenges': ['infraestructura', 'estigma_social'],
|
| 111 |
-
'strengths': ['union_vecinal', 'cultura_popular']
|
| 112 |
-
},
|
| 113 |
-
'palermo': {
|
| 114 |
-
'solidarity': 60, 'resources': 85, 'creativity': 75,
|
| 115 |
-
'challenges': ['gentrificacion', 'burbuja_social'],
|
| 116 |
-
'strengths': ['startups', 'vida_nocturna']
|
| 117 |
-
},
|
| 118 |
-
'san_telmo': {
|
| 119 |
-
'solidarity': 80, 'resources': 70, 'creativity': 95,
|
| 120 |
-
'challenges': ['turismo_masivo'],
|
| 121 |
-
'strengths': ['tango', 'arte', 'historia']
|
| 122 |
-
},
|
| 123 |
-
'mataderos': {
|
| 124 |
-
'solidarity': 90, 'resources': 50, 'creativity': 85,
|
| 125 |
-
'challenges': ['infraestructura'],
|
| 126 |
-
'strengths': ['folklore', 'tradicion_gaucha']
|
| 127 |
-
}
|
| 128 |
-
}
|
| 129 |
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
# Fondo: Skyline de Buenos Aires
|
| 141 |
-
self.draw_buenos_aires_skyline(draw, game_state)
|
| 142 |
-
|
| 143 |
-
# Primer plano: Vecindario actual
|
| 144 |
-
self.draw_neighborhood(draw, game_state)
|
| 145 |
-
|
| 146 |
-
# Personajes: Vecinos interactuando
|
| 147 |
-
self.draw_community_life(draw, game_state)
|
| 148 |
-
|
| 149 |
-
# UI: Información de supervivencia
|
| 150 |
-
self.draw_survival_ui(draw, game_state)
|
| 151 |
-
|
| 152 |
-
# Efectos especiales: Crisis o celebraciones
|
| 153 |
-
self.draw_dynamic_effects(draw, game_state)
|
| 154 |
-
|
| 155 |
-
return img
|
| 156 |
-
|
| 157 |
-
def draw_buenos_aires_skyline(self, draw, game_state):
|
| 158 |
-
"""Dibuja el skyline icónico de Buenos Aires"""
|
| 159 |
-
# Río de la Plata
|
| 160 |
-
draw.rectangle([0, 400, 900, 600], fill=(30, 60, 120))
|
| 161 |
-
|
| 162 |
-
# Puerto Madero
|
| 163 |
-
for i in range(5):
|
| 164 |
-
building_height = 250 + random.randint(-50, 50)
|
| 165 |
-
draw.rectangle([700 + i*30, 600-building_height, 720 + i*30, 600],
|
| 166 |
-
fill=(40, 40, 60), outline=(80, 80, 100))
|
| 167 |
-
|
| 168 |
-
# Microcentro
|
| 169 |
-
for i in range(8):
|
| 170 |
-
building_height = 200 + random.randint(-100, 100)
|
| 171 |
-
draw.rectangle([200 + i*40, 600-building_height, 230 + i*40, 600],
|
| 172 |
-
fill=(60, 60, 80), outline=(100, 100, 120))
|
| 173 |
-
|
| 174 |
-
# Obelisco (si estamos en el centro)
|
| 175 |
-
if game_state.get('location') == 'microcentro':
|
| 176 |
-
draw.polygon([(450, 150), (445, 400), (455, 400)],
|
| 177 |
-
fill=(200, 200, 200), outline=(255, 255, 255))
|
| 178 |
-
|
| 179 |
-
def draw_neighborhood(self, draw, game_state):
|
| 180 |
-
"""Dibuja el barrio actual con detalles auténticos"""
|
| 181 |
-
neighborhood = game_state.get('current_neighborhood', 'villa_31')
|
| 182 |
-
|
| 183 |
-
if neighborhood == 'villa_31':
|
| 184 |
-
self.draw_villa_scene(draw, game_state)
|
| 185 |
-
elif neighborhood == 'palermo':
|
| 186 |
-
self.draw_palermo_scene(draw, game_state)
|
| 187 |
-
elif neighborhood == 'san_telmo':
|
| 188 |
-
self.draw_san_telmo_scene(draw, game_state)
|
| 189 |
-
|
| 190 |
-
def draw_villa_scene(self, draw, game_state):
|
| 191 |
-
"""Villa 31: Casas coloridas, solidaridad, creatividad"""
|
| 192 |
-
# Casas de chapa coloridas
|
| 193 |
-
colors = [(255, 100, 100), (100, 255, 100), (100, 100, 255), (255, 255, 100)]
|
| 194 |
-
for i in range(6):
|
| 195 |
-
x = 50 + i * 120
|
| 196 |
-
y = 350 + random.randint(-30, 30)
|
| 197 |
-
color = random.choice(colors)
|
| 198 |
|
| 199 |
-
|
| 200 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
|
| 202 |
-
|
| 203 |
-
draw.polygon([(x-5, y), (x+40, y-20), (x+85, y)],
|
| 204 |
-
fill=(150, 150, 150), outline=(100, 100, 100))
|
| 205 |
|
| 206 |
-
#
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
draw.rectangle([300, 480, 310, 520], outline=(255, 255, 255), width=3)
|
| 214 |
-
draw.rectangle([590, 480, 600, 520], outline=(255, 255, 255), width=3)
|
| 215 |
-
|
| 216 |
-
def draw_community_life(self, draw, game_state):
|
| 217 |
-
"""Dibuja la vida comunitaria argentina"""
|
| 218 |
-
# Ronda de mate
|
| 219 |
-
mate_circle_x, mate_circle_y = 200, 500
|
| 220 |
-
for i in range(5):
|
| 221 |
-
angle = i * (math.pi * 2 / 5)
|
| 222 |
-
x = mate_circle_x + 40 * math.cos(angle)
|
| 223 |
-
y = mate_circle_y + 40 * math.sin(angle)
|
| 224 |
|
| 225 |
-
#
|
| 226 |
-
|
|
|
|
| 227 |
|
| 228 |
-
|
| 229 |
-
if i == 0:
|
| 230 |
-
draw.ellipse([x-3, y-5, x+3, y+2], fill=(100, 50, 0), outline=(150, 100, 50))
|
| 231 |
-
|
| 232 |
-
# Parrilla/asado
|
| 233 |
-
if game_state.get('day_time') == 'tarde':
|
| 234 |
-
asado_x, asado_y = 700, 500
|
| 235 |
-
draw.rectangle([asado_x, asado_y, asado_x+60, asado_y+30],
|
| 236 |
-
fill=(50, 50, 50), outline=(100, 100, 100))
|
| 237 |
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
smoke_x = asado_x + 30 + random.randint(-10, 10)
|
| 242 |
-
draw.ellipse([smoke_x-5, smoke_y-5, smoke_x+5, smoke_y+5],
|
| 243 |
-
fill=(200, 200, 200, 100))
|
| 244 |
-
|
| 245 |
-
# Chicos jugando
|
| 246 |
-
for i in range(3):
|
| 247 |
-
child_x = 400 + i*50 + random.randint(-20, 20)
|
| 248 |
-
child_y = 470 + random.randint(-10, 10)
|
| 249 |
-
draw.ellipse([child_x-5, child_y-10, child_x+5, child_y+5],
|
| 250 |
-
fill=(180, 140, 100), outline=(120, 100, 80))
|
| 251 |
-
|
| 252 |
-
# Pelota de fútbol
|
| 253 |
-
ball_x = 450 + 20 * math.sin(time.time() * 2)
|
| 254 |
-
draw.ellipse([ball_x-8, 485, ball_x+8, 501],
|
| 255 |
-
fill=(255, 255, 255), outline=(0, 0, 0), width=2)
|
| 256 |
|
| 257 |
-
def
|
| 258 |
-
"""
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
|
| 274 |
-
|
| 275 |
-
resource_names = {
|
| 276 |
-
'pesos': '💰 Pesos', 'dolares': '💵 Dólares', 'comida': '🍖 Comida',
|
| 277 |
-
'esperanza': '💪 Esperanza', 'solidaridad': '🤝 Solidaridad', 'trabajo': '🔨 Trabajo'
|
| 278 |
-
}
|
| 279 |
-
# draw.text([210, y_offset], f"{resource_names.get(resource, resource)}: {value}", fill=(255, 255, 255))
|
| 280 |
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
# Indicador de crisis
|
| 284 |
-
crisis_intensity = game_state.get('crisis_level', 1)
|
| 285 |
-
crisis_color = (255, 255 - crisis_intensity * 50, 255 - crisis_intensity * 50)
|
| 286 |
-
draw.rectangle([320, 10, 500, 40], fill=crisis_color, outline=(255, 255, 255))
|
| 287 |
-
|
| 288 |
-
# Día y situación
|
| 289 |
-
day = game_state.get('day', 1)
|
| 290 |
-
draw.rectangle([320, 50, 500, 80], fill=(20, 20, 20), outline=(100, 100, 100))
|
| 291 |
|
| 292 |
-
def
|
| 293 |
-
"""
|
| 294 |
-
if
|
| 295 |
-
return
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
|
| 301 |
-
class
|
|
|
|
| 302 |
def __init__(self):
|
|
|
|
| 303 |
self.engine = ArgentinaGameEngine()
|
| 304 |
self.renderer = GameRenderer()
|
| 305 |
-
self.
|
| 306 |
-
self.neighborhood_system = NeighborhoodSystem()
|
| 307 |
|
| 308 |
-
# Estado del juego
|
| 309 |
self.game_state = {
|
| 310 |
'resources': self.engine.resources.copy(),
|
| 311 |
'day': 1,
|
| 312 |
'current_neighborhood': 'villa_31',
|
| 313 |
'crisis_level': 1,
|
| 314 |
-
'
|
| 315 |
-
'
|
|
|
|
|
|
|
| 316 |
}
|
| 317 |
|
| 318 |
-
#
|
| 319 |
-
self.
|
| 320 |
-
self.
|
|
|
|
| 321 |
|
| 322 |
-
def
|
| 323 |
-
"""Procesa
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
if action == "trabajar":
|
| 325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
elif action == "ayudar_vecinos":
|
| 327 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
elif action == "buscar_rebusque":
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
elif action == "hacer_asado":
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
|
| 349 |
-
|
|
|
|
|
|
|
| 350 |
|
| 351 |
-
#
|
| 352 |
-
|
| 353 |
|
| 354 |
-
|
| 355 |
-
|
|
|
|
| 356 |
|
| 357 |
-
#
|
| 358 |
-
if
|
| 359 |
-
self.engine.player.stress_level += 5
|
| 360 |
-
self.game_state['resources']['esperanza'] -= 5
|
| 361 |
|
| 362 |
-
return
|
| 363 |
|
| 364 |
-
def
|
| 365 |
-
"""
|
| 366 |
-
|
| 367 |
-
self.game_state['
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
# Costo en recursos pero ganancia en red social
|
| 371 |
-
self.game_state['resources']['comida'] -= 10
|
| 372 |
-
self.game_state['resources']['pesos'] -= 200
|
| 373 |
-
|
| 374 |
-
# Chance de reciprocidad
|
| 375 |
-
if random.random() < 0.3:
|
| 376 |
-
favor_returned = random.choice([
|
| 377 |
-
"Te devolvieron un favor con trabajo",
|
| 378 |
-
"Te invitaron a un asado",
|
| 379 |
-
"Te prestaron herramientas",
|
| 380 |
-
"Te cuidaron a los chicos"
|
| 381 |
-
])
|
| 382 |
-
self.game_state['resources']['trabajo'] += 15
|
| 383 |
-
return self.advance_day(), f"Ayudaste a tus vecinos. {favor_returned}."
|
| 384 |
-
|
| 385 |
-
return self.advance_day(), f"Ayudaste a tus vecinos. Ganaste {solidarity_gain} puntos de solidaridad."
|
| 386 |
|
| 387 |
-
def
|
| 388 |
-
"""
|
| 389 |
-
rebusque_skill = self.engine.player.skills['rebusque']
|
| 390 |
-
success_chance = rebusque_skill / 100
|
| 391 |
-
|
| 392 |
-
if random.random() < success_chance:
|
| 393 |
-
rebusque_options = [
|
| 394 |
-
"Vendiste empanadas caseras",
|
| 395 |
-
"Arreglaste celulares",
|
| 396 |
-
"Hiciste changas de albañilería",
|
| 397 |
-
"Vendiste en la feria",
|
| 398 |
-
"Diste clases particulares"
|
| 399 |
-
]
|
| 400 |
-
|
| 401 |
-
chosen_rebusque = random.choice(rebusque_options)
|
| 402 |
-
income = random.randint(500, 2000)
|
| 403 |
-
self.game_state['resources']['pesos'] += income
|
| 404 |
-
self.engine.player.skills['rebusque'] += 2
|
| 405 |
-
|
| 406 |
-
return self.advance_day(), f"{chosen_rebusque}. Ganaste ${income} pesos!"
|
| 407 |
-
else:
|
| 408 |
-
self.game_state['resources']['esperanza'] -= 10
|
| 409 |
-
return self.advance_day(), "El rebusque no funcionó hoy. Seguí intentando."
|
| 410 |
-
|
| 411 |
-
def make_asado(self):
|
| 412 |
-
"""Hacer asado - tradición argentina que mejora la moral"""
|
| 413 |
-
if self.game_state['resources']['pesos'] >= 1500:
|
| 414 |
-
self.game_state['resources']['pesos'] -= 1500
|
| 415 |
-
self.game_state['resources']['solidaridad'] += 25
|
| 416 |
-
self.game_state['resources']['esperanza'] += 20
|
| 417 |
-
|
| 418 |
-
# Bonus de comunidad
|
| 419 |
-
self.engine.player.community_reputation += 10
|
| 420 |
-
|
| 421 |
-
return self.advance_day(), "Hiciste un asado bárbaro. Todo el barrio vino, mejoró la moral general!"
|
| 422 |
-
else:
|
| 423 |
-
return self.advance_day(), "No tenés suficiente plata para el asado. Seguí juntando."
|
| 424 |
-
|
| 425 |
-
def advance_day(self):
|
| 426 |
-
"""Avanza el día con eventos dinámicos"""
|
| 427 |
self.game_state['day'] += 1
|
| 428 |
|
| 429 |
-
# Inflación
|
| 430 |
-
|
|
|
|
| 431 |
|
| 432 |
-
#
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
'pesos': random.randint(100, 500)
|
| 436 |
-
}
|
| 437 |
|
| 438 |
-
|
| 439 |
-
|
|
|
|
|
|
|
|
|
|
| 440 |
self.game_state['resources'][resource] = max(0, self.game_state['resources'][resource])
|
| 441 |
|
| 442 |
-
# Eventos
|
| 443 |
if random.random() < 0.2:
|
| 444 |
-
self.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
|
| 446 |
-
#
|
| 447 |
-
|
| 448 |
-
|
|
|
|
|
|
|
| 449 |
|
| 450 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
|
| 452 |
-
def
|
| 453 |
-
"""Eventos
|
| 454 |
-
|
| 455 |
-
"
|
| 456 |
-
"
|
| 457 |
-
"
|
| 458 |
-
"
|
| 459 |
-
"
|
| 460 |
-
"Apareció laburo extra en el barrio"
|
| 461 |
]
|
| 462 |
|
| 463 |
-
event = random.choice(
|
| 464 |
-
self.
|
| 465 |
-
|
| 466 |
-
#
|
| 467 |
-
if "
|
| 468 |
-
self.game_state['resources']['
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
self.game_state['resources']['pesos'] +=
|
| 473 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 474 |
|
| 475 |
-
def
|
| 476 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
return {
|
| 478 |
"📅 Día": self.game_state['day'],
|
| 479 |
"🏠 Barrio": self.game_state['current_neighborhood'].replace('_', ' ').title(),
|
|
@@ -482,29 +441,303 @@ class CrisisYCorajeGame:
|
|
| 482 |
"🍖 Comida": f"{self.game_state['resources']['comida']:.0f}%",
|
| 483 |
"💪 Esperanza": f"{self.game_state['resources']['esperanza']:.0f}%",
|
| 484 |
"🤝 Solidaridad": f"{self.game_state['resources']['solidaridad']:.0f}%",
|
| 485 |
-
"📈 Inflación": f"{self.
|
| 486 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 488 |
|
| 489 |
-
def
|
| 490 |
-
|
|
|
|
| 491 |
|
| 492 |
with gr.Blocks(
|
| 493 |
-
title="🇦🇷 CRISIS Y CORAJE -
|
| 494 |
-
theme=gr.themes.Soft()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 495 |
) as interface:
|
| 496 |
|
| 497 |
gr.HTML("""
|
| 498 |
-
<div
|
| 499 |
-
|
| 500 |
-
<h1 style="color: white; font-size: 3em; text-shadow: 2px 2px 4px rgba(0,0,0,0.7); margin: 0;">
|
| 501 |
🇦🇷 CRISIS Y CORAJE
|
| 502 |
</h1>
|
| 503 |
-
<h2 style="
|
| 504 |
-
|
| 505 |
</h2>
|
| 506 |
-
<p style="
|
| 507 |
-
|
| 508 |
</p>
|
| 509 |
</div>
|
| 510 |
""")
|
|
@@ -512,81 +745,94 @@ def create_revolutionary_interface():
|
|
| 512 |
with gr.Row():
|
| 513 |
with gr.Column(scale=2):
|
| 514 |
game_display = gr.Image(
|
| 515 |
-
label="🌆 Tu Barrio
|
| 516 |
width=900,
|
| 517 |
height=600
|
| 518 |
)
|
| 519 |
|
| 520 |
-
gr.Markdown("### 🎮 ACCIONES
|
| 521 |
with gr.Row():
|
| 522 |
-
work_btn = gr.Button("💼 TRABAJAR", variant="primary",
|
| 523 |
-
help_btn = gr.Button("🤝 AYUDAR VECINOS", variant="secondary",
|
| 524 |
-
rebusque_btn = gr.Button("💡 REBUSQUE", variant="stop",
|
| 525 |
|
| 526 |
with gr.Row():
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
asado_btn = gr.Button("🔥 HACER ASADO", variant="stop", size="lg")
|
| 530 |
|
| 531 |
with gr.Column(scale=1):
|
| 532 |
gr.Markdown("### 📊 ESTADO DE SUPERVIVENCIA")
|
| 533 |
status_display = gr.JSON(
|
| 534 |
-
label="
|
| 535 |
-
value=game.
|
| 536 |
)
|
| 537 |
|
| 538 |
-
gr.Markdown("###
|
| 539 |
news_display = gr.Textbox(
|
| 540 |
-
value="Bienvenido a
|
| 541 |
-
label="📺 Canal
|
| 542 |
lines=4,
|
| 543 |
interactive=False
|
| 544 |
)
|
| 545 |
|
| 546 |
-
gr.Markdown("###
|
| 547 |
-
gr.HTML("""
|
| 548 |
-
<div style="background: #2d3436; color: white; padding: 15px; border-radius: 10px;">
|
| 549 |
-
<p><strong
|
| 550 |
-
<
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
<li
|
| 554 |
-
<li
|
| 555 |
-
<li
|
|
|
|
| 556 |
</ul>
|
| 557 |
-
<p><em>¡Sobreviví con creatividad argentina!</em></p>
|
| 558 |
</div>
|
| 559 |
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 560 |
|
| 561 |
-
#
|
| 562 |
-
def handle_action(action_name):
|
| 563 |
-
frame, message = game.process_action(action_name)
|
| 564 |
-
status = game.get_game_status()
|
| 565 |
-
return frame, status, message
|
| 566 |
-
|
| 567 |
-
work_btn.click(fn=lambda: handle_action("trabajar"),
|
| 568 |
-
outputs=[game_display, status_display, news_display])
|
| 569 |
-
help_btn.click(fn=lambda: handle_action("ayudar_vecinos"),
|
| 570 |
-
outputs=[game_display, status_display, news_display])
|
| 571 |
-
rebusque_btn.click(fn=lambda: handle_action("buscar_rebusque"),
|
| 572 |
-
outputs=[game_display, status_display, news_display])
|
| 573 |
-
organize_btn.click(fn=lambda: handle_action("organizar_comunitario"),
|
| 574 |
-
outputs=[game_display, status_display, news_display])
|
| 575 |
-
save_btn.click(fn=lambda: handle_action("ahorrar_dolares"),
|
| 576 |
-
outputs=[game_display, status_display, news_display])
|
| 577 |
-
asado_btn.click(fn=lambda: handle_action("hacer_asado"),
|
| 578 |
-
outputs=[game_display, status_display, news_display])
|
| 579 |
-
|
| 580 |
-
# Inicialización
|
| 581 |
interface.load(
|
| 582 |
-
fn=lambda: (
|
| 583 |
-
|
| 584 |
-
|
|
|
|
|
|
|
| 585 |
outputs=[game_display, status_display, news_display]
|
| 586 |
)
|
| 587 |
|
| 588 |
return interface
|
| 589 |
|
| 590 |
if __name__ == "__main__":
|
| 591 |
-
interface =
|
| 592 |
-
interface.launch(share=True, show_error=True)
|
|
|
|
| 1 |
+
# app.py - CRISIS Y CORAJE CON IA NARRATIVA
|
| 2 |
import gradio as gr
|
| 3 |
import time
|
| 4 |
import math
|
| 5 |
import random
|
| 6 |
+
import os
|
| 7 |
from PIL import Image, ImageDraw
|
| 8 |
+
from groq import Groq
|
| 9 |
+
import json
|
| 10 |
|
| 11 |
+
class AIDialogueSystem:
|
| 12 |
+
"""Sistema de diálogos con IA usando Groq"""
|
| 13 |
def __init__(self):
|
| 14 |
+
try:
|
| 15 |
+
self.client = Groq(api_key=os.getenv("GROQ_API_KEY"))
|
| 16 |
+
self.context_memory = []
|
| 17 |
+
self.character_personalities = self._load_character_personalities()
|
| 18 |
+
self.ai_enabled = True
|
| 19 |
+
except Exception as e:
|
| 20 |
+
print(f"Groq no disponible: {e}")
|
| 21 |
+
self.ai_enabled = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
+
def _load_character_personalities(self):
|
| 24 |
+
return {
|
| 25 |
+
'narrator': {
|
| 26 |
+
'role': 'Narrador porteño con experiencia en crisis argentinas',
|
| 27 |
+
'style': 'Realista, empático, usa lunfardo y jerga argentina',
|
| 28 |
+
'knowledge': 'Historia argentina reciente, crisis económicas, cultura popular'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
},
|
| 30 |
+
'dona_rosa': {
|
| 31 |
+
'role': 'Vecina mayor, sabia del barrio',
|
| 32 |
+
'style': 'Maternal, directa, llena de consejos prácticos',
|
| 33 |
+
'knowledge': 'Supervivencia en crisis, redes vecinales, cocina criolla'
|
| 34 |
},
|
| 35 |
+
'el_checo': {
|
| 36 |
+
'role': 'Parrillero del barrio, organizador social',
|
| 37 |
+
'style': 'Carismático, solidario, siempre con una anécdota',
|
| 38 |
+
'knowledge': 'Asados, organización comunitaria, fútbol'
|
| 39 |
},
|
| 40 |
+
'comerciante': {
|
| 41 |
+
'role': 'Dueño del almacén, conoce la economía barrial',
|
| 42 |
+
'style': 'Pragmático, conoce precios y mercado, observador',
|
| 43 |
+
'knowledge': 'Economía local, precios, crédito informal'
|
| 44 |
}
|
| 45 |
}
|
| 46 |
|
| 47 |
+
def generate_contextual_dialogue(self, character, situation, player_action, game_state):
|
| 48 |
+
"""Genera diálogo contextual usando IA"""
|
| 49 |
+
if not self.ai_enabled:
|
| 50 |
+
return self._fallback_dialogue(character, situation)
|
| 51 |
+
|
| 52 |
+
try:
|
| 53 |
+
personality = self.character_personalities.get(character, self.character_personalities['narrator'])
|
| 54 |
+
|
| 55 |
+
prompt = f"""
|
| 56 |
+
Sos {personality['role']} en un juego sobre supervivencia en Argentina durante crisis económicas.
|
| 57 |
+
Estilo: {personality['style']}
|
| 58 |
+
Conocimiento: {personality['knowledge']}
|
| 59 |
|
| 60 |
+
CONTEXTO DEL JUEGO:
|
| 61 |
+
- Día: {game_state.get('day', 1)}
|
| 62 |
+
- Barrio: {game_state.get('current_neighborhood', 'villa_31')}
|
| 63 |
+
- Recursos del jugador: {json.dumps(game_state.get('resources', {}), indent=2)}
|
| 64 |
+
- Inflación actual: {game_state.get('inflation_rate', 1.0):.2f}x
|
| 65 |
+
- Último evento: {game_state.get('last_event', 'Inicio del juego')}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
|
| 67 |
+
SITUACIÓN ACTUAL: {situation}
|
| 68 |
+
ACCIÓN DEL JUGADOR: {player_action}
|
| 69 |
+
|
| 70 |
+
Recordá que estás en Argentina, usá expresiones auténticas. Sé empático pero realista sobre las crisis.
|
| 71 |
+
Mencioná detalles específicos argentinos cuando sea relevante.
|
| 72 |
+
Máximo 2-3 oraciones, directo y conversacional.
|
| 73 |
+
|
| 74 |
+
Respuesta como {personality['role']}:
|
| 75 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
+
response = self.client.chat.completions.create(
|
| 78 |
+
messages=[
|
| 79 |
+
{"role": "system", "content": "Sos un personaje argentino en un juego de supervivencia ambientado en Argentina. Usá lenguaje auténtico porteno y mantené el tono realista pero esperanzador."},
|
| 80 |
+
{"role": "user", "content": prompt}
|
| 81 |
+
],
|
| 82 |
+
model="llama-3.1-8b-instant",
|
| 83 |
+
temperature=0.8,
|
| 84 |
+
max_tokens=150
|
| 85 |
+
)
|
| 86 |
|
| 87 |
+
dialogue = response.choices[0].message.content.strip()
|
|
|
|
|
|
|
| 88 |
|
| 89 |
+
# Guardar en memoria para contexto futuro
|
| 90 |
+
self.context_memory.append({
|
| 91 |
+
'character': character,
|
| 92 |
+
'situation': situation,
|
| 93 |
+
'dialogue': dialogue,
|
| 94 |
+
'game_day': game_state.get('day', 1)
|
| 95 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
+
# Mantener solo los últimos 10 diálogos
|
| 98 |
+
if len(self.context_memory) > 10:
|
| 99 |
+
self.context_memory.pop(0)
|
| 100 |
|
| 101 |
+
return dialogue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
+
except Exception as e:
|
| 104 |
+
print(f"Error en IA: {e}")
|
| 105 |
+
return self._fallback_dialogue(character, situation)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
+
def generate_neighborhood_news(self, game_state):
|
| 108 |
+
"""Genera noticias dinámicas del barrio usando IA"""
|
| 109 |
+
if not self.ai_enabled:
|
| 110 |
+
return "El barrio sigue adelante con solidaridad y creatividad."
|
| 111 |
+
|
| 112 |
+
try:
|
| 113 |
+
prompt = f"""
|
| 114 |
+
Sos un periodista local que cubre noticias del barrio en Argentina.
|
| 115 |
+
Generá una noticia breve (1-2 líneas) basada en:
|
| 116 |
+
|
| 117 |
+
Estado actual:
|
| 118 |
+
- Día {game_state.get('day', 1)}
|
| 119 |
+
- Barrio: {game_state.get('current_neighborhood', 'villa_31')}
|
| 120 |
+
- Nivel de crisis: {game_state.get('crisis_level', 1)}/5
|
| 121 |
+
- Solidaridad comunitaria: {game_state.get('resources', {}).get('solidaridad', 50)}%
|
| 122 |
+
|
| 123 |
+
Hacé que sea realista pero no deprimente. Enfocate en la resistencia y creatividad argentina.
|
| 124 |
+
Incluí detalles específicos del barrio si es relevante.
|
| 125 |
+
|
| 126 |
+
Noticia breve:
|
| 127 |
+
"""
|
| 128 |
|
| 129 |
+
response = self.client.chat.completions.create(
|
| 130 |
+
messages=[
|
| 131 |
+
{"role": "system", "content": "Sos un periodista barrial argentino que ve el lado humano de las crisis."},
|
| 132 |
+
{"role": "user", "content": prompt}
|
| 133 |
+
],
|
| 134 |
+
model="llama-3.1-8b-instant",
|
| 135 |
+
temperature=0.7,
|
| 136 |
+
max_tokens=100
|
| 137 |
+
)
|
| 138 |
|
| 139 |
+
return response.choices[0].message.content.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
|
| 141 |
+
except Exception as e:
|
| 142 |
+
return "Los vecinos siguen organizándose para enfrentar juntos los desafíos."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
+
def generate_crisis_commentary(self, crisis_level, resources):
|
| 145 |
+
"""Genera comentario sobre la situación económica"""
|
| 146 |
+
if not self.ai_enabled:
|
| 147 |
+
return "La situación económica sigue desafiante pero manejable."
|
| 148 |
+
|
| 149 |
+
try:
|
| 150 |
+
prompt = f"""
|
| 151 |
+
Sos un analista económico argentino con sensibilidad social.
|
| 152 |
+
Comentá brevemente la situación basada en:
|
| 153 |
+
- Nivel de crisis: {crisis_level}/5
|
| 154 |
+
- Recursos familiares: pesos {resources.get('pesos', 0)}, dólares {resources.get('dolares', 0)}
|
| 155 |
+
- Nivel de esperanza: {resources.get('esperanza', 50)}%
|
| 156 |
+
|
| 157 |
+
Sé realista pero no catastrófico. Mencioná estrategias argentinas típicas.
|
| 158 |
+
Máximo 2 líneas.
|
| 159 |
+
|
| 160 |
+
Análisis:
|
| 161 |
+
"""
|
| 162 |
+
response = self.client.chat.completions.create(
|
| 163 |
+
messages=[
|
| 164 |
+
{"role": "system", "content": "Sos un economista argentino que entiende las estrategias populares de supervivencia."},
|
| 165 |
+
{"role": "user", "content": prompt}
|
| 166 |
+
],
|
| 167 |
+
model="llama-3.1-8b-instant",
|
| 168 |
+
temperature=0.6,
|
| 169 |
+
max_tokens=80
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
return response.choices[0].message.content.strip()
|
| 173 |
+
|
| 174 |
+
except Exception as e:
|
| 175 |
+
return "El rebusque y la solidaridad siguen siendo clave en estos momentos."
|
| 176 |
+
|
| 177 |
+
def _fallback_dialogue(self, character, situation):
|
| 178 |
+
"""Diálogos de respaldo si la IA no está disponible"""
|
| 179 |
+
fallbacks = {
|
| 180 |
+
'narrator': [
|
| 181 |
+
"La vida en Argentina nunca es fácil, pero siempre hay una manera de salir adelante.",
|
| 182 |
+
"En el barrio todos se conocen, y eso marca la diferencia en tiempos difíciles.",
|
| 183 |
+
"El rebusque argentino nunca falla, che."
|
| 184 |
+
],
|
| 185 |
+
'dona_rosa': [
|
| 186 |
+
"Mirá, pibe, en mis años he visto de todo. Esto también se supera.",
|
| 187 |
+
"¿Viste? Siempre es mejor cuando nos ayudamos entre vecinos.",
|
| 188 |
+
"Con un buen guiso y buena onda, todo se arregla."
|
| 189 |
+
],
|
| 190 |
+
'el_checo': [
|
| 191 |
+
"¿Vamos haciendo un asadito para levantar el ánimo?",
|
| 192 |
+
"En la cancha y en la vida, lo que importa es el equipo.",
|
| 193 |
+
"Acá en el barrio nunca falta una mano amiga."
|
| 194 |
+
]
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
character_lines = fallbacks.get(character, fallbacks['narrator'])
|
| 198 |
+
return random.choice(character_lines)
|
| 199 |
|
| 200 |
+
class EnhancedArgentinaGame:
|
| 201 |
+
"""Versión mejorada del juego con IA narrativa"""
|
| 202 |
def __init__(self):
|
| 203 |
+
# Sistema base
|
| 204 |
self.engine = ArgentinaGameEngine()
|
| 205 |
self.renderer = GameRenderer()
|
| 206 |
+
self.ai_dialogue = AIDialogueSystem()
|
|
|
|
| 207 |
|
| 208 |
+
# Estado expandido del juego
|
| 209 |
self.game_state = {
|
| 210 |
'resources': self.engine.resources.copy(),
|
| 211 |
'day': 1,
|
| 212 |
'current_neighborhood': 'villa_31',
|
| 213 |
'crisis_level': 1,
|
| 214 |
+
'inflation_rate': 1.0,
|
| 215 |
+
'last_event': 'Inicio del juego',
|
| 216 |
+
'current_character': None,
|
| 217 |
+
'story_progress': 0
|
| 218 |
}
|
| 219 |
|
| 220 |
+
# Sistema de eventos narrativos
|
| 221 |
+
self.story_events = []
|
| 222 |
+
self.character_interactions = 0
|
| 223 |
+
self.neighborhood_reputation = 50
|
| 224 |
|
| 225 |
+
def process_enhanced_action(self, action):
|
| 226 |
+
"""Procesa acciones con narrativa IA mejorada"""
|
| 227 |
+
# Acción base
|
| 228 |
+
old_resources = self.game_state['resources'].copy()
|
| 229 |
+
message = ""
|
| 230 |
+
character_dialogue = ""
|
| 231 |
+
|
| 232 |
if action == "trabajar":
|
| 233 |
+
income = self._calculate_work_income()
|
| 234 |
+
self.game_state['resources']['pesos'] += income
|
| 235 |
+
self.game_state['resources']['trabajo'] = min(100, self.game_state['resources']['trabajo'] + 10)
|
| 236 |
+
|
| 237 |
+
# Generar diálogo contextual
|
| 238 |
+
situation = f"El jugador fue a trabajar y ganó ${income} pesos en un día de crisis nivel {self.game_state['crisis_level']}"
|
| 239 |
+
character_dialogue = self.ai_dialogue.generate_contextual_dialogue(
|
| 240 |
+
'narrator', situation, 'trabajar', self.game_state
|
| 241 |
+
)
|
| 242 |
+
message = f"Trabajaste y ganaste ${income} pesos."
|
| 243 |
+
|
| 244 |
elif action == "ayudar_vecinos":
|
| 245 |
+
solidarity_gain = random.randint(15, 25)
|
| 246 |
+
self.game_state['resources']['solidaridad'] += solidarity_gain
|
| 247 |
+
self.game_state['resources']['esperanza'] += 10
|
| 248 |
+
self.game_state['resources']['pesos'] -= 200
|
| 249 |
+
self.character_interactions += 1
|
| 250 |
+
|
| 251 |
+
# Elegir personaje aleatorio para interactuar
|
| 252 |
+
characters = ['dona_rosa', 'el_checo', 'comerciante']
|
| 253 |
+
chosen_character = random.choice(characters)
|
| 254 |
+
self.game_state['current_character'] = chosen_character
|
| 255 |
+
|
| 256 |
+
situation = f"El jugador ayudó a los vecinos, gastando $200 pero ganando {solidarity_gain} puntos de solidaridad"
|
| 257 |
+
character_dialogue = self.ai_dialogue.generate_contextual_dialogue(
|
| 258 |
+
chosen_character, situation, 'ayudar_vecinos', self.game_state
|
| 259 |
+
)
|
| 260 |
+
message = f"Ayudaste a tus vecinos. +{solidarity_gain} solidaridad"
|
| 261 |
+
|
| 262 |
elif action == "buscar_rebusque":
|
| 263 |
+
rebusque_success = random.random() < 0.7
|
| 264 |
+
if rebusque_success:
|
| 265 |
+
income = random.randint(800, 2000)
|
| 266 |
+
self.game_state['resources']['pesos'] += income
|
| 267 |
+
situation = f"El rebusque del jugador fue exitoso, ganando ${income} pesos"
|
| 268 |
+
message = f"¡El rebusque funcionó! Ganaste ${income}"
|
| 269 |
+
else:
|
| 270 |
+
self.game_state['resources']['esperanza'] -= 8
|
| 271 |
+
situation = f"El rebusque del jugador no funcionó, perdiendo esperanza"
|
| 272 |
+
message = "El rebusque no salió bien hoy"
|
| 273 |
+
|
| 274 |
+
character_dialogue = self.ai_dialogue.generate_contextual_dialogue(
|
| 275 |
+
'narrator', situation, 'buscar_rebusque', self.game_state
|
| 276 |
+
)
|
| 277 |
+
|
| 278 |
elif action == "hacer_asado":
|
| 279 |
+
asado_cost = 1500
|
| 280 |
+
if self.game_state['resources']['pesos'] >= asado_cost:
|
| 281 |
+
self.game_state['resources']['pesos'] -= asado_cost
|
| 282 |
+
self.game_state['resources']['solidaridad'] += 25
|
| 283 |
+
self.game_state['resources']['esperanza'] += 20
|
| 284 |
+
self.neighborhood_reputation += 15
|
| 285 |
+
|
| 286 |
+
situation = f"El jugador organizó un asado que costó ${asado_cost}, mejorando la moral del barrio"
|
| 287 |
+
character_dialogue = self.ai_dialogue.generate_contextual_dialogue(
|
| 288 |
+
'el_checo', situation, 'hacer_asado', self.game_state
|
| 289 |
+
)
|
| 290 |
+
message = f"¡Asado bárbaro! Toda la cuadra vino."
|
| 291 |
+
else:
|
| 292 |
+
situation = "El jugador quiso hacer asado pero no tenía suficiente plata"
|
| 293 |
+
character_dialogue = self.ai_dialogue.generate_contextual_dialogue(
|
| 294 |
+
'dona_rosa', situation, 'hacer_asado', self.game_state
|
| 295 |
+
)
|
| 296 |
+
message = "No tenés suficiente plata para el asado, pibe."
|
| 297 |
+
|
| 298 |
+
elif action == "ahorrar_dolares":
|
| 299 |
+
peso_cost = 1000
|
| 300 |
+
if self.game_state['resources']['pesos'] >= peso_cost:
|
| 301 |
+
self.game_state['resources']['pesos'] -= peso_cost
|
| 302 |
+
self.game_state['resources']['dolares'] += 1
|
| 303 |
+
|
| 304 |
+
situation = f"El jugador compró US$1 por ${peso_cost} pesos como protección contra inflación"
|
| 305 |
+
character_dialogue = self.ai_dialogue.generate_contextual_dialogue(
|
| 306 |
+
'comerciante', situation, 'ahorrar_dolares', self.game_state
|
| 307 |
+
)
|
| 308 |
+
message = "Compraste US$1. Protección contra inflación."
|
| 309 |
+
else:
|
| 310 |
+
message = "No tenés suficientes pesos para comprar dólares."
|
| 311 |
+
character_dialogue = "Los dólares están carísimos, pero hay que cuidarse de la inflación."
|
| 312 |
|
| 313 |
+
# Sistema de eventos especiales basados en progreso
|
| 314 |
+
if self.game_state['day'] % 7 == 0:
|
| 315 |
+
self._trigger_weekly_event()
|
| 316 |
|
| 317 |
+
# Avanzar día con narrativa mejorada
|
| 318 |
+
self._advance_enhanced_day()
|
| 319 |
|
| 320 |
+
# Generar frame y status
|
| 321 |
+
frame = self.renderer.render_argentina_frame(self.game_state)
|
| 322 |
+
status = self.get_enhanced_status()
|
| 323 |
|
| 324 |
+
# Combinar mensaje de acción con diálogo de personaje
|
| 325 |
+
full_message = f"{message}\n\n💬 {character_dialogue}" if character_dialogue else message
|
|
|
|
|
|
|
| 326 |
|
| 327 |
+
return frame, status, full_message
|
| 328 |
|
| 329 |
+
def _calculate_work_income(self):
|
| 330 |
+
"""Calcula ingresos de trabajo según crisis y profesión"""
|
| 331 |
+
base_income = random.randint(1500, 3500)
|
| 332 |
+
crisis_modifier = max(0.5, 1.5 - (self.game_state['crisis_level'] * 0.2))
|
| 333 |
+
return int(base_income * crisis_modifier)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
|
| 335 |
+
def _advance_enhanced_day(self):
|
| 336 |
+
"""Avanza el día con eventos narrativos"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
self.game_state['day'] += 1
|
| 338 |
|
| 339 |
+
# Inflación dinámica
|
| 340 |
+
inflation_increase = random.uniform(0.01, 0.04) * self.game_state['crisis_level']
|
| 341 |
+
self.game_state['inflation_rate'] += inflation_increase
|
| 342 |
|
| 343 |
+
# Gastos diarios ajustados por inflación
|
| 344 |
+
daily_food_cost = random.randint(5, 12) * self.game_state['inflation_rate']
|
| 345 |
+
daily_living_cost = random.randint(200, 500) * self.game_state['inflation_rate']
|
|
|
|
|
|
|
| 346 |
|
| 347 |
+
self.game_state['resources']['comida'] -= daily_food_cost
|
| 348 |
+
self.game_state['resources']['pesos'] -= daily_living_cost
|
| 349 |
+
|
| 350 |
+
# Mantener recursos en rango válido
|
| 351 |
+
for resource in self.game_state['resources']:
|
| 352 |
self.game_state['resources'][resource] = max(0, self.game_state['resources'][resource])
|
| 353 |
|
| 354 |
+
# Eventos aleatorios con narrativa IA
|
| 355 |
if random.random() < 0.2:
|
| 356 |
+
self._trigger_ai_event()
|
| 357 |
+
|
| 358 |
+
# Actualizar nivel de crisis basado en condiciones
|
| 359 |
+
self._update_crisis_level()
|
| 360 |
+
|
| 361 |
+
def _trigger_ai_event(self):
|
| 362 |
+
"""Eventos aleatorios con narrativa generada por IA"""
|
| 363 |
+
base_events = [
|
| 364 |
+
("Se cortó la luz", "solidaridad", 15, "Los vecinos se organizaron para afrontar el corte"),
|
| 365 |
+
("Llegó mercadería al almacén", "comida", 25, "Doña Rosa te guardó algunos productos"),
|
| 366 |
+
("Paro de transporte", "trabajo", -20, "No pudiste llegar al trabajo por el paro"),
|
| 367 |
+
("Feria en la plaza", "pesos", 600, "Vendiste algunas cosas en la feria del barrio"),
|
| 368 |
+
("Reunión vecinal", "solidaridad", 20, "Se organizó una asamblea para mejorar el barrio")
|
| 369 |
+
]
|
| 370 |
+
|
| 371 |
+
event_name, resource, change, context = random.choice(base_events)
|
| 372 |
+
self.game_state['resources'][resource] += change
|
| 373 |
+
self.game_state['last_event'] = event_name
|
| 374 |
|
| 375 |
+
# Generar comentario IA sobre el evento
|
| 376 |
+
situation = f"Ocurrió el evento: {event_name}. {context}. Cambió {resource} en {change} puntos."
|
| 377 |
+
ai_comment = self.ai_dialogue.generate_contextual_dialogue(
|
| 378 |
+
'narrator', situation, 'evento_aleatorio', self.game_state
|
| 379 |
+
)
|
| 380 |
|
| 381 |
+
self.story_events.append({
|
| 382 |
+
'day': self.game_state['day'],
|
| 383 |
+
'event': event_name,
|
| 384 |
+
'comment': ai_comment
|
| 385 |
+
})
|
| 386 |
|
| 387 |
+
def _trigger_weekly_event(self):
|
| 388 |
+
"""Eventos semanales especiales con narrativa extendida"""
|
| 389 |
+
weekly_events = [
|
| 390 |
+
"Asamblea vecinal extraordinaria",
|
| 391 |
+
"Llegada de ayuda municipal",
|
| 392 |
+
"Feria gastronómica del barrio",
|
| 393 |
+
"Torneo de fútbol inter-barrios",
|
| 394 |
+
"Jornada de trabajo comunitario"
|
|
|
|
| 395 |
]
|
| 396 |
|
| 397 |
+
event = random.choice(weekly_events)
|
| 398 |
+
self.game_state['last_event'] = f"Evento Semanal: {event}"
|
| 399 |
+
|
| 400 |
+
# Efectos especiales de eventos semanales
|
| 401 |
+
if "Asamblea" in event:
|
| 402 |
+
self.game_state['resources']['solidaridad'] += 30
|
| 403 |
+
self.game_state['resources']['esperanza'] += 15
|
| 404 |
+
elif "ayuda municipal" in event:
|
| 405 |
+
self.game_state['resources']['comida'] += 40
|
| 406 |
+
self.game_state['resources']['pesos'] += 1000
|
| 407 |
+
elif "Feria gastronómica" in event:
|
| 408 |
+
self.game_state['resources']['pesos'] += random.randint(800, 2000)
|
| 409 |
+
self.game_state['resources']['solidaridad'] += 20
|
| 410 |
+
elif "Torneo" in event:
|
| 411 |
+
self.game_state['resources']['esperanza'] += 25
|
| 412 |
+
self.neighborhood_reputation += 10
|
| 413 |
+
elif "trabajo comunitario" in event:
|
| 414 |
+
self.game_state['resources']['solidaridad'] += 35
|
| 415 |
+
self.game_state['resources']['trabajo'] += 20
|
| 416 |
|
| 417 |
+
def _update_crisis_level(self):
|
| 418 |
+
"""Actualiza nivel de crisis basado en condiciones"""
|
| 419 |
+
# Crisis basada en recursos e inflación
|
| 420 |
+
avg_resources = sum(self.game_state['resources'].values()) / len(self.game_state['resources'])
|
| 421 |
+
inflation_factor = min(5, self.game_state['inflation_rate'])
|
| 422 |
+
|
| 423 |
+
if avg_resources < 30 or inflation_factor > 3:
|
| 424 |
+
self.game_state['crisis_level'] = min(5, self.game_state['crisis_level'] + 1)
|
| 425 |
+
elif avg_resources > 70 and inflation_factor < 2:
|
| 426 |
+
self.game_state['crisis_level'] = max(1, self.game_state['crisis_level'] - 1)
|
| 427 |
+
|
| 428 |
+
def get_enhanced_status(self):
|
| 429 |
+
"""Estado del juego con información narrativa"""
|
| 430 |
+
# Generar análisis económico IA
|
| 431 |
+
economic_analysis = self.ai_dialogue.generate_crisis_commentary(
|
| 432 |
+
self.game_state['crisis_level'],
|
| 433 |
+
self.game_state['resources']
|
| 434 |
+
)
|
| 435 |
+
|
| 436 |
return {
|
| 437 |
"📅 Día": self.game_state['day'],
|
| 438 |
"🏠 Barrio": self.game_state['current_neighborhood'].replace('_', ' ').title(),
|
|
|
|
| 441 |
"🍖 Comida": f"{self.game_state['resources']['comida']:.0f}%",
|
| 442 |
"💪 Esperanza": f"{self.game_state['resources']['esperanza']:.0f}%",
|
| 443 |
"🤝 Solidaridad": f"{self.game_state['resources']['solidaridad']:.0f}%",
|
| 444 |
+
"📈 Inflación": f"{self.game_state['inflation_rate']:.2f}x",
|
| 445 |
+
"🏆 Reputación": f"{self.neighborhood_reputation}/100",
|
| 446 |
+
"📊 Análisis IA": economic_analysis
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
def get_neighborhood_news(self):
|
| 450 |
+
"""Genera noticias del barrio con IA"""
|
| 451 |
+
return self.ai_dialogue.generate_neighborhood_news(self.game_state)
|
| 452 |
+
|
| 453 |
+
# Continúo con las otras clases necesarias...
|
| 454 |
+
class ArgentinaGameEngine:
|
| 455 |
+
"""Motor base del juego"""
|
| 456 |
+
def __init__(self):
|
| 457 |
+
self.resources = {
|
| 458 |
+
'pesos': 1200,
|
| 459 |
+
'dolares': 30,
|
| 460 |
+
'comida': 75,
|
| 461 |
+
'esperanza': 85,
|
| 462 |
+
'solidaridad': 80,
|
| 463 |
+
'trabajo': 65
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
class GameRenderer:
|
| 467 |
+
"""Renderizador mejorado"""
|
| 468 |
+
def __init__(self):
|
| 469 |
+
self.animation_frame = 0
|
| 470 |
+
|
| 471 |
+
def render_argentina_frame(self, game_state) -> Image.Image:
|
| 472 |
+
"""Renderiza con detalles mejorados"""
|
| 473 |
+
img = Image.new('RGB', (900, 600), (15, 25, 50))
|
| 474 |
+
draw = ImageDraw.Draw(img)
|
| 475 |
+
|
| 476 |
+
# Skyline dinámico según hora del día
|
| 477 |
+
self._draw_dynamic_skyline(draw, game_state)
|
| 478 |
+
|
| 479 |
+
# Barrio con actividad según solidaridad
|
| 480 |
+
self._draw_active_neighborhood(draw, game_state)
|
| 481 |
+
|
| 482 |
+
# Personaje con animaciones
|
| 483 |
+
self._draw_animated_character(draw, game_state)
|
| 484 |
+
|
| 485 |
+
# UI mejorada con gradientes
|
| 486 |
+
self._draw_enhanced_ui(draw, game_state)
|
| 487 |
+
|
| 488 |
+
return img
|
| 489 |
+
|
| 490 |
+
def _draw_dynamic_skyline(self, draw, game_state):
|
| 491 |
+
"""Skyline que cambia según el estado del juego"""
|
| 492 |
+
# Color del cielo según esperanza
|
| 493 |
+
hope_level = game_state.get('resources', {}).get('esperanza', 50)
|
| 494 |
+
sky_blue = min(255, int(60 + hope_level * 1.6))
|
| 495 |
+
|
| 496 |
+
# Gradiente de cielo
|
| 497 |
+
for y in range(200):
|
| 498 |
+
color_intensity = int(sky_blue * (1 - y/400))
|
| 499 |
+
draw.rectangle([0, y, 900, y+1], fill=(15, 25, color_intensity))
|
| 500 |
+
|
| 501 |
+
# Edificios con luces según recursos
|
| 502 |
+
prosperity = sum(game_state.get('resources', {}).values()) / 500
|
| 503 |
+
buildings = [
|
| 504 |
+
(100, 120, 140, 400), (160, 100, 200, 400), (220, 140, 260, 400),
|
| 505 |
+
(700, 80, 750, 350), (770, 110, 820, 350)
|
| 506 |
+
]
|
| 507 |
+
|
| 508 |
+
for building in buildings:
|
| 509 |
+
draw.rectangle(building, fill=(30, 30, 50), outline=(60, 60, 80))
|
| 510 |
+
|
| 511 |
+
# Ventanas encendidas según prosperidad
|
| 512 |
+
for window_y in range(building[1] + 20, building[3], 25):
|
| 513 |
+
for window_x in range(building[0] + 8, building[2], 18):
|
| 514 |
+
if random.random() < prosperity:
|
| 515 |
+
draw.rectangle([window_x, window_y, window_x+10, window_y+10],
|
| 516 |
+
fill=(255, 255, 180))
|
| 517 |
+
|
| 518 |
+
def _draw_active_neighborhood(self, draw, game_state):
|
| 519 |
+
"""Barrio con actividad dinámica"""
|
| 520 |
+
solidarity = game_state.get('resources', {}).get('solidaridad', 50)
|
| 521 |
+
|
| 522 |
+
# Base del neighborhood
|
| 523 |
+
draw.rectangle([0, 400, 900, 600], fill=(40, 60, 30))
|
| 524 |
+
|
| 525 |
+
# Casas con colores según solidaridad
|
| 526 |
+
house_colors = [(200, 100, 100), (100, 200, 100), (100, 100, 200), (200, 200, 100)]
|
| 527 |
+
brightness_multiplier = 0.6 + (solidarity / 200)
|
| 528 |
+
|
| 529 |
+
for i in range(6):
|
| 530 |
+
x = 60 + i * 130
|
| 531 |
+
y = 350 + random.randint(-15, 15)
|
| 532 |
+
base_color = random.choice(house_colors)
|
| 533 |
+
bright_color = tuple(int(c * brightness_multiplier) for c in base_color)
|
| 534 |
+
|
| 535 |
+
# Casa principal
|
| 536 |
+
draw.rectangle([x, y, x+70, y+70], fill=bright_color, outline=(0, 0, 0), width=2)
|
| 537 |
+
|
| 538 |
+
# Techo
|
| 539 |
+
draw.polygon([(x-5, y), (x+35, y-12), (x+75, y)], fill=(120, 120, 120))
|
| 540 |
+
|
| 541 |
+
# Detalles según solidaridad
|
| 542 |
+
if solidarity > 70:
|
| 543 |
+
# Jardincito al frente
|
| 544 |
+
draw.rectangle([x-10, y+70, x+80, y+80], fill=(80, 120, 40))
|
| 545 |
+
# Flores
|
| 546 |
+
for flower_x in range(x, x+70, 15):
|
| 547 |
+
draw.ellipse([flower_x-2, y+72, flower_x+2, y+76], fill=(255, 100, 150))
|
| 548 |
+
|
| 549 |
+
# Actividad comunitaria según solidaridad
|
| 550 |
+
if solidarity > 60:
|
| 551 |
+
self._draw_mate_circle(draw, 180, 520)
|
| 552 |
+
if solidarity > 80:
|
| 553 |
+
self._draw_community_garden(draw, 600, 480)
|
| 554 |
+
|
| 555 |
+
def _draw_mate_circle(self, draw, center_x, center_y):
|
| 556 |
+
"""Dibuja ronda de mate"""
|
| 557 |
+
for i in range(5):
|
| 558 |
+
angle = i * (math.pi * 2 / 5)
|
| 559 |
+
person_x = center_x + 35 * math.cos(angle)
|
| 560 |
+
person_y = center_y + 35 * math.sin(angle)
|
| 561 |
+
|
| 562 |
+
# Persona
|
| 563 |
+
draw.ellipse([person_x-6, person_y-12, person_x+6, person_y+6],
|
| 564 |
+
fill=(140, 110, 80), outline=(100, 80, 60))
|
| 565 |
+
|
| 566 |
+
# Mate en el centro
|
| 567 |
+
if i == 0:
|
| 568 |
+
draw.ellipse([center_x-4, center_y-4, center_x+4, center_y+4], fill=(80, 40, 0))
|
| 569 |
+
|
| 570 |
+
def _draw_community_garden(self, draw, x, y):
|
| 571 |
+
"""Jardín comunitario"""
|
| 572 |
+
# Base del jardín
|
| 573 |
+
draw.rectangle([x, y, x+80, y+60], fill=(60, 100, 40))
|
| 574 |
+
|
| 575 |
+
# Plantas
|
| 576 |
+
for plant_x in range(x+10, x+70, 20):
|
| 577 |
+
for plant_y in range(y+10, y+50, 15):
|
| 578 |
+
draw.ellipse([plant_x-3, plant_y-3, plant_x+3, plant_y+3], fill=(100, 180, 60))
|
| 579 |
+
|
| 580 |
+
def _draw_animated_character(self, draw, game_state):
|
| 581 |
+
"""Personaje con animaciones"""
|
| 582 |
+
x, y = 420, 450
|
| 583 |
+
|
| 584 |
+
# Animación de respiración
|
| 585 |
+
breath_offset = math.sin(time.time() * 2) * 2
|
| 586 |
+
|
| 587 |
+
# Sombra
|
| 588 |
+
draw.ellipse([x-10, y+15, x+10, y+18], fill=(0, 0, 0, 120))
|
| 589 |
+
|
| 590 |
+
# Cuerpo con animación
|
| 591 |
+
body_y = y + breath_offset
|
| 592 |
+
draw.ellipse([x-12, body_y-18, x+12, body_y+12], fill=(130, 100, 70), outline=(90, 70, 50), width=2)
|
| 593 |
+
|
| 594 |
+
# Remera argentina
|
| 595 |
+
draw.rectangle([x-10, body_y-15, x+10, body_y+5], fill=(100, 150, 255), outline=(255, 255, 255))
|
| 596 |
+
|
| 597 |
+
# Cabeza
|
| 598 |
+
head_y = body_y - 25 + breath_offset * 0.5
|
| 599 |
+
draw.ellipse([x-8, head_y-15, x+8, head_y+5], fill=(160, 130, 90), outline=(120, 100, 70))
|
| 600 |
+
|
| 601 |
+
# Expresión según esperanza
|
| 602 |
+
hope = game_state.get('resources', {}).get('esperanza', 50)
|
| 603 |
+
if hope > 70:
|
| 604 |
+
# Sonrisa
|
| 605 |
+
draw.arc([x-4, head_y-5, x+4, head_y+2], 0, 180, fill=(0, 0, 0), width=2)
|
| 606 |
+
elif hope < 30:
|
| 607 |
+
# Preocupación
|
| 608 |
+
draw.arc([x-4, head_y+2, x+4, head_y-5], 0, 180, fill=(0, 0, 0), width=2)
|
| 609 |
+
|
| 610 |
+
# Ojos
|
| 611 |
+
draw.ellipse([x-5, head_y-8, x-2, head_y-5], fill=(255, 255, 255))
|
| 612 |
+
draw.ellipse([x+2, head_y-8, x+5, head_y-5], fill=(255, 255, 255))
|
| 613 |
+
draw.ellipse([x-4, head_y-7, x-3, head_y-6], fill=(0, 0, 0))
|
| 614 |
+
draw.ellipse([x+3, head_y-7, x+4, head_y-6], fill=(0, 0, 0))
|
| 615 |
+
|
| 616 |
+
# Mate en la mano con brillo
|
| 617 |
+
mate_x = x + 10
|
| 618 |
+
mate_glow = int(150 + 50 * math.sin(time.time() * 3))
|
| 619 |
+
draw.ellipse([mate_x-3, body_y-8, mate_x+3, body_y-2],
|
| 620 |
+
fill=(100, 50, 0), outline=(mate_glow, mate_glow//2, 0), width=2)
|
| 621 |
+
|
| 622 |
+
def _draw_enhanced_ui(self, draw, game_state):
|
| 623 |
+
"""UI mejorada con efectos visuales"""
|
| 624 |
+
resources = game_state.get('resources', {})
|
| 625 |
+
|
| 626 |
+
# Panel principal con gradiente
|
| 627 |
+
for i in range(170):
|
| 628 |
+
alpha = int(180 * (1 - i/170))
|
| 629 |
+
draw.rectangle([10, 10+i, 350, 11+i], fill=(0, 0, 0, alpha))
|
| 630 |
+
|
| 631 |
+
draw.rectangle([10, 10, 350, 180], outline=(100, 150, 200), width=2)
|
| 632 |
+
|
| 633 |
+
# Título
|
| 634 |
+
crisis_level = game_state.get('crisis_level', 1)
|
| 635 |
+
crisis_colors = [(100, 255, 100), (150, 255, 100), (255, 255, 100), (255, 150, 100), (255, 100, 100)]
|
| 636 |
+
title_color = crisis_colors[min(4, crisis_level-1)]
|
| 637 |
+
|
| 638 |
+
# Recursos con barras animadas
|
| 639 |
+
y_pos = 35
|
| 640 |
+
for resource, value in resources.items():
|
| 641 |
+
# Barra de fondo con gradiente
|
| 642 |
+
draw.rectangle([25, y_pos, 220, y_pos+12], fill=(30, 30, 30))
|
| 643 |
+
|
| 644 |
+
# Barra de valor con animación
|
| 645 |
+
bar_width = int((max(0, min(value, 100)) / 100) * 195)
|
| 646 |
+
bar_color = self._get_animated_resource_color(resource, value)
|
| 647 |
+
|
| 648 |
+
if bar_width > 0:
|
| 649 |
+
# Efecto de brillo en la barra
|
| 650 |
+
for i in range(bar_width):
|
| 651 |
+
brightness = int(255 * (0.7 + 0.3 * math.sin(time.time() * 2 + i/10)))
|
| 652 |
+
color_with_glow = tuple(min(255, c + brightness//4) for c in bar_color)
|
| 653 |
+
draw.rectangle([25+i, y_pos, 26+i, y_pos+12], fill=color_with_glow)
|
| 654 |
+
|
| 655 |
+
# Iconos de recursos
|
| 656 |
+
resource_icons = {
|
| 657 |
+
'pesos': '💰', 'dolares': '💵', 'comida': '🍖',
|
| 658 |
+
'esperanza': '💪', 'solidaridad': '🤝', 'trabajo': '🔨'
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
# Valor numérico
|
| 662 |
+
y_pos += 18
|
| 663 |
+
|
| 664 |
+
# Indicador de inflación animado
|
| 665 |
+
inflation = game_state.get('inflation_rate', 1.0)
|
| 666 |
+
inflation_intensity = min(255, int(100 + inflation * 30))
|
| 667 |
+
inflation_color = (255, 255-inflation_intensity//2, 255-inflation_intensity)
|
| 668 |
+
|
| 669 |
+
# Pulso según inflación
|
| 670 |
+
pulse = math.sin(time.time() * (1 + inflation)) * 10
|
| 671 |
+
draw.rectangle([230, 35, 340, 60], fill=inflation_color, outline=(255, 255, 255), width=2)
|
| 672 |
+
|
| 673 |
+
# Crisis meter con efectos
|
| 674 |
+
crisis_width = int((crisis_level / 5) * 100)
|
| 675 |
+
crisis_color = crisis_colors[min(4, crisis_level-1)]
|
| 676 |
+
draw.rectangle([230, 70, 340, 85], fill=(50, 50, 50))
|
| 677 |
+
draw.rectangle([230, 70, 230 + crisis_width, 85], fill=crisis_color)
|
| 678 |
+
|
| 679 |
+
def _get_animated_resource_color(self, resource, value):
|
| 680 |
+
"""Color animado según el recurso y valor"""
|
| 681 |
+
base_colors = {
|
| 682 |
+
'pesos': (255, 215, 0), # Dorado
|
| 683 |
+
'dolares': (100, 255, 100), # Verde dólar
|
| 684 |
+
'comida': (255, 140, 0), # Naranja
|
| 685 |
+
'esperanza': (100, 149, 237), # Azul esperanza
|
| 686 |
+
'solidaridad': (255, 105, 180), # Rosa solidario
|
| 687 |
+
'trabajo': (139, 69, 19) # Marrón trabajo
|
| 688 |
}
|
| 689 |
+
|
| 690 |
+
base_color = base_colors.get(resource, (255, 255, 255))
|
| 691 |
+
|
| 692 |
+
# Intensidad según valor
|
| 693 |
+
if value > 70:
|
| 694 |
+
multiplier = 1.0
|
| 695 |
+
elif value > 30:
|
| 696 |
+
multiplier = 0.8
|
| 697 |
+
else:
|
| 698 |
+
multiplier = 0.6
|
| 699 |
+
|
| 700 |
+
# Efecto pulsante en valores críticos
|
| 701 |
+
if value < 20:
|
| 702 |
+
pulse_effect = 0.8 + 0.4 * math.sin(time.time() * 4)
|
| 703 |
+
multiplier *= pulse_effect
|
| 704 |
+
|
| 705 |
+
return tuple(int(c * multiplier) for c in base_color)
|
| 706 |
|
| 707 |
+
def create_ai_enhanced_interface():
|
| 708 |
+
"""Interfaz mejorada con IA"""
|
| 709 |
+
game = EnhancedArgentinaGame()
|
| 710 |
|
| 711 |
with gr.Blocks(
|
| 712 |
+
title="🇦🇷 CRISIS Y CORAJE - Con IA Narrativa",
|
| 713 |
+
theme=gr.themes.Soft(),
|
| 714 |
+
css="""
|
| 715 |
+
.enhanced-header {
|
| 716 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 717 |
+
border-radius: 15px;
|
| 718 |
+
padding: 25px;
|
| 719 |
+
color: white;
|
| 720 |
+
text-align: center;
|
| 721 |
+
margin: 15px 0;
|
| 722 |
+
}
|
| 723 |
+
.action-button {
|
| 724 |
+
font-size: 1.1em;
|
| 725 |
+
padding: 12px;
|
| 726 |
+
margin: 5px;
|
| 727 |
+
}
|
| 728 |
+
"""
|
| 729 |
) as interface:
|
| 730 |
|
| 731 |
gr.HTML("""
|
| 732 |
+
<div class="enhanced-header">
|
| 733 |
+
<h1 style="font-size: 2.8em; margin: 0; text-shadow: 2px 2px 4px rgba(0,0,0,0.7);">
|
|
|
|
| 734 |
🇦🇷 CRISIS Y CORAJE
|
| 735 |
</h1>
|
| 736 |
+
<h2 style="font-size: 1.4em; margin: 10px 0; opacity: 0.9;">
|
| 737 |
+
Con Inteligencia Artificial Narrativa
|
| 738 |
</h2>
|
| 739 |
+
<p style="font-size: 1.1em; margin: 5px 0; opacity: 0.8;">
|
| 740 |
+
Supervivencia, Solidaridad y Cultura Argentina • Powered by Groq AI
|
| 741 |
</p>
|
| 742 |
</div>
|
| 743 |
""")
|
|
|
|
| 745 |
with gr.Row():
|
| 746 |
with gr.Column(scale=2):
|
| 747 |
game_display = gr.Image(
|
| 748 |
+
label="🌆 Tu Barrio Argentino",
|
| 749 |
width=900,
|
| 750 |
height=600
|
| 751 |
)
|
| 752 |
|
| 753 |
+
gr.Markdown("### 🎮 ACCIONES DE SUPERVIVENCIA")
|
| 754 |
with gr.Row():
|
| 755 |
+
work_btn = gr.Button("💼 TRABAJAR", variant="primary", elem_classes="action-button")
|
| 756 |
+
help_btn = gr.Button("🤝 AYUDAR VECINOS", variant="secondary", elem_classes="action-button")
|
| 757 |
+
rebusque_btn = gr.Button("💡 REBUSQUE", variant="stop", elem_classes="action-button")
|
| 758 |
|
| 759 |
with gr.Row():
|
| 760 |
+
save_btn = gr.Button("💵 COMPRAR DÓLARES", variant="primary", elem_classes="action-button")
|
| 761 |
+
asado_btn = gr.Button("🔥 HACER ASADO", variant="stop", elem_classes="action-button")
|
|
|
|
| 762 |
|
| 763 |
with gr.Column(scale=1):
|
| 764 |
gr.Markdown("### 📊 ESTADO DE SUPERVIVENCIA")
|
| 765 |
status_display = gr.JSON(
|
| 766 |
+
label="📈 Recursos y Análisis IA",
|
| 767 |
+
value=game.get_enhanced_status()
|
| 768 |
)
|
| 769 |
|
| 770 |
+
gr.Markdown("### 🗞️ NOTICIAS DEL BARRIO")
|
| 771 |
news_display = gr.Textbox(
|
| 772 |
+
value="🎮 ¡Bienvenido a Crisis y Coraje con IA! Los diálogos son generados dinámicamente por inteligencia artificial para crear una experiencia narrativa única.",
|
| 773 |
+
label="📺 Canal Barrial + IA",
|
| 774 |
lines=4,
|
| 775 |
interactive=False
|
| 776 |
)
|
| 777 |
|
| 778 |
+
gr.Markdown("### 🤖 SISTEMA DE IA")
|
| 779 |
+
ai_status = gr.HTML(f"""
|
| 780 |
+
<div style="background: #2d3436; color: white; padding: 15px; border-radius: 10px; margin: 10px 0;">
|
| 781 |
+
<p><strong>🧠 IA Narrativa:</strong> {'🟢 Activa' if game.ai_dialogue.ai_enabled else '🔴 Inactiva'}</p>
|
| 782 |
+
<p><strong>🎭 Personajes:</strong> Doña Rosa, El Checo, Comerciante</p>
|
| 783 |
+
<p><strong>🎯 Características:</strong></p>
|
| 784 |
+
<ul style="margin: 5px 0; padding-left: 20px; font-size: 0.9em;">
|
| 785 |
+
<li>Diálogos contextuales dinámicos</li>
|
| 786 |
+
<li>Análisis económico inteligente</li>
|
| 787 |
+
<li>Noticias del barrio generadas</li>
|
| 788 |
+
<li>Narrativa adaptativa al progreso</li>
|
| 789 |
</ul>
|
|
|
|
| 790 |
</div>
|
| 791 |
""")
|
| 792 |
+
|
| 793 |
+
# Botón para generar noticias frescas
|
| 794 |
+
news_btn = gr.Button("📰 GENERAR NOTICIAS IA", variant="secondary")
|
| 795 |
+
|
| 796 |
+
# Conectar acciones principales
|
| 797 |
+
work_btn.click(
|
| 798 |
+
fn=lambda: game.process_enhanced_action("trabajar"),
|
| 799 |
+
outputs=[game_display, status_display, news_display]
|
| 800 |
+
)
|
| 801 |
+
help_btn.click(
|
| 802 |
+
fn=lambda: game.process_enhanced_action("ayudar_vecinos"),
|
| 803 |
+
outputs=[game_display, status_display, news_display]
|
| 804 |
+
)
|
| 805 |
+
rebusque_btn.click(
|
| 806 |
+
fn=lambda: game.process_enhanced_action("buscar_rebusque"),
|
| 807 |
+
outputs=[game_display, status_display, news_display]
|
| 808 |
+
)
|
| 809 |
+
save_btn.click(
|
| 810 |
+
fn=lambda: game.process_enhanced_action("ahorrar_dolares"),
|
| 811 |
+
outputs=[game_display, status_display, news_display]
|
| 812 |
+
)
|
| 813 |
+
asado_btn.click(
|
| 814 |
+
fn=lambda: game.process_enhanced_action("hacer_asado"),
|
| 815 |
+
outputs=[game_display, status_display, news_display]
|
| 816 |
+
)
|
| 817 |
+
|
| 818 |
+
# Botón de noticias IA
|
| 819 |
+
news_btn.click(
|
| 820 |
+
fn=lambda: game.get_neighborhood_news(),
|
| 821 |
+
outputs=news_display
|
| 822 |
+
)
|
| 823 |
|
| 824 |
+
# Estado inicial
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 825 |
interface.load(
|
| 826 |
+
fn=lambda: (
|
| 827 |
+
game.renderer.render_argentina_frame(game.game_state),
|
| 828 |
+
game.get_enhanced_status(),
|
| 829 |
+
"🎮 ¡Crisis y Coraje con IA activado! Cada acción generará diálogos únicos y contextuales. Los personajes reaccionarán de manera inteligente a tus decisiones."
|
| 830 |
+
),
|
| 831 |
outputs=[game_display, status_display, news_display]
|
| 832 |
)
|
| 833 |
|
| 834 |
return interface
|
| 835 |
|
| 836 |
if __name__ == "__main__":
|
| 837 |
+
interface = create_ai_enhanced_interface()
|
| 838 |
+
interface.launch(share=True, show_error=True, show_api=False)
|