Lukeetah commited on
Commit
ded95f4
·
verified ·
1 Parent(s): a32b0b9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +787 -353
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py - SIMCIUDAD ARGENTINA FUNCIONAL Y ÉPICO
2
  import gradio as gr
3
  import time
4
  import math
@@ -7,587 +7,1021 @@ import os
7
  from PIL import Image, ImageDraw
8
  import json
9
 
10
- # Groq AI Integration
11
  try:
12
  from groq import Groq
13
  GROQ_AVAILABLE = True
14
- groq_client = Groq(api_key=os.getenv("GROQ_API_KEY"))
15
  except ImportError:
16
  GROQ_AVAILABLE = False
17
  groq_client = None
18
 
19
  class AIUrbanPlanner:
20
- """IA para planificación urbana argentina"""
21
  def __init__(self):
22
  self.enabled = GROQ_AVAILABLE and groq_client is not None
23
 
24
  def generate_city_advice(self, city_state):
25
- """Genera consejos de planificación urbana"""
26
  if not self.enabled:
27
- return "Construí más plazas para aumentar la felicidad de los vecinos."
 
 
 
 
 
 
28
 
29
  try:
30
  prompt = f"""
31
  Sos un urbanista argentino experto. Analizá esta ciudad y dá consejos breves:
32
 
33
- Población: {city_state.get('population', 0)}
34
  Felicidad: {city_state.get('happiness', 0)}%
35
- Presupuesto: ${city_state.get('budget', 0)}
36
  Edificios: {city_state.get('buildings', 0)}
 
37
 
38
- Dá UN consejo específico y argentino en 1-2 líneas máximo.
 
39
  """
40
 
41
  response = groq_client.chat.completions.create(
42
  messages=[{"role": "user", "content": prompt}],
43
  model="llama-3.1-8b-instant",
44
- temperature=0.7,
45
- max_tokens=80
46
  )
47
 
48
  return response.choices[0].message.content.strip()
49
- except:
50
- return "La ciudad necesita más espacios verdes para el bienestar."
 
51
 
52
- class ArgentineCityEngine:
53
- """Motor de la ciudad argentina"""
54
  def __init__(self):
55
- # Estado básico de la ciudad
56
- self.population = 150
 
 
57
  self.happiness = 75
58
- self.budget = 25000
59
- self.buildings = {}
60
- self.grid_size = 30
 
 
 
 
 
 
61
 
62
- # Recursos
63
  self.resources = {
 
64
  'energia': 100,
65
  'agua': 100,
66
  'seguridad': 70,
67
  'educacion': 60,
68
  'salud': 65,
69
  'cultura': 80,
70
- 'empleo': 75
 
 
 
71
  }
72
 
73
- # Tipos de edificios
74
  self.building_types = {
75
- 'casa': {'cost': 1000, 'color': (139, 69, 19), 'pop': 4, 'happiness': 2},
76
- 'villa': {'cost': 500, 'color': (255, 150, 100), 'pop': 8, 'happiness': -2},
77
- 'edificio': {'cost': 5000, 'color': (150, 150, 200), 'pop': 20, 'happiness': 1},
78
- 'escuela': {'cost': 8000, 'color': (255, 255, 100), 'educacion': 20, 'happiness': 10},
79
- 'hospital': {'cost': 15000, 'color': (255, 100, 100), 'salud': 30, 'happiness': 15},
80
- 'cancha': {'cost': 3000, 'color': (100, 255, 100), 'happiness': 25, 'cultura': 10},
81
- 'parrilla': {'cost': 2000, 'color': (200, 100, 50), 'happiness': 20, 'cultura': 15},
82
- 'plaza': {'cost': 1500, 'color': (150, 255, 150), 'happiness': 15, 'cultura': 5},
83
- 'fabrica': {'cost': 12000, 'color': (150, 100, 100), 'empleo': 30, 'happiness': -5}
 
 
 
 
84
  }
85
 
86
- # Inicializar con algunos edificios
87
- self._initialize_city()
88
-
89
- def _initialize_city(self):
90
- """Inicializa la ciudad con edificios básicos"""
91
- initial_buildings = [
92
- (15, 15, 'plaza'),
93
- (12, 12, 'casa'),
94
- (18, 12, 'casa'),
95
- (12, 18, 'casa'),
96
- (18, 18, 'casa'),
97
- (10, 15, 'escuela')
98
- ]
99
 
100
- for x, y, building_type in initial_buildings:
101
- self.buildings[(x, y)] = building_type
102
 
103
- class CityRenderer:
104
- """Renderizador de la ciudad"""
105
  def __init__(self):
106
- self.tile_size = 25
107
  self.animation_time = 0
 
108
 
109
- def render_city(self, city_engine) -> Image.Image:
110
- """Renderiza la ciudad completa"""
111
- width, height = 1000, 700
112
- img = Image.new('RGB', (width, height), (30, 50, 30))
113
  draw = ImageDraw.Draw(img)
114
 
115
  self.animation_time += 0.1
116
 
117
- # Fondo de la ciudad
118
- self._draw_background(draw, width, height, city_engine)
119
 
120
- # Grid de calles
121
- self._draw_streets(draw, width, height)
122
 
123
- # Edificios
124
- self._draw_buildings(draw, city_engine)
125
 
126
- # Efectos dinámicos
127
- self._draw_city_effects(draw, city_engine, width, height)
 
 
 
 
 
 
128
 
129
  return img
130
 
131
- def _draw_background(self, draw, width, height, city):
132
- """Fondo dinámico según felicidad"""
133
  happiness = city.happiness
134
 
135
- # Color del cielo según felicidad
136
  if happiness > 80:
137
- sky_start = (135, 206, 250)
138
- sky_end = (100, 149, 237)
139
  elif happiness > 60:
140
- sky_start = (119, 136, 153)
141
- sky_end = (105, 105, 105)
 
142
  else:
143
- sky_start = (70, 70, 70)
144
- sky_end = (50, 50, 50)
145
 
146
  # Gradiente de cielo
147
  for y in range(200):
148
- ratio = y / 200
149
- color = tuple(int(start * (1 - ratio) + end * ratio)
150
- for start, end in zip(sky_start, sky_end))
151
- draw.rectangle([0, y, width, y + 1], fill=color)
152
 
153
- # Base verde
154
- draw.rectangle([0, 200, width, height], fill=(40, 80, 40))
 
 
 
 
155
 
156
- def _draw_streets(self, draw, width, height):
157
- """Calles de la ciudad"""
158
- street_color = (80, 80, 80)
159
-
160
- # Calles horizontales
161
- for y in range(200, height, self.tile_size * 3):
162
- draw.rectangle([0, y, width, y + self.tile_size], fill=street_color)
163
 
164
- # Calles verticales
165
- for x in range(0, width, self.tile_size * 3):
166
- draw.rectangle([x, 200, x + self.tile_size, height], fill=street_color)
 
 
 
 
 
 
 
 
 
167
 
168
- # Líneas divisorias
169
- line_color = (255, 255, 255)
170
- for y in range(200, height, self.tile_size):
171
- draw.line([(0, y), (width, y)], fill=(60, 80, 60), width=1)
172
 
173
- for x in range(0, width, self.tile_size):
174
- draw.line([(x, 200), (x, height)], fill=(60, 80, 60), width=1)
 
175
 
176
- def _draw_buildings(self, draw, city):
177
- """Dibuja todos los edificios"""
178
- for (grid_x, grid_y), building_type in city.buildings.items():
179
  screen_x = grid_x * self.tile_size
180
- screen_y = 200 + grid_y * self.tile_size
181
 
182
- if building_type in city.building_types:
183
- self._draw_single_building(draw, screen_x, screen_y, building_type, city)
184
-
185
- def _draw_single_building(self, draw, x, y, building_type, city):
186
- """Dibuja un edificio específico"""
187
- building_info = city.building_types[building_type]
188
- color = building_info['color']
189
-
190
- if building_type == 'casa':
191
- self._draw_house(draw, x, y, color)
192
- elif building_type == 'villa':
193
- self._draw_villa(draw, x, y, color)
194
- elif building_type == 'edificio':
195
- self._draw_apartment(draw, x, y, color)
196
- elif building_type == 'escuela':
197
- self._draw_school(draw, x, y, color)
198
- elif building_type == 'hospital':
199
- self._draw_hospital(draw, x, y, color)
200
- elif building_type == 'cancha':
201
- self._draw_field(draw, x, y, color)
202
- elif building_type == 'parrilla':
203
- self._draw_grill(draw, x, y, color)
204
- elif building_type == 'plaza':
205
- self._draw_plaza(draw, x, y, color)
206
- elif building_type == 'fabrica':
207
- self._draw_factory(draw, x, y, color)
 
208
 
209
- def _draw_house(self, draw, x, y, color):
210
- """Casa argentina típica"""
211
- # Base
212
- draw.rectangle([x + 2, y + 8, x + 22, y + 23], fill=color, outline=(0, 0, 0), width=1)
213
- # Techo
214
- draw.polygon([(x + 1, y + 8), (x + 12, y + 2), (x + 23, y + 8)],
215
- fill=(160, 82, 45), outline=(100, 50, 25))
 
 
 
 
 
 
 
216
  # Puerta
217
- draw.rectangle([x + 10, y + 15, x + 14, y + 23], fill=(101, 67, 33))
218
- # Ventanas
219
- draw.rectangle([x + 5, y + 12, x + 8, y + 15], fill=(200, 200, 255), outline=(0, 0, 0))
220
- draw.rectangle([x + 16, y + 12, x + 19, y + 15], fill=(200, 200, 255), outline=(0, 0, 0))
 
 
 
 
 
 
 
 
 
221
 
222
- def _draw_villa(self, draw, x, y, color):
223
- """Casa de villa colorida"""
 
 
 
224
  # Casa principal
225
- draw.rectangle([x + 1, y + 10, x + 23, y + 23], fill=color, outline=(0, 0, 0), width=2)
 
226
  # Techo de chapa
227
- draw.polygon([(x, y + 10), (x + 12, y + 5), (x + 24, y + 10)], fill=(150, 150, 150))
228
- # Extensión
229
- draw.rectangle([x + 18, y + 15, x + 24, y + 23], fill=color, outline=(0, 0, 0))
230
- # Antena
231
- draw.line([(x + 20, y + 5), (x + 20, y + 1)], fill=(200, 200, 200), width=2)
232
- draw.ellipse([x + 18, y + 1, x + 22, y + 3], outline=(200, 200, 200))
 
 
 
 
 
 
 
 
 
233
 
234
- def _draw_apartment(self, draw, x, y, color):
235
  """Edificio de departamentos"""
 
 
236
  # Estructura principal más alta
237
- draw.rectangle([x + 1, y + 0, x + 23, y + 23], fill=color, outline=(0, 0, 0), width=1)
 
238
  # Ventanas en pisos
239
- for floor in range(3):
240
- floor_y = y + 5 + floor * 6
241
  for window in range(3):
242
  window_x = x + 4 + window * 6
 
 
243
  draw.rectangle([window_x, floor_y, window_x + 3, floor_y + 3],
244
- fill=(255, 255, 200), outline=(0, 0, 0))
245
 
246
- def _draw_school(self, draw, x, y, color):
247
  """Escuela con bandera argentina"""
 
 
248
  # Edificio principal
249
- draw.rectangle([x + 1, y + 8, x + 23, y + 23], fill=color, outline=(0, 0, 0), width=2)
 
250
  # Bandera argentina
251
  draw.rectangle([x + 20, y + 2, x + 22, y + 8], fill=(100, 150, 255)) # Azul
252
  draw.rectangle([x + 20, y + 4, x + 22, y + 6], fill=(255, 255, 255)) # Blanco
 
253
  # Asta
254
  draw.line([(x + 22, y + 8), (x + 22, y + 2)], fill=(139, 69, 19), width=2)
255
- # Patio
256
- draw.rectangle([x + 5, y + 12, x + 19, y + 20], fill=(200, 200, 200), outline=(150, 150, 150))
 
 
 
 
 
 
 
 
257
 
258
- def _draw_hospital(self, draw, x, y, color):
259
  """Hospital con cruz roja"""
 
 
260
  # Edificio
261
- draw.rectangle([x + 1, y + 5, x + 23, y + 23], fill=color, outline=(0, 0, 0), width=2)
 
262
  # Cruz roja
263
- draw.rectangle([x + 10, y + 8, x + 14, y + 18], fill=(255, 0, 0))
264
- draw.rectangle([x + 7, y + 11, x + 17, y + 15], fill=(255, 0, 0))
265
- # Ambulancia
266
- draw.rectangle([x + 2, y + 19, x + 10, y + 23], fill=(255, 255, 255), outline=(0, 0, 0))
267
- draw.ellipse([x + 3, y + 21, x + 5, y + 23], fill=(0, 0, 0))
268
- draw.ellipse([x + 7, y + 21, x + 9, y + 23], fill=(0, 0, 0))
 
 
269
 
270
- def _draw_field(self, draw, x, y, color):
271
- """Cancha de fútbol"""
272
  # Césped
273
- draw.rectangle([x, y + 5, x + 24, y + 23], fill=color, outline=(255, 255, 255), width=2)
274
- # Línea del medio
275
- draw.line([(x + 12, y + 5), (x + 12, y + 23)], fill=(255, 255, 255), width=2)
276
- # Círculo central
277
- draw.ellipse([x + 8, y + 11, x + 16, y + 17], outline=(255, 255, 255), width=2)
 
 
278
  # Arcos
279
- draw.rectangle([x, y + 10, x + 3, y + 18], outline=(255, 255, 255), width=2)
280
- draw.rectangle([x + 21, y + 10, x + 24, y + 18], outline=(255, 255, 255), width=2)
 
 
 
 
 
 
 
 
281
  # Pelota animada
282
- ball_x = x + 12 + int(5 * math.sin(self.animation_time))
283
- ball_y = y + 14
284
- draw.ellipse([ball_x, ball_y, ball_x + 2, ball_y + 2], fill=(255, 255, 255), outline=(0, 0, 0))
285
 
286
- def _draw_grill(self, draw, x, y, color):
287
- """Parrilla con humo"""
288
  # Base de la parrilla
289
- draw.rectangle([x + 8, y + 15, x + 16, y + 23], fill=(50, 50, 50), outline=(100, 100, 100))
290
- # Humo animado
 
291
  for i in range(3):
292
- smoke_x = x + 12 + random.randint(-2, 2)
293
- smoke_y = y + 15 - i * 5 + int(2 * math.sin(self.animation_time + i))
294
- size = 2 + i
295
- draw.ellipse([smoke_x - size, smoke_y - size, smoke_x + size, smoke_y + size],
296
- fill=(180, 180, 180))
297
- # Personas alrededor
298
- for person_pos in [(x + 4, y + 18), (x + 18, y + 18)]:
299
- draw.ellipse([person_pos[0], person_pos[1], person_pos[0] + 3, person_pos[1] + 5],
300
- fill=(180, 140, 100))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
- def _draw_plaza(self, draw, x, y, color):
303
- """Plaza con árboles"""
304
- # Base verde
305
- draw.rectangle([x + 1, y + 8, x + 23, y + 23], fill=color, outline=(100, 150, 100))
 
 
306
  # Senderos
307
- draw.line([(x + 1, y + 15), (x + 23, y + 15)], fill=(160, 140, 120), width=3)
308
- draw.line([(x + 12, y + 8), (x + 12, y + 23)], fill=(160, 140, 120), width=3)
 
 
 
 
309
  # Árboles
310
- tree_positions = [(x + 5, y + 11), (x + 19, y + 11), (x + 5, y + 19), (x + 19, y + 19)]
311
  for tree_x, tree_y in tree_positions:
312
  # Tronco
313
  draw.rectangle([tree_x, tree_y + 3, tree_x + 2, tree_y + 8], fill=(101, 67, 33))
314
  # Copa
315
  draw.ellipse([tree_x - 2, tree_y, tree_x + 4, tree_y + 6], fill=(50, 150, 50))
 
 
 
 
 
 
 
 
 
316
 
317
- def _draw_factory(self, draw, x, y, color):
318
  """Fábrica con chimeneas"""
 
 
319
  # Edificio principal
320
- draw.rectangle([x + 1, y + 8, x + 23, y + 23], fill=color, outline=(0, 0, 0), width=2)
 
321
  # Chimeneas
322
  for chimney_x in [x + 6, x + 12, x + 18]:
323
  draw.rectangle([chimney_x, y + 2, chimney_x + 3, chimney_x + 8], fill=(80, 80, 80))
324
  # Humo
325
  smoke_y = y + 2 - int(3 * math.sin(self.animation_time))
326
  draw.ellipse([chimney_x, smoke_y - 2, chimney_x + 3, smoke_y + 1], fill=(150, 150, 150))
 
327
  # Ventanas industriales
328
  for window_x in range(x + 4, x + 20, 4):
329
  draw.rectangle([window_x, y + 12, window_x + 2, window_x + 15],
330
  fill=(255, 200, 100), outline=(0, 0, 0))
331
 
332
- def _draw_city_effects(self, draw, city, width, height):
333
- """Efectos dinámicos de la ciudad"""
334
- # Tráfico en las calles
335
- if city.happiness > 50:
336
- for i in range(3):
337
- car_x = (100 + i * 200 + int(self.animation_time * 30)) % width
338
- car_y = 220 + i * 75
339
- # Auto simple
340
- draw.rectangle([car_x, car_y, car_x + 15, car_y + 8],
341
- fill=(200, 100, 100), outline=(0, 0, 0))
342
- # Ruedas
343
- draw.ellipse([car_x + 2, car_y + 6, car_x + 5, car_y + 9], fill=(50, 50, 50))
344
- draw.ellipse([car_x + 10, car_y + 6, car_x + 13, car_y + 9], fill=(50, 50, 50))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
 
346
- class SimCityGame:
347
- """Juego principal"""
348
  def __init__(self):
349
- self.city = ArgentineCityEngine()
350
- self.renderer = CityRenderer()
351
  self.ai_planner = AIUrbanPlanner()
 
 
 
352
  self.day = 1
353
 
354
  def place_building(self, building_type, grid_x, grid_y):
355
- """Coloca un edificio"""
356
  try:
357
  grid_x, grid_y = int(grid_x), int(grid_y)
358
 
359
  if building_type not in self.city.building_types:
360
- return "Tipo de edificio inválido", self.get_status(), "Error en tipo de edificio"
361
 
362
- if (grid_x, grid_y) in self.city.buildings:
363
- return "Ya hay un edificio ahí", self.get_status(), "Posición ocupada"
364
 
365
- cost = self.city.building_types[building_type]['cost']
366
- if self.city.budget < cost:
367
- return f"No tenés suficiente presupuesto (${cost:,})", self.get_status(), "Sin presupuesto"
368
 
369
- # Construir
370
- self.city.budget -= cost
371
- self.city.buildings[(grid_x, grid_y)] = building_type
372
 
373
- # Aplicar efectos
374
- building_info = self.city.building_types[building_type]
375
- for effect, value in building_info.items():
376
- if effect == 'pop':
377
- self.city.population += value
378
- elif effect == 'happiness':
379
- self.city.happiness = max(0, min(100, self.city.happiness + value))
380
- elif effect in self.city.resources:
381
- self.city.resources[effect] = max(0, min(100, self.city.resources[effect] + value))
382
 
383
  # Generar consejo IA
384
  advice = self.ai_planner.generate_city_advice({
385
  'population': self.city.population,
386
  'happiness': self.city.happiness,
387
- 'budget': self.city.budget,
388
- 'buildings': len(self.city.buildings)
 
389
  })
390
 
391
- frame = self.renderer.render_city(self.city)
392
- status = self.get_status()
393
- message = f"✅ {building_type.title()} construido por ${cost:,}"
394
 
395
- return frame, status, f"{message}\n\n🤖 IA: {advice}"
396
 
397
  except Exception as e:
398
- return "Error en construcción", self.get_status(), f"Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
 
400
  def simulate_day(self):
401
- """Simula un día"""
402
  try:
403
  self.day += 1
404
 
405
- # Ingresos
406
- daily_income = self.city.population * 15 + len(self.city.buildings) * 200
407
- self.city.budget += daily_income
 
 
408
 
409
  # Gastos de mantenimiento
410
- maintenance = len(self.city.buildings) * 50
411
- self.city.budget -= maintenance
 
 
 
 
412
 
413
  # Eventos aleatorios
414
  event_message = ""
