Como Hacer El Busca Minas en Visual Estudio

23
intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5 Juego MinesWeeper (busca minas) Hay un juego de lógica que vino con Windows 95, llamado MinesWeeper (busca minas) cuyo objetivo es encontrar, sin pisar ninguna, las minas que hay en un campo minado usando la ayuda que consiste en mostrar en las celdas próximas a donde se pisa, la cantidad de minas que hay alrededor de ellas. Empecemos creando un Windows Forms Application, cambiando el título del Form (propiedad Text) a MinesWeeper, colocando un botón sobre el Form cerca de su esquina superior izquierda, haciendo su propiedad Size igual a 30,30, borrando el valor de su propiedad Text (equivale a una cadena nula), sacándole 9 copias disponiéndolas en una fila. Verificamos que la propiedad Tag de cada botón está en blanco, pero que la propiedad TabIndex sigue el orden 0, 1, 2, …, 8, 9. Por tanto, podemos, podemos usar la secuencia que natural que sigue esta propiedad para individualizar cada botón en vez de usar, asignándola a mano, la propiedad Tag, como hicimos en el ejemplo Tic Tac Toe. Verifiquemos que podemos usar esta propiedad pulsando doble el primer botón (TabIndex 0) evento Click que aparece, en el modo de código, copiamos el siguiente código: foreach (System.Windows.Forms.Control ctrl in this.Controls) ctrl.Text = (Convert.ToString(ctrl.TabIndex)); Esto recorre cada uno de los, hasta ahora, 10 botones que hemos colocado en el Form, toma el valor de su propiedad TabIndex, lo convierte a string y se lo asigna a la propiedad Text del control (en este caso botón). Si ejecutamos la aplicación y pulsamos el primer botón veremos como los 10 botones muestran los dígitos del 0 al 9, como esperábamos.

Transcript of Como Hacer El Busca Minas en Visual Estudio

Page 1: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Juego MinesWeeper(busca minas)

Hay un juego de lógica que vino con Windows 95, llamado MinesWeeper (busca minas) cuyo objetivo es encontrar, sin pisar ninguna, las minas que hay en un campo minado usando la ayuda que consiste en mostrar en las celdas próximas a donde se pisa, la cantidad de minas que hay alrededor de ellas.

Empecemos creando un Windows Forms Application, cambiando el título del Form (propiedad Text) a MinesWeeper, colocando un botón sobre el Form cerca de su esquina superior izquierda, haciendo su propiedad Size igual a 30,30, borrando el valor de su propiedad Text (equivale a una cadena nula), sacándole 9 copias disponiéndolas en una fila.

Verificamos que la propiedad Tag de cada botón está en blanco, pero que la propiedad TabIndex sigue el orden 0, 1, 2, …, 8, 9. Por tanto, podemos, podemos usar la secuencia que natural que sigue esta propiedad para individualizar cada botón en vez de usar, asignándola a mano, la propiedad Tag, como hicimos en el ejemplo Tic Tac Toe.

Verifiquemos que podemos usar esta propiedad pulsando doble el primer botón (TabIndex 0) evento Click que aparece, en el modo de código, copiamos el siguiente código:

foreach (System.Windows.Forms.Control ctrl in this.Controls) ctrl.Text = (Convert.ToString(ctrl.TabIndex));

Esto recorre cada uno de los, hasta ahora, 10 botones que hemos colocado en el Form, toma el valor de su propiedad TabIndex, lo convierte a string y se lo asigna a la propiedad Text del control (en este caso botón). Si ejecutamos la aplicación y pulsamos el primer botón veremos como los 10 botones muestran los dígitos del 0 al 9, como esperábamos.

Esto nos garantiza que, realmente, la propiedad TabIndex, que es colocada automáticamente a cada botón por Visual C# la podemos usar más adelante para acceder a cada botón individualmente, mediante el valor de su propiedad TabIndex, que tan generosamente nos auto-numera Visual C#. Ahora le seleccionamos los diez botones y le sacamos una copia, de manera que obtengamos 20, y la colocamos próximo a la primera.

Page 2: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Verificamos que, efectivamente, los valores TabIndex de los nuevos botones van de 10 a 19. Por lo que ahora, podemos seleccionar los 20 botones y sacarle una copia, obteniendo 40. Y estos cuarenta le sacamos una copia, obteniendo 80, y, finalmente, selecionamos las últimas 20 copias y le sacamos una copia, para completar un total de 100 botones, con sus TabIndex que van de 0 a 99. Note que hay que redimensionar el Form para albergar cómodamente a los 100 botones.

Ahora necesitamos crear una matriz, de ámbito global, para colocar las minas, de manera aleatoria, y determinar cuántas minas hay alrededor de cada celda. Copiamos el siguiente texto inmediatamente después de la llave, {, que inicia el cuerpo de la clase parcial (partial class).

int[] mt = new int[100]; // matriz de estado

Page 3: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Ahora redactamos un método que genere las minas y las “coloque” al azar sobre el trasfondo del tablero de botones. Este método lo insertamos inmediatamente después del método Click.

private void minas(){

}

Note que este método luego de la directriz private tiene el tipo de dato void, sin valor -que no es, en realidad, un tipo de dato- porque NO retorna valor, y por la misma razón, que no recibe valor, es que entre los paréntesis no hay nada. Este método, pues, no recibe ni retorna nada, simplemente realiza una acción silenciosa, a saber, colocar ciertos valores en la matriz mt, pero como esta es de ámbito global, no hay que pasarla al método como parámetro.

Lo primero que hace el método es asegurar que todas las posiciones en la matriz tengan los números enteros no negativos desde cero a 99, para poder “barajar” todos estos valores.

private void minas() { for (int k = 0; k < 100; k++) mt[k] = k;

}

Para generar las minas al azar, declararemos una variable aleatoria de ámbito global, az, desde la cual invocaremos el método Next para obtener los números aleatorios que neceitamos. Esto hacemos debajo de la línea en que declaramos la matriz, mt.

Random az = new Random(); // variable aleatoria

En el método minas declaramos tres variables enteras; una para la posición aleatoria, a; otra para apuntar a la última posición actual, u; y otra para intercambiar los valores en la matriz mt que están en las posiciones a y u, t.

private void minas() { int a, u, t; // posición recorrido, posición, temporal

for (int k = 0; k < 100; k++) mt[k] = k;

}

Page 4: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Ahora barajamos los 100 valores colocados en la mt. Para ello redactamos un ciclo for con la variable contador u, la cual toma todos los valores enteros entre 99 y 1, de manera regresiva, y para cada valor de u generamos un valor aleatorio, a, invocando el método Next de la variable aleatoria az. Entonces copiamos el valor en matriz mt que está en la posición a variable t, copiamos el que está en la posición u de mt a la posición a de mt, y finalmente copiamos el valor de t ala posición u de mt. El código para esto se inserta en el método minas a seguidas del ciclo for anterior. Note que el valor de a es siempre menor que u.

for (u = 99; u > 0; u--) { a = az.Next(u); t = mt[a]; mt[a] = mt[u]; mt[u] = t; }

Verifiquemos los valores han sido barajados cambiando el código del evento Click (botón 1).

minas();

foreach (System.Windows.Forms.Control ctrl in this.Controls) ctrl.Text = (Convert.ToString(mt[ctrl.TabIndex]));

Ejecutamos el programa y comprobamos que, cada vez que pulsamos el primer botón, los números en los 100 botones se muestran los números del 0 a l 99, pero en distinto orden, pues cada vez que se ejecuta el método minas se barajan nuevamente las posiciones de los botones en la matriz de estado.

Si queremos, pues, sólo 25 minas distribuidas al azar, podemos tomar los números aleatorios que queden en las primeras 25 posiciones de la matriz de estado, aunque servirían igual las últimas 25, o cualquier otro segmento.

Por tanto, si copiamos los 25 valores de la matriz de estado en un arreglo de 25 posiciones, hacemos cero todos los elementos de la matriz de estado y colocamos un 9 en las posiciones de la matriz de estado que indican los valores en la matriz de 25 valores, equivale a colocar 25 minas al azar en nuestro campo minado. Empecemos por declarar, a nivel global la matriz de 25 valores (debajo de la variable aleatoria az).

int[] mn = new int[25]; // posiciones de las minas

Page 5: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Ahora copiamos las primeras 25 posiciones de la matriz de estado a la matriz de posiones de las minas, hacemos cero todas las posiciones de la matriz de estado y hacemos igual a 9 las 25 posiciones indicadas en las posiciones de las minas. Este es el código, hasta ahora, del método minas.

int a, u, t; // posición recorrido, posición, temporal

for (int k = 0; k < 100; k++) mt[k] = k;

for (u = 99; u > 0; u--) { a = az.Next(u); t = mt[a]; mt[a] = mt[u]; mt[u] = t; }

for (int k = 0; k < 25; k++) mn[k] = mt[k];

for (int k = 0; k < 100; k++) mt[k] = 0;

for (int k = 0; k < 25; k++) mt[mn[k]] = 9;

Ejecutamos la palicación y comprobamos que cada vez que pulsamos el prime botón, se despliegan 25 nueves en los botones y el resto son ceros. Cada vez que se pulsa el botón, las posiciones de los 9s cambia de manera aleatoria, como debe ser. ¡Podríamos jugar a la lotería con esto!

Puede que el lector se pregunte porqué pusimos un nueve para indicar que hay una mina en una determinada celda. La razón es que la vecindad de cada celda está compuesta por un máximo de 8 celdas.

1 2 3

8 x 4

7 6 5

Page 6: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Por tanto, en la matriz de estado podemos usar los números de 0 a 8 para indicar el número de minas que hay alrededor de una celda dada, y el 9 para indicar que en ella misma hay una mina. Debemos, ya que hemos logrado minar el campo, determinar, para las celdas en que no hay minas, determinar cuántas minas hay a su alrededor.

Lo primero es recordar que en nuestro caso el campo minado es de 10x10 celdas, esto es, un total de 100 celdas, que hemos numerado de 0 a 99. Se recordará del ejercicio Tic Tac Toe (cerito-cruz), que las celdas estaban numeradas de 0 a 8, pero como para el usuario era un matriz de 3x3, usamos dos funciones sencillas para obtener, dada la posición lineal de la celda (Tag), la fila y la columna de la celda, según la veía el usurio.

f = p / 3; c = p % 3;

Donde p es la posición lineal, f la fila y c la columna. Usaremos el mismo truco esta vez para obtener dada la posición lineal de la celda (TabIndex del botón) la fila y la columna, usando las mismas fórmulas pero cambiando el 3 por un 10, porque ahora nuestro tablero es de 10x10.

f = p / 10; c = p % 10;

Para determinar el número de minas que hay alrededor de una celda (si en ella no hay mina, naturalmente) tenemos que recorrer la vecindad de la celda, que, salvo para las celdas que están en los bordes (fila 0, colunma 9, fila 9 y columna 0) que tiene 8 celdas.

1 -> 2 3

8 f,c 4

7 6 5

Note que hemos colocado una flecha en la celda que está en la esquina superior izquierda de la vecindad de la celda (f,c) para indicar el sentido que adoptamos para recorrer la vecindad en busca del número de minas que hay alrededor de la celda (f,c).

Podríamos redactar un código más largo para realizar este recorriod, pero podemos simplificar un poco las cosas si consideramos en las coordenadas de la celda 1 son (f-1, c-1), las de la celda 2 son (f-1, c+0), las de la celda 3 son (f+1, c+1), y así sucesivamente.

Esto es, podemos crear dos arreglos, dx y dy que contengan los incrementos que hay que hacer al valor de f y c para obtener las coordenadas de las celdas en la vecindad de la celda (f, c).

int[ ] dx = new int[8] {-1, 0, 1, 1, 1, 0, -1, -1};

int[ ] dy = new int[8] {-1, -1, -1, 0, 1, 1, 1, 0};

Page 7: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Por tanto, ahora basta redactar un ciclo con un contador de 0 a 8 para visitar las 8 celdas alrededor de la celda (f,c) y obtener las coordenadas de cada una de sus vecinas. Claro, cuando el valor la nueva coordenada resultante sea menor que 0 o mayor que 9, significará que estamos en una celda del borde, y en este caso debemos ignorar estas coordenadas, pues nos llevan fuera del campo minado.

Así que si llamamos vx y vy a las coordenadas de las celdas en la vecindad de la celda (f, c), nuestro código para recorrer la vecindad de una celda dada sería algo así.

for ( int v = 0; v < 9; v++){

vx = f + dx[v];vy = c + dy[v];

} Pero necesitamos una manera de saber a qué celda (TabIndex) estamos apuntando de manera lineal, porque, a fin de cuentas, nuestras celdas (botones) tienen asignado un valor lineal de 0 a 99, no dos rectangulares, f y c. Dicho de otra manera, necesitamos una función inversa que nos permita, dadas la fila y la columna de una celda, obtener su posición lineal en el campo minado. Para dicha nuestra, esta función es bien sencilla.

p = 10 * f + c;

Por ejemplo, la celda (3,4) tiene posición lineal 10*3 + 4, que es 34, que lo convertimos de vuelta a filas y columnas, comprobamos que nos f = 3, c = 4. Y si consideramos el caso de la celda (0,9), p = 10*0 + 9, que nos da de vuelta (0, 9). El lector debe asegurarse de esta función simepre nos da el valor correcto.

Si tomamos en cuenta lo apuntado acerca de no salirnos del campo minado, entonces vemos que una vez obtenidas las coordenadas bidimensionales de una celda mediante los valores de vx y vy, basta obtener el valor de p, según la función recién estrenada, y caer en cuenta que su valor debe estar entre 0 y 100, porque de otra manera caemos fuera del campo minado.

Por tanto, para recorrer todo el campo minado nos basta un ciclo con un contador de 0 a 99, y para cada uno de estos valores recorremos sus ocho posibles vecinas, ignorando los casos en que caemos fuera del campo.

int mv, f, c;

for (int t = 0; t < 100; t++){

if (mt[t] != 9){

mv = 0;f = t / 10;c = t % 10;

for (int v = 0; v < 9; v++){

vx = f + dx[v];vy = c + dy[v];p = 10* vx + vy;if (p > -1 && p < 100 && mt[p] == 9) mv++;

}

mt = mv;}

}

Page 8: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Este código empieza por declarar la variable entera mv para llevar la cuenta de la cantidad de minas en la vecindad de una celda, y las variables f y c para obtener la fila y la columna de cada celda visitada en la matriz mt. A seguidas hay un ciclo for para recorrer todas las celdas en la matriz mt, desde la 0 hasta la 99. Y dentro de este ciclo for hay otro, de manera que se aplica a cada una de las celdas en la matriz mt.

En este ciclo interior lo primero que se pregunta, con un if, si esta celda de la matriz NO hay un 9, porque de ser así significa que hay una mina y no tiene sentido contar cuántas minas hay a su alrededor, porque tal cosa sólo tiene sentido en caso de que NO haya una mina en la celda investigada.

Así que en caso de que la celda no contenga un 9 entonces se have cero a la cantidad de minas encontradas en la vecindad de la celda actual, mv, y se calcula la fila y la columna de la celda visitada en la matriz mt.

Entonces se recorre con un ciclo for las 8 celdas en la vecindad de la celda actual usando la variable contador v. Calculamos las coordenadas de la celda en la vecindad de la celda (f, c) usando las variables vx y vy, y este valor lo convertimos en su equivalente lineal, p.

Si el valor de p obtenido es mayor que -1 y menor que 100, entonces se trata de una celda dentro del espacio minado, por tanto, si además para esta misma posición en la matriz mt hay un 9, entonces hemos encontrado una mina en la vecindad de la celda (f, c) y la contamos incrementando el valor de la variable mv, que lleva la cuenta de las minas encontradas en esta vecindad.

Una vez salimos del ciclo fo que recorre la vecindad de la celda (f, c), asignamos a la celda en la matriz mt el valor mv, para que refleje el número de minas encontradas en su vecindad.

Este código es un poco largo, por lo que conviene encapsularlo en un método, digamos, vecindad, el cual no retorna valor porque lo que hace es colocar en la matriz mt los valores correspondientes a los números de minas en las vecindades de las celdas y la matriz misma, mt, está declarada como de ámbito global. Tampoco necesitamos pasar ningún parámetro al método vecindad por las mismas razones. Por tanto, nuestro método vecindad, que insertamos inmediatamente debajo del método minas, queda así:

private void vecindad(){

int mv, f, c;

for (int t = 0; t < 100; t++){

if (mt[t] != 9){

mv = 0;f = t / 10;c = t % 10;

for (int v = 0; v < 9; v++){

vx = f + dx[v];vy = c + dy[v];p = 10* vx + vy;if (p > -1 && p < 100 && mt[p] == 9) mv++;

}

mt = mv;}

}}

Page 9: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Al insertar el método vecindad Visual C# nos informa que las variables dx y dy no han sido declaradas, lo cual es cierto. Surge ahora la inquietud de dónde declarar estas variables, y la respuesta obvia es que como su ámbito debe ser global, porque deben estar disponibles para cada celda de la matriz mt al recorrerla, se deben declarar inmediatamente debajo de donde declaramos el arreglo para almacenar temporalmente las posiciones aleatorias de las 25 minas.

int[] dx = new int[8] { -1, 0, 1, 1, 1, 0, -1, -1 }; // delta x para calcular vecindad int[] dy = new int[8] { -1, -1, -1, 0, 1, 1, 1, 0 }; // delta y

Pero también Visual C# nos informa que nos falta declarar las variables vx, vy y p, lo cual debe hacerse dentro del propio método vecindad, es decir, inmediatamente debajo de la declaración de mv, f y c.

int mv, f, c; // minas en vecindad, fila, columna int vx, vy, pv; // x de vecindad, y de vecindad, posición vecindad

Note que hemos insertado los comentarios para las 6 variables declaradas en la cabecera del método vecindad, y, note que hemos cambiado, por considerarlo más apropiado, el nombre de la variable p por

Page 10: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

pv. Con todo, Visual C# nos sigue informando que queda un error y es que estamos tratando de asigna a la matriz completa mt un valor escalar y esto no se puede.

mt = mv;

Lo que en realidad queremos es asignarle el valor mv, cantidad de minas encontradas en la vecindad de la celda de posición lineal t, es decir, mt[t].

mt[t] = mv;

Estamos, pues, listos para verificar si todas estas tonterías que hemos estado diciendo realmente funcionan. Para ello nos vamos al código del evento Click (botón 1) e insertamos una invocación al método vecindad, inmediatamente después de invocar al método minas. Recordemos que hasta ahora al pulsar el botón se desplegaban puros 9 y ceros, pero esperamos que ahora se desplieguen los 9 y los dígitos del 0 al 8 correspondientes alrededor de cada 9.

vecindad();

Ejecutamos la aplicación, pulsamos el botón y vemos que se queda pensando un rato, y luego, para nuestra vergüenza, produce un error en tiempo ejecución.

Page 11: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Nos dice que nos aseguremos que no estamos excediendo los límites del máximo índice de una lista, y al ver la línea que parece en amarillo vemos que el arreglo sospechoso es dx, ¿por qué?, porque la variable v toma todos los valores de 0 a 9, pero la posición 9 no existe en el arreglo dx, de hecho, tampoco en dy.

Por tanto, para corregir este error basta recordar que los arreglos dx y dy fueron declarados de tamaño 8, esto es, sólo existen en ellos las posiciones de 0 a 7, y estamos pretendiendo acceder las posiciones de la 0 a la 8, de ahí el error, ¡pues la posición 8 no existe! Este error se corrige, sencillamente, cambiando el 9 del for por 8. Detenemos la aplicación y cambiamos el valor.

Ejecutamos nuevamente la aplicación y pulsamos el primero botón, esta vez con los dedos cruzados, a ver qué pasa. Aquí entre nosotros, esto es magia blanca… Cada vez que pulsamos el botón las minas cambian de posición, pues se generan nuevamente y nuevamente se calculan se calculan y despliegan los valores de la vecindad de cada mina. ¡Parece que no hablábamos tonterías, después de todo! Es increíble lo que un poco de código C# puede hacer, ¡y qué divertido!

Pero hemos estado probando nuestro código, a medida que lo hemos ido creando, invocando nuestros métodos desde el evento Click del primer botón, pero es claro que ésta no es la manera como funciona realmente el juego. Sin embargo, como somos personas normales y no súper genios que pueden planificar todo el código de antemano, perdiéndose buena parte de la diversión, hemos ido, por decirlo así, usado al evento Click del botón 1 como un andamio para apoyarnos al construir este juego, que es un poco complicadito, por lo que hemos visto.

Sería adecuado que los eventos minas y vecindad, que genera las minas al azar y calcula los valores de las vecindades, respectivamente, se ejecutaran al momento en que el Form se despliegue, y esto logramos colocando estas invocaciones en el evento por omisión del Form, a saber, Form_Load, el cual

Page 12: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

se genera rápidamente al hacer doble clic sobre el Form, siempre que no haya otro componente (como un botón, por ejemplo) debajo. Así que pulsamos doble sobre el Form y se genera el evento Form_Load (si ya el lector descuidado no lo ha hecho…). Note que se genera al final del código en la clase, por lo que quedan las dos llaves cerradas luego de él. También el cursor de texto está listo para recibir nuestro código.

Cortamos las invocaciones de los eventos minas y vecindad del evento Click del botón 1 y las pegamos en el interior del evento Form_Load.

Al ejecutar la aplicación comprobamos que ahora los valores aparecen la primera vez que se pulsa el botón (aunque los eventos minas y vecindad ha sido ejecutados, pues este evento se ejecuta antes que la forma, incluso, se dibuje), pero no cambian al pulsar otra vez el botón.

Esto es así porque ahora los eventos minas y vecindad se ejecutan una sola vez y al pulsar el botón no se muestran cambios porque no los ha habido. Ahora podremos redactar el código para comportamiento esperado de los botones.

Pero antes el lector debe notar que el número de minas mostrado en las celdas no siempre es correcto, pues a veces en el borde vertical de la izquierda cuenta las minas que estén en el borde de la derecha, y, en general, las minas que están en un borde se cuentan en el otro, y esto se debe a que en realidad es un error asumir que porque el valor de pv esté entre 0 y 100, y es válido, pues hemos caído dentro de la matriz NO es correcto aceptar que una celda del borde izquierdo, por ejemplo, está en la vecindad de otra, en la misma fila, pero en el borde derecho.

Por tanto, tendremos apelar, estrictamente, a los valores de f y c, esto es, de la fila y la columna, como criterio para establecer la vecindad a una celda. Naturalmente, este error sólo aplica a las celdas en los bordes, y cuando, específicamente, caen minas en los bordes. El código corregido es éste.

if (vx > -1 && vx < 10 && vy > -1 && vy < 10 && mt[pv] == 9) mv++;

Page 13: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Esta línea de código sustituye a la del if que hay en el método vecindad.

Ahora sí podemos proceder a redactar el código del botón 1 para que responda adecuadamente. Empecemos por hacer que muestre el estado o valor numérico que hay en la matriz de estado subyacente en esa misma posición. Sustituimos el código actual en el evento Click del botón 1 por:

int t = ActiveControl.TabIndex;

ActiveControl.Text = Convert.ToString(mt[t]);

Note que se crea la variable entera t y se le asigna el valor del TabIndex del control. Luego se convierte a string el valor de la matriz de estado que está en la posición t, es decir, el que corresponde a la cantidad de minas que hay en la vecindad del botón correspondiente. Hacemos que los 100 botones compartan el evento Click del botón 1 y podremos comprobar que al pulsar cualquier botón nos muestra el número de minas que hay alrededor de cada botón pulsado.

Ya estamos casi terminando. Ahora tenemos que hacer que cuando se pulse un botón donde hay una mina se muestren todos todos los botones donde hay mina, y, en lugar de un 9 vamos a mostrar un asterisco en lugar de un nueve.

Para mostrar que el juego se acabó porque se ha pisado una mina, y mostrar todas las casillas donde hay una mina, con un asterisco, recorreremos los 100 botones y verificaremos si en la matriz de estado hay un nueve en la celda correspondiente, y en ese caso mostramos un asterisco en ese botón.

int t = ActiveControl.TabIndex;

if (mt[t] != 9) ActiveControl.Text = Convert.ToString(mt[t]); else foreach (System.Windows.Forms.Control ctrl in this.Controls) { int ta = ctrl.TabIndex; if (mt[ta] == 9) ctrl.Text = "*"; }

Page 14: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Vayamos al modo de diseño y aumentemos el Font, digamos de los botones, digamos y pongámoslo en negritas. Para que los asteriscos salgan en rojo el código completo del evento Click del botón 1 es:

private void button1_Click(object sender, EventArgs e) { int t = ActiveControl.TabIndex;

if (mt[t] != 9) ActiveControl.Text = Convert.ToString(mt[t]); else foreach (System.Windows.Forms.Control ctrl in this.Controls) { int ta = ctrl.TabIndex; if (mt[ta] == 9) { ctrl.ForeColor = Color.Red; ctrl.Text = "*"; } } }

Note que cuando hay un nueve en la matriz de estado el color de la propiedad ForeColor se cambia a rojo, para que los asteriscos salgan rojos. Sólo nos falta que al pulsar el botón derecho sobre un botón aparezca un carácter especial en el botón, para indicar que el usuario CREE que en esa posición hay una mina.

Pero aquí nos encontramos con un problemita que nos tomó horas de investigación en la web, pero finalmente, gracias a Dios, hallamos un respueta simple. El problemita es que Visual C#, y de hecho, .NET, no tiene un evento right-click para los botones, sino que asume que cuando se pulsa un botón se ha pulsado el botón izquierdo, left-click.

Si hacemos, como un nuestro caso, los botones respondan al evento Click, no podremos discriminar cuándo el usurio ha pulsado el botón izquierdo o el derecho, cuando ha hecho left o right click. Para discriminar entre ambos botones invocamos el evento MouseDown, que se dispara cada vez que se pulse el botón, y una vez allí preguntamos cuál de los dos botones fue pulsado, ¡simple! Un ejemplo podría ser:

private void button1_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) MessageBox.Show("Right click.");

if (e.Button == MouseButtons.Left) MessageBox.Show("Left click."); }

Note que el argumento e que es pasado al evento contiene una propiedad Button, y cuando su valor es MouseButtons.Right significa que se ha pulsado el botón derecho, y cuando es Mouse.Buttons.Left significa que se ha pulsado el botón izquierdo.

Ya nosotros hemos asociado el código que hemos probado al evento Click del botón, pero eso no es problema, porque si dejamos el evento sin código, el evento se dispararía, pero no tendría acciones

Page 15: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

concretas qué ejecutar, por lo que resulta inofensivo. Lo que haremos, pues, es agregar el evento MouseDown al primer botón y mover allí el código que teníamos en el evento Click (dejándolo ahora vacío, por supuesto). Luego haremos provición para discriminar entre un left-click y un right-click. Empecemos, pues, por asociar el evento MouseDown al primer botón. Lo primero es ir al modo de diseño y seleccionar el primer botón.

Asegurando que el pointer del mouse esté sobre el botón seleccionado, pulsamos el botón derecho del mouse (right-click) para que se despliegue el menú de contexto y pulsamos la opción Properties.

En el centana de propiedades pulsamos el botón de eventos, allí buscamos el evento MouseDown y lo pulsamos doble (double click).

Se muestra entonces el modo de código y el cursor en el interior del recién auto añadido evento MouseDown.

Page 16: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

Seleccionamos todo el código en el interior del evento button1_Click (entre la dos llaves, sin incluirlas) y lo cortamos (Ctrl+X).

Entonces lo pegamos (Ctrl+V) en el interior del método MouseDown.

Ejecutamos la aplicación para comprobar que ahora sólo el primer botón responde al evento MouseDown (Click, mientras tanto…), por lo cual debemos compartir este evento con todos los botones, como hicimos con el evento Click en su momento. Para ello seleccionamos todos los botones y en la ventana de propiedades, como la pestaña de eventos está todavía expuesta, seleccionamos el evento Botton1_MouseDowm.

Ejecutamos la aplicación y comprobamos que todos los botones responden al pulsarlos, tanto con el botón izquierdo, pero no al derecho, exactamente como antes del traslado de código. Lo cual significa

Page 17: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

que nos falta discriminar, por código, entre clic izquierdo y clic derecho. Para lograr esto nos aseguramos que el código en el interior del evento MouseDown sea:

if (e.Button == MouseButtons.Left) { int t = ActiveControl.TabIndex;

if (mt[t] != 9) ActiveControl.Text = Convert.ToString(mt[t]); else foreach (System.Windows.Forms.Control ctrl in this.Controls) { int ta = ctrl.TabIndex;

if (mt[ta] == 9) { ctrl.ForeColor = Color.Red; ctrl.Text = "*"; } } }

if (e.Button == MouseButtons.Right) MessageBox.Show("Right click.");

Note que es exactamente el mismo código de antes, excepto que se le han añadido las líneas sombreadas. Ejecutamos la aplicación y comprobamos que al pulsar el botón izquierdo del mouse sobre un botón cualquiera, responde como antes, pero al pulsar el botón derecho se despliega una caja de diálogo, que no se cierra y no nos permite seguir jugando, hasta pulsamos el botón OK que muestra en su interior. Por cierto, aprovechemos y modifiquemos la propiedad Position del Form para que la caja de diálogo salga en el centro del For,, ¡y no nos obligue a irla a buscar! Para ello tenemos que pulsar, primero, el botón Properties den la ventana de propiedades para que nos muestre las propiedades y no los eventos.

Ahora nuestro programa está respondiendo ambos botones del mouse. Nos falta ahora que al pulsar el derecho no nos ejecute el código de prueba que muestra la caja de diálogo, sino el que realmente deseamos, a saber, que se muestre un carácter especial, digamos un ¶, para indicar que el usuario cree que en esa celda hay un a mina.

Para ello, basta que remplacemos la invocación al MessageBox cuando se pulsa el botón derecho por este código:

ActiveControl.Text = "¶";

Es decir, el código debe lucir así.

Al ejecutar la aplicación nos encontramos con una desagradable sorpresa, al pulsar el botón derecho sobre cualquier botón el carácter especial se muestra no sobre el botón que pulsamos, sino sobre ¡el botón que esté activo! ¿Cómo resolver esto? Luce complicado porque cuando se pulsa el botón izquierdo el botón pulsado se convierte

Page 18: Como Hacer El Busca Minas en Visual Estudio

intec. elementos (ing-202). ecabrera. mayo 2010. minesWeeper, ejemplo 5

automáticamente en el control activo, pero no ocurre así con al pulsar el botón derecho. Pero luego de un par de horas de búsqueda en la web, encontramos esta sencilla solución.

if (e.Button == MouseButtons.Right) { Button btn = (Button)sender; btn.Text = "¶"; }

Lo que hace este código es instanciar a un botón, btn, el parámetro sender que envía Windows al evento MouseDown, y asignarle el carácter ¶ a su propiedad Text. Así que el código completo del evento MouseDown es:

private void button1_MouseDown(object sender, MouseEventArgs e) {

if (e.Button == MouseButtons.Left) { int t = ActiveControl.TabIndex;

if (mt[t] != 9) ActiveControl.Text = Convert.ToString(mt[t]); else foreach (System.Windows.Forms.Control ctrl in this.Controls) { int ta = ctrl.TabIndex;

if (mt[ta] == 9) { ctrl.ForeColor = Color.Red; ctrl.Text = "*"; } } }

if (e.Button == MouseButtons.Right) { Button btn = (Button)sender; btn.Text = "¶"; } }

Ya podemos jugar al busca minas. Se deja como tarea al lector que al pulsar una mina se despliegue una caja de diálogo diciendo “Lo siento.” al usuario y cuando se cierre esta caja de diálogo se reinicie el juego, esto es, limpiando los Text de los botones y generando nuevamente las minas.

También se deja de tarea hacer que el campo minado sea de 20x20, para soportar tres niveles de juego: Principiante, Intermedio y Avanzado, con campos minados de 5x5, 10x10 y 20x20, en que se generan 5, 25 y 50 minas, respectivamente.

Para reducir el campo minado a la vista del usuario se asigna el valor false a la propiedad Visible de los botones que quedan fuera del juego y se reduce el el Size del Form consecuentemente.

ecabrera, mayo 20010.