├── .gitignore
├── requirements.txt
├── icono.ico
├── Juego_de_la_vida_Jonatandb.gif
├── .replit
├── Readme.md
└── JuegoDeLaVida.py
/.gitignore:
--------------------------------------------------------------------------------
1 | venv
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy==1.25.0
2 | pygame==2.4.0
3 |
--------------------------------------------------------------------------------
/icono.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jonatandb/DotCSV-Juego-de-la-vida/HEAD/icono.ico
--------------------------------------------------------------------------------
/Juego_de_la_vida_Jonatandb.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jonatandb/DotCSV-Juego-de-la-vida/HEAD/Juego_de_la_vida_Jonatandb.gif
--------------------------------------------------------------------------------
/.replit:
--------------------------------------------------------------------------------
1 | run = ["python3", "JuegoDeLaVida.py"]
2 | language = "python3"
3 | entrypoint = "JuegoDeLaVida.py"
4 | hidden = ["venv", ".config", "**/__pycache__", "**/.mypy_cache", "**/*.pyc"]
5 |
6 | audio = true
7 |
8 | [nix]
9 | channel = "stable-21_11"
10 |
11 | [gitHubImport]
12 | requiredFiles = [".replit", "replit.nix", "venv", ".config"]
13 |
14 | [interpreter]
15 | command = [
16 | "prybar-python310",
17 | "-q",
18 | "--ps1",
19 | "\u0001\u001b[33m\u0002\u0001\u001b[00m\u0002 ",
20 | "-i"
21 | ]
22 |
23 | [env]
24 | VIRTUAL_ENV = "/home/runner/${REPL_SLUG}/venv"
25 | PATH = "${VIRTUAL_ENV}/bin"
26 | PYTHONPATH="${VIRTUAL_ENV}/lib/python3.10/site-packages"
27 | REPLIT_POETRY_PYPI_REPOSITORY="https://package-proxy.replit.com/pypi/"
28 |
29 | [debugger]
30 | support = true
31 |
32 | [debugger.interactive]
33 | transport = "localhost:0"
34 | startCommand = ["dap-python", "main.py"]
35 |
36 | [debugger.interactive.integratedAdapter]
37 | dapTcpAddress = "localhost:0"
38 |
39 | [debugger.interactive.initializeMessage]
40 | command = "initialize"
41 | type = "request"
42 |
43 | [debugger.interactive.initializeMessage.arguments]
44 | adapterID = "debugpy"
45 | clientID = "replit"
46 | clientName = "replit.com"
47 | columnsStartAt1 = true
48 | linesStartAt1 = true
49 | locale = "en-us"
50 | pathFormat = "path"
51 | supportsInvalidatedEvent = true
52 | supportsProgressReporting = true
53 | supportsRunInTerminalRequest = true
54 | supportsVariablePaging = true
55 | supportsVariableType = true
56 |
57 | [debugger.interactive.launchMessage]
58 | command = "attach"
59 | type = "request"
60 |
61 | [debugger.interactive.launchMessage.arguments]
62 | logging = {}
63 |
64 | [packager]
65 | language = "python3"
66 | ignoredPackages = ["unit_tests"]
67 |
68 | [packager.features]
69 | enabledForHosting = false
70 | packageSearch = true
71 | guessImports = true
72 |
73 | [languages]
74 |
75 | [languages.python3]
76 | pattern = "**/*.py"
77 |
78 | [languages.python3.languageServer]
79 | start = "pylsp"
80 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # "Programando el Juego de La Vida... en 10 MINUTOS!"
2 | - Inspirada en la versión del autor del canal de Youtube -> DotCSV (Video de referencia [acá](https://www.youtube.com/watch?v=qPtKv9fSHZY))
3 |
4 | ---
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | >
13 | ## 🚀 Mi versión online
14 | - https://replit.com/@Jonatandb/DotCSV-Juego-de-la-vida
15 | - Require una cuenta gratuita de [Replit](https://replit.com/) para poder ejecutarlo online.
16 |
17 | ---
18 |
19 | ## 👨🏻💻 Tecnologías usadas
20 | - Python
21 | - PyGame
22 |
23 | ---
24 |
25 |
26 | ## Los criterios para la vida o la muerte de una célula son:
27 |
28 | - Una célula muerta con exactamente 3 células vecinas vivas "nace" (es decir, al turno siguiente estará viva).
29 |
30 | - Una célula viva con 2 o 3 células vecinas vivas sigue viva, en otro caso muere (por "soledad" o "superpoblación").
31 |
32 | ## Cómo jugar:
33 |
34 | - ### Al abrir el sitio con el juego presionar el botón "Run" para que comience
35 |
36 | - ### Click a una celda le da "vida" y si estaba "viva" entonces se la "mata"
37 |
38 | - ### Click medio del mouse Pausa / Reanuda el juego
39 |
40 | - #### El juego pausado se puede utilizar para crear autómatas :-)
41 |
42 | ### Jugalo online haciendo click acá: Juego de la vida by Jonatandb :-)
43 |
44 | ---
45 |
46 | ## Páginas consultadas:
47 |
48 | ### VSCode:
49 |
50 | - Getting Started with Python in VS Code
51 |
52 | - Python extension for Visual Studio Code
53 |
54 |
55 | - Configuro VSCode para que no muestre un alerta erronea sobre pygame
56 |
57 | - Why does it say that module pygame has no init member?
58 |
59 | - Configuro VSCode para que el entorno de debugging por defecto sea el que ejecuta el archivo actual y para que también formatee el código automáticamente usando black, luego de investigar las opciones disponibles (Black, yapf y autopep8) y leer éstas páginas:
60 |
61 | - Blog: A comparison of autopep8, black, and yapf - Code formatters for Python
62 |
63 | - Auto formatters for Python 👨💻🤖
64 |
65 | ### Game of life:
66 |
67 | - Wikipedia: Juego de la vida
68 |
69 |
70 | - Oscilador (autómata celular)
71 |
72 |
73 | - John H. Conway hablando del Juego de la vida
74 |
75 | ### PyGame
76 |
77 | - PyGame Tutorial: Getting Started
78 |
79 | - Python Casting
80 |
81 | - Pygame Front Page - Documents
82 |
83 | - Pygame: pygame.display.set_mode
84 |
85 | - 🚀 A Starter Guide to Pygame 📀
86 |
87 | - Pygame display.set_caption
88 |
89 | - pygame.key
90 |
91 | ### Incrustar el Repl en el readme para que se pueda jugar directo desde Github:
92 |
93 | - Pygame web apps?
94 |
95 | - is it possible to run pygame or pyglet in a browser?
96 |
97 | - How to get HTML file form URL in Python
98 |
99 | - PIP - Requirements Files
100 |
101 | - Conditional import of modules in Python
102 |
103 | - Any way to embed Pygame in webpages?
104 |
105 | - Embed openstreetmap iframe in github markdown
106 |
107 | ### Generar el ícono programaticamente utilizando SVG para no depender del archivo .ico:
108 |
109 | - Pygame.org - pygame.image.load()
110 |
111 | - drawSvg - A Python 3 library for programmatically generating SVG images
112 |
113 | - The Python Logo
114 |
115 |
116 | - Python Functions
117 |
118 |
119 | - SVG rendering in a PyGame application
120 |
121 | - Python Check If File or Directory Exists
122 |
123 | ### Hacer que funcione clonar el repositorio desde Repl.it y que el juego se inicie correctamente al presionar Run:
124 |
125 | - Configuring the run button
126 |
127 | - Repl from Repo
128 |
129 | - Configuring GitHub repos to run on Repl.it and contributing back
130 |
131 | ### Python:
132 |
133 | - Python String Interpolation
134 |
135 | - Python integer incrementing with ++
136 |
137 | ### Búsquedas varias:
138 |
139 | - Iteration & Conway’s Game of Life
140 |
141 | ---
142 |
143 |
144 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/JuegoDeLaVida.py:
--------------------------------------------------------------------------------
1 | # https://github.com/Jonatandb/DotCSV-Juego-de-la-vida
2 | # -*- coding: utf-8 -*-
3 | #
4 | import pygame
5 | import time, os
6 | import numpy as np
7 |
8 | # Hago que la ventana aparezca centrada en Windows
9 | os.environ["SDL_VIDEO_CENTERED"] = "1"
10 |
11 | pygame.init()
12 |
13 | # Establezco el título de la ventana:
14 | pygame.display.set_caption("Juego de la vida - Jonatandb")
15 |
16 | # Carga el icono si existe
17 | iconPath = "./icono.ico"
18 |
19 | if os.path.exists(iconPath):
20 |
21 | icono = pygame.image.load(iconPath)
22 |
23 | # Establece el icono de la ventana
24 | pygame.display.set_icon(icono)
25 |
26 | # Defino ancho y alto de la ventana
27 | width, height = 700, 700
28 |
29 | # Creación de la ventana
30 | screen = pygame.display.set_mode((height, width))
31 |
32 | # Color de fondo, casi negro
33 | bg = 25, 25, 25
34 |
35 | # Pinto el fondo con el color elegido (bg)
36 | screen.fill(bg)
37 |
38 | # Cantidad de celdas en cada eje
39 | nxC, nyC = 60, 60
40 |
41 | # Ancho y alto de cada celda
42 | dimCW = width / nxC
43 | dimCH = height / nyC
44 |
45 | # Estructura de datos que contiene todos los estados de las diferentes celdas
46 | # Estados de las celdas: Vivas = 1 - Muertas = 0
47 | # Inicializo matriz con ceros
48 | gameState = np.zeros((nxC, nyC))
49 |
50 | # Autómata palo:
51 | # 0 1 0
52 | # 0 1 0
53 | # 0 1 0
54 | # gameState[5, 3] = 1
55 | # gameState[5, 4] = 1
56 | # gameState[5, 5] = 1
57 |
58 | # Autómata móvil:
59 | # 0 1 0
60 | # 0 0 1
61 | # 1 1 1
62 | # gameState[21, 21] = 1
63 | # gameState[22, 22] = 1
64 | # gameState[22, 23] = 1
65 | # gameState[21, 23] = 1
66 | # gameState[20, 23] = 1
67 |
68 | # Versión de mi autómata que siempre aparece centrada: Autómata Jonatandb :)
69 | posInitX = int((nxC / 2) - 3)
70 | posInitY = int((nyC / 2) - 5)
71 | gameState[posInitX, posInitY] = 1
72 | gameState[posInitX + 1, posInitY] = 1
73 | gameState[posInitX + 2, posInitY] = 1
74 | gameState[posInitX + 3, posInitY] = 1
75 |
76 | gameState[posInitX + 3, posInitY + 1] = 1
77 | gameState[posInitX + 3, posInitY + 2] = 1
78 |
79 | gameState[posInitX, posInitY + 3] = 1
80 | gameState[posInitX + 3, posInitY + 3] = 1
81 |
82 | gameState[posInitX, posInitY + 4] = 1
83 | gameState[posInitX + 1, posInitY + 4] = 1
84 | gameState[posInitX + 2, posInitY + 4] = 1
85 | gameState[posInitX + 3, posInitY + 4] = 1
86 |
87 | # Control de la ejecución - En True se inicia pausado (Para poder ver la forma inicial de los aútomatas):
88 | pauseExec = True
89 |
90 | # Controla la finalización del juego:
91 | endGame = False
92 |
93 | # Acumulador de cantidad de iteraciones:
94 | iteration = 0
95 |
96 | clock = pygame.time.Clock()
97 |
98 | # Bucle de ejecución principal (Main Loop):
99 | while not endGame:
100 |
101 | newGameState = np.copy(gameState)
102 |
103 | # Vuelvo a colorear la pantalla con el color de fondo
104 | screen.fill(bg)
105 |
106 | # Agrego pequeña pausa para que el cpu no trabaje al 100%
107 | time.sleep(0.1)
108 |
109 | # Registro de eventos de teclado y mouse
110 | ev = pygame.event.get()
111 |
112 | # Contador de población:
113 | population = 0
114 |
115 | for event in ev:
116 |
117 | # Si cierran la ventana finalizo el juego
118 | if event.type == pygame.QUIT:
119 | endGame = True
120 | break
121 |
122 | if event.type == pygame.KEYDOWN:
123 |
124 | # Si tocan escape finalizo el juego
125 | if event.key == pygame.K_ESCAPE:
126 | endGame = True
127 | break
128 |
129 | # Si tocan la tecla r limpio la grilla, reseteo población e iteración y pongo pausa
130 | if event.key == pygame.K_r:
131 | iteration = 0
132 | gameState = np.zeros((nxC, nyC))
133 | newGameState = np.zeros((nxC, nyC))
134 | pauseExec = True
135 | else:
136 | # Si tocan cualquier tecla no contemplada, pauso o reanudo el juego
137 | pauseExec = not pauseExec
138 |
139 | # Detección de click del mouse:
140 | mouseClick = pygame.mouse.get_pressed()
141 |
142 | # Obtención de posición del cursor en la pantalla:
143 | # Si se hace click con cualquier botón del mouse, se obtiene un valor en mouseClick mayor a cero
144 | if sum(mouseClick) > 0:
145 |
146 | # Click del medio pausa / reanuda el juego
147 | if mouseClick[1]:
148 |
149 | pauseExec = not pauseExec
150 |
151 | else:
152 |
153 | # Obtengo las coordenadas del cursor del mouse en pixeles
154 | posX, posY, = pygame.mouse.get_pos()
155 |
156 | # Convierto de coordenadas en pixeles a celda clickeada en la grilla
157 | celX, celY = int(np.floor(posX / dimCW)), int(np.floor(posY / dimCH))
158 |
159 | # Click izquierdo y derecho permutan entre vida y muerte
160 | newGameState[celX, celY] = not gameState[celX, celY]
161 |
162 | if not pauseExec:
163 | # Incremento el contador de generaciones
164 | iteration += 1
165 |
166 | # Recorro cada una de las celdas generadas
167 | for y in range(0, nxC):
168 | for x in range(0, nyC):
169 |
170 | if not pauseExec:
171 |
172 | # Cálculo del número de vecinos cercanos
173 | n_neigh = (
174 | gameState[(x - 1) % nxC, (y - 1) % nyC]
175 | + gameState[x % nxC, (y - 1) % nyC]
176 | + gameState[(x + 1) % nxC, (y - 1) % nyC]
177 | + gameState[(x - 1) % nxC, y % nyC]
178 | + gameState[(x + 1) % nxC, y % nyC]
179 | + gameState[(x - 1) % nxC, (y + 1) % nyC]
180 | + gameState[x % nxC, (y + 1) % nyC]
181 | + gameState[(x + 1) % nxC, (y + 1) % nyC]
182 | )
183 |
184 | # Una célula muerta con exactamente 3 células vecinas vivas "nace"
185 | # (es decir, al turno siguiente estará viva).
186 | if gameState[x, y] == 0 and n_neigh == 3:
187 | newGameState[x, y] = 1
188 |
189 | # Una célula viva con 2 o 3 células vecinas vivas sigue viva,
190 | # en otro caso muere (por "soledad" o "superpoblación")
191 | elif gameState[x, y] == 1 and (n_neigh < 2 or n_neigh > 3):
192 | newGameState[x, y] = 0
193 |
194 | # Incremento el contador de población:
195 | if gameState[x, y] == 1:
196 | population += 1
197 |
198 | # Creación del polígono de cada celda a dibujar
199 | poly = [
200 | (int(x * dimCW), int(y * dimCH)),
201 | (int((x + 1) * dimCW), int(y * dimCH)),
202 | (int((x + 1) * dimCW), int((y + 1) * dimCH)),
203 | (int(x * dimCW), int((y + 1) * dimCH)),
204 | ]
205 |
206 | if newGameState[x, y] == 0:
207 | # Dibujado de la celda para cada par de x e y:
208 | # screen -> Pantalla donde dibujar
209 | # (128, 128, 128) -> Color a utilizar para dibujar, en este caso un gris
210 | # poly -> Puntos que definan al poligono que se está dibujando
211 | pygame.draw.polygon(screen, (128, 128, 128), poly, 1)
212 | else:
213 | if pauseExec:
214 | # Con el juego pausado pinto de gris las celdas
215 | pygame.draw.polygon(screen, (128, 128, 128), poly, 0)
216 | else:
217 | # Con el juego ejecutándose pinto de blanco las celdas
218 | pygame.draw.polygon(screen, (255, 255, 255), poly, 0)
219 |
220 | # Actualizo el título de la ventana
221 | title = f"Juego de la vida - Jonatandb - Población: {population} - Generación: {iteration}"
222 | if pauseExec:
223 | title += " - [PAUSADO]"
224 | pygame.display.set_caption(title)
225 | print(title)
226 |
227 | # Actualizo gameState
228 | gameState = np.copy(newGameState)
229 |
230 | # Muestro y actualizo los fotogramas en cada iteración del bucle principal
231 | pygame.display.flip()
232 |
233 | # Fuerzo a que se renderice como mucho 60 frames por segundo, para no consumir CPU demás
234 | # https://www.pygame.org/docs/ref/time.html#pygame.time.Clock.tick
235 | clock.tick(60)
236 |
237 | print("Juego finalizado - jonatandb@gmail.com")
238 |
--------------------------------------------------------------------------------