415
- if random.random() < 0.3:
416
- events = [
417
- ("🎉 Festival de Tango", "cultura", 15, "happiness", 10),
418
- ("⚽ Clásico River-Boca", "happiness", 20, "cultura", 5),
419
- ("🌧️ Lluvia beneficiosa", "agua", 20, "happiness", 5),
420
- ("💼 Nueva inversión", "budget", 5000, "empleo", 10),
421
- ("📚 Programa educativo", "educacion", 15, "happiness", 8)
422
- ]
423
-
424
- event_name, res1, val1, res2, val2 = random.choice(events)
425
- event_message = f"📰 {event_name}"
426
-
427
- # Aplicar efectos
428
- if res1 == 'budget':
429
- self.city.budget += val1
430
- elif res1 == 'happiness':
431
- self.city.happiness = max(0, min(100, self.city.happiness + val1))
432
- elif res1 in self.city.resources:
433
- self.city.resources[res1] = max(0, min(100, self.city.resources[res1] + val1))
434
-
435
- if res2 == 'happiness':
436
- self.city.happiness = max(0, min(100, self.city.happiness + val2))
437
- elif res2 in self.city.resources:
438
- self.city.resources[res2] = max(0, min(100, self.city.resources[res2] + val2))
439
 
440
- frame = self.renderer.render_city(self.city)
441
- status = self.get_status()
442
 
443
- message = f"📅 Día {self.day} simulado\n💰 Ingresos: ${daily_income:,}\n💸 Gastos: ${maintenance:,}"
444
  if event_message:
445
- message += f"\n{event_message}"
 
446
 
447
  return frame, status, message
448
 
449
  except Exception as e:
450
- return "Error en simulación", self.get_status(), f"Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
- def get_status(self):
453
- """Estado actual de la ciudad"""
454
  try:
455
- building_count = len(self.city.buildings)
456
  return {
457
  "🏙️ Población": f"{self.city.population:,} habitantes",
458
  "😊 Felicidad": f"{self.city.happiness}%",
459
- "💰 Presupuesto": f"${self.city.budget:,}",
 
 
 
460
  "🏗️ Edificios": f"{building_count} construidos",
461
  "📅 Día": self.day,
462
  "⚡ Energía": f"{self.city.resources['energia']}%",
463
  "💧 Agua": f"{self.city.resources['agua']}%",
464
- "🎓 Educación": f"{self.city.resources['educacion']}%"
 
 
 
465
  }
466
  except Exception as e:
467
  return {"Error": str(e)}
468
 
469
  def create_simcity_interface():
470
- """Interfaz principal del juego"""
471
- game = SimCityGame()
472
 
473
  with gr.Blocks(
474
- title="🇦🇷 SIMCIUDAD ARGENTINA",
475
- theme=gr.themes.Soft()
 
 
 
 
 
476
  ) as interface:
477
 
478
  gr.HTML("""
479
- <div style="background: linear-gradient(135deg, #74b9ff 0%, #0984e3 50%, #00b894 100%);
480
- padding: 25px; text-align: center; border-radius: 15px; margin: 15px;">
481
- <h1 style="color: white; font-size: 2.5em; margin: 0; text-shadow: 2px 2px 4px rgba(0,0,0,0.7);">
482
  🏙️ SIMCIUDAD ARGENTINA 🇦🇷
483
  </h1>
484
- <p style="color: #f1f2f6; font-size: 1.1em; margin: 10px 0;">
485
- Construí la Ciudad Argentina de tus Sueños Gráficos Isométricos • Mecánicas Realistas • Cultura Auténtica
 
 
 
486
  </p>
487
  </div>
488
  """)
489
 
490
  with gr.Row():
491
  with gr.Column(scale=2):
492
- # Pantalla principal del juego
493
  city_display = gr.Image(
494
  label="🌆 Tu Ciudad Argentina",
495
  width=1000,
496
  height=700,
497
- value=game.renderer.render_city(game.city)
498
  )
499
 
500
  gr.Markdown("### 🏗️ HERRAMIENTAS DE CONSTRUCCIÓN")
 
 
 
501
 
 
 
502
  with gr.Row():
503
- grid_x = gr.Number(label="Coordenada X", value=10, minimum=0, maximum=30)
504
- grid_y = gr.Number(label="Coordenada Y", value=10, minimum=0, maximum=20)
 
 
505
 
506
- # Botones de construcción
 
507
  with gr.Row():
508
- casa_btn = gr.Button("🏠 CASA ($1,000)", variant="secondary")
509
- villa_btn = gr.Button("🏘️ VILLA ($500)", variant="secondary")
510
- edificio_btn = gr.Button("🏢 EDIFICIO ($5,000)", variant="primary")
 
511
 
 
 
512
  with gr.Row():
513
- escuela_btn = gr.Button("🏫 ESCUELA ($8,000)", variant="primary")
514
- hospital_btn = gr.Button("🏥 HOSPITAL ($15,000)", variant="stop")
515
- cancha_btn = gr.Button(" CANCHA ($3,000)", variant="secondary")
 
516
 
 
 
517
  with gr.Row():
518
- parrilla_btn = gr.Button("🔥 PARRILLA ($2,000)", variant="stop")
519
- plaza_btn = gr.Button("🌳 PLAZA ($1,500)", variant="secondary")
520
- fabrica_btn = gr.Button("🏭 FÁBRICA ($12,000)", variant="primary")
521
 
522
- # Botón de simulación
523
  with gr.Row():
524
- simular_btn = gr.Button("⏭️ SIMULAR DÍA", variant="primary", size="lg")
525
 
526
  with gr.Column(scale=1):
527
- # Estado de la ciudad
528
  gr.Markdown("### 📊 ESTADO DE LA CIUDAD")
529
- status_display = gr.JSON(
530
- label="📈 Estadísticas",
531
- value=game.get_status()
532
  )
533
 
534
- # Eventos y noticias
535
- gr.Markdown("### 📰 EVENTOS RECIENTES")
536
- news_display = gr.Textbox(
537
- label="📺 Noticias Urbanas",
538
- value="🎮 ¡Bienvenido al SimCity Argentino! Empezá construyendo casas y servicios para tu comunidad.",
539
- lines=5,
540
  interactive=False
541
  )
542
 
543
- # Info de construcción
544
  gr.Markdown("### 💰 GUÍA DE CONSTRUCCIÓN")
545
  gr.HTML("""
546
- <div style="background: #2d3436; color: white; padding: 15px; border-radius: 10px; font-size: 0.9em;">
547
- <p><strong>🏗️ Efectos de Edificios:</strong></p>
548
  <ul style="margin: 5px 0; padding-left: 15px;">
549
- <li>🏠 Casa: +4 población, +2 felicidad</li>
550
- <li>🏘️ Villa: +8 población, -2 felicidad</li>
551
- <li>🏢 Edificio: +20 población, +1 felicidad</li>
552
- <li>🏫 Escuela: +20 educación, +10 felicidad</li>
553
- <li>🏥 Hospital: +30 salud, +15 felicidad</li>
554
- <li>⚽ Cancha: +25 felicidad, +10 cultura</li>
555
- <li>🔥 Parrilla: +20 felicidad, +15 cultura</li>
556
- <li>🌳 Plaza: +15 felicidad, +5 cultura</li>
557
- <li>🏭 Fábrica: +30 empleo, -5 felicidad</li>
 
 
 
 
558
  </ul>
559
  </div>
560
  """)
561
 
562
- # Conectar funciones
563
  def build_structure(building_type, x, y):
564
  return game.place_building(building_type, x, y)
565
 
566
- # Conectar botones
567
- casa_btn.click(fn=lambda x, y: build_structure("casa", x, y),
568
- inputs=[grid_x, grid_y], outputs=[city_display, status_display, news_display])
569
- villa_btn.click(fn=lambda x, y: build_structure("villa", x, y),
570
- inputs=[grid_x, grid_y], outputs=[city_display, status_display, news_display])
571
- edificio_btn.click(fn=lambda x, y: build_structure("edificio", x, y),
572
- inputs=[grid_x, grid_y], outputs=[city_display, status_display, news_display])
573
- escuela_btn.click(fn=lambda x, y: build_structure("escuela", x, y),
574
- inputs=[grid_x, grid_y], outputs=[city_display, status_display, news_display])
575
- hospital_btn.click(fn=lambda x, y: build_structure("hospital", x, y),
576
- inputs=[grid_x, grid_y], outputs=[city_display, status_display, news_display])
577
- cancha_btn.click(fn=lambda x, y: build_structure("cancha", x, y),
578
- inputs=[grid_x, grid_y], outputs=[city_display, status_display, news_display])
579
- parrilla_btn.click(fn=lambda x, y: build_structure("parrilla", x, y),
580
- inputs=[grid_x, grid_y], outputs=[city_display, status_display, news_display])
581
- plaza_btn.click(fn=lambda x, y: build_structure("plaza", x, y),
582
- inputs=[grid_x, grid_y], outputs=[city_display, status_display, news_display])
583
- fabrica_btn.click(fn=lambda x, y: build_structure("fabrica", x, y),
584
- inputs=[grid_x, grid_y], outputs=[city_display, status_display, news_display])
585
-
586
- # Simulación
587
- simular_btn.click(fn=game.simulate_day, outputs=[city_display, status_display, news_display])
588
-
589
- return interface
590
-
591
- if __name__ == "__main__":
592
- interface = create_simcity_interface()
593
- interface.launch(share=True, show_error=True)
 
1
+ # app.py - SIMCIUDAD ARGENTINA FUNCIONAL COMPLETA
2
  import gradio as gr
3
  import time
4
  import math
 
7
  from PIL import Image, ImageDraw
8
  import json
9
 
10
+ # Groq AI Integration (mejorada)
11
  try:
12
  from groq import Groq
13
  GROQ_AVAILABLE = True
14
+ groq_client = Groq(api_key=os.getenv("GROQ_API_KEY")) if os.getenv("GROQ_API_KEY") else None
15
  except ImportError:
16
  GROQ_AVAILABLE = False
17
  groq_client = None
18
 
19
  class AIUrbanPlanner:
20
+ """IA para planificación urbana argentina con Groq"""
21
  def __init__(self):
22
  self.enabled = GROQ_AVAILABLE and groq_client is not None
23
 
24
  def generate_city_advice(self, city_state):
25
+ """Genera consejos de planificación urbana inteligentes"""
26
  if not self.enabled:
27
+ fallback_advice = [
28
+ "Construí más plazas para aumentar la felicidad vecinal.",
29
+ "Las parrillas mejoran la cultura y la moral del barrio.",
30
+ "Balanceá viviendas con servicios públicos.",
31
+ "Las canchas de fútbol son clave para la identidad argentina."
32
+ ]
33
+ return random.choice(fallback_advice)
34
 
35
  try:
36
  prompt = f"""
37
  Sos un urbanista argentino experto. Analizá esta ciudad y dá consejos breves:
38
 
39
+ Población: {city_state.get('population', 0)} habitantes
40
  Felicidad: {city_state.get('happiness', 0)}%
41
+ Presupuesto: ${city_state.get('budget', 0):,}
42
  Edificios: {city_state.get('buildings', 0)}
43
+ Identidad Cultural: {city_state.get('culture', 80)}%
44
 
45
+ Dá UN consejo específico y argentino en máximo 2 líneas.
46
+ Usá jerga porteña cuando sea apropiado.
47
  """
48
 
49
  response = groq_client.chat.completions.create(
50
  messages=[{"role": "user", "content": prompt}],
51
  model="llama-3.1-8b-instant",
52
+ temperature=0.8,
53
+ max_tokens=100
54
  )
55
 
56
  return response.choices[0].message.content.strip()
57
+ except Exception as e:
58
+ print(f"Error Groq: {e}")
59
+ return "La ciudad necesita más espacios verdes y culturales para prosperar."
60
 
61
+ class ArgentineCityBuilder:
62
+ """SimCity Argentino con mecánicas revolucionarias"""
63
  def __init__(self):
64
+ self.width, self.height = 1000, 700
65
+ self.grid_size = 20
66
+ self.city_grid = {} # Coordenadas -> tipo de edificio
67
+ self.population = 100
68
  self.happiness = 75
69
+ self.economy = 1000
70
+ self.inflation = 1.0
71
+ self.cultural_identity = 90
72
+
73
+ # Sistemas dinámicos
74
+ self.neighborhoods = {}
75
+ self.transport_network = {}
76
+ self.cultural_centers = {}
77
+ self.businesses = {}
78
 
79
+ # Recursos complejos (CORREGIDO - todos los recursos necesarios)
80
  self.resources = {
81
+ 'presupuesto': 50000,
82
  'energia': 100,
83
  'agua': 100,
84
  'seguridad': 70,
85
  'educacion': 60,
86
  'salud': 65,
87
  'cultura': 80,
88
+ 'empleo': 75,
89
+ 'vivienda': 50,
90
+ 'transport': 60, # AGREGADO
91
+ 'pollution': 0 # AGREGADO
92
  }
93
 
94
+ # Edificios disponibles (CORREGIDOS - todos los efectos)
95
  self.building_types = {
96
+ 'casa': {'cost': 1000, 'pop': 4, 'happiness': 2, 'vivienda': 5},
97
+ 'villa': {'cost': 500, 'pop': 8, 'happiness': -2, 'solidarity': 15, 'vivienda': 8},
98
+ 'edificio': {'cost': 5000, 'pop': 20, 'happiness': 1, 'vivienda': 15},
99
+ 'escuela': {'cost': 8000, 'educacion': 20, 'happiness': 10},
100
+ 'hospital': {'cost': 15000, 'salud': 30, 'happiness': 15},
101
+ 'cancha': {'cost': 3000, 'happiness': 25, 'cultura': 10},
102
+ 'parrilla': {'cost': 2000, 'happiness': 20, 'cultura': 15},
103
+ 'centro_cultural': {'cost': 10000, 'cultura': 40, 'educacion': 10},
104
+ 'fabrica': {'cost': 12000, 'empleo': 30, 'pollution': -10, 'happiness': -5},
105
+ 'plaza': {'cost': 1500, 'happiness': 15, 'cultura': 5},
106
+ 'subte': {'cost': 20000, 'transport': 50, 'happiness': 10},
107
+ 'comisaria': {'cost': 7000, 'seguridad': 25, 'happiness': -5},
108
+ 'kiosco': {'cost': 800, 'happiness': 5, 'empleo': 2}
109
  }
110
 
111
+ # Inicializar ciudad base
112
+ self._initialize_base_city()
113
+
114
+ def _initialize_base_city(self):
115
+ """Crea la ciudad inicial con algunos edificios"""
116
+ # Plaza central
117
+ self.city_grid[(25, 17)] = 'plaza'
118
+
119
+ # Algunas casas iniciales
120
+ initial_houses = [(20, 15), (30, 15), (25, 12), (25, 22)]
121
+ for pos in initial_houses:
122
+ self.city_grid[pos] = 'casa'
 
123
 
124
+ # Una escuela inicial
125
+ self.city_grid[(15, 20)] = 'escuela'
126
 
127
+ class AdvancedCityRenderer:
128
+ """Renderizador avanzado con gráficos isométricos COMPLETO"""
129
  def __init__(self):
 
130
  self.animation_time = 0
131
+ self.tile_size = 20
132
 
133
+ def render_city_frame(self, city_builder) -> Image.Image:
134
+ """Renderiza la ciudad con vista isométrica mejorada"""
135
+ img = Image.new('RGB', (city_builder.width, city_builder.height), (20, 40, 20))
 
136
  draw = ImageDraw.Draw(img)
137
 
138
  self.animation_time += 0.1
139
 
140
+ # Fondo: Cielo dinámico según felicidad
141
+ self._draw_dynamic_sky(draw, city_builder)
142
 
143
+ # Grid base de la ciudad
144
+ self._draw_city_grid(draw, city_builder)
145
 
146
+ # Edificios con perspectiva isométrica
147
+ self._draw_isometric_buildings(draw, city_builder)
148
 
149
+ # Sistemas dinámicos (tráfico, personas, etc.)
150
+ self._draw_city_life(draw, city_builder)
151
+
152
+ # UI avanzada
153
+ self._draw_advanced_ui(draw, city_builder)
154
+
155
+ # Efectos especiales
156
+ self._draw_special_effects(draw, city_builder)
157
 
158
  return img
159
 
160
+ def _draw_dynamic_sky(self, draw, city):
161
+ """Cielo que cambia según el estado de la ciudad"""
162
  happiness = city.happiness
163
 
164
+ # Color base del cielo
165
  if happiness > 80:
166
+ sky_color = (135, 206, 250) # Azul cielo feliz
 
167
  elif happiness > 60:
168
+ sky_color = (176, 196, 222) # Azul grisáceo
169
+ elif happiness > 40:
170
+ sky_color = (119, 136, 153) # Gris claro
171
  else:
172
+ sky_color = (105, 105, 105) # Gris oscuro
 
173
 
174
  # Gradiente de cielo
175
  for y in range(200):
176
+ alpha = 1.0 - (y / 400)
177
+ color = tuple(int(c * alpha + 20 * (1-alpha)) for c in sky_color)
178
+ draw.rectangle([0, y, city.width, y+2], fill=color)
 
179
 
180
+ # Nubes dinámicas
181
+ cloud_offset = int(self.animation_time * 20) % city.width
182
+ for i in range(3):
183
+ x = (i * 300 + cloud_offset) % (city.width + 100)
184
+ y = 50 + i * 30
185
+ self._draw_cloud(draw, x, y, happiness)
186
 
187
+ def _draw_cloud(self, draw, x, y, happiness):
188
+ """Dibuja nube con forma según felicidad"""
189
+ cloud_color = (255, 255, 255) if happiness > 50 else (200, 200, 200)
 
 
 
 
190
 
