Seguimos buscando aplicaciones a los nuevos motores de inteligencia aritificial. Hoy es el turno de uno de los más clásicos, el buscaminas. A través de la IA vamos a generar el Buscaminas, programado con Inteligencia Artificial lo haremos en HTML para poder ser ejecutado en cualquier buscador.

Antes de ponernos a ello, un poco de información sobre el cásico juego.

Buscaminas generado por Inteligencia Artificial

El juego del Buscaminas es un juego de lógica y estrategia en el que el jugador debe encontrar y marcar todas las minas escondidas en un tablero de juego. El objetivo es ubicar todas las minas sin detonar ninguna de ellas, para lo cual el jugador debe tener una buena habilidad para deducir dónde se encuentran las minas. El tablero de juego se compone de celdas, que pueden estar vacías, contener una mina o contener números que indican el número de minas adyacentes a la celda en cuestión. El jugador puede marcar las celdas sospechosas para indicar que creen que hay una mina, lo que les ayuda a recordar cuál es el área a la que deben prestar atención. – By IA


Una vez puestos en contexto, vamos al lío.

Nota importante: como sabemos, las IA no siempre generan el mismo outputs para los mismos inputs; Por tanto, el desarrollo llevado aquí, es muy probable que no se pueda replicar 100%. No obstante, al ser un juego sencillo, es muy posible que obtengamos buenos resultados con códigos distintos.

Herramientas a utilizar:

  • Notepad++: para pegar nuestro código y guardar el archivo.

Pasos a seguir

Paso 1: Solicitamos a la IA de Open AI el código a través del PlayGround. Para ello hay muchas formas de pedírselo y cuantos más detalles le demos, más rica será la respuesta. En mi caso le especifiqué un tablero concreto de 3*3.

Instrucción en PlayGround para generar buscaminas 3*3

Dependiendo de la calidad de nuestro “prompt”, tendremos mejor o peor respuesta y tendremos que retocarla más o menos. En mi caso, he tenido que hacer varias pruebas para conseguir la primera base.


Paso 2: Analizar el código de salida. Es muy posible que tengáis que repetir los dos primeros pasos, si la calidad del código que nos genera no cumple con nuestras expectativas,

Consejo: Insistir tratando de ser detallado con las instrucciones, de modo que en una de las iteraciones puedas conseguir una buena base sobre la que trabajar.

<html>
<head>
<title>BuscaMinas</title>
<style type="text/css">
.casilla {
    width: 30px;
    height: 30px;
    text-align: center;
    border: 1px solid #999;
    font-size: 18px;
    float: left;
    background-color: #eee;
}
.casilla.vacia {
    background-color: #fff;
}
.casilla.mina {
    background-color: #f00;
    color: #fff;
    font-weight: bold;
}
</style>
</head>
<body>
<div>
    <h1>BuscaMinas</h1>
    <div>
        <button onclick="generarTablero()">Reiniciar</button>
    </div>
    <div id="tablero">
        
    </div>
