Lukeetah commited on
Commit
1cb2dab
·
verified ·
1 Parent(s): 471af9b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +814 -0
app.py ADDED
@@ -0,0 +1,814 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py - SIMCIUDAD ARGENTINA: REVOLUCIÓN GRÁFICA
2
+ import gradio as gr
3
+ import time
4
+ import math
5
+ import random
6
+ import os
7
+ from PIL import Image, ImageDraw, ImageFont
8
+ import json
9
+
10
+ try:
11
+ from groq import Groq
12
+ GROQ_AVAILABLE = True
13
+ except ImportError:
14
+ GROQ_AVAILABLE = False
15
+
16
+ class ArgentineCityBuilder:
17
+ """SimCity Argentino con mecánicas revolucionarias"""
18
+ def __init__(self):
19
+ self.width, self.height = 1000, 700
20
+ self.grid_size = 20
21
+ self.city_grid = {} # Coordenadas -> tipo de edificio
22
+ self.population = 100
23
+ self.happiness = 75
24
+ self.economy = 1000
25
+ self.inflation = 1.0
26
+ self.cultural_identity = 90
27
+
28
+ # Sistemas dinámicos
29
+ self.neighborhoods = {}
30
+ self.transport_network = {}
31
+ self.cultural_centers = {}
32
+ self.businesses = {}
33
+
34
+ # Recursos complejos
35
+ self.resources = {
36
+ 'presupuesto': 50000,
37
+ 'energia': 100,
38
+ 'agua': 100,
39
+ 'seguridad': 70,
40
+ 'educacion': 60,
41
+ 'salud': 65,
42
+ 'cultura': 80,
43
+ 'empleo': 75,
44
+ 'vivienda': 50
45
+ }
46
+
47
+ # Edificios disponibles
48
+ self.building_types = {
49
+ 'casa': {'cost': 1000, 'pop': 4, 'happiness': 2},
50
+ 'villa': {'cost': 500, 'pop': 8, 'happiness': -2, 'solidarity': 15},
51
+ 'edificio': {'cost': 5000, 'pop': 20, 'happiness': 1},
52
+ 'escuela': {'cost': 8000, 'education': 20, 'happiness': 10},
53
+ 'hospital': {'cost': 15000, 'health': 30, 'happiness': 15},
54
+ 'cancha': {'cost': 3000, 'happiness': 25, 'culture': 10},
55
+ 'parrilla': {'cost': 2000, 'happiness': 20, 'culture': 15},
56
+ 'centro_cultural': {'cost': 10000, 'culture': 40, 'education': 10},
57
+ 'fabrica': {'cost': 12000, 'employment': 30, 'pollution': -10},
58
+ 'plaza': {'cost': 1500, 'happiness': 15, 'culture': 5},
59
+ 'subte': {'cost': 20000, 'transport': 50, 'happiness': 10},
60
+ 'comisaria': {'cost': 7000, 'security': 25, 'happiness': -5},
61
+ 'kiosco': {'cost': 800, 'happiness': 5, 'employment': 2}
62
+ }
63
+
64
+ # Inicializar ciudad base
65
+ self._initialize_base_city()
66
+
67
+ def _initialize_base_city(self):
68
+ """Crea la ciudad inicial con algunos edificios"""
69
+ # Plaza central
70
+ self.city_grid[(25, 17)] = 'plaza'
71
+
72
+ # Algunas casas iniciales
73
+ initial_houses = [(20, 15), (30, 15), (25, 12), (25, 22)]
74
+ for pos in initial_houses:
75
+ self.city_grid[pos] = 'casa'
76
+
77
+ # Una escuela inicial
78
+ self.city_grid[(15, 20)] = 'escuela'
79
+
80
+ class AdvancedCityRenderer:
81
+ """Renderizador avanzado con gráficos isométricos"""
82
+ def __init__(self):
83
+ self.animation_time = 0
84
+ self.tile_size = 20
85
+
86
+ def render_city_frame(self, city_builder) -> Image.Image:
87
+ """Renderiza la ciudad con vista isométrica mejorada"""
88
+ img = Image.new('RGB', (city_builder.width, city_builder.height), (20, 40, 20))
89
+ draw = ImageDraw.Draw(img)
90
+
91
+ self.animation_time += 0.1
92
+
93
+ # Fondo: Cielo dinámico según felicidad
94
+ self._draw_dynamic_sky(draw, city_builder)
95
+
96
+ # Grid base de la ciudad
97
+ self._draw_city_grid(draw, city_builder)
98
+
99
+ # Edificios con perspectiva isométrica
100
+ self._draw_isometric_buildings(draw, city_builder)
101
+
102
+ # Sistemas dinámicos (tráfico, personas, etc.)
103
+ self._draw_city_life(draw, city_builder)
104
+
105
+ # UI avanzada
106
+ self._draw_advanced_ui(draw, city_builder)
107
+
108
+ # Efectos especiales
109
+ self._draw_special_effects(draw, city_builder)
110
+
111
+ return img
112
+
113
+ def _draw_dynamic_sky(self, draw, city):
114
+ """Cielo que cambia según el estado de la ciudad"""
115
+ happiness = city.happiness
116
+
117
+ # Color base del cielo
118
+ if happiness > 80:
119
+ sky_color = (135, 206, 250) # Azul cielo feliz
120
+ elif happiness > 60:
121
+ sky_color = (176, 196, 222) # Azul grisáceo
122
+ elif happiness > 40:
123
+ sky_color = (119, 136, 153) # Gris claro
124
+ else:
125
+ sky_color = (105, 105, 105) # Gris oscuro
126
+
127
+ # Gradiente de cielo
128
+ for y in range(200):
129
+ alpha = 1.0 - (y / 400)
130
+ color = tuple(int(c * alpha + 20 * (1-alpha)) for c in sky_color)
131
+ draw.rectangle([0, y, city.width, y+2], fill=color)
132
+
133
+ # Nubes dinámicas
134
+ cloud_offset = int(self.animation_time * 20) % city.width
135
+ for i in range(3):
136
+ x = (i * 300 + cloud_offset) % (city.width + 100)
137
+ y = 50 + i * 30
138
+ self._draw_cloud(draw, x, y, happiness)
139
+
140
+ def _draw_cloud(self, draw, x, y, happiness):
141
+ """Dibuja nube con forma según felicidad"""
142
+ cloud_color = (255, 255, 255, 180) if happiness > 50 else (200, 200, 200, 150)
143
+
144
+ # Forma de nube
145
+ for i in range(5):
146
+ offset_x = i * 15 + random.randint(-5, 5)
147
+ offset_y = random.randint(-8, 8)
148
+ size = 20 + random.randint(-5, 10)
149
+ draw.ellipse([x + offset_x - size//2, y + offset_y - size//2,
150
+ x + offset_x + size//2, y + offset_y + size//2],
151
+ fill=cloud_color[:3])
152
+
153
+ def _draw_city_grid(self, draw, city):
154
+ """Grid isométrico de la ciudad"""
155
+ grid_color = (60, 80, 60) if city.cultural_identity > 50 else (80, 80, 80)
156
+
157
+ # Grid horizontal
158
+ for y in range(0, city.height, self.tile_size):
159
+ draw.line([0, y, city.width, y], fill=grid_color, width=1)
160
+
161
+ # Grid vertical
162
+ for x in range(0, city.width, self.tile_size):
163
+ draw.line([x, 0, x, city.height], fill=grid_color, width=1)
164
+
165
+ def _draw_isometric_buildings(self, draw, city):
166
+ """Edificios con perspectiva isométrica"""
167
+ for (grid_x, grid_y), building_type in city.city_grid.items():
168
+ screen_x = grid_x * self.tile_size
169
+ screen_y = grid_y * self.tile_size
170
+
171
+ if building_type == 'casa':
172
+ self._draw_house_isometric(draw, screen_x, screen_y, city.happiness)
173
+ elif building_type == 'villa':
174
+ self._draw_villa_house(draw, screen_x, screen_y, city.cultural_identity)
175
+ elif building_type == 'edificio':
176
+ self._draw_apartment_building(draw, screen_x, screen_y, city.economy)
177
+ elif building_type == 'escuela':
178
+ self._draw_school(draw, screen_x, screen_y, city.resources['educacion'])
179
+ elif building_type == 'hospital':
180
+ self._draw_hospital(draw, screen_x, screen_y, city.resources['salud'])
181
+ elif building_type == 'cancha':
182
+ self._draw_soccer_field(draw, screen_x, screen_y, city.happiness)
183
+ elif building_type == 'parrilla':
184
+ self._draw_parrilla(draw, screen_x, screen_y, city.cultural_identity)
185
+ elif building_type == 'plaza':
186
+ self._draw_plaza(draw, screen_x, screen_y, city.happiness)
187
+ elif building_type == 'fabrica':
188
+ self._draw_factory(draw, screen_x, screen_y, city.resources['empleo'])
189
+ elif building_type == 'centro_cultural':
190
+ self._draw_cultural_center(draw, screen_x, screen_y, city.resources['cultura'])
191
+ elif building_type == 'kiosco':
192
+ self._draw_kiosco(draw, screen_x, screen_y, city.economy)
193
+
194
+ def _draw_house_isometric(self, draw, x, y, happiness):
195
+ """Casa con perspectiva isométrica"""
196
+ # Base de la casa
197
+ house_color = (139, 69, 19) if happiness > 60 else (101, 67, 33)
198
+
199
+ # Paredes (vista isométrica)
200
+ wall_points = [
201
+ (x + 2, y + 15), (x + 18, y + 15),
202
+ (x + 18, y + 2), (x + 2, y + 2)
203
+ ]
204
+ draw.polygon(wall_points, fill=house_color, outline=(80, 40, 0), width=1)
205
+
206
+ # Techo isométrico
207
+ roof_color = (160, 82, 45) if happiness > 70 else (120, 60, 30)
208
+ roof_points = [
209
+ (x, y), (x + 10, y - 5), (x + 20, y), (x + 10, y + 5)
210
+ ]
211
+ draw.polygon(roof_points, fill=roof_color, outline=(100, 50, 25))
212
+
213
+ # Puerta
214
+ draw.rectangle([x + 8, y + 10, x + 12, y + 15], fill=(101, 67, 33))
215
+
216
+ # Ventanas con luz según felicidad
217
+ window_color = (255, 255, 200) if happiness > 50 else (150, 150, 150)
218
+ draw.rectangle([x + 4, y + 8, x + 7, y + 11], fill=window_color, outline=(0, 0, 0))
219
+ draw.rectangle([x + 13, y + 8, x + 16, y + 11], fill=window_color, outline=(0, 0, 0))
220
+
221
+ # Jardincito si hay alta felicidad
222
+ if happiness > 80:
223
+ for i in range(3):
224
+ flower_x = x + 2 + i * 4
225
+ flower_y = y + 16
226
+ draw.ellipse([flower_x, flower_y, flower_x + 2, flower_y + 2],
227
+ fill=(255, 100, 150))
228
+
229
+ def _draw_villa_house(self, draw, x, y, cultural_identity):
230
+ """Casa de villa con colores vibrantes"""
231
+ villa_colors = [(255, 100, 100), (100, 255, 100), (100, 100, 255),
232
+ (255, 255, 100), (255, 100, 255)]
233
+ color = random.choice(villa_colors)
234
+
235
+ # Casa principal más grande
236
+ draw.rectangle([x, y + 5, x + 20, y + 18], fill=color, outline=(0, 0, 0), width=2)
237
+
238
+ # Techo de chapa
239
+ draw.polygon([(x-2, y+5), (x+10, y-2), (x+22, y+5)], fill=(150, 150, 150))
240
+
241
+ # Extensión autoconstruida
242
+ if cultural_identity > 60:
243
+ draw.rectangle([x + 16, y + 12, x + 25, y + 18], fill=color, outline=(0, 0, 0))
244
+
245
+ # Antena parabólica
246
+ draw.ellipse([x + 15, y - 1, x + 18, y + 2], outline=(200, 200, 200), width=2)
247
+
248
+ # Tendedero con ropa
249
+ draw.line([(x + 2, y + 3), (x + 18, y + 3)], fill=(100, 100, 100), width=1)
250
+ for i in range(3):
251
+ cloth_x = x + 4 + i * 5
252
+ draw.rectangle([cloth_x, y + 3, cloth_x + 3, y + 8],
253
+ fill=random.choice([(255, 0, 0), (0, 255, 0), (0, 0, 255)]))
254
+
255
+ def _draw_soccer_field(self, draw, x, y, happiness):
256
+ """Cancha de fútbol con actividad"""
257
+ # Césped
258
+ grass_color = (50, 150, 50) if happiness > 60 else (80, 120, 80)
259
+ draw.rectangle([x, y, x + 40, y + 25], fill=grass_color, outline=(255, 255, 255), width=2)
260
+
261
+ # Líneas de la cancha
262
+ draw.line([(x + 20, y), (x + 20, y + 25)], fill=(255, 255, 255), width=2)
263
+ draw.ellipse([x + 15, y + 10, x + 25, y + 15], outline=(255, 255, 255), width=2)
264
+
265
+ # Arcos
266
+ draw.rectangle([x, y + 8, x + 3, y + 17], outline=(255, 255, 255), width=2)
267
+ draw.rectangle([x + 37, y + 8, x + 40, y + 17], outline=(255, 255, 255), width=2)
268
+
269
+ # Jugadores animados
270
+ if happiness > 70:
271
+ for i in range(6):
272
+ player_x = x + 5 + i * 6 + int(3 * math.sin(self.animation_time + i))
273
+ player_y = y + 8 + int(2 * math.cos(self.animation_time + i * 1.5))
274
+ draw.ellipse([player_x, player_y, player_x + 3, player_y + 5],
275
+ fill=(180, 140, 100))
276
+
277
+ # Pelota animada
278
+ ball_x = x + 20 + int(10 * math.sin(self.animation_time * 2))
279
+ ball_y = y + 12 + int(5 * math.cos(self.animation_time * 2))
280
+ draw.ellipse([ball_x, ball_y, ball_x + 3, ball_y + 3], fill=(255, 255, 255), outline=(0, 0, 0))
281
+
282
+ def _draw_parrilla(self, draw, x, y, cultural_identity):
283
+ """Parrilla argentina con humo"""
284
+ # Base de la parrilla
285
+ draw.rectangle([x + 5, y + 10, x + 15, y + 18], fill=(50, 50, 50), outline=(100, 100, 100))
286
+
287
+ # Parrilla
288
+ for i in range(3):
289
+ draw.line([(x + 6 + i * 3, y + 12), (x + 6 + i * 3, y + 16)],
290
+ fill=(150, 150, 150), width=2)
291
+
292
+ # Carne en la parrilla
293
+ if cultural_identity > 70:
294
+ for i in range(2):
295
+ meat_x = x + 7 + i * 4
296
+ meat_y = y + 13
297
+ draw.ellipse([meat_x, meat_y, meat_x + 3, meat_y + 2], fill=(139, 69, 19))
298
+
299
+ # Humo animado
300
+ for i in range(4):
301
+ smoke_x = x + 10 + random.randint(-3, 3)
302
+ smoke_y = y + 8 - i * 4 + int(2 * math.sin(self.animation_time * 2 + i))
303
+ size = 3 + i
304
+ draw.ellipse([smoke_x - size//2, smoke_y - size//2,
305
+ smoke_x + size//2, smoke_y + size//2],
306
+ fill=(200, 200, 200, 150 - i * 30))
307
+
308
+ # Gente alrededor
309
+ if cultural_identity > 60:
310
+ for i in range(3):
311
+ person_x = x + 2 + i * 8
312
+ person_y = y + 15
313
+ draw.ellipse([person_x, person_y, person_x + 4, person_y + 8],
314
+ fill=(180, 140, 100))
315
+
316
+ def _draw_plaza(self, draw, x, y, happiness):
317
+ """Plaza con actividades"""
318
+ # Base verde de la plaza
319
+ plaza_color = (100, 180, 100) if happiness > 50 else (120, 140, 120)
320
+ draw.rectangle([x, y, x + 30, y + 30], fill=plaza_color, outline=(80, 120, 80))
321
+
322
+ # Senderos
323
+ draw.line([(x, y + 15), (x + 30, y + 15)], fill=(160, 140, 120), width=3)
324
+ draw.line([(x + 15, y), (x + 15, y + 30)], fill=(160, 140, 120), width=3)
325
+
326
+ # Monumento central
327
+ draw.rectangle([x + 12, y + 12, x + 18, y + 18], fill=(180, 180, 180), outline=(120, 120, 120))
328
+
329
+ # Árboles
330
+ for tree_pos in [(x + 5, y + 5), (x + 25, y + 5), (x + 5, y + 25), (x + 25, y + 25)]:
331
+ # Tronco
332
+ draw.rectangle([tree_pos[0], tree_pos[1] + 5, tree_pos[0] + 2, tree_pos[1] + 10],
333
+ fill=(101, 67, 33))
334
+ # Copa
335
+ draw.ellipse([tree_pos[0] - 3, tree_pos[1], tree_pos[0] + 5, tree_pos[1] + 8],
336
+ fill=(50, 150, 50))
337
+
338
+ # Actividades según felicidad
339
+ if happiness > 70:
340
+ # Ronda de mate
341
+ for i in range(4):
342
+ angle = i * (math.pi / 2)
343
+ person_x = x + 15 + int(8 * math.cos(angle))
344
+ person_y = y + 15 + int(8 * math.sin(angle))
345
+ draw.ellipse([person_x, person_y, person_x + 3, person_y + 5],
346
+ fill=(180, 140, 100))
347
+
348
+ def _draw_city_life(self, draw, city):
349
+ """Sistemas dinámicos de vida urbana"""
350
+ # Tráfico animado en calles principales
351
+ if city.resources['transport'] > 30:
352
+ self._draw_animated_traffic(draw, city)
353
+
354
+ # Personas caminando
355
+ if city.happiness > 40:
356
+ self._draw_pedestrians(draw, city)
357
+
358
+ # Efectos de contaminación
359
+ if city.resources.get('pollution', 0) < -20:
360
+ self._draw_pollution_effects(draw, city)
361
+
362
+ def _draw_animated_traffic(self, draw, city):
363
+ """Tráfico animado en las calles"""
364
+ # Autobuses y colectivos
365
+ for i in range(3):
366
+ bus_x = (100 + i * 300 + int(self.animation_time * 50)) % city.width
367
+ bus_y = 200 + i * 100
368
+
369
+ # Colectivo argentino
370
+ draw.rectangle([bus_x, bus_y, bus_x + 25, bus_y + 12],
371
+ fill=(255, 200, 0), outline=(0, 0, 0), width=2)
372
+ # Ventanas
373
+ for window in range(3):
374
+ draw.rectangle([bus_x + 3 + window * 6, bus_y + 2,
375
+ bus_x + 8 + window * 6, bus_y + 7],
376
+ fill=(150, 200, 255))
377
+ # Ruedas
378
+ draw.ellipse([bus_x + 2, bus_y + 10, bus_x + 6, bus_y + 14], fill=(50, 50, 50))
379
+ draw.ellipse([bus_x + 19, bus_y + 10, bus_x + 23, bus_y + 14], fill=(50, 50, 50))
380
+
381
+ def _draw_pedestrians(self, draw, city):
382
+ """Peatones animados"""
383
+ for i in range(8):
384
+ person_x = (50 + i * 120 + int(self.animation_time * 30 + i * 50)) % city.width
385
+ person_y = 300 + i * 40 + int(5 * math.sin(self.animation_time + i))
386
+
387
+ # Persona simple
388
+ draw.ellipse([person_x, person_y, person_x + 4, person_y + 8],
389
+ fill=(180, 140, 100))
390
+
391
+ # Mate en la mano si alta cultura
392
+ if city.cultural_identity > 70 and i % 3 == 0:
393
+ draw.ellipse([person_x + 3, person_y + 2, person_x + 6, person_y + 5],
394
+ fill=(100, 50, 0))
395
+
396
+ def _draw_advanced_ui(self, draw, city):
397
+ """UI avanzada con gráficos informativos"""
398
+ # Panel principal con transparencia
399
+ panel_width = 300
400
+ panel_height = 200
401
+
402
+ # Panel de fondo con gradiente
403
+ for i in range(panel_height):
404
+ alpha = int(200 * (1 - i/panel_height))
405
+ draw.rectangle([10, 10 + i, 10 + panel_width, 11 + i],
406
+ fill=(0, 0, 0, alpha))
407
+
408
+ # Borde elegante
409
+ draw.rectangle([10, 10, 10 + panel_width, 10 + panel_height],
410
+ outline=(100, 150, 255), width=3)
411
+
412
+ # Información de la ciudad
413
+ info_y = 25
414
+ city_info = [
415
+ f"🏙️ Población: {city.population:,}",
416
+ f"😊 Felicidad: {city.happiness}%",
417
+ f"💰 Economía: ${city.economy:,}",
418
+ f"🇦🇷 Identidad: {city.cultural_identity}%",
419
+ f"📈 Inflación: {city.inflation:.1f}x"
420
+ ]
421
+
422
+ for info in city_info:
423
+ # Sombra del texto
424
+ info_y += 20
425
+
426
+ # Barras de recursos en el lado derecho
427
+ self._draw_resource_bars(draw, city, city.width - 250, 20)
428
+
429
+ # Mini mapa en esquina inferior derecha
430
+ self._draw_mini_map(draw, city, city.width - 150, city.height - 120)
431
+
432
+ def _draw_resource_bars(self, draw, city, start_x, start_y):
433
+ """Barras de recursos con efectos visuales"""
434
+ resources = city.resources
435
+ bar_width = 200
436
+ bar_height = 15
437
+
438
+ resource_colors = {
439
+ 'presupuesto': (255, 215, 0), 'energia': (255, 255, 0),
440
+ 'agua': (0, 191, 255), 'seguridad': (255, 99, 71),
441
+ 'educacion': (147, 112, 219), 'salud': (255, 20, 147),
442
+ 'cultura': (255, 140, 0), 'empleo': (50, 205, 50),
443
+ 'vivienda': (139, 69, 19)
444
+ }
445
+
446
+ y_offset = start_y
447
+ for resource, value in resources.items():
448
+ if resource == 'presupuesto':
449
+ # Presupuesto se muestra diferente
450
+ budget_text = f"💰 ${value:,}"
451
+ y_offset += 25
452
+ continue
453
+
454
+ # Normalizar valor para barra (0-100)
455
+ normalized_value = max(0, min(100, value)) if isinstance(value, (int, float)) else 50
456
+
457
+ # Barra de fondo
458
+ draw.rectangle([start_x, y_offset, start_x + bar_width, y_offset + bar_height],
459
+ fill=(40, 40, 40), outline=(100, 100, 100))
460
+
461
+ # Barra de valor con gradiente
462
+ fill_width = int((normalized_value / 100) * (bar_width - 4))
463
+ color = resource_colors.get(resource, (150, 150, 150))
464
+
465
+ # Efecto de brillo
466
+ for i in range(fill_width):
467
+ brightness = 0.7 + 0.3 * math.sin(self.animation_time + i/10)
468
+ bright_color = tuple(int(c * brightness) for c in color)
469
+ draw.rectangle([start_x + 2 + i, y_offset + 2,
470
+ start_x + 3 + i, y_offset + bar_height - 2],
471
+ fill=bright_color)
472
+
473
+ # Etiqueta del recurso
474
+ resource_names = {
475
+ 'energia': '⚡ Energía', 'agua': '💧 Agua', 'seguridad': '🛡️ Seguridad',
476
+ 'educacion': '📚 Educación', 'salud': '🏥 Salud', 'cultura': '🎭 Cultura',
477
+ 'empleo': '💼 Empleo', 'vivienda': '🏠 Vivienda'
478
+ }
479
+
480
+ y_offset += 22
481
+
482
+ def _draw_mini_map(self, draw, city, start_x, start_y):
483
+ """Mini mapa de la ciudad"""
484
+ map_width, map_height = 120, 100
485
+
486
+ # Fondo del mini mapa
487
+ draw.rectangle([start_x, start_y, start_x + map_width, start_y + map_height],
488
+ fill=(20, 20, 20), outline=(150, 150, 150), width=2)
489
+
490
+ # Edificios en el mini mapa
491
+ scale = 2
492
+ for (grid_x, grid_y), building_type in city.city_grid.items():
493
+ mini_x = start_x + (grid_x * scale) % map_width
494
+ mini_y = start_y + (grid_y * scale) % map_height
495
+
496
+ building_colors = {
497
+ 'casa': (100, 150, 100), 'villa': (255, 150, 100),
498
+ 'edificio': (150, 150, 200), 'escuela': (200, 200, 100),
499
+ 'hospital': (255, 100, 100), 'cancha': (100, 255, 100),
500
+ 'plaza': (150, 255, 150), 'fabrica': (150, 100, 100)
501
+ }
502
+
503
+ color = building_colors.get(building_type, (100, 100, 100))
504
+ draw.rectangle([mini_x, mini_y, mini_x + scale, mini_y + scale], fill=color)
505
+
506
+ class EnhancedCityGame:
507
+ """Juego de ciudad argentino mejorado"""
508
+ def __init__(self):
509
+ self.city = ArgentineCityBuilder()
510
+ self.renderer = AdvancedCityRenderer()
511
+ self.selected_building = None
512
+ self.game_speed = 1
513
+ self.events_log = []
514
+
515
+ def place_building(self, building_type, grid_x, grid_y):
516
+ """Coloca un edificio en la ciudad"""
517
+ if building_type not in self.city.building_types:
518
+ return "Tipo de edificio inválido"
519
+
520
+ building_info = self.city.building_types[building_type]
521
+ cost = building_info['cost']
522
+
523
+ # Verificar si se puede pagar
524
+ if self.city.resources['presupuesto'] < cost:
525
+ return f"No tenés suficiente presupuesto. Necesitás ${cost:,}"
526
+
527
+ # Verificar si la posición está libre
528
+ if (grid_x, grid_y) in self.city.city_grid:
529
+ return "Ya hay un edificio en esa posición"
530
+
531
+ # Construir edificio
532
+ self.city.resources['presupuesto'] -= cost
533
+ self.city.city_grid[(grid_x, grid_y)] = building_type
534
+
535
+ # Aplicar efectos del edificio
536
+ self._apply_building_effects(building_type, building_info)
537
+
538
+ # Actualizar estadísticas de la ciudad
539
+ self._update_city_stats()
540
+
541
+ return f"✅ {building_type.replace('_', ' ').title()} construido por ${cost:,}"
542
+
543
+ def _apply_building_effects(self, building_type, building_info):
544
+ """Aplica los efectos de un edificio a la ciudad"""
545
+ for effect, value in building_info.items():
546
+ if effect == 'cost':
547
+ continue
548
+ elif effect == 'pop':
549
+ self.city.population += value
550
+ elif effect == 'happiness':
551
+ self.city.happiness = min(100, max(0, self.city.happiness + value))
552
+ elif effect == 'solidarity':
553
+ self.city.cultural_identity = min(100, max(0, self.city.cultural_identity + value))
554
+ elif effect in self.city.resources:
555
+ current_value = self.city.resources[effect]
556
+ self.city.resources[effect] = min(100, max(0, current_value + value))
557
+
558
+ def _update_city_stats(self):
559
+ """Actualiza estadísticas generales de la ciudad"""
560
+ # Calcular economía basada en edificios
561
+ economy_buildings = ['fabrica', 'kiosco', 'centro_cultural']
562
+ economy_bonus = sum(10000 for pos, building in self.city.city_grid.items()
563
+ if building in economy_buildings)
564
+ self.city.economy = 1000 + economy_bonus + self.city.population * 50
565
+
566
+ # Inflación aumenta con el tiempo y población
567
+ self.city.inflation += 0.01 + (len(self.city.city_grid) * 0.001)
568
+
569
+ # Felicidad general
570
+ happiness_factors = [
571
+ self.city.resources.get('cultura', 50),
572
+ self.city.resources.get('seguridad', 50),
573
+ self.city.resources.get('empleo', 50),
574
+ self.city.cultural_identity
575
+ ]
576
+ self.city.happiness = sum(happiness_factors) // len(happiness_factors)
577
+
578
+ def simulate_day(self):
579
+ """Simula un día en la ciudad"""
580
+ # Ingresos diarios
581
+ daily_income = self.city.population * 10 + self.city.economy * 0.01
582
+ self.city.resources['presupuesto'] += int(daily_income)
583
+
584
+ # Gastos de mantenimiento
585
+ maintenance_cost = len(self.city.city_grid) * 100
586
+ self.city.resources['presupuesto'] -= maintenance_cost
587
+
588
+ # Efectos de la inflación
589
+ self.city.resources['presupuesto'] = int(self.city.resources['presupuesto'] / self.city.inflation)
590
+
591
+ # Eventos aleatorios
592
+ if random.random() < 0.2:
593
+ self._trigger_city_event()
594
+
595
+ # Actualizar todo
596
+ self._update_city_stats()
597
+
598
+ return f"💰 Ingresos: ${daily_income:,.0f} | 💸 Gastos: ${maintenance_cost:,} | 📈 Inflación: {self.city.inflation:.2f}x"
599
+
600
+ def _trigger_city_event(self):
601
+ """Eventos aleatorios de la ciudad"""
602
+ events = [
603
+ ("🎉 Festival de Tango", "cultura", 15, "happiness", 20),
604
+ ("⚽ River vs Boca", "happiness", 30, "cultura", 10),
605
+ ("🏭 Nueva fábrica privada", "empleo", 20, "presupuesto", 5000),
606
+ ("🌧️ Lluvia torrencial", "agua", 25, "energia", -10),
607
+ ("📚 Programa de alfabetización", "educacion", 15, "cultura", 10),
608
+ ("🏥 Campaña de vacunación", "salud", 20, "happiness", 15),
609
+ ("💸 Crisis económica menor", "presupuesto", -3000, "happiness", -10),
610
+ ("🎭 Nuevo centro cultural", "cultura", 25, "educacion", 10)
611
+ ]
612
+
613
+ event_name, res1, val1, res2, val2 = random.choice(events)
614
+
615
+ # Aplicar efectos
616
+ if res1 == 'presupuesto':
617
+ self.city.resources[res1] += val1
618
+ else:
619
+ self.city.resources[res1] = min(100, max(0, self.city.resources[res1] + val1))
620
+
621
+ if res2 == 'presupuesto':
622
+ self.city.resources[res2] += val2
623
+ elif res2 == 'happiness':
624
+ self.city.happiness = min(100, max(0, self.city.happiness + val2))
625
+ else:
626
+ self.city.resources[res2] = min(100, max(0, self.city.resources[res2] + val2))
627
+
628
+ self.events_log.append(event_name)
629
+ return event_name
630
+
631
+ def get_city_status(self):
632
+ """Status completo de la ciudad"""
633
+ return {
634
+ "🏙️ Población": f"{self.city.population:,} habitantes",
635
+ "😊 Felicidad": f"{self.city.happiness}%",
636
+ "💰 Presupuesto": f"${self.city.resources['presupuesto']:,}",
637
+ "📊 Economía": f"${self.city.economy:,}",
638
+ "🇦🇷 Identidad": f"{self.city.cultural_identity}%",
639
+ "📈 Inflación": f"{self.city.inflation:.2f}x",
640
+ "🏗️ Edificios": f"{len(self.city.city_grid)} construidos",
641
+ "⚡ Recursos": f"Energía {self.city.resources['energia']}% | Agua {self.city.resources['agua']}%"
642
+ }
643
+
644
+ def create_simcity_interface():
645
+ """Interfaz del SimCity Argentino"""
646
+ game = EnhancedCityGame()
647
+
648
+ with gr.Blocks(
649
+ title="🇦🇷 SIMCIUDAD ARGENTINA - Construcción Épica",
650
+ theme=gr.themes.Glass(),
651
+ css="""
652
+ .building-btn { font-size: 1.1em; padding: 10px; margin: 3px; }
653
+ .city-header { background: linear-gradient(45deg, #74b9ff, #0984e3, #00b894); }
654
+ """
655
+ ) as interface:
656
+
657
+ gr.HTML("""
658
+ <div class="city-header" style="padding: 25px; text-align: center; border-radius: 15px; margin: 15px;">
659
+ <h1 style="color: white; font-size: 3em; margin: 0; text-shadow: 3px 3px 6px rgba(0,0,0,0.7);">
660
+ 🏙️ SIMCIUDAD ARGENTINA 🇦🇷
661
+ </h1>
662
+ <h2 style="color: #ddd; font-size: 1.3em; margin: 10px 0;">
663
+ Construí la Ciudad Argentina de tus Sueños
664
+ </h2>
665
+ <p style="color: #f1f2f6; font-size: 1.1em;">
666
+ Gráficos Isométricos • Mecánicas Realistas • Cultura Auténtica
667
+ </p>
668
+ </div>
669
+ """)
670
+
671
+ with gr.Row():
672
+ with gr.Column(scale=2):
673
+ city_display = gr.Image(
674
+ label="🌆 Tu Ciudad Argentina",
675
+ width=1000,
676
+ height=700
677
+ )
678
+
679
+ gr.Markdown("### 🏗️ HERRAMIENTAS DE CONSTRUCCIÓN")
680
+ with gr.Row():
681
+ # Coordenadas para construcción
682
+ grid_x_input = gr.Number(label="Coordenada X", value=20, minimum=0, maximum=50)
683
+ grid_y_input = gr.Number(label="Coordenada Y", value=20, minimum=0, maximum=35)
684
+
685
+ with gr.Row():
686
+ house_btn = gr.Button("🏠 CASA", variant="secondary", elem_classes="building-btn")
687
+ villa_btn = gr.Button("🏘️ VILLA", variant="secondary", elem_classes="building-btn")
688
+ building_btn = gr.Button("🏢 EDIFICIO", variant="primary", elem_classes="building-btn")
689
+
690
+ with gr.Row():
691
+ school_btn = gr.Button("🏫 ESCUELA", variant="primary", elem_classes="building-btn")
692
+ hospital_btn = gr.Button("🏥 HOSPITAL", variant="stop", elem_classes="building-btn")
693
+ field_btn = gr.Button("⚽ CANCHA", variant="secondary", elem_classes="building-btn")
694
+
695
+ with gr.Row():
696
+ grill_btn = gr.Button("🔥 PARRILLA", variant="stop", elem_classes="building-btn")
697
+ plaza_btn = gr.Button("🌳 PLAZA", variant="secondary", elem_classes="building-btn")
698
+ factory_btn = gr.Button("🏭 FÁBRICA", variant="primary", elem_classes="building-btn")
699
+
700
+ with gr.Row():
701
+ simulate_btn = gr.Button("⏭️ SIMULAR DÍA", variant="primary", size="lg")
702
+
703
+ with gr.Column(scale=1):
704
+ gr.Markdown("### 📊 ESTADO DE LA CIUDAD")
705
+ city_status = gr.JSON(
706
+ label="📈 Estadísticas",
707
+ value=game.get_city_status()
708
+ )
709
+
710
+ gr.Markdown("### 📰 EVENTOS RECIENTES")
711
+ events_display = gr.Textbox(
712
+ value="🎮 ¡Bienvenido al SimCity Argentino! Construí tu ciudad con identidad nacional.",
713
+ label="📺 Noticias Urbanas",
714
+ lines=5,
715
+ interactive=False
716
+ )
717
+
718
+ gr.Markdown("### 💰 COSTOS DE CONSTRUCCIÓN")
719
+ gr.HTML("""
720
+ <div style="background: #2d3436; color: white; padding: 15px; border-radius: 10px;">
721
+ <p><strong>💵 Precios:</strong></p>
722
+ <ul style="font-size: 0.9em; margin: 5px 0; padding-left: 15px;">
723
+ <li>🏠 Casa: $1,000</li>
724
+ <li>🏘️ Villa: $500</li>
725
+ <li>🏢 Edificio: $5,000</li>
726
+ <li>🏫 Escuela: $8,000</li>
727
+ <li>🏥 Hospital: $15,000</li>
728
+ <li>⚽ Cancha: $3,000</li>
729
+ <li>🔥 Parrilla: $2,000</li>
730
+ <li>🌳 Plaza: $1,500</li>
731
+ <li>🏭 Fábrica: $12,000</li>
732
+ </ul>
733
+ </div>
734
+ """)
735
+
736
+ # Funciones de construcción
737
+ def build_structure(building_type, x, y):
738
+ message = game.place_building(building_type, int(x), int(y))
739
+ frame = game.renderer.render_city_frame(game.city)
740
+ status = game.get_city_status()
741
+ return frame, status, message
742
+
743
+ # Conectar botones
744
+ house_btn.click(
745
+ fn=lambda x, y: build_structure("casa", x, y),
746
+ inputs=[grid_x_input, grid_y_input],
747
+ outputs=[city_display, city_status, events_display]
748
+ )
749
+ villa_btn.click(
750
+ fn=lambda x, y: build_structure("villa", x, y),
751
+ inputs=[grid_x_input, grid_y_input],
752
+ outputs=[city_display, city_status, events_display]
753
+ )
754
+ building_btn.click(
755
+ fn=lambda x, y: build_structure("edificio", x, y),
756
+ inputs=[grid_x_input, grid_y_input],
757
+ outputs=[city_display, city_status, events_display]
758
+ )
759
+ school_btn.click(
760
+ fn=lambda x, y: build_structure("escuela", x, y),
761
+ inputs=[grid_x_input, grid_y_input],
762
+ outputs=[city_display, city_status, events_display]
763
+ )
764
+ hospital_btn.click(
765
+ fn=lambda x, y: build_structure("hospital", x, y),
766
+ inputs=[grid_x_input, grid_y_input],
767
+ outputs=[city_display, city_status, events_display]
768
+ )
769
+ field_btn.click(
770
+ fn=lambda x, y: build_structure("cancha", x, y),
771
+ inputs=[grid_x_input, grid_y_input],
772
+ outputs=[city_display, city_status, events_display]
773
+ )
774
+ grill_btn.click(
775
+ fn=lambda x, y: build_structure("parrilla", x, y),
776
+ inputs=[grid_x_input, grid_y_input],
777
+ outputs=[city_display, city_status, events_display]
778
+ )
779
+ plaza_btn.click(
780
+ fn=lambda x, y: build_structure("plaza", x, y),
781
+ inputs=[grid_x_input, grid_y_input],
782
+ outputs=[city_display, city_status, events_display]
783
+ )
784
+ factory_btn.click(
785
+ fn=lambda x, y: build_structure("fabrica", x, y),
786
+ inputs=[grid_x_input, grid_y_input],
787
+ outputs=[city_display, city_status, events_display]
788
+ )
789
+
790
+ # Simulación de día
791
+ simulate_btn.click(
792
+ fn=lambda: (
793
+ game.renderer.render_city_frame(game.city),
794
+ game.get_city_status(),
795
+ game.simulate_day()
796
+ ),
797
+ outputs=[city_display, city_status, events_display]
798
+ )
799
+
800
+ # Estado inicial
801
+ interface.load(
802
+ fn=lambda: (
803
+ game.renderer.render_city_frame(game.city),
804
+ game.get_city_status(),
805
+ "🎮 ¡SimCiudad Argentina iniciado! Empezá construyendo casas y escuelas."
806
+ ),
807
+ outputs=[city_display, city_status, events_display]
808
+ )
809
+
810
+ return interface
811
+
812
+ if __name__ == "__main__":
813
+ interface = create_simcity_interface()
814
+ interface.launch(share=True, show_error=True)