191
+ # Forma de nube
192
+ for i in range(5):
193
+ offset_x = i * 15 + random.randint(-5, 5)
194
+ offset_y = random.randint(-8, 8)
195
+ size = 20 + random.randint(-5, 10)
196
+ draw.ellipse([x + offset_x - size//2, y + offset_y - size//2,
197
+ x + offset_x + size//2, y + offset_y + size//2],
198
+ fill=cloud_color)
199
+
200
+ def _draw_city_grid(self, draw, city):
201
+ """Grid isométrico de la ciudad"""
202
+ grid_color = (60, 80, 60) if city.cultural_identity > 50 else (80, 80, 80)
203
 
204
+ # Grid horizontal
205
+ for y in range(0, city.height, self.tile_size):
206
+ draw.line([0, y, city.width, y], fill=grid_color, width=1)
 
207
 
208
+ # Grid vertical
209
+ for x in range(0, city.width, self.tile_size):
210
+ draw.line([x, 0, x, city.height], fill=grid_color, width=1)
211
 
212
+ def _draw_isometric_buildings(self, draw, city):
213
+ """Edificios con perspectiva isométrica - TODOS LOS TIPOS"""
214
+ for (grid_x, grid_y), building_type in city.city_grid.items():
215
  screen_x = grid_x * self.tile_size
216
+ screen_y = grid_y * self.tile_size
217
 
218
+ # TODOS los tipos de edificios implementados
219
+ if building_type == 'casa':
220
+ self._draw_house_isometric(draw, screen_x, screen_y, city.happiness)
221
+ elif building_type == 'villa':
222
+ self._draw_villa_house(draw, screen_x, screen_y, city.cultural_identity)
223
+ elif building_type == 'edificio':
224
+ self._draw_apartment_building(draw, screen_x, screen_y, city.economy)
225
+ elif building_type == 'escuela':
226
+ self._draw_school(draw, screen_x, screen_y, city.resources['educacion'])
227
+ elif building_type == 'hospital':
228
+ self._draw_hospital(draw, screen_x, screen_y, city.resources['salud'])
229
+ elif building_type == 'cancha':
230
+ self._draw_soccer_field(draw, screen_x, screen_y, city.happiness)
231
+ elif building_type == 'parrilla':
232
+ self._draw_parrilla(draw, screen_x, screen_y, city.cultural_identity)
233
+ elif building_type == 'plaza':
234
+ self._draw_plaza(draw, screen_x, screen_y, city.happiness)
235
+ elif building_type == 'fabrica':
236
+ self._draw_factory(draw, screen_x, screen_y, city.resources['empleo'])
237
+ elif building_type == 'centro_cultural':
238
+ self._draw_cultural_center(draw, screen_x, screen_y, city.resources['cultura'])
239
+ elif building_type == 'kiosco':
240
+ self._draw_kiosco(draw, screen_x, screen_y, city.economy)
241
+ elif building_type == 'subte':
242
+ self._draw_subway(draw, screen_x, screen_y, city.resources['transport'])
243
+ elif building_type == 'comisaria':
244
+ self._draw_police_station(draw, screen_x, screen_y, city.resources['seguridad'])
245
 
246
+ # TODOS los métodos de dibujo de edificios (implementados completamente)
247
+ def _draw_house_isometric(self, draw, x, y, happiness):
248
+ """Casa con perspectiva isométrica"""
249
+ house_color = (139, 69, 19) if happiness > 60 else (101, 67, 33)
250
+
251
+ # Paredes
252
+ wall_points = [(x + 2, y + 15), (x + 18, y + 15), (x + 18, y + 2), (x + 2, y + 2)]
253
+ draw.polygon(wall_points, fill=house_color, outline=(80, 40, 0), width=1)
254
+
255
+ # Techo isométrico
256
+ roof_color = (160, 82, 45) if happiness > 70 else (120, 60, 30)
257
+ roof_points = [(x, y), (x + 10, y - 5), (x + 20, y), (x + 10, y + 5)]
258
+ draw.polygon(roof_points, fill=roof_color, outline=(100, 50, 25))
259
+
260
  # Puerta
261
+ draw.rectangle([x + 8, y + 10, x + 12, y + 15], fill=(101, 67, 33))
262
+
263
+ # Ventanas con luz según felicidad
264
+ window_color = (255, 255, 200) if happiness > 50 else (150, 150, 150)
265
+ draw.rectangle([x + 4, y + 8, x + 7, y + 11], fill=window_color, outline=(0, 0, 0))
266
+ draw.rectangle([x + 13, y + 8, x + 16, y + 11], fill=window_color, outline=(0, 0, 0))
267
+
268
+ # Jardincito si hay alta felicidad
269
+ if happiness > 80:
270
+ for i in range(3):
271
+ flower_x = x + 2 + i * 4
272
+ flower_y = y + 16
273
+ draw.ellipse([flower_x, flower_y, flower_x + 2, flower_y + 2], fill=(255, 100, 150))
274
 
275
+ def _draw_villa_house(self, draw, x, y, cultural_identity):
276
+ """Casa de villa con colores vibrantes"""
277
+ villa_colors = [(255, 100, 100), (100, 255, 100), (100, 100, 255), (255, 255, 100), (255, 100, 255)]
278
+ color = random.choice(villa_colors)
279
+
280
  # Casa principal
281
+ draw.rectangle([x, y + 5, x + 20, y + 18], fill=color, outline=(0, 0, 0), width=2)
282
+
283
  # Techo de chapa
284
+ draw.polygon([(x-2, y+5), (x+10, y-2), (x+22, y+5)], fill=(150, 150, 150))
285
+
286
+ # Extensión autoconstruida
287
+ if cultural_identity > 60:
288
+ draw.rectangle([x + 16, y + 12, x + 25, y + 18], fill=color, outline=(0, 0, 0))
289
+
290
+ # Antena parabólica
291
+ draw.ellipse([x + 15, y - 1, x + 18, y + 2], outline=(200, 200, 200), width=2)
292
+
293
+ # Tendedero con ropa
294
+ draw.line([(x + 2, y + 3), (x + 18, y + 3)], fill=(100, 100, 100), width=1)
295
+ for i in range(3):
296
+ cloth_x = x + 4 + i * 5
297
+ draw.rectangle([cloth_x, y + 3, cloth_x + 3, y + 8],
298
+ fill=random.choice([(255, 0, 0), (0, 255, 0), (0, 0, 255)]))
299
 
300
+ def _draw_apartment_building(self, draw, x, y, economy):
301
  """Edificio de departamentos"""
302
+ building_color = (120, 120, 160) if economy > 5000 else (100, 100, 140)
303
+
304
  # Estructura principal más alta
305
+ draw.rectangle([x + 1, y - 10, x + 23, y + 18], fill=building_color, outline=(0, 0, 0), width=1)
306
+
307
  # Ventanas en pisos
308
+ for floor in range(4):
309
+ floor_y = y - 5 + floor * 6
310
  for window in range(3):
311
  window_x = x + 4 + window * 6
312
+ light_on = economy > 3000 and random.random() < 0.7
313
+ window_color = (255, 255, 200) if light_on else (100, 100, 120)
314
  draw.rectangle([window_x, floor_y, window_x + 3, floor_y + 3],
315
+ fill=window_color, outline=(0, 0, 0))
316
 
317
+ def _draw_school(self, draw, x, y, education_level):
318
  """Escuela con bandera argentina"""
319
+ school_color = (255, 255, 100) if education_level > 70 else (200, 200, 80)
320
+
321
  # Edificio principal
322
+ draw.rectangle([x + 1, y + 8, x + 23, y + 18], fill=school_color, outline=(0, 0, 0), width=2)
323
+
324
  # Bandera argentina
325
  draw.rectangle([x + 20, y + 2, x + 22, y + 8], fill=(100, 150, 255)) # Azul
326
  draw.rectangle([x + 20, y + 4, x + 22, y + 6], fill=(255, 255, 255)) # Blanco
327
+
328
  # Asta
329
  draw.line([(x + 22, y + 8), (x + 22, y + 2)], fill=(139, 69, 19), width=2)
330
+
331
+ # Patio escolar
332
+ draw.rectangle([x + 5, y + 12, x + 19, y + 16], fill=(200, 200, 200), outline=(150, 150, 150))
333
+
334
+ # Niños en el patio si alta educación
335
+ if education_level > 60:
336
+ for i in range(3):
337
+ child_x = x + 7 + i * 4
338
+ child_y = y + 13
339
+ draw.ellipse([child_x, child_y, child_x + 2, child_y + 3], fill=(180, 140, 100))
340
 
341
+ def _draw_hospital(self, draw, x, y, health_level):
342
  """Hospital con cruz roja"""
343
+ hospital_color = (255, 100, 100) if health_level > 70 else (200, 80, 80)
344
+
345
  # Edificio
346
+ draw.rectangle([x + 1, y + 5, x + 23, y + 18], fill=hospital_color, outline=(0, 0, 0), width=2)
347
+
348
  # Cruz roja
349
+ draw.rectangle([x + 10, y + 8, x + 14, y + 15], fill=(255, 0, 0))
350
+ draw.rectangle([x + 7, y + 10, x + 17, y + 13], fill=(255, 0, 0))
351
+
352
+ # Ambulancia si hospital activo
353
+ if health_level > 50:
354
+ draw.rectangle([x + 2, y + 15, x + 10, y + 18], fill=(255, 255, 255), outline=(0, 0, 0))
355
+ draw.ellipse([x + 3, y + 16, x + 5, y + 18], fill=(0, 0, 0))
356
+ draw.ellipse([x + 7, y + 16, x + 9, y + 18], fill=(0, 0, 0))
357
 
358
+ def _draw_soccer_field(self, draw, x, y, happiness):
359
+ """Cancha de fútbol con actividad"""
360
  # Césped
361
+ grass_color = (50, 150, 50) if happiness > 60 else (80, 120, 80)
362
+ draw.rectangle([x, y, x + 40, y + 25], fill=grass_color, outline=(255, 255, 255), width=2)
363
+
364
+ # Líneas de la cancha
365
+ draw.line([(x + 20, y), (x + 20, y + 25)], fill=(255, 255, 255), width=2)
366
+ draw.ellipse([x + 15, y + 10, x + 25, y + 15], outline=(255, 255, 255), width=2)
367
+
368
  # Arcos
369
+ draw.rectangle([x, y + 8, x + 3, y + 17], outline=(255, 255, 255), width=2)
370
+ draw.rectangle([x + 37, y + 8, x + 40, y + 17], outline=(255, 255, 255), width=2)
371
+
372
+ # Jugadores animados
373
+ if happiness > 70:
374
+ for i in range(6):
375
+ player_x = x + 5 + i * 6 + int(3 * math.sin(self.animation_time + i))
376
+ player_y = y + 8 + int(2 * math.cos(self.animation_time + i * 1.5))
377
+ draw.ellipse([player_x, player_y, player_x + 3, player_y + 5], fill=(180, 140, 100))
378
+
379
  # Pelota animada
380
+ ball_x = x + 20 + int(10 * math.sin(self.animation_time * 2))
381
+ ball_y = y + 12 + int(5 * math.cos(self.animation_time * 2))
382
+ draw.ellipse([ball_x, ball_y, ball_x + 3, ball_y + 3], fill=(255, 255, 255), outline=(0, 0, 0))
383
 
384
+ def _draw_parrilla(self, draw, x, y, cultural_identity):
385
+ """Parrilla argentina con humo"""
386
  # Base de la parrilla
387
+ draw.rectangle([x + 5, y + 10, x + 15, y + 18], fill=(50, 50, 50), outline=(100, 100, 100))
388
+
389
+ # Parrilla
390
  for i in range(3):
391
+ draw.line([(x + 6 + i * 3, y + 12), (x + 6 + i * 3, y + 16)], fill=(150, 150, 150), width=2)
392
+
393
+ # Carne en la parrilla
394
+ if cultural_identity > 70:
395
+ for i in range(2):
396
+ meat_x = x + 7 + i * 4
397
+ meat_y = y + 13
398
+ draw.ellipse([meat_x, meat_y, meat_x + 3, meat_y + 2], fill=(139, 69, 19))
399
+
400
+ # Humo animado
401
+ for i in range(4):
402
+ smoke_x = x + 10 + random.randint(-3, 3)
403
+ smoke_y = y + 8 - i * 4 + int(2 * math.sin(self.animation_time * 2 + i))
404
+ size = 3 + i
405
+ draw.ellipse([smoke_x - size//2, smoke_y - size//2, smoke_x + size//2, smoke_y + size//2],
406
+ fill=(200, 200, 200))
407
+
408
+ # Gente alrededor
409
+ if cultural_identity > 60:
410
+ for i in range(3):
411
+ person_x = x + 2 + i * 8
412
+ person_y = y + 15
413
+ draw.ellipse([person_x, person_y, person_x + 4, person_y + 8], fill=(180, 140, 100))
414
 
415
+ def _draw_plaza(self, draw, x, y, happiness):
416
+ """Plaza con actividades"""
417
+ # Base verde de la plaza
418
+ plaza_color = (100, 180, 100) if happiness > 50 else (120, 140, 120)
419
+ draw.rectangle([x, y, x + 30, y + 30], fill=plaza_color, outline=(80, 120, 80))
420
+
421
  # Senderos
422
+ draw.line([(x, y + 15), (x + 30, y + 15)], fill=(160, 140, 120), width=3)
423
+ draw.line([(x + 15, y), (x + 15, y + 30)], fill=(160, 140, 120), width=3)
424
+
425
+ # Monumento central
426
+ draw.rectangle([x + 12, y + 12, x + 18, y + 18], fill=(180, 180, 180), outline=(120, 120, 120))
427
+
428
  # Árboles
429
+ tree_positions = [(x + 5, y + 5), (x + 25, y + 5), (x + 5, y + 25), (x + 25, y + 25)]
430
  for tree_x, tree_y in tree_positions:
431
  # Tronco
432
  draw.rectangle([tree_x, tree_y + 3, tree_x + 2, tree_y + 8], fill=(101, 67, 33))
433
  # Copa
434
  draw.ellipse([tree_x - 2, tree_y, tree_x + 4, tree_y + 6], fill=(50, 150, 50))
435
+
436
+ # Actividades según felicidad
437
+ if happiness > 70:
438
+ # Ronda de mate
439
+ for i in range(4):
440
+ angle = i * (math.pi / 2)
441
+ person_x = x + 15 + int(8 * math.cos(angle))
442
+ person_y = y + 15 + int(8 * math.sin(angle))
443
+ draw.ellipse([person_x, person_y, person_x + 3, person_y + 5], fill=(180, 140, 100))
444
 
445
+ def _draw_factory(self, draw, x, y, employment_level):
446
  """Fábrica con chimeneas"""
447
+ factory_color = (150, 100, 100) if employment_level > 60 else (120, 80, 80)
448
+
449
  # Edificio principal
450
+ draw.rectangle([x + 1, y + 8, x + 23, y + 18], fill=factory_color, outline=(0, 0, 0), width=2)
451
+
452
  # Chimeneas
453
  for chimney_x in [x + 6, x + 12, x + 18]:
454
  draw.rectangle([chimney_x, y + 2, chimney_x + 3, chimney_x + 8], fill=(80, 80, 80))
455
  # Humo
456
  smoke_y = y + 2 - int(3 * math.sin(self.animation_time))
457
  draw.ellipse([chimney_x, smoke_y - 2, chimney_x + 3, smoke_y + 1], fill=(150, 150, 150))
458
+
459
  # Ventanas industriales
460
  for window_x in range(x + 4, x + 20, 4):
461
  draw.rectangle([window_x, y + 12, window_x + 2, window_x + 15],
462
  fill=(255, 200, 100), outline=(0, 0, 0))
463
 
464
+ def _draw_cultural_center(self, draw, x, y, culture_level):
465
+ """Centro cultural"""
466
+ center_color = (255, 140, 0) if culture_level > 70 else (200, 100, 0)
467
+
468
+ # Edificio principal
469
+ draw.rectangle([x + 1, y + 5, x + 23, y + 18], fill=center_color, outline=(0, 0, 0), width=2)
470
+
471
+ # Cartelera cultural
472
+ draw.rectangle([x + 5, y + 8, x + 19, y + 15], fill=(255, 255, 255), outline=(0, 0, 0))
473
+
474
+ # Máscaras de teatro
475
+ draw.ellipse([x + 7, y + 10, x + 11, y + 13], fill=(255, 255, 255), outline=(0, 0, 0))
476
+ draw.ellipse([x + 13, y + 10, x + 17, y + 13], fill=(255, 255, 255), outline=(0, 0, 0))
477
+
478
+ def _draw_kiosco(self, draw, x, y, economy):
479
+ """Kiosco de barrio"""
480
+ kiosco_color = (200, 150, 100) if economy > 2000 else (150, 100, 80)
481
+
482
+ # Estructura pequeña
483
+ draw.rectangle([x + 5, y + 10, x + 15, y + 18], fill=kiosco_color, outline=(0, 0, 0), width=2)
484
+
485
+ # Cartel
486
+ draw.rectangle([x + 7, y + 8, x + 13, y + 10], fill=(255, 0, 0))
487
+
488
+ # Productos en ventana
489
+ for i in range(3):
490
+ product_x = x + 6 + i * 3
491
+ draw.rectangle([product_x, y + 12, product_x + 2, y + 15],
492
+ fill=random.choice([(255, 0, 0), (0, 255, 0), (0, 0, 255)]))
493
+
494
+ def _draw_subway(self, draw, x, y, transport_level):
495
+ """Estación de subte"""
496
+ # Entrada subterránea
497
+ draw.rectangle([x + 5, y + 10, x + 15, y + 18], fill=(100, 100, 100), outline=(0, 0, 0), width=2)
498
+
499
+ # Cartel de subte
500
+ draw.rectangle([x + 7, y + 5, x + 13, y + 8], fill=(0, 100, 200))
501
+
502
+ # Escaleras
503
+ for i in range(3):
504
+ step_y = y + 12 + i * 2
505
+ draw.line([(x + 6, step_y), (x + 14, step_y)], fill=(150, 150, 150), width=1)
506
+
507
+ def _draw_police_station(self, draw, x, y, security_level):
508
+ """Comisaría"""
509
+ station_color = (100, 100, 150) if security_level > 70 else (80, 80, 120)
510
+
511
+ # Edificio
512
+ draw.rectangle([x + 1, y + 5, x + 23, y + 18], fill=station_color, outline=(0, 0, 0), width=2)
513
+
514
+ # Escudo policial
515
+ draw.ellipse([x + 10, y + 8, x + 14, y + 12], fill=(255, 255, 255), outline=(0, 0, 0))
516
+
517
+ # Patrullero
518
+ if security_level > 50:
519
+ draw.rectangle([x + 16, y + 15, x + 22, y + 18], fill=(255, 255, 255), outline=(0, 0, 0))
520
+ draw.ellipse([x + 17, y + 17, x + 19, y + 19], fill=(0, 0, 0))
521
+ draw.ellipse([x + 20, y + 17, x + 22, y + 19], fill=(0, 0, 0))
522
+
523
+ def _draw_city_life(self, draw, city):
524
+ """Sistemas dinámicos de vida urbana"""
525
+ # Tráfico animado en calles principales
526
+ if city.resources['transport'] > 30:
527
+ self._draw_animated_traffic(draw, city)
528
+
529
+ # Personas caminando
530
+ if city.happiness > 40:
531
+ self._draw_pedestrians(draw, city)
532
+
533
+ # Efectos de contaminación
534
+ if city.resources.get('pollution', 0) < -20:
535
+ self._draw_pollution_effects(draw, city)
536
+
537
+ def _draw_animated_traffic(self, draw, city):
538
+ """Tráfico animado en las calles"""
539
+ # Colectivos argentinos
540
+ for i in range(3):
541
+ bus_x = (100 + i * 300 + int(self.animation_time * 50)) % city.width
542
+ bus_y = 200 + i * 100
543
+
544
+ # Colectivo
545
+ draw.rectangle([bus_x, bus_y, bus_x + 25, bus_y + 12],
546
+ fill=(255, 200, 0), outline=(0, 0, 0), width=2)
547
+ # Ventanas
548
+ for window in range(3):
549
+ draw.rectangle([bus_x + 3 + window * 6, bus_y + 2,
550
+ bus_x + 8 + window * 6, bus_y + 7],
551
+ fill=(150, 200, 255))
552
+ # Ruedas
553
+ draw.ellipse([bus_x + 2, bus_y + 10, bus_x + 6, bus_y + 14], fill=(50, 50, 50))
554
+ draw.ellipse([bus_x + 19, bus_y + 10, bus_x + 23, bus_y + 14], fill=(50, 50, 50))
555
+
556
+ def _draw_pedestrians(self, draw, city):
557
+ """Peatones animados"""
558
+ for i in range(8):
559
+ person_x = (50 + i * 120 + int(self.animation_time * 30 + i * 50)) % city.width
560
+ person_y = 300 + i * 40 + int(5 * math.sin(self.animation_time + i))
561
+
562
+ # Persona simple
563
+ draw.ellipse([person_x, person_y, person_x + 4, person_y + 8], fill=(180, 140, 100))
564
+
565
+ # Mate en la mano si alta cultura
566
+ if city.cultural_identity > 70 and i % 3 == 0:
567
+ draw.ellipse([person_x + 3, person_y + 2, person_x + 6, person_y + 5], fill=(100, 50, 0))
568
+
569
+ def _draw_pollution_effects(self, draw, city):
570
+ """Efectos de contaminación"""
571
+ pollution_level = abs(city.resources.get('pollution', 0))
572
+ if pollution_level > 20:
573
+ # Smog gris
574
+ for i in range(int(pollution_level / 5)):
575
+ smog_x = random.randint(0, city.width)
576
+ smog_y = random.randint(100, 300)
577
+ size = random.randint(10, 30)
578
+ draw.ellipse([smog_x - size, smog_y - size, smog_x + size, smog_y + size],
579
+ fill=(120, 120, 120))
580
+
581
+ def _draw_advanced_ui(self, draw, city):
582
+ """UI avanzada con gráficos informativos"""
583
+ # Panel principal con transparencia
584
+ panel_width = 300
585
+ panel_height = 200
586
+
587
+ # Panel de fondo con gradiente
588
+ for i in range(panel_height):
589
+ alpha = int(180 - i)
590
+ color = (0, 0, 0) if alpha > 0 else (20, 20, 20)
591
+ draw.rectangle([10, 10 + i, 10 + panel_width, 11 + i], fill=color)
592
+
593
+ # Borde elegante
594
+ draw.rectangle([10, 10, 10 + panel_width, 10 + panel_height],
595
+ outline=(100, 150, 255), width=3)
596
+
597
+ # Información de la ciudad (texto simplificado por limitaciones de PIL)
598
+ info_y = 25
599
+ city_info = [
600
+ f"Población: {city.population:,}",
601
+ f"Felicidad: {city.happiness}%",
602
+ f"Economía: ${city.economy:,}",
603
+ f"Identidad: {city.cultural_identity}%",
604
+ f"Inflación: {city.inflation:.1f}x"
605
+ ]
606
+
607
+ # Barras de recursos en el lado derecho
608
+ self._draw_resource_bars(draw, city, city.width - 250, 20)
609
+
610
+ # Mini mapa en esquina inferior derecha
611
+ self._draw_mini_map(draw, city, city.width - 150, city.height - 120)
612
+
613
+ def _draw_resource_bars(self, draw, city, start_x, start_y):
614
+ """Barras de recursos con efectos visuales"""
615
+ resources = city.resources
616
+ bar_width = 200
617
+ bar_height = 15
618
+
619
+ resource_colors = {
620
+ 'presupuesto': (255, 215, 0), 'energia': (255, 255, 0),
621
+ 'agua': (0, 191, 255), 'seguridad': (255, 99, 71),
622
+ 'educacion': (147, 112, 219), 'salud': (255, 20, 147),
623
+ 'cultura': (255, 140, 0), 'empleo': (50, 205, 50),
624
+ 'vivienda': (139, 69, 19), 'transport': (100, 149, 237),
625
+ 'pollution': (120, 120, 120)
626
+ }
627
+
628
+ y_offset = start_y
629
+ for resource, value in resources.items():
630
+ if resource == 'presupuesto':
631
+ # Presupuesto se muestra diferente
632
+ y_offset += 25
633
+ continue
634
+
635
+ # Normalizar valor para barra (0-100)
636
+ if resource == 'pollution':
637
+ normalized_value = max(0, min(100, abs(value)))
638
+ else:
639
+ normalized_value = max(0, min(100, value)) if isinstance(value, (int, float)) else 50
640
+
641
+ # Barra de fondo
642
+ draw.rectangle([start_x, y_offset, start_x + bar_width, y_offset + bar_height],
643
+ fill=(40, 40, 40), outline=(100, 100, 100))
644
+
645
+ # Barra de valor con gradiente
646
+ fill_width = int((normalized_value / 100) * (bar_width - 4))
647
+ color = resource_colors.get(resource, (150, 150, 150))
648
+
649
+ # Efecto de brillo
650
+ for i in range(fill_width):
651
+ brightness = 0.7 + 0.3 * math.sin(self.animation_time + i/10)
652
+ bright_color = tuple(int(c * brightness) for c in color)
653
+ draw.rectangle([start_x + 2 + i, y_offset + 2,
654
+ start_x + 3 + i, y_offset + bar_height - 2],
655
+ fill=bright_color)
656
+
657
+ y_offset += 22
658
+
659
+ def _draw_mini_map(self, draw, city, start_x, start_y):
660
+ """Mini mapa de la ciudad"""
661
+ map_width, map_height = 120, 100
662
+
663
+ # Fondo del mini mapa
664
+ draw.rectangle([start_x, start_y, start_x + map_width, start_y + map_height],
665
+ fill=(20, 20, 20), outline=(150, 150, 150), width=2)
666
+
667
+ # Edificios en el mini mapa
668
+ scale = 2
669
+ for (grid_x, grid_y), building_type in city.city_grid.items():
670
+ mini_x = start_x + (grid_x * scale) % map_width
671
+ mini_y = start_y + (grid_y * scale) % map_height
672
+
673
+ building_colors = {
674
+ 'casa': (100, 150, 100), 'villa': (255, 150, 100),
675
+ 'edificio': (150, 150, 200), 'escuela': (200, 200, 100),
676
+ 'hospital': (255, 100, 100), 'cancha': (100, 255, 100),
677
+ 'plaza': (150, 255, 150), 'fabrica': (150, 100, 100),
678
+ 'centro_cultural': (255, 140, 0), 'kiosco': (200, 150, 100),
679
+ 'subte': (100, 149, 237), 'comisaria': (100, 100, 150),
680
+ 'parrilla': (200, 100, 50)
681
+ }
682
+
683
+ color = building_colors.get(building_type, (100, 100, 100))
684
+ draw.rectangle([mini_x, mini_y, mini_x + scale, mini_y + scale], fill=color)
685
+
686
+ def _draw_special_effects(self, draw, city):
687
+ """Efectos especiales adicionales"""
688
+ # Efectos de festivales si alta cultura
689
+ if city.resources['cultura'] > 80:
690
+ for i in range(5):
691
+ firework_x = random.randint(100, city.width - 100)
692
+ firework_y = random.randint(50, 150)
693
+ size = random.randint(3, 8)
694
+ color = random.choice([(255, 100, 100), (100, 255, 100), (100, 100, 255), (255, 255, 100)])
695
+ draw.ellipse([firework_x - size, firework_y - size,
696
+ firework_x + size, firework_y + size], fill=color)
697
 
698
+ class EnhancedCityGame:
699
+ """Juego de ciudad argentino mejorado COMPLETO"""
700
  def __init__(self):
701
+ self.city = ArgentineCityBuilder()
702
+ self.renderer = AdvancedCityRenderer()
703
  self.ai_planner = AIUrbanPlanner()
704
+ self.selected_building = None
705
+ self.game_speed = 1
706
+ self.events_log = []
707
  self.day = 1
708
 
709
  def place_building(self, building_type, grid_x, grid_y):
710
+ """Coloca un edificio en la ciudad - CORREGIDO"""
711
  try:
712
  grid_x, grid_y = int(grid_x), int(grid_y)
713
 
714
  if building_type not in self.city.building_types:
715
+ return "Tipo de edificio inválido", self.get_city_status(), "Error: Tipo de edificio no válido"
716
 
717
+ if (grid_x, grid_y) in self.city.city_grid:
718
+ return "Ya hay un edificio ahí", self.get_city_status(), "Error: Posición ocupada"
719
 
720
+ building_info = self.city.building_types[building_type]
721
+ cost = building_info['cost']
 
722
 
723
+ if self.city.resources['presupuesto'] < cost:
724
+ return f"No tenés suficiente presupuesto (${cost:,})", self.get_city_status(), "Error: Sin presupuesto"
 
725
 
726
+ # Construir edificio
727
+ self.city.resources['presupuesto'] -= cost
728
+ self.city.city_grid[(grid_x, grid_y)] = building_type
729
+
730
+ # Aplicar efectos CORREGIDOS
731
+ self._apply_building_effects(building_type, building_info)
732
+
733
+ # Actualizar estadísticas
734
+ self._update_city_stats()
735
 
736
  # Generar consejo IA
737
  advice = self.ai_planner.generate_city_advice({
738
  'population': self.city.population,
739
  'happiness': self.city.happiness,
740
+ 'budget': self.city.resources['presupuesto'],
741
+ 'buildings': len(self.city.city_grid),
742
+ 'culture': self.city.cultural_identity
743
  })
744
 
745
+ frame = self.renderer.render_city_frame(self.city)
746
+ status = self.get_city_status()
747
+ message = f"✅ {building_type.replace('_', ' ').title()} construido por ${cost:,}"
748
 
749
+ return frame, status, f"{message}\n\n🤖 IA Urbana: {advice}"
750
 
751
  except Exception as e:
752
+ print(f"Error en construcción: {e}")
753
+ return self.renderer.render_city_frame(self.city), self.get_city_status(), f"Error: {str(e)}"
754
+
755
+ def _apply_building_effects(self, building_type, building_info):
756
+ """Aplica los efectos de un edificio a la ciudad - CORREGIDO"""
757
+ for effect, value in building_info.items():
758
+ if effect == 'cost':
759
+ continue
760
+ elif effect == 'pop':
761
+ self.city.population += value
762
+ elif effect == 'happiness':
763
+ self.city.happiness = min(100, max(0, self.city.happiness + value))
764
+ elif effect == 'solidarity':
765
+ self.city.cultural_identity = min(100, max(0, self.city.cultural_identity + value))
766
+ elif effect in self.city.resources:
767
+ if effect == 'pollution':
768
+ # Pollution es negativa, así que sumamos el valor negativo
769
+ self.city.resources[effect] += value
770
+ else:
771
+ current_value = self.city.resources[effect]
772
+ self.city.resources[effect] = min(100, max(0, current_value + value))
773
+
774
+ def _update_city_stats(self):
775
+ """Actualiza estadísticas generales de la ciudad"""
776
+ # Calcular economía basada en edificios
777
+ economy_buildings = ['fabrica', 'kiosco', 'centro_cultural', 'subte']
778
+ economy_bonus = sum(5000 for pos, building in self.city.city_grid.items()
779
+ if building in economy_buildings)
780
+ self.city.economy = 1000 + economy_bonus + self.city.population * 30
781
+
782
+ # Inflación aumenta con el tiempo y población
783
+ self.city.inflation += 0.005 + (len(self.city.city_grid) * 0.001)
784
+
785
+ # Felicidad general basada en múltiples factores
786
+ happiness_factors = [
787
+ self.city.resources.get('cultura', 50),
788
+ self.city.resources.get('seguridad', 50),
789
+ self.city.resources.get('empleo', 50),
790
+ self.city.resources.get('salud', 50),
791
+ self.city.resources.get('educacion', 50),
792
+ self.city.cultural_identity
793
+ ]
794
+ self.city.happiness = sum(happiness_factors) // len(happiness_factors)
795
+
796
+ # Limitar felicidad
797
+ self.city.happiness = max(0, min(100, self.city.happiness))
798
 
799
  def simulate_day(self):
800
+ """Simula un día en la ciudad"""
801
  try:
802
  self.day += 1
803
 
804
+ # Ingresos diarios
805
+ base_income = self.city.population * 12
806
+ economy_bonus = self.city.economy * 0.008
807
+ daily_income = int(base_income + economy_bonus)
808
+ self.city.resources['presupuesto'] += daily_income
809
 
810
  # Gastos de mantenimiento
811
+ maintenance_cost = len(self.city.city_grid) * 80
812
+ self.city.resources['presupuesto'] -= maintenance_cost
813
+
814
+ # Efectos de la inflación (más realista)
815
+ inflation_impact = int(self.city.resources['presupuesto'] * 0.01)
816
+ self.city.resources['presupuesto'] -= inflation_impact
817
 
818
  # Eventos aleatorios
819
  event_message = ""
820
+ if random.random() < 0.25:
821
+ event_message = self._trigger_city_event()
822
+
823
+ # Actualizar todo
824
+ self._update_city_stats()
825
+
826
+ # Generar análisis IA del día
827
+ ai_analysis = self.ai_planner.generate_city_advice({
828
+ 'population': self.city.population,
829
+ 'happiness': self.city.happiness,
830
+ 'budget': self.city.resources['presupuesto'],
831
+ 'buildings': len(self.city.city_grid),
832
+ 'culture': self.city.cultural_identity
833
+ })
 
 
 
 
 
 
 
 
 
 
834
 
835
+ frame = self.renderer.render_city_frame(self.city)
836
+ status = self.get_city_status()
837
 
838
+ message = f"📅 Día {self.day} completado\n💰 Ingresos: ${daily_income:,}\n💸 Gastos: ${maintenance_cost:,}"
839
  if event_message:
840
+ message += f"\n📰 {event_message}"
841
+ message += f"\n\n🤖 Análisis IA: {ai_analysis}"
842
 
843
  return frame, status, message
844
 
845
  except Exception as e:
846
+ print(f"Error en simulación: {e}")
847
+ return self.renderer.render_city_frame(self.city), self.get_city_status(), f"Error: {str(e)}"
848
+
849
+ def _trigger_city_event(self):
850
+ """Eventos aleatorios de la ciudad - MEJORADOS"""
851
+ events = [
852
+ ("🎉 Festival de Tango en San Telmo", "cultura", 15, "happiness", 20),
853
+ ("⚽ Superclásico River vs Boca", "happiness", 30, "cultura", 10),
854
+ ("🏭 Inversión extranjera en fábrica", "empleo", 20, "presupuesto", 8000),
855
+ ("🌧️ Lluvia beneficiosa para la ciudad", "agua", 25, "energia", -5),
856
+ ("📚 Programa nacional de alfabetización", "educacion", 18, "cultura", 12),
857
+ ("🏥 Campaña de vacunación masiva", "salud", 25, "happiness", 15),
858
+ ("💸 Crisis económica menor", "presupuesto", -5000, "happiness", -8),
859
+ ("🎭 Inauguración de centro cultural", "cultura", 30, "educacion", 15),
860
+ ("🚌 Mejoras en transporte público", "transport", 20, "happiness", 12),
861
+ ("🛡️ Operativo de seguridad exitoso", "seguridad", 15, "happiness", 8),
862
+ ("🏠 Programa de vivienda social", "vivienda", 25, "happiness", 18),
863
+ ("🔥 Festival del Asado Argentino", "cultura", 20, "happiness", 25)
864
+ ]
865
+
866
+ event_name, res1, val1, res2, val2 = random.choice(events)
867
+
868
+ # Aplicar efectos
869
+ if res1 == 'presupuesto':
870
+ self.city.resources[res1] += val1
871
+ elif res1 == 'happiness':
872
+ self.city.happiness = min(100, max(0, self.city.happiness + val1))
873
+ elif res1 in self.city.resources:
874
+ self.city.resources[res1] = min(100, max(0, self.city.resources[res1] + val1))
875
+
876
+ if res2 == 'presupuesto':
877
+ self.city.resources[res2] += val2
878
+ elif res2 == 'happiness':
879
+ self.city.happiness = min(100, max(0, self.city.happiness + val2))
880
+ elif res2 in self.city.resources:
881
+ self.city.resources[res2] = min(100, max(0, self.city.resources[res2] + val2))
882
+
883
+ self.events_log.append(event_name)
884
+ return event_name
885
 
886
+ def get_city_status(self):
887
+ """Status completo de la ciudad"""
888
  try:
889
+ building_count = len(self.city.city_grid)
890
  return {
891
  "🏙️ Población": f"{self.city.population:,} habitantes",
892
  "😊 Felicidad": f"{self.city.happiness}%",
893
+ "💰 Presupuesto": f"${self.city.resources['presupuesto']:,}",
894
+ "📊 Economía": f"${self.city.economy:,}",
895
+ "🇦🇷 Identidad": f"{self.city.cultural_identity}%",
896
+ "📈 Inflación": f"{self.city.inflation:.2f}x",
897
  "🏗️ Edificios": f"{building_count} construidos",
898
  "📅 Día": self.day,
899
  "⚡ Energía": f"{self.city.resources['energia']}%",
900
  "💧 Agua": f"{self.city.resources['agua']}%",
901
+ "🎓 Educación": f"{self.city.resources['educacion']}%",
902
+ "🏥 Salud": f"{self.city.resources['salud']}%",
903
+ "🎭 Cultura": f"{self.city.resources['cultura']}%",
904
+ "💼 Empleo": f"{self.city.resources['empleo']}%"
905
  }
906
  except Exception as e:
907
  return {"Error": str(e)}
908
 
909
  def create_simcity_interface():
910
+ """Interfaz del SimCity Argentino COMPLETA"""
911
+ game = EnhancedCityGame()
912
 
913
  with gr.Blocks(
914
+ title="🇦🇷 SIMCIUDAD ARGENTINA - Construcción Épica",
915
+ theme=gr.themes.Glass(),
916
+ css="""
917
+ .building-btn { font-size: 1.0em; padding: 8px; margin: 2px; }
918
+ .city-header { background: linear-gradient(45deg, #74b9ff, #0984e3, #00b894); }
919
+ .status-panel { background: #2d3436; border-radius: 10px; }
920
+ """
921
  ) as interface:
922
 
923
  gr.HTML("""
924
+ <div class="city-header" style="padding: 25px; text-align: center; border-radius: 15px; margin: 15px;">
925
+ <h1 style="color: white; font-size: 2.8em; margin: 0; text-shadow: 3px 3px 6px rgba(0,0,0,0.7);">
 
926
  🏙️ SIMCIUDAD ARGENTINA 🇦🇷
927
  </h1>
928
+ <h2 style="color: #ddd; font-size: 1.2em; margin: 10px 0;">
929
+ Construí la Ciudad Argentina de tus Sueños con IA
930
+ </h2>
931
+ <p style="color: #f1f2f6; font-size: 1.0em;">
932
+ Gráficos Isométricos • Mecánicas Realistas • Cultura Auténtica • Powered by Groq AI
933
  </p>
934
  </div>
935
  """)
936
 
937
  with gr.Row():
938
  with gr.Column(scale=2):
 
939
  city_display = gr.Image(
940
  label="🌆 Tu Ciudad Argentina",
941
  width=1000,
942
  height=700,
943
+ value=game.renderer.render_city_frame(game.city)
944
  )
945
 
946
  gr.Markdown("### 🏗️ HERRAMIENTAS DE CONSTRUCCIÓN")
947
+ with gr.Row():
948
+ grid_x_input = gr.Number(label="Coordenada X", value=20, minimum=0, maximum=45)
949
+ grid_y_input = gr.Number(label="Coordenada Y", value=20, minimum=0, maximum=30)
950
 
951
+ # Edificios residenciales
952
+ gr.Markdown("#### 🏠 RESIDENCIALES")
953
  with gr.Row():
954
+ house_btn = gr.Button("🏠 CASA ($1K)", variant="secondary", elem_classes="building-btn")
955
+ villa_btn = gr.Button("🏘️ VILLA ($500)", variant="secondary", elem_classes="building-btn")
956
+ building_btn = gr.Button("🏢 EDIFICIO ($5K)", variant="primary", elem_classes="building-btn")
957
+ kiosco_btn = gr.Button("🏪 KIOSCO ($800)", variant="secondary", elem_classes="building-btn")
958
 
959
+ # Servicios públicos
960
+ gr.Markdown("#### 🏛️ SERVICIOS PÚBLICOS")
961
  with gr.Row():
962
+ school_btn = gr.Button("🏫 ESCUELA ($8K)", variant="primary", elem_classes="building-btn")
963
+ hospital_btn = gr.Button("🏥 HOSPITAL ($15K)", variant="stop", elem_classes="building-btn")
964
+ police_btn = gr.Button("🛡️ COMISARÍA ($7K)", variant="stop", elem_classes="building-btn")
965
+ subway_btn = gr.Button("🚇 SUBTE ($20K)", variant="primary", elem_classes="building-btn")
966
 
967
+ # Cultura y entretenimiento
968
+ gr.Markdown("#### 🎭 CULTURA Y ENTRETENIMIENTO")
969
  with gr.Row():
970
+ field_btn = gr.Button(" CANCHA ($3K)", variant="secondary", elem_classes="building-btn")
971
+ grill_btn = gr.Button("🔥 PARRILLA ($2K)", variant="stop", elem_classes="building-btn")
972
+ cultural_btn = gr.Button("🎭 C.CULTURAL ($10K)", variant="primary", elem_classes="building-btn")
973
+ plaza_btn = gr.Button("🌳 PLAZA ($1.5K)", variant="secondary", elem_classes="building-btn")
974
 
975
+ # Industria
976
+ gr.Markdown("#### 🏭 INDUSTRIA")
977
  with gr.Row():
978
+ factory_btn = gr.Button("🏭 FÁBRICA ($12K)", variant="primary", elem_classes="building-btn")
 
 
979
 
980
+ # Simulación
981
  with gr.Row():
982
+ simulate_btn = gr.Button("⏭️ SIMULAR DÍA", variant="primary", size="lg")
983
 
984
  with gr.Column(scale=1):
 
985
  gr.Markdown("### 📊 ESTADO DE LA CIUDAD")
986
+ city_status = gr.JSON(
987
+ label="📈 Estadísticas Completas",
988
+ value=game.get_city_status()
989
  )
990
 
991
+ gr.Markdown("### 📰 NOTICIAS Y EVENTOS")
992
+ events_display = gr.Textbox(
993
+ 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.",
994
+ label="📺 Canal de Noticias Urbanas",
995
+ lines=6,
 
996
  interactive=False
997
  )
998
 
 
999
  gr.Markdown("### 💰 GUÍA DE CONSTRUCCIÓN")
1000
  gr.HTML("""
1001
+ <div style="background: #2d3436; color: white; padding: 15px; border-radius: 10px; font-size: 0.85em;">
1002
+ <p><strong>💵 Precios y Efectos:</strong></p>
1003
  <ul style="margin: 5px 0; padding-left: 15px;">
1004
+ <li>🏠 Casa: $1,000 (+4 pop, +2 felicidad)</li>
1005
+ <li>🏘️ Villa: $500 (+8 pop, -2 felicidad, +15 solidaridad)</li>
1006
+ <li>🏢 Edificio: $5,000 (+20 pop, +1 felicidad)</li>
1007
+ <li>🏫 Escuela: $8,000 (+20 educación)</li>
1008
+ <li>🏥 Hospital: $15,000 (+30 salud)</li>
1009
+ <li>⚽ Cancha: $3,000 (+25 felicidad, +10 cultura)</li>
1010
+ <li>🔥 Parrilla: $2,000 (+20 felicidad, +15 cultura)</li>
1011
+ <li>🌳 Plaza: $1,500 (+15 felicidad, +5 cultura)</li>
1012
+ <li>🏭 Fábrica: $12,000 (+30 empleo, -10 pollution)</li>
1013
+ <li>🎭 C.Cultural: $10,000 (+40 cultura)</li>
1014
+ <li>🛡️ Comisaría: $7,000 (+25 seguridad)</li>
1015
+ <li>🚇 Subte: $20,000 (+50 transporte)</li>
1016
+ <li>🏪 Kiosco: $800 (+5 felicidad, +2 empleo)</li>
1017
  </ul>
1018
  </div>
1019
  """)
1020
 
1021
+ # Funciones de construcción mejoradas
1022
  def build_structure(building_type, x, y):
1023
  return game.place_building(building_type, x, y)
1024
 
1025
+ # Conectar TODOS los botones
1026
+ house_btn.click(fn=lambda x, y: build_structure("casa", x, y),
1027
+ inputs=[grid_x_input, grid_y_input],