</div>
<script>
    var tablero = [];
    var ancho = 3;
    var alto = 3;
    var minas = 1;
    
    function generarTablero(){
        tablero = [];
        let tableroHTML = '';
        for(let y = 0; y < alto; y++){
            let fila = [];
            for(let x = 0; x < ancho; x++){
                let casilla = {
                    mina: false,
                    marcada: false,
                    descubierta: false
                };
                
                fila.push(casilla);
                
                tableroHTML += `<div class="casilla" data-x="${x}" data-y="${y}" onclick="descubrirCasilla(${x},${y})" oncontextmenu="marcarCasilla(${x},${y})"></div>`
            }
            tablero.push(fila);
        }
        document.getElementById('tablero').innerHTML = tableroHTML;
        generarMinas(minas);
    }
    
    function generarMinas(cantidadMinas){
        for(let i = 0; i < cantidadMinas; i++){
            let x = Math.floor(Math.random() * ancho);
            let y = Math.floor(Math.random() * alto);
            tablero[y][x].mina = true;
        }
    }
    
    function descubrirCasilla(x, y){
        if(tablero[y][x].marcada || tablero[y][x].descubierta) return;
        if(tablero[y][x].mina){
            descubrirTodasLasMinas();
            alert('Game Over');
        } else {
            tablero[y][x].descubierta = true;
            renderizarTablero();
            if(comprobarVictoria()){
                alert('Ganaste');
            }
        }
    }
    
    function marcarCasilla(x, y){
        tablero[y][x].marcada = !tablero[y][x].marcada;
        renderizarTablero();
    }
    
    function descubrirTodasLasMinas(){
        tablero.forEach(function(fila, y){
            fila.forEach(function(casilla, x){
                if(casilla.mina){
                    tablero[y][x].descubierta = true;
                }
            });
        });
        renderizarTablero();
    }
    
    function comprobarVictoria(){
        let sinDescubrir = tablero.every(function(fila){
            return fila.every(function(casilla){
                return casilla.mina || casilla.descubierta;
            });
        });
        return sinDescubrir;
    }
    
    function renderizarTablero(){
        tablero.forEach(function(fila, y){
            fila.forEach(function(casilla, x){
                let celda = document.querySelector(`div[data-x="${x}"][data-y="${y}"]`);
                if(casilla.descubierta){
                    celda.classList.add('vacia');
                    if(casilla.mina){
                        celda.innerText = 'X';
                    } else {
                        let cantidadMinasAlrededor = contarMinasAlrededor(x, y);
                        celda.innerText = cantidadMinasAlrededor > 0 ? cantidadMinasAlrededor : '';
                    }
                } else if(casilla.marcada){
                    celda.innerText = 'M';
                } else {
                    celda.innerText = '';
                }
            });
        });
    }
    
    function contarMinasAlrededor(x, y){
        let minasAlrededor = 0;
        for(let dy = -1; dy <= 1; dy++){
            for(let dx = -1; dx <= 1; dx++){
                if(dx == 0 && dy == 0) continue;
                let xAlrededor = x + dx;
                let yAlrededor = y + dy;
                if(xAlrededor >= 0 && xAlrededor < ancho && yAlrededor >= 0 && yAlrededor < alto){
                    if(tablero[yAlrededor][xAlrededor].mina){
                        minasAlrededor++;
                    }
                }
            }
        }
        return minasAlrededor;
    }
    
    generarTablero();
	window.onload = generarTablero;
</script>
</body>
</html>

Si guardáis este código como archivo “.html”, observaréis que hay un pequeño fallo.

Aplicación ejecutada en Chrome.

Exacto!! Ha generado la cuadrícula, pero la esta representando con forma de fila. Esto podemos comprobarlo con la opción Inspeccionar de chrome y veremos los elementos que forman el objeto.


Paso 3: Corrección de errores.

En este paso, corregimos el error de mostrar solo una fila y lo hemos hecho pidiéndole a la IA que lo arregle ella sola. Así que despué de insistir 3 veces buscando distintos “prompts”, nos ha devuelto lo que buscabamos.

document.getElementById('tablero').style.display = 'grid';
document.getElementById('tablero').style.gridTemplateColumns = 'repeat(25, 30px)';

Con estas dos instrucciones, ya colocamos bien el campo de juego:

Buscaminas con tablero de 3*3

Ahora es momento de jugar y ver si responde a nuestras expectativas. Y como no, intentar ganar alguna partida.

Partida ganada

Paso 4: modificar al gusto. Una vez que tenemos esta base generada a una velocidad impresionante mediante la IA, es buen momento para cambiar las características del juego. Se me ocurren varias ideas que hacer con el código.

  • Cambiar el tamaño del tablero y el número de minas.
  • Cambiar la estética de la aplicación haciendola más atractiva.
  • Introducir parámetros para que el usuario elija la dificultad y por tanto, el número de minas.
  • Descubrimiento de varias casillas a la vez, como en el juego original.

Os dejo estas ideas para que vayáis practicando y el código de la última versión.

Espero os haya gustado este Buscaminas programado con Inteligencia Artificial. Seguiremos haciendo experimentos.


CÓDIGO FINAL

<html>
<head>
<title>BuscaMinas</title>
<style type="text/css">
.casilla {
    width: 30px;
    height: 30px;
    text-align: center;
    border: 1px solid #999;
    font-size: 18px;
    float: left;
    background-color: #eee;
}
.casilla.vacia {
    background-color: #fff;
}
.casilla.mina {
    background-color: #f00;
    color: #fff;
    font-weight: bold;
}
</style>
</head>
<body>
<div>
    <h1>BuscaMinas</h1>
    <div>
        <button onclick="generarTablero()">Reiniciar</button>
    </div>
    <div id="tablero">
        
    </div>
</div>
<script>
    var tablero = [];
    var ancho = 15;
    var alto = 15;
    var minas = 5;
    
    function generarTablero(){
        tablero = [];
        let tableroHTML = '';
        for(let y = 0; y < alto; y++){
            let fila = [];
            for(let x = 0; x < ancho; x++){
                let casilla = {
                    mina: false,
                    marcada: false,
                    descubierta: false
                };
                
                fila.push(casilla);
                
                tableroHTML += `<div class="casilla" data-x="${x}" data-y="${y}" onclick="descubrirCasilla(${x},${y})" oncontextmenu="marcarCasilla(${x},${y})"></div>`
				
            }
            tablero.push(fila);
			
        }
        document.getElementById('tablero').innerHTML = tableroHTML;
		document.getElementById('tablero').style.display = 'grid';
		document.getElementById('tablero').style.gridTemplateColumns = 'repeat(15, 30px)';
		
        generarMinas(minas);
    }
	
	
	
    
    function generarMinas(cantidadMinas){
        for(let i = 0; i < cantidadMinas; i++){
            let x = Math.floor(Math.random() * ancho);
            let y = Math.floor(Math.random() * alto);
            tablero[y][x].mina = true;
        }
    }
    
    function descubrirCasilla(x, y){
        if(tablero[y][x].marcada || tablero[y][x].descubierta) return;
        if(tablero[y][x].mina){
            descubrirTodasLasMinas();
            alert('Game Over');
        } else {
            tablero[y][x].descubierta = true;
            renderizarTablero();
            if(comprobarVictoria()){
                alert('Ganaste');
            }
        }
    }
    
    function marcarCasilla(x, y){
        tablero[y][x].marcada = !tablero[y][x].marcada;
        renderizarTablero();
    }
    
    function descubrirTodasLasMinas(){
        tablero.forEach(function(fila, y){
            fila.forEach(function(casilla, x){
                if(casilla.mina){
                    tablero[y][x].descubierta = true;
                }
            });
        });
        renderizarTablero();
    }
    
    function comprobarVictoria(){
        let sinDescubrir = tablero.every(function(fila){
            return fila.every(function(casilla){
                return casilla.mina || casilla.descubierta;
            });
        });
        return sinDescubrir;
    }
    
    function renderizarTablero(){
        tablero.forEach(function(fila, y){
            fila.forEach(function(casilla, x){
                let celda = document.querySelector(`div[data-x="${x}"][data-y="${y}"]`);
                if(casilla.descubierta){
                    celda.classList.add('vacia');
                    if(casilla.mina){
                        celda.innerText = 'X';
                    } else {
                        let cantidadMinasAlrededor = contarMinasAlrededor(x, y);
                        celda.innerText = cantidadMinasAlrededor > 0 ? cantidadMinasAlrededor : '';
                    }
                } else if(casilla.marcada){
                    celda.innerText = 'M';
                } else {
                    celda.innerText = '';
                }
            });
        });
    }
    
    function contarMinasAlrededor(x, y){
        let minasAlrededor = 0;
        for(let dy = -1; dy <= 1; dy++){
            for(let dx = -1; dx <= 1; dx++){
                if(dx == 0 && dy == 0) continue;
                let xAlrededor = x + dx;
                let yAlrededor = y + dy;
                if(xAlrededor >= 0 && xAlrededor < ancho && yAlrededor >= 0 && yAlrededor < alto){
                    if(tablero[yAlrededor][xAlrededor].mina){
                        minasAlrededor++;
                    }
                }
            }
        }
        return minasAlrededor;
    }
    
    generarTablero();
</script>
</body>
</html>