Apuntes_Python/05_diez_proyectos/07-minesweeper/buscaminas.py
2022-12-24 22:41:20 -03:00

179 lines
6.6 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import random
import re
import os
# Limpiar Pantalla
cl = lambda: os.system('clear') if os.name == 'posix' else os.system('cls')
class Tablero:
"""
Creación de objeto tablero que representa al juego 'BuscaMinas'
"""
def __init__(self, dim_size, num_bombs):
self.dim_size = dim_size
self.num_bombs = num_bombs
# Crear tablero (helper function?)
self.tablero = self.hacer_nuevo_tablero() # Plantar bombas
self.asignar_valores_a_tablero()
# Inicializar un set() para hacer seguimiento de las locaciones descubiertas
# se almacenan tuplas (fila, columna) en este set
self.excavado = set()
def hacer_nuevo_tablero(self):
"""
Cronstruye un nuevo tablero según dim_size y num_bombs.
Basado en una lista de listas (represantación 2-D)
"""
# generar nuevo tablero
tablero = [[None for _ in range(self.dim_size)] for _ in range(self.dim_size)]
# Contruye algo como esto:
# [ [None, None, .., None],
# [None, None, .., None],
# [... ...],
# [None, None, .., None] ]
# Plantar bombas
bombas_plantadas = 0
while bombas_plantadas < self.num_bombs:
loc = random.randint(0, self.dim_size ** 2 - 1)
fil = loc // self.dim_size
col = loc % self.dim_size
if tablero[fil][col] == '💥️':
continue # ignorar
tablero[fil][col] = '💥️' # plantar bomba
bombas_plantadas += 1
return tablero
def asignar_valores_a_tablero(self):
"""
Asigna números enteros de 0 a 8 a cada espacio vacío, según
cuantas bombas existan al rededor
"""
for f in range(self.dim_size):
for c in range(self.dim_size):
if self.tablero[f][c] == '💥️':
continue # si ya es una bomba
self.tablero[f][c] = self.get_num_bombs_cercanas(f, c)
def get_num_bombs_cercanas(self, fil, col):
"""
Itera cada cuadrante cercano y suma el número de bombas
'top left: (fil-1, col-1) top middle: (fil-1, col) top right: (fil-1, col+1)'
' left: ( fil, col-1) right : ( fil, col+1)'
'bot left: (fil+1, col-1) bot middle: (fil+1, col) bot right: (fil+1, col+1)'
"""
num_bombs_cerca = 0
for f in range( max(0, fil-1), min(self.dim_size-1, fil+1) +1):
for c in range( max(0, col-1), min(self.dim_size-1, col+1) +1):
if f == fil and c == col:
continue # posicion original, no checkear
if self.tablero[f][c] == '💥️':
num_bombs_cerca += 1
return num_bombs_cerca
def excavar(self, fil, col):
"""
Busca minas en la posicion, retorna True si tiene exito
False si es una bomba (gameover), si no hay bombas cercanas
la busqueda sigue recursivamente
"""
self.excavado.add((fil, col)) # seguimiento de locaciones 'excavadas'
if self.tablero[fil][col] == '💥️':
return False
elif self.tablero[fil][col] > 0:
return True
# self.tablero[fil][col] == 0
for f in range( max(0, fil-1), min(self.dim_size-1, fil+1) +1):
for c in range( max(0, col-1), min(self.dim_size-1, col+1) +1):
if (f, c) in self.excavado:
continue # no 'excava' donde ya se ha 'excavado'
self.excavar(f, c)
return True
def __str__(self):
"""
Retorna un string con la represantación del tablero de juego
"""
tablero_visible = [[None for _ in range(self.dim_size) ] for _ in range(self.dim_size)]
for fil in range(self.dim_size):
for col in range(self.dim_size):
if (fil, col) in self.excavado:
tablero_visible[fil][col] = str(self.tablero[fil][col])
else:
tablero_visible[fil][col] = ' '
# Formato de string para represantación de tablero
string_rep = ''
anchos = [] # anchos maximo de columna para print
for idx in range(self.dim_size):
columnas = map(lambda x: x[idx], tablero_visible)
anchos.append(len(max(columnas, key = len)))
# print the csv strings
indices = [i for i in range(self.dim_size)]
indices_fil = ' '
cells = []
for idx, col in enumerate(indices):
format = '%-' + str(anchos[idx]) + "s"
cells.append(format % (col))
indices_fil += ' '.join(cells)
indices_fil += ' \n'
for i in range(len(tablero_visible)):
fila = tablero_visible[i]
string_rep += f'{i} |'
cells = []
for idx, col in enumerate(fila):
format = '%-' + str(anchos[idx]) + "s"
cells.append(format % (col))
string_rep += ' |'.join(cells)
string_rep += ' |\n'
str_len = int(len(string_rep) / self.dim_size)
string_rep = indices_fil + '-'*str_len + '\n' + string_rep + '-'*str_len
return string_rep
def jugar(dim_size = 10, num_bombs = 10):
"""
Comenzar juego
Paso 1 : Crear tablero y plantar bombas
Paso 2 : Mostrar tablero y preguntar donde excavar
Paso 3a: Si se elige el lugar donde hay una bomba, muestra Game Over
Paso 3b: Si el cuadrante no es una bomba, excava recursivamente hasta un cuadrante
cercano a una bomba.
Paso 4 : Repetir pasos 2 y 3 hasta que no queden espacios para excavar -> Victoria!
"""
# Paso 1
safe = True
tablero = Tablero(dim_size ,num_bombs)
# Paso 2
while len(tablero.excavado) < tablero.dim_size ** 2 - num_bombs:
cl()
print(tablero)
# 3, 4 o 3, 4 o 3, 4
user_input = re.split(',(\\s)*', input("Ingresa coordenada para buscar\n-> fila, columna : "))
fil, col = int(user_input[0]), int(user_input[-1])
if fil < 0 or fil >= tablero.dim_size or col < 0 or col >= dim_size:
print("Posición invalida, prueba otra.")
continue
# si es valida
safe = tablero.excavar(fil, col)
if not safe:
break # game over
if safe:
print("\n 🎉FELICITACIONES!!🎊️ Ganaste 😎️ ")
else:
cl()
tablero.excavado = [(f, c) for f in range(tablero.dim_size) for c in range(tablero.dim_size)]
print(tablero)
print("\n Perdiste! 👻️ \n")
if __name__ == '__main__':
jugar()