Sergio Sepúlveda
Sthefany Blanco
Juan Franco
¿Qué tiene el buscaminas?
Ya en esta entrega publicaremos el buscaminas terminado y funcional, dentro de sus funciones y características estan:
Destapar minas
Verificar cuantas minas hay alrededor de una casilla
Colocar Banderas
Despejar las casillas vacias del tablero hasta encontrar una mina
El juego maneja un temporizador
El juego reproduce sonidos cuando se gana el juego, cuando se pierde, cuando destapa una mina y cuando coloca una bandera
El juego cuenta con imagenes que indican el estado de la partida
Diseño de la interfaz...
Esto es lo que ve el usuario al iniciar el programa:
Cuando el usuario da click al boton inicar, se muestra el tablero de juego y el tiempo empieza a correr:
Cuando el usuario da click en un boton del tablero (y este no es una mina) esta se pondrá de color gris y mostrará un número indicando cuantas minas hay alrededor
o si la casilla es vacia el programa buscará las casillas vacias a su alrededor hasta encontrar una mina
Si el usuario quiere colocar una bandera, deberá marcar la opcion de "Colocar Banderas" en la parte inferior de la venata y hacer click en el tablero, al hacer click la casilla se pintará de color azul y el indicador de banderas aumentará en 1
El usuario ganará el juego cuando marque como banderas todas las casillas que sean minas, pero si marca 50 banderas y estas no corresponden con las minas, el programa mostrará un mensaje al usuario diciendole que puso mal las banderas y que lo intente otra vez
Si el usuario intenta poner una bandera en una casilla que ya destapó el programa enviará un mensaje diciendo que poner una bandera ahi no es necesario
Y si el usuario intenta destapar una casilla en la que hay una bandera, el programa enviará un mensaje diciendo que debe retirarla
Código:
Variables globales:
///La variable mapa es un arreglo en donde se guardarán las minas que se asignen aleatoriamete
int[] mapa = new int[352];
///la variable espacio es una matriz que representa el tablero de juego la cual consta de 22 columnas y 16 filas (352 botones en total)
int[,] espacio = new int[22,16];
/// la variable casilas_vacias es un arreglo que contiene números correspondientes a los id de los botones nulos del juego
int[] casillas_vacias = new int[]
{1,2,3,4,7,8,9,10,11,12,13,14,15,16,19,20,21,22,
23,24,25,26,29,30,31,32,33,34,35,36,37,38,41,42,43,44,
45,46,47,48,49,50,53,54,55,56,57,58,61,62,63,64,65,66,
67,68,69,70,71,72,75,76,77,78,79,80,83,84,85,86,87,88,
89,90,91,92,107,108,109,110,
111,112,113,114,129,130,131,132,
133,134,139,140,147,148,153,154,
155,156,161,162,169,170,175,176,
223,224,239,240,
245,246,261,262,
267,268,271,272,273,274,275,276,277,278,279,280,283,284,
289,290,293,294,295,296,297,298,299,300,301,302,305,306,
309,310,311,312,313,314,319,320,325,326,327,328,329,330,
331,332,333,334,335,336,341,342,347,348,349,350,351,352};
/// La variable casillas_minas es un arreglo que albergará numeros correspondientes a los botones que serán asignados como minas
int[] casillas_minas = new int[50];
/// La variable banderas es un arreglo que contiene números correspondientes a los botones a los que se le han puesto una bandera
int[] banderas = new int[50];
/// La variable contador_banderas tiene el conteo de las banderas que se han puesto, este aumentara hasta 50
int contador_banderas = 0;
/// Estas variables de tipo entero se usan en el método del timer para contar minutos y segundos
int segundos = 0;
int minutos = 0;
private void BotonIniciar_Click(object sender, EventArgs e)
{
/// Instancia del metodo cargar
cargar();
/// Linea de codigo que esconde el boton iniciar y muestra el tablero de juego
BotonIniciar.Hide();
/// Linea de codigo que inicia el temporizador
timer1.Enabled = true;
/// Al dar click en el boton iniciar el temporizador se inicia en 0 asi como el contador de banderas
segundos = 0;
minutos = 0;
contador_banderas = 0;
Tiempomin.Text = "Tiempo: 00";
Tiemposeg.Text = ":00";
cantbanderas.Text = "Banderas utilizadas: 0/50";
}
Método que inicia el juego cargando las minas dejando un valor de 9 en las casillas que sean minas y en las casillas que no lo sean busca cuantas minas tiene alrededor estableciendo un valor de 0 a 8
public void cargar()
{
/// La variable r se usa para instanciar aleatorios
Random r = new Random();
/// Ciclo for que recorre la matriz espacio e iguala todos sus valores a 0
for (int i = 1; i < espacio.GetLength(0); i++)
{
for (int j = 1; j < espacio.GetLength(1); j++)
{
espacio[i, j] = 0;
}
}
/// Ciclo for que recorre el arreglo mapa y la matriz espacio dejando todos sus valores en 0 y reestablece las características predeterminadas de los botones
for (int i = 1; i < mapa.Length; i++)
{
/// La variable lista se usa para guardar las coordenadas que retorne el metodo recuperar coordenadas
List<int> lista =null;
/// Instancia del metodo recuperar coordenadas el cual devuelve 2 valores correspondientes a las coordenadas en la matriz espacio
lista= recuperarCoordenadas(i);
espacio[lista[1], lista[0]] = 0;
mapa[i] = 0;
/// Linea de codigo que busca un boton segun su id y lo guarda en la variable boton
Button boton = (Button)this.Controls.Find("button" + i, false)[0];
boton.Text = "" + i;
/// If en donde se distinguen los botones nulos de los jugables y se "pintan" con su color correspondiente
if (casillas_vacias.Contains(i) == true)
{
boton.ForeColor = Color.Black;
boton.BackColor = Color.Black;
}
else
{
boton.ForeColor = Color.Lime;
boton.BackColor = Color.Lime;
}
}
/// Indice que representa la cantidad de iteraciones del ciclo while el cual aumentará hasta 50 que es la cantidad de minas en el juego
int indice = 0;
/// Ciclo while que genera un aleatorio el cual corresponde a unas coordenadas de la matriz espacio donde se asignará el numero 9 que es igual a una mina
while (indice < 50)
{
/// Variable que contiene la cantidad de minas que se han puesto
int contador_minas = 0;
/// Variable aleatoria entre 1 y 352, este numero corresponde a la casilla donde se asignará la mina
int mina = r.Next(1, 352);
/// Booleano con el cual se verifica que la variable mina no sea una casilla vacia
bool es_vacia = casillas_vacias.Contains(mina);
if (es_vacia == false)
{
/// Booleano con el que se verifica que la mina no haya sido asignada
bool es_mina = casillas_minas.Contains(mina);
if (es_mina == false)
{
/// Lista donde se almacenan las coordenadas donde se aloja la mina
List<int> lista = recuperarCoordenadas(mina);
/// Variables donde se guardan las coordenadas en x y en y de la mina
int y = lista[0];
int x = lista[1];
/// Se asigna el valor 9 (el cual es equivalente a una mina) en las coordenadas dentro de la matriz espacio y el arreglo mapa
espacio[x,y]=9;
mapa[mina] = 9;
casillas_minas[indice] = mina;
/// Ciclo que recorre la matriz espacio buscando minas, por cada mina suma 1 al contador
for (int j = 0; j < espacio.GetLength(1); j++)
{
for (int i = 0; i < espacio.GetLength(0); i++)
{
if (espacio[i, j] == 9)
{
contador_minas++;
}
}
}
/// Serie de if´s que suman 1 al valor de las casillas contiguas a la casilla donde se ubicó la mina
/// Casilla de abajo
if (x + 1 < 22)
if (espacio[x + 1, y] < 9)
espacio[x + 1, y] = espacio[x + 1, y] + 1;
///Casilla de la derecha
if (y + 1 < 16)
if (espacio[x, y + 1] < 9)
espacio[x, y + 1] = espacio[x, y + 1]+1;
/// Casilla de arriba
if (x - 1 >= 0)
if (espacio[x - 1, y] < 9)
espacio[x - 1, y]++;
/// Casilla de la izquierda
if (y - 1 >= 0)
if (espacio[x, y - 1] < 9)
espacio[x, y - 1]++;
/// Casilla diagonal abajo derecha
if (x + 1 < 22 && y + 1 < 16)
if (espacio[x + 1, y + 1] < 9)
espacio[x + 1, y + 1]++;
/// Casilla diagonal abajo-izquierda
if (x + 1 < 22 && y - 1 >= 0)
if (espacio[x + 1, y - 1] < 9)
espacio[x + 1, y - 1]++;
/// Casilla diagonal arriba-izquierda
if (x - 1 >= 0 && y - 1 >= 0)
if (espacio[x - 1, y - 1] < 9)
espacio[x - 1, y - 1]++;
/// Casilla diagonal arriba-derecha
if (x - 1 >= 0 && y + 1 < 16)
if (espacio[x - 1, y + 1] < 9)
espacio[x - 1, y + 1]++;
/// Iguala el contador de minas al indice para que cuando este sea 50 se salga del ciclo
indice = contador_minas;
}
}
}
}
Método que destapa un botón y averigua si es o no una mina, si es una mina mostrará el estado de derrota en el juego, si no lo es llamará al método de descubrir área
public void destaparmina(object sender)
{
/// Linea de codigo que instancia el metodo SonidoLaser que reproduce el sonido de un laser
SonidoLaser();
try
{
/// Variable que atrapa el nombre del boton con el cual fue invocado el método
Button b = (Button)sender;
/// Variable auxiliar que busca el valor del espacio correspondiente al numero del boton en el arreglo mapa
int aux = mapa[Int32.Parse(b.Text)];
/// Lista en donde se guardan las coordenadas del boton presionado
List<int> lista = recuperarCoordenadas(int.Parse(b.Text));
/// Variables que tienen las coordenadas en x y en y del boton
int y = lista[0];
int x = lista[1];
/// If que pregunta si el boton a destapar es una bandera, si es asi muestra un mensaje diciendole al usuario que debe quitar la bandera para destapar la mina
if (b.ForeColor == Color.Blue)
{
DialogResult dialogResult = MessageBox.Show("Retira la bandera antes de destapar la casilla", "Destapar casilla", MessageBoxButtons.OK);
}
else
{
/// Booleano que verifica si el boton presionado es una mina
bool es_mina = false;
if (aux == 9)
{
es_mina = true;
}
/// Si el boton presionado es una mina, se destapará todo el tablero indicando las casillas que eran minas
if (es_mina == true)
{
/// Variable indice para recorrer la matriz espacio
int indice = 1;
/// Ciclo que recorre la matriz espacio asignando sus valores a cada boton
for (int j = 0; j < espacio.GetLength(1); j++)
{
for (int i = 0; i < espacio.GetLength(0); i++)
{
/// Linea de codigo que busca un boton segun su id y lo guarda en la variable boton
Button casilla = (Button)this.Controls.Find("button" + indice, false)[0];
/// Linea de codigo que asigna al boton un texto con el valor de la matriz espacio segun sus coordenadas
casilla.Text = "" + espacio[i, j];
indice++;
}
}
/// Ciclo for que recorre el arreglo mapa buscando aquellas casillas que son minas
for (int i = 1; i < mapa.Length; i++)
{
/// Si el valor del espacio es igual a 9 pintará el botón de rojo indicando que es una mina
if (mapa[i] == 9)
{
Button bomba = (Button)this.Controls.Find("button" + i, false)[0];
bomba.BackColor = Color.Red;
bomba.ForeColor = Color.Red;
}
/// En caso contrario pintará la casilla de color gris indicando que no es una mina
else
{
Button bomba = (Button)this.Controls.Find("button" + i, false)[0];
bomba.BackColor = Color.DarkGray;
bomba.ForeColor = Color.Black;
}
}
/// Linea de codigo que detiene el temporizador
timer1.Stop();
/// Lineas de codigo que cambian la imagen de la nave por la de una explosion
Nave.Hide();
nexpl.Show();
/// Linea de codigo que reproduce el sonido de una explosion
SonidoExplosion();
/// Linea de código que muestra un mensaje diciendo que el juego ha terminado
DialogResult dialogResult = MessageBox.Show("Perdiste\n ¿Quieres comenzar de nuevo?", "GameOver", MessageBoxButtons.YesNo);
/// Si el usuario responde que si el juego se reiniciará
if (dialogResult == DialogResult.Yes)
{
/// Linea de codigo que vuelve a mostrar el boton de inicio
BotonIniciar.Show();
/// Lineas de codigo que reestablecen la imagen de la nave
nexpl.Hide();
Nave.Show();
}
/// Si el usuario responde que no el juego se cerrará
else if (dialogResult == DialogResult.No)
{
this.Close();
}
}
/// Si el boton presionado no es una mina, se invocará el método descubrirArea enviando el id del botón y sus coordenadas
else
{
DescubrirArea(x, y, int.Parse(b.Text));
}
}
}
catch { }
}
Método que descubre todas las casillas vacías cercanas a un botón presionado en caso de que este sea vacio, si este tiene minas cercanas escribirá la cantidad correspondiente en el botón
public void DescubrirArea(int x, int y, int i)
{
/// Linea que busca el botón segun su id y lo guarda en la variable boton
Button boton = (Button)this.Controls.Find("button" + i, false)[0];
/// Si la casilla esta tapada
if (boton.ForeColor == Color.Lime)
{
/// Descubre la casilla
boton.BackColor = Color.DarkGray;
boton.ForeColor = Color.Black;
boton.Text = "" + espacio[x, y];
/// Si no hay minas cercanas
if (espacio[x, y] == 0)
{
/// Ciclo que recorre las casillas cercanas
for (int f2 = Math.Max(0, x - 1); f2 < Math.Min(22, x + 2); f2++)
{
for (int c2 = Math.Max(0, y - 1); c2 < Math.Min(16, y + 2); c2++)
{
/// Invoca el metodo recuperarNboton que obtiene el número de los botones cercanos al que se presionó y lo guarda en la variable nboton
int nboton = recuperarNboton(f2, c2);
/// Incoca recursivamente el metodo descubrir area para las casillas cercanas
DescubrirArea(f2, c2, nboton);
}
}
}
}
}
public void colocarBandera(object sender)
{
/// Linea de codigo que reproduce el sonido de un escudo de energia
SonidoEscudo();
try
{
/// Lineas de codigo que recuperan el id del boton sobre el que se puso la bandera
Button b = (Button)sender;
int boton = Int32.Parse(b.Text);
/// Variable que cuenta la cantidad de banderas que se pusieron correctamente
int aciertos = 0;
/// Booleano que pregunta si el boton presionado ya es una bandera
bool es_bandera = banderas.Contains(boton);
/// If que pregunta si el boton ya ha sido destapado por medio de la característica ForeColor, si es así mostrará un mensaje diciendo que la bandera no se puede poner ahi
Button bandera = (Button)this.Controls.Find("button" + boton, false)[0];
if (bandera.ForeColor == Color.Black)
{
DialogResult dialogResult = MessageBox.Show("Ya sabes que esta casilla no tiene minas\n por lo que una bandera es innecesaria", "Bandera Innecesaria", MessageBoxButtons.OK);
}
else
/// En caso contrario preguntará si el boton presionado no es una banderá, si no es bandera colocará la bandera en el boton y si es una bandera la retirará
{
if (es_bandera == false)
{
/// Pregunta si el usuario ya ha usado todas las banderas
if (contador_banderas < 50)
{
/// Lineas de codigo que pintan el boton de azul
bandera.BackColor = Color.Blue;
bandera.ForeColor = Color.Blue;
/// Linea de código que introduce una bandera en el arreglo de las banderas
banderas[contador_banderas] = boton;
/// Se suma 1 al contador de banderas indicando que se colocó una bandera y se muestra al usuario el cambio en el contador
contador_banderas = contador_banderas + 1;
this.cantbanderas.Text = "Banderas utilizadas:" + contador_banderas + "/50";
/// Ciclo for que recorre el arreglo de banderas preguntando si sus valores corresponden a los de las minas, si es así le sumará uno a al contador de aciertos
for (int i = 0; i < banderas.Length; i++)
{
if (casillas_minas.Contains(banderas[i]))
{
aciertos++;
}
}
/// Si la variable aciertos es igual a 50 quiere decir que el usuario puso todas las banderas correctamente y que ganó el juego
if (aciertos == 50)
{
/// Lineas de código que muestran el estado de victoria del juego, se reproduce un sonido de un marciano muriendo y las imagenes de los marcianos se reemplazan por explosiones
SonidoMarciano();
timer1.Stop();
Marciano1.Hide();
Marciano2.Hide();
Marciano3.Hide();
mexpl1.Show();
mexpl2.Show();
mexpl3.Show();
///Linea de código que muestra un mensaje diciendo que el juego ha terminado
DialogResult dialogResult = MessageBox.Show("Has descubierto todas las minas\n ¿Quieres comenzar de nuevo?", "GameOver", MessageBoxButtons.YesNo);
///Si el usuario responde que si el juego se reiniciará
if (dialogResult == DialogResult.Yes)
{
BotonIniciar.Show();
Marciano1.Show();
Marciano2.Show();
Marciano3.Show();
mexpl1.Hide();
mexpl2.Hide();
mexpl3.Hide();
}
///Si el usuario responde que no el juego se cerrará
else if (dialogResult == DialogResult.No)
{
this.Close();
}
}
}
/// Si los aciertos no son 50 y el usuario puso todas las banderas, el programa enviará un mensaje informandole que puso las banderas incorrectamente
else
{
DialogResult dialogResult = MessageBox.Show("Has puesto todas las banderas, pero estas no coinciden con las minas\n inténtalo de nuevo", "Intenta otra vez", MessageBoxButtons.OK);
}
}
/// Si el usuario presionó un botón que ya es una bandera, el programa procederá a quitar la bandera del boton
else
{
/// Lineas de código que devuelven el botón a su estado original
bandera.BackColor = Color.Lime;
bandera.ForeColor = Color.Lime;
/// El contador de banderas se reduce en 1, indicando que la bandera fue retirada
contador_banderas = contador_banderas - 1;
/// Variable de tipo int que indica la pocisión en el arreglo de banderas
int pocision = 0;
/// Ciclo for que busca la pocisión en el arreglo de las banderas
for (int i = 0; i < banderas.Length; i++)
{
if(banderas[i]==boton)
{
pocision = i;
}
}
/// Se deja el valor de la pocisión encontrada en 0
banderas[pocision] = 0;
/// Se muestra al usuario que retiró una bandera
this.cantbanderas.Text = "Banderas utilizadas:" + contador_banderas + "/50";
}
}
}
catch { }
}
private List<int> recuperarCoordenadas(int num)
{
/// Variable que actua de contador para limitar hasta donde se sumarán los indices i y j de la matriz espacio
int pos = 0;
/// Lista donde se guardará el resultado de la búsqueda
List<int> resultado = new List<int>();
/// Ciclo que recorre la matriz verificando que la variable pos sea igual al numero -1, cuando sea asi le asignará a resultado los valores de las coordenadas i y j de la matriz espacio
for (int i = 0; i < espacio.GetLength(1); i++)
{
for (int j = 0; j < espacio.GetLength(0); j++)
{
if(pos==num-1)
{
resultado.Add(i);
resultado.Add(j);
}
else
{
pos++;
}
}
}
return resultado;
}
private int recuperarNboton(int x,int y)
{
/// Variable de tipo int en la que se guardará la pocisión del boton que se quiere guardar
int pos = 0;
/// Matriz auxiliar con la cual se buscarán las coordenadas del boton
int[,] eaux = new int[22, 16];
/// Ciclo for que deja todos los valores de la matriz eaux en 0
for (int i = 0; i < eaux.GetLength(0); i++)
{
for (int j = 0; j < eaux.GetLength(1); j++)
{
eaux[i, j] = 0;
}
}
/// Linea que deja el valor de la matriz en 1 según las coordenadas del boton al que se le quiere encontrar su id
eaux[x, y] = 1;
/// For que recorre la matriz eaux buscando las coordenadas en donde su valor sea igual a 1
for (int j = 0; j < eaux.GetLength(1); j++)
{
for (int i = 0; i < eaux.GetLength(0); i++)
{
/// Si el valor en la matriz es igual a 1 deja los valores de i y j en 100 y se sale del ciclo for
if (eaux[i, j] == 1)
{
j = 100;
i = 100;
}
/// De no ser así le suma 1 a la pocisión
else
{
pos=pos+1;
}
/// Si i es igual a 22 le suma 1 para que continúe con la siguiente fila
if (i == 22)
{
i++;
}
}
/// Si j es igual a 16 le suma 1 para que continúe con la siguiente columna
if (j == 16)
{
j++;
}
}
return pos;
}
private void button346_Click_1(object sender, EventArgs e)
{
if (ColocarBanderas.Checked) {colocarBandera(sender);} else {destaparmina(sender);}
}
Manejador de eventos del botón salir
Para añadir recursos necesarios para reproducir sonidos y poner imagenes nos vamos a proyecto y propiedades de buscaminas
private void BotonSalir_Click(object sender, EventArgs e) { this.Close(); }
Método para controlar el temporizador del juego, este se ejecutará 1 vez cada segundo
private void timer1_Tick(object sender, EventArgs e)
{
/// Le suma 1 a los segundos
segundos++;
/// Pregunta si los segundos están entre 0 y 9, si es así añade un 0 al texto que muestra los segundos (esto es mera estética)
if (segundos >= 0 && segundos <= 9)
{
Tiemposeg.Text = ":0" + segundos.ToString();
}
/// Si no es así pone en el texto los segundos tal y como están
else
{
Tiemposeg.Text = ":"+segundos.ToString();
}
/// Si los segundos son iguales a 60, le sumará uno a los minutos y dejará los segundos en 0
if (segundos == 60)
{
segundos = 0;
Tiemposeg.Text = ":00";
minutos++;
/// Pregunta si los minutos están entre 0 y 9, si es así añade un 0 al texto que muestra los minutos (esto es mera estética)
if (minutos >= 0 && minutos <= 9)
{
Tiempomin.Text = "Tiempo: 0" + minutos.ToString();
}
/// Si no es así, pone los minutos tal y como están
else
{
Tiempomin.Text = "Tiempo: "+minutos.ToString();
}
}
}
Método para cargar y reproducir sonidos, en la variable player que es de tipo SoundPlayer, carga el sonido que está dentro de los recursos del proyecto y luego reproduce el sonido cargado (aqui ponemos uno de ellos ya que los otros son iguales y solo cambian en el recurso que se carga)
private void SonidoLaser()
{
SoundPlayer player = new SoundPlayer(global::Buscaminas.Properties.Resources.Laser);
player.Play();
}
Para añadir recursos necesarios para reproducir sonidos y poner imagenes nos vamos a proyecto y propiedades de buscaminas