├── .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 | Juego de la vida | Versión Jonatandb 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 | Support me on Ko-fi 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 | --------------------------------------------------------------------------------