Buscaminas en Python - Programación de juego

Buscaminas en Python – Programación de juego

En mi blog ya te he enseñado a programar el juego de buscaminas en varios lenguajes pero hoy vamos a programarlo con Python.

Será el mismo juego en donde interactúas a través de la terminal y se imprime el tablero del juego buscaminas con las celdas que tiene y el conteo de minas cercanas o un asterisco.

Recuerda que todo esto lo vamos a programar usando Python y algunas listas.

Enunciado

Debes crear un programa en Python que le permita al usuario jugar al juego Buscaminas, considerando que posee 6×6 casillas y 3 minas.

En la figura 1(a) se muestra la interfaz que debes lograr en el programa que construyas. Observa que las filas se referencian con las letras mayúsculas entre la A y la F, y las columnas con números entre el 1 y el 6.

Para simular casillas “cerradas” debes usar un punto “.”. Para referenciar una casilla en particular debes escribir primero la fila (letra) y luego la columna (número).

En el ejemplo la casilla que está destacada en el círculo rojo es la E2. En la figura 1(b), puedes observar el detalle de la impresión por pantalla.

Considera que en cada casilla cabe un carácter. Las casillas que se muestran vacías poseen un espacio en blanco.

Reglas del juego buscaminas en Python

Posiciones de minas

1. Al iniciar tu programa debe solicitar las posiciones donde deben ocultarse las 3 minas. Estas posiciones se deben ingresar en un solo string.

En él se deben indicar las posiciones de las 3 minas (escribiendo primero la fila – letra – y luego la columna – número).

Debes tener cuidado con las posiciones que podrían estar repetidas: en este caso podrían haber menos de 3 minas.

Abriendo casillas

2. Luego, tu programa debe pedir al usuario la casilla que desea abrir. La posición de la casilla debe indicar primero la fila (letra) y luego la columna (número). En este punto, se pueden dar 3 posibles escenarios:

2.1. La casilla no tiene una mina: En este caso tu programa deberá contar en las casillas en torno a la escogida, el número de minas que hay.

De esta manera, el programa le debe mostrar el tablero con ese número en la posición que escogió.

Además, en las siguientes jugadas, el programa debe mostrar el tablero con el valor calculado para todas las casillas ya abiertas.

Ganar y perder

2.2. La casilla no tiene una mina, y es la última que falta abrir: en este caso tu programa debe mostrar al usuario el tablero con un asterisco “*” en las posiciones donde estaban las minas con los valores de las minas entorno de cada casilla.

Abajo del tablero tu programa debe mostrar el mensaje “GANASTE”.

2.3. La casilla tiene una mina: En este caso tu programa debe mostrar al usuario el tablero con un asterisco “*” en las posiciones donde estaban las minas, los valores de las casillas que se alcanzaron a abrir, y un punto para las casillas que no se abrieron.

Abajo del tablero tu programa debe mostrar el mensaje “PERDISTE”.

Buscaminas en Python

Buscaminas en Python - Programación de juego
Buscaminas en Python – Programación de juego

Arriba coloqué el enunciado de este ejercicio, en donde básicamente debemos programar el Minesweeper en Python solicitando las minas al inicio y jugando a través de un tablero.

A continuación veamos la solución al juego:

MINA = "*"
ESPACIO_SIN_ABRIR = "."
ESPACIO_ABIERTO = "-"
LETRAS = "ABCDEF"
FILAS = len(LETRAS)
COLUMNAS = 6
tablero = []
HA_GANADO = False
HA_PERDIDO = False


def inicializar_tablero():
    global tablero
    tablero = []
    """
    Rellena el tablero
    """
    for fila in range(FILAS):
        tablero.append([])
        for columna in range(COLUMNAS):
            tablero[fila].append(ESPACIO_SIN_ABRIR)


def numero_a_letra(numero):
    """
    Convierte el número (comenzando en 0) a la letra
    """
    return LETRAS[numero]


def letra_a_numero(letra):
    """
    Devuelve el número que le corresponde a la letra COMENZANDO EN 0. Por ejemplo
    A: 0
    B: 1
    """
    # Nota: si en algún momento se deseara que el tablero fuera más grande, solo sería cuestión de agregar más letras a la cadena
    numero = LETRAS.index(letra)
    return numero


def obtener_indices_a_partir_de_coordenadas(coordenadas):
    """
    Devuelve, a partir de una coordenada como A1, las coordenadas
    reales de la matriz; es decir, los índices. Por ejemplo, para
    A1 devolvería [0, 0]
    """
    letra = coordenadas[0:1]
    fila = letra_a_numero(letra)
    columna = int(coordenadas[1:2]) - 1
    return fila, columna


def colocar_minas_en_tablero(posiciones):
    global tablero
    for posicion in posiciones:
        fila, columna = obtener_indices_a_partir_de_coordenadas(posicion)
        tablero[fila][columna] = MINA


def iniciar_tablero_con_string(posiciones_string):
    # Convertimos a mayúscula para que podamos comparar de mejor manera
    posiciones_string = posiciones_string.upper()
    posiciones_separadas = []
    primera_posicion = posiciones_string[0:2]
    segunda_posicion = posiciones_string[2:4]
    tercera_posicion = posiciones_string[4:6]
    if not primera_posicion in posiciones_separadas:
        posiciones_separadas.append(primera_posicion)
    if not segunda_posicion in posiciones_separadas:
        posiciones_separadas.append(segunda_posicion)
    if not tercera_posicion in posiciones_separadas:
        posiciones_separadas.append(tercera_posicion)
    colocar_minas_en_tablero(posiciones_separadas)


def obtener_minas_cercanas(fila, columna):
    conteo = 0
    if fila <= 0:
        fila_inicio = 0
    else:
        fila_inicio = fila - 1
    if fila + 1 >= FILAS:
        fila_fin = FILAS - 1
    else:
        fila_fin = fila + 1

    if columna <= 0:
        columna_inicio = 0
    else:
        columna_inicio = columna - 1

    if columna + 1 >= COLUMNAS:
        columna_fin = COLUMNAS - 1
    else:
        columna_fin = columna + 1

    for f in range(fila_inicio, fila_fin + 1):
        for c in range(columna_inicio, columna_fin + 1):
            # Si es la central, la omitimos
            if f == fila and c == columna:
                continue
            if tablero[f][c] == MINA:
                conteo += 1
    return str(conteo)


def imprimir_tablero():
    print("")
    ultima_casilla = False
    # Imprimir esquina superior izquierda
    print("  ", end="")
    # Imprimir resto de encabezado
    for columna in range(COLUMNAS):
        print(str(columna + 1), end=" ")
    # Salto de línea
    print("")
    # Imprimir contenido...
    numero_fila = 0
    for fila in tablero:
        letra = numero_a_letra(numero_fila)
        print(letra, end=" ")
        for numero_columna, dato in enumerate(fila):
            # El tablero tiene los verdaderos datos, pero nosotros imprimimos otros para que el usuario no "descubra" lo que hay debajo
            verdadero_dato = ""
            if dato == MINA:
                if HA_GANADO or HA_PERDIDO:
                    verdadero_dato = MINA
                else:
                    verdadero_dato = ESPACIO_SIN_ABRIR
            elif dato == ESPACIO_ABIERTO:
                verdadero_dato = obtener_minas_cercanas(numero_fila, numero_columna)
            elif dato == ESPACIO_SIN_ABRIR:
                verdadero_dato = "."

            print(verdadero_dato, end=" ")
        print("")
        numero_fila += 1
    if HA_GANADO:
        print("GANASTE")
    elif HA_PERDIDO:
        print("PERDISTE")


def abrir_casilla(coordenadas):
    global HA_GANADO, HA_PERDIDO, tablero
    fila, columna = obtener_indices_a_partir_de_coordenadas(coordenadas)
    # Qué había en la casilla?
    elemento_actual = tablero[fila][columna]
    # Si hay una mina, pierde y ya no se modifica nada
    if elemento_actual == MINA:
        HA_PERDIDO = True
        return

    # Si es un elemento sin abri, lo abre
    if elemento_actual == ESPACIO_SIN_ABRIR:
        tablero[fila][columna] = ESPACIO_ABIERTO
    # Comprobamos si hay casillas sin abrir
    if no_hay_casillas_sin_abrir():
        HA_GANADO = True


def no_hay_casillas_sin_abrir():
    for fila in tablero:
        for columna in fila:
            if columna == ESPACIO_SIN_ABRIR:
                return False
    return True


def solicitar_coordenadas():
    while True:
        coordenadas = input("Ingresa el string con las posiciones de las minas: ")
        if len(coordenadas) == COLUMNAS:
            return coordenadas
        else:
            print("Coordenadas no válidas. Intenta de nuevo")


def solicitar_casilla():
    while True:
        casilla = input("Ingresa la casilla del tablero que quieres abrir: ")
        casilla = casilla.upper()
        if len(casilla) != 2:
            print("Debes introducir una letra y un número")
            continue
        if not casilla[0].isalpha():
            print("El primer valor debe ser una letra")
            continue
        if not casilla[1].isdigit():
            print("El segundo valor debe ser un número")
            continue
        if not casilla[0] in LETRAS:
            print("La letra debe estar en el rango " + LETRAS)
            continue
        if int(casilla[1]) <= 0 or int(casilla[1]) > COLUMNAS:
            print(f"El número debe estar en el rango 1-{COLUMNAS}")
            continue
        return casilla


def main():
    inicializar_tablero()
    coordenadas = solicitar_coordenadas()
    iniciar_tablero_con_string(coordenadas)
    imprimir_tablero()
    while not HA_PERDIDO and not HA_GANADO:
        casilla = solicitar_casilla()
        abrir_casilla(casilla)
        imprimir_tablero()


main()

Lo que hacemos es trabajar con cadenas para parsear o interpretar la entrada de las minas que el usuario quiere colocar y luego usar una matriz o una lista de listas de Python como tablero.

La salida ya la has visto en la imagen que acompaña al encabezado. Me parece que este buscaminas en Python fue la primera implementación que hice de ese juego, y después vinieron las versiones de C y C++.

Te invito a seguir leyendo más ejercicios de Python en mi blog.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *