miércoles, 18 de noviembre de 2015

Buscaminas para 2 jugadores con música y conexion a base de datos hecho con windows forms

Hecho por:
Sergio Andrés Sepúlveda Cruz
Juan David Franco

Para esta entrega tenemos una modificación de nuestro primer buscaminas (Ver Aqui) este buscaminas será para 2 jugadores lo que nos hace cambiar por completo la mecánica del juego ya que el juego se manejará por turnos, el jugador en turno destapará una casilla, si esta es una mina ya no terminará el juego si no que restara uno al marcador del jugador, en caso contrario le sumara 1 al marcador.

También ahora al destapar una casilla sin minas solo se destapará una casilla y no en area sin importar que esta no tenga minas alrededor, este cambio se hizo por que creemos que se acortaba mucho el proceso del juego además de que si un jugador destapaba un area de casillas vacias ya obtendria una gran ventaja de puntos con respecto al otro lo que haria el juego muy desbalanceado


Manejo de los datos:
Para poder manejar los puntajes del juego utilizamos una base de datos creada en Access (hay que tener en cuenta que para que la aplicacion funcione debe haber una version de Access 2007 en adelante en el equipo).

Una vez creamos la base de datos (la cual se llama Buscaminas.accdb) la cargamos al proyecto de la siguiente manera:

En la ventana origenes de datos agregamos un nuevo origen de datos y seleccionamos la opcion base de datos

Seleccionamos la opcion conjunto de datos

Seleccionamos el string de conexion, aqui ya aparece uno por que lo tenemos creado pero seleccionamos Nueva conexion

Nos aparecerá una ventana para seleccionar nuestra base de datos, en el campo de origen de datos le damos al boton cambiar
En la ventana que nos aparece seleccionamos Archivo de base de datos de microsoft access
Luego le damos al boton examinar en el campo nombre de archivo de la base de datos y buscamos nuestra base de datos access
Le damos a aceptar y luego a siguiente, aqui nos preguntarán si deseamos copiar el archivo al proyecto, si le damos a si cada vez que se compile el programa este copiará el archivo de base de datos que seleccionamos a la carpeta release del proyecto y el programa se conectará y hara los cambios a la base de datos en la carpeta release durante su ejecución. esto quiere decir que cada vez que ejecutemos el proyecto DESDE EL VISUAL STUDIO no se veran los cambios en la base pero si lo hacemos desde el ejecutable en la carpeta release si se veran.

En caso de darle no el programa mantendrá la base de datos en su lugar y siempre se conectara y hara los cambios en esta base de datos mientras se ejecuta. resulta mejor copiar la base de datos al proyecto por que eso lo hará mas portable ya que el string de conexion hace referencia a la dirección donde se aloja la base de datos access, cuando copiamos la base de datos al proyecto el string de conexion hará referencia a la carpeta release del proyecto mientras que si no la copiamos el string de conexion hará referencia a una carpeta externa al proyecto lo que quiere decir que la conexion a la base de datos solo servirá en el pc donde se creo la base de datos

Damos a si y seleccionamos las tablas y campos que queramos traer al proyecto y damos a finalizar

Ya para coectarnos a la base de datos, ingresarle registros y eliminarlos creamos una clase llamada BDBuscaminas la cual contiene metodos para hacer estas operaciones

Código clase BDBuscaminas
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Buscaminas.Properties;
using System.Configuration;
using System.Data.SqlClient;
using System.Data.OleDb;
using System.Windows.Forms;

namespace Buscaminas
{
    /// <summary>
    /// Clase para hacer conexión, ingresar datos y eliminar datos en la base de datos buscaminas
    /// </summary>
    class BDBuscaminas
    {
        /// <summary>
        /// Método que guarda datos en una base de datos, tiene como parámetros el nombre del jugador, su marcador y la cantidad de segundos que duró el juego
        /// El campo id es un entero autoincremental, por esto no se incluye
        /// </summary>
        /// <param name="nombre">nombre del jugador</param>
        /// <param name="puntos">marcador del jugador</param>
        /// <param name="tiempo">tiempo en segundos de la partida</param>
        public static void guardarDatos(string nombre, int puntos, int tiempo) 
        {
            ///Variable de tipo OleDbConnection que guarda el string de conexion que se encuentra en el archivo settings del proyecto
            OleDbConnection conectar = new OleDbConnection(Settings.Default.BuscaminasConnectionString.ToString());
            try
            {
                ///Abre la conexion con la base de datos
                conectar.Open();
                ///String en donde se guardará la sentencia para guardar el nombre del jugador, su marcador y tiempo de partida en la base de datos
                string insertar = "insert INTO Puntuaciones (Nombre,Puntos,Tiempo) VALUES ('" + nombre + "'," + puntos + "," + tiempo + ")";
                ///Variable de tipo OleDbCommando que contiene la sentencia y la conexion a la base de datos
                OleDbCommand comandoinsertar = new OleDbCommand(insertar, conectar);
                ///Ejecuta el comando 
                comandoinsertar.ExecuteNonQuery();
            }
            ///si captura alguna excepción en la conexion a la base o ejecución del query mostrará un mensaje de error y mostrara la excepcion
            catch (OleDbException e)
            {
                conectar.Close();
                MessageBox.Show("No se pudo realizar la operacion\n" + e.Message.ToString(), "Error de datos", MessageBoxButtons.OK);
            }
            ///al terminar la operacion cierra la conexion con la base de datos
            finally 
            {
                conectar.Close();
            }
        }

        /// <summary>
        /// Método para borrar todos los datos de la tabla puntuaciones
        /// </summary>
        public static void borrarDatos()
        {
            ///Variable de tipo OleDbConnection que guarda el string de conexion que se encuentra en el archivo settings del proyecto
            OleDbConnection conectar = new OleDbConnection(Settings.Default.BuscaminasConnectionString.ToString());
            try 
            {
                ///Abre la conexion con la base de datos
                conectar.Open();
                ///String para guardar el query que borra todos los datos de la tabla
                String borrar = "DELETE * FROM Puntuaciones;";
                ///Variable de tipo OleDbCommand que contiene el string con el query y la conexion
                OleDbCommand comandoborrar = new OleDbCommand(borrar, conectar);
                ///Ejecuta el comando
                comandoborrar.ExecuteNonQuery();
                ///String para guardar un query que reinicia el autoincremento del campo Id a 1 (se hace de este modo por que el comando truncate no existe en access)
                String reset = "ALTER TABLE Puntuaciones ALTER COLUMN Id COUNTER (1, 1);";
                ///Variable de tipo OleDbCommand que contiene el string con el query y la conexion
                OleDbCommand comandoreset = new OleDbCommand(reset, conectar);
                ///Ejecuta el comando
                comandoreset.ExecuteNonQuery();
            }
            ///si captura alguna excepción en la conexion a la base o ejecución del query mostrará un mensaje de error y mostrara la excepcion
            catch (OleDbException e)
            {
                conectar.Close();
                MessageBox.Show("No se pudo realizar la operacion\n"+e.Message.ToString(), "Error de conexion", MessageBoxButtons.OK);
            }
            ///Al finalizar la operacion cierra la conexion
            finally
            {
                conectar.Close();
            }
        }
    }
}

Ejecucion del programa:

aparece el menu principal con 3 botones, uno para iniciar el jeugo, otro para acceder a la tabla de puntuaciones y otro para salir del juego, ademas aparecen 2 textbox para ingresar los nombres de los jugadores, además empieza a sonar la musica de inicio del juego
si damos a iniciar y no hemos ingresado nombres nos notificará que debemos ingresarlos
Sucederá lo mismo si ingresamos nombres que sean mayores a 10 caracteres
Ingresamos nombres validos y le damos a inicuar, sonará un rayo laser y se mostrará el tablero del juego reproduciendo la musica del juego
Es el turno del jugador 1 el hace click en una casilla y si esta es una mina le restará 1 a su marcador en caso contrario le sumara 1, despues de validar esto terminará el turno del jugador 1
Podemos ver que el jugador 1 expoto una mina por eso le restamos 1 a su marcador, tambien vemos que ahora es el turno del jugador 2 y que el contador de minas explotadas incremento en 1

en el turno del jugador 2 el descubrio una casilla sin mina y por eso le sumo uno a su marcador, podemos ver tambien que vuelve a ser el turno del jugador 1

Si le damos a una casilla que ya destapó nos mandará un mensaje

tambien podemos cambiar la música del juego con los botones azules....
damos al boton con la flecha hacia la derecha y vemos que cambia el nombre de la cancion y por ende la cancion que esta sonando.

si le damos al otro boton azul podemos ver que se reproduce la cancion anterior

Toda la musica de la aplicación fue tomada del juego F-Zero para la consola SNES (Super Nintendo) de 16-bit

el juego se desarrollará de esta manera hasta que suceda una de las 2 condiciones: 
1. exploten todas las 50 minas del tablero
2. se descubran todos los 134 espacios vacios del juego
cuando se cumplan estas condiciones sonara una explosion si es la primera o un marciano muriendo si es la segunda, el juego terminará y declarará a un ganador o empate y sonara la música del final del juego
Guardara los datos y le preguntará al usuario si desea salir del juego, si responde si, cerrara la aplicacion si no volvera al menu de inicio

Código de la clase form1
using Buscaminas;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Media;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            ///Al iniciarse la aplicacion se cargan los componentes del formulario y se reproduce la musica de apertura del juego
            Opening();
            InitializeComponent();
        }
        ///Variable para contar las veces que los jugadores destapan una casilla sin mina
        int seguros = 0;
        ///Variable para contar las veces que los jugadores destapan una casilla con mina
        int explosiones = 0;
        ///Variable para determinar el turno de los jugadores
        int turno = 1;
        ///Variables para determinar el marcador de los jugadores
        int marcador1 = 0;
        int marcador2 = 0;
        ///Variable para determinar la canción que está sonando
        int pista = 1;
        ///Variables que guardan los nombres de los jugadores
        string n1;
        string n2;
        ///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];
        /// Estas variables de tipo entero se usan en el método del timer para contar minutos y segundos
        int segundos = 0;
        int minutos = 0;
        /// Variable que guarda la cantidad de segundos que duró el juego
        int totalsegundos = 0;
        
        
        /// <summary>
        /// Manejador de eventos del boton iniciar, regresa las variables globales a su valor de declaración, 
        /// regresa la interfaz al estado de inicio del juego y valida que se hayan ingresado los nombres de los jugadores
        /// siendo estos de 10 caracteres o menores
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BotonIniciar_Click(object sender, EventArgs e)
        {
            ///Se guardan los nombres de los jugadores en n1 y n2
            n1 = Nombre1.Text;
            n2 = Nombre2.Text;
            ///Si estos son mayores de 10 caracteres enviara un mensaje diciendo que debe cambiarlos
            ///(esta validacion se hizo por estética de la interfaz al ser muy largos los label con los nombres interferirían con los botones)
            if (n1.Length > 10 || n2.Length > 10)
            {
                DialogResult dialogResult = MessageBox.Show("Nombres demasiado largos haz que sean menores a 10 caracteres", "Nombres", MessageBoxButtons.OK);
            }
            ///Si son menores a 10 caracteres verifica que se hayan escrito nombres
            else 
            {
                if (n1.Equals("") || n2.Equals(""))
                {
                    DialogResult dialogResult = MessageBox.Show("Escribe los nombres de los jugadores", "Nombres", MessageBoxButtons.OK);
                }
                ///Se se han escrito nombres menores a 10 caracteres empieza el juego
                else
                {
                    /// Instancia del metodo cargar
                    cargar();
                    ///Método que reproduce el sonido de un laser
                    SonidoLaser();
                    ///El nombre de la canción que suena se reestablece al primero
                    nombrecancion.Text = "♫ Mute City ♫";
                    ///El label para contar las minas que se han hallado se devuelve a 0
                    explosion.Text = "Minas explotadas 0/50";
                    ///La variable para reproducir la cancion se regresa a 1 y se reproduce esta cancion llamando al metodo musica con el parametro pista = 1
                    pista = 1;
                    Musica(pista);
                    ///Se esconde el menu del inicio para mostrar el tablero del juego
                    BotonIniciar.Hide();
                    BotonPuntuaciones.Hide();
                    BotonSalir.Hide();
                    label1.Hide();
                    label2.Hide();
                    Nombre1.Hide();
                    Nombre2.Hide();
                    /// Linea de codigo que inicia el temporizador
                    timer1.Enabled = true;
                    /// Las variables globales se regresan a su estado de declaración
                    seguros = 0;
                    explosiones = 0;
                    turno = 1;
                    marcador1 = 0;
                    marcador2 = 0;
                    segundos = 0;
                    minutos = 0;
                    /// La interfaz regresa a su estado de inicio del juego 
                    Tiempomin.Text = "Tiempo: 00";
                    Tiemposeg.Text = ":00";
                    Jugador1.Text = n1 + ": 0";
                    Jugador2.Text = n2 + ": 0";
                    Juega.Text = "Juega: " + n1;
                } 
            }
        }
        
        /// <summary>
        /// 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
        /// </summary>
        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;
                    }
                }
            }
        }
        
        /// <summary>
        /// Método que destapa un botón y averigua si es o no una mina, si es una mina restará 1 al marcador del jugador en turno, si no lo es le sumará 1 al marcador
        /// </summary>
        /// <param name="sender"></param>
        public void destaparmina(object sender)
        {
            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 ya fue destapado, si es asi mostrará un mensaje correspondiente
                if (b.ForeColor != Color.Lime)
                {
                    DialogResult dialogResult = MessageBox.Show("Esta casilla ya fue descubierta", "Casilla descubierta", 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á la casilla indicando que es mina
                    if (es_mina == true)
                    {
                        b.ForeColor = Color.Red;
                        b.BackColor = Color.Red;
                        /// Le suma 1 a la variable explosiones y actualiza el label
                        explosiones++;
                        explosion.Text = "Minas explotadas: " + explosiones + "/50";
                        /// Si el turno es del jugador 1 le resta 1 a su marcador lo actualiza en la interfaz y cambia el turno
                        if (turno == 1)
                        {
                            marcador1--;
                            Jugador1.Text = n1 + ": " + marcador1;
                            turno++;
                            Juega.Text = "Juega: "+n2;
                        }
                        else
                        /// Si el turno es del jugador 2 le resta 1 a si marcador lo actualiza en la interfaz y cambia el turno
                        {
                            marcador2--;
                            Jugador2.Text = n2 + ": " + marcador2;
                            turno--;
                            Juega.Text = "Juega: "+n1;
                        }
                        /// Si han explotado todas las minas se dará el juego por terminado y se guardarán las puntuaciones en una base de datos access
                        if (explosiones == 50) 
                        {
                            /// Se detiene el temporizador
                            timer1.Stop();
                            /// Se muestra la explosión de la nave y un sonido de explosión
                            Nave.Hide();
                            nexpl.Show();
                            SonidoExplosion();
                            ///En el label que muestra el turno se muestra el mensaje game over
                            Juega.Text = "Game Over";
                            /// Se reproduce la musica del fin del juego
                            Ending();
                            /// Se calcula la cantidad de segundos que se jugó y se guarda en la variable totalsegundos
                            totalsegundos = (minutos * 60) + segundos;
                            /// Si el marcador 1 es mayor al marcador 2 declara al jugador 1 como ganador y guarda los datos de ambos jugadores en una base de datos access
                            if (marcador1 > marcador2)
                            {
                                ///Mensaje que declara al jugador 1 como ganador
                                DialogResult gano1 = MessageBox.Show(n1 + " ha ganado la partida con "+ marcador1 +" puntos a "+ marcador2 +" puntos", "Ganador: " + n1, MessageBoxButtons.OK);
                                ///Llamada al metodo guardar datos de la clase BDBuscaminas enviando como parametros el nombre del jugador, su marcador y el tiempo que duró la partida
                                BDBuscaminas.guardarDatos(n1, marcador1, totalsegundos);
                                BDBuscaminas.guardarDatos(n2, marcador2, totalsegundos);
                            }
                            /// Si el marcador 2 es mayor al 1 declara al jugador 2 como ganador y guarda los datos de ambos jugadores en una base de datos access
                            else if(marcador1<marcador2)
                            {
                                DialogResult gano2 = MessageBox.Show(n2 + " ha ganado la partida con " + marcador2 + " puntos a " + marcador1 + " puntos", "Ganador: " + n2, MessageBoxButtons.OK);
                                BDBuscaminas.guardarDatos(n2, marcador2, totalsegundos);
                                BDBuscaminas.guardarDatos(n1, marcador1, totalsegundos);
                            }
                            /// Si no se da ninguno de los 2 casos se declara el empate y se guardan los datos de ambos jugadores en una base de datos access
                            else
                            {
                                DialogResult empate= MessageBox.Show("Juego empatado a "+ marcador1 +" puntos","Empate",MessageBoxButtons.OK);
                                BDBuscaminas.guardarDatos(n1, marcador1, totalsegundos);
                                BDBuscaminas.guardarDatos(n2, marcador2, totalsegundos);
                            }
                            /// Muestra un mensaje diciendo que explotaron todas las minas y pregunta si quieren salir del juego
                            DialogResult dialogResult = MessageBox.Show("Han explotado todas las minas \n ¿Quieren salir del juego?", "Game Over", MessageBoxButtons.YesNo);
                            /// Si el usuario responde que no el juego se reiniciará
                            if (dialogResult == DialogResult.No)
                            {
                                /// Lineas de codigo que regresan la interfaz al menu de inicio
                                BotonIniciar.Show();
                                BotonPuntuaciones.Show();
                                BotonSalir.Show();
                                label1.Show();
                                label2.Show();
                                Nombre1.Show();
                                Nombre2.Show();
                                Nombre1.Text = "";
                                Nombre2.Text = "";
                                explosion.Text = "Minas explotadas 0/50";
                                Tiempomin.Text = "Tiempo :00";
                                Tiemposeg.Text = ":00";
                                /// Lineas de codigo que reestablecen la imagen de la nave y vuelven a reproducir la musica de inicio del juego
                                nexpl.Hide();
                                Nave.Show();
                                Opening();
                            }
                            /// Si el usuario responde que si el juego se cerrará
                            else if (dialogResult == DialogResult.Yes)
                            {
                                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 y se sumara 1 al marcador del jugador en turno
                    else
                    {
                        /// Método para descubrir el boton
                        DescubrirArea(x, y, int.Parse(b.Text));
                        /// Le suma 1 a los seguros que es el contador de veces que se destapan casillas sin mina
                        seguros++;
                        /// Si es el turno del jugador 1 le suma 1 al marcador, actualiza la interfaz y cambia el turno
                        if (turno == 1)
                        {
                            marcador1++;
                            Jugador1.Text = n1 + ": " + marcador1;
                            turno++;
                            Juega.Text = "Juega: " + n2;
                        }
                        /// Si el turno es del jugador 2 le suma 1 al marcador, actualiza la interfaz y cambia el turno
                        else
                        {
                            marcador2++;
                            Jugador2.Text = n2 + ": " + marcador2;
                            turno--;
                            Juega.Text = "Juega: " + n1;
                        }
                        /// Si el contador de casillas descubiertas sin mina llega a 134 que es la cantidad de casillas que no tienen mina, el juego se da por terminado y se guardan las puntuaciones en una base de datos access
                        if (seguros == 134)
                        {
                            ///Suena el sonido del marciano muriendo
                            SonidoMarciano();
                            ///Se detiene el temporizador
                            timer1.Stop();
                            ///Explotan los marcianos
                            Marciano1.Hide();
                            Marciano2.Hide();
                            Marciano3.Hide();
                            mexpl1.Show();
                            mexpl2.Show();
                            mexpl3.Show();
                            ///Se indica en el label del turno que el juego termino
                            Juega.Text = "Game Over";
                            ///Suena la musica del final del juego
                            Ending();
                            ///Se calcula la cantidad de segundos que duró el juego
                            totalsegundos = (minutos * 60) + segundos;
                            /// Si el marcador 1 es mayor al marcador 2 declara al jugador 1 como ganador y guarda los datos de ambos jugadores en una base de datos access
                            if (marcador1 > marcador2)
                            {
                                ///Mensaje que declara al jugador 1 como ganador
                                DialogResult gano1 = MessageBox.Show(n1 + " ha ganado la partida con " + marcador1 + " puntos a " + marcador2 + " puntos", "Ganador: " + n1, MessageBoxButtons.OK);
                                ///Llamada al metodo guardar datos de la clase BDBuscaminas enviando como parametros el nombre del jugador, su marcador y el tiempo que duró la partida
                                BDBuscaminas.guardarDatos(n1, marcador1, totalsegundos);
                                BDBuscaminas.guardarDatos(n2, marcador2, totalsegundos);
                            }
                            /// Si el marcador 2 es mayor al 1 declara al jugador 2 como ganador y guarda los datos de ambos jugadores en una base de datos access
                            else if (marcador1 < marcador2)
                            {
                                DialogResult gano2 = MessageBox.Show(n2 + " ha ganado la partida con " + marcador2 + " puntos a " + marcador1 + " puntos", "Ganador: " + n2, MessageBoxButtons.OK);
                                BDBuscaminas.guardarDatos(n2, marcador2, totalsegundos);
                                BDBuscaminas.guardarDatos(n1, marcador1, totalsegundos);
                            }
                            /// Si no se da ninguno de los 2 casos se declara el empate y se guardan los datos de ambos jugadores en una base de datos access
                            else
                            {
                                DialogResult empate = MessageBox.Show("Juego empatado a " + marcador1 + " puntos", "Empate", MessageBoxButtons.OK);
                                BDBuscaminas.guardarDatos(n1, marcador1, totalsegundos);
                                BDBuscaminas.guardarDatos(n2, marcador2, totalsegundos);
                            }
                            /// Se muestra un mensaje diciendo que las minas han sido descubiertas y pregunta si quieren salir del juego
                            DialogResult dialogResult = MessageBox.Show("Se han descubierto las minas restantes\n ¿Salir del juego?", "Game Over", MessageBoxButtons.YesNo);
                            /// Si el usuario responde que no se mostrara el menu de inicio de la interfaz
                            if (dialogResult == DialogResult.No)
                            {
                                BotonIniciar.Show();
                                BotonPuntuaciones.Show();
                                BotonSalir.Show();
                                Marciano1.Show();
                                Marciano2.Show();
                                Marciano3.Show();
                                mexpl1.Hide();
                                mexpl2.Hide();
                                mexpl3.Hide();
                                label1.Show();
                                label2.Show();
                                Nombre1.Show();
                                Nombre2.Show();
                                Nombre1.Text = "";
                                Nombre2.Text = "";
                                explosion.Text = "Minas explotadas 0/50";
                                Tiempomin.Text = "Tiempo :00";
                                Tiemposeg.Text = ":00";
                                Opening();
                            }
                            /// Si el usuario responde que si el juego se cerrará
                            else if (dialogResult == DialogResult.Yes)
                            {
                                this.Close();
                            }
                        }
                    }
                } 
            }
            catch { }
        }

        /// <summary>
        /// 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
        /// </summary>
        /// <param name="x">Coordenada en x del botón</param>
        /// <param name="y">Coordenada en y del botón</param>
        /// <param name="i">Id del botón</param>
        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];

            }
        }

        /// <summary>
        /// Método que recupera las coordenadas de un botón según su id
        /// </summary>
        /// <param name="num">Id del botón</param>
        /// <returns>retorna una lista con 2 enteros que corresponden a las coordenadas del botón</returns>
        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;
        }

        
HAY UN METODO DE ESTOS POR CADA BOTON DEL TABLERO DE JUEGO

        /// Serie de métodos para manejar los eventos de los botones que componen el tablero de juego

        private void button5_Click(object sender, EventArgs e)
        {
            destaparmina(sender);
            
        }

/// Manejador de eventos del botón salir
        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étodos 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
        private void SonidoLaser()
        {
            SoundPlayer player = new SoundPlayer(global::Buscaminas.Properties.Resources.Laser);
            player.PlaySync();
        }
        
        private void SonidoExplosion()
        {
            SoundPlayer player = new SoundPlayer(global::Buscaminas.Properties.Resources.Explosion);
            player.PlaySync();
        }
        private void SonidoMarciano()
        {
            SoundPlayer player = new SoundPlayer(global::Buscaminas.Properties.Resources.Kill);
            player.PlaySync();
        }

        /// <summary>
        /// Método que reproduce una canción y que tiene como parametro un número que determinará la cancion a reproducir por medio de un switch
        /// </summary>
        /// <param name="i">parametro para determinar la cancion a reproducir</param>
        private void Musica(int i) 
        {
            switch (i) 
            {
                case 1:
                    SoundPlayer MuteCity = new SoundPlayer(global::Buscaminas.Properties.Resources.MuteCity);
                    MuteCity.PlayLooping();

                    break;
                case 2:
                    SoundPlayer BigBlue = new SoundPlayer(global::Buscaminas.Properties.Resources.BigBlue);
                    BigBlue.PlayLooping();
                    break;
                case 3:
                    SoundPlayer SandOcean = new SoundPlayer(global::Buscaminas.Properties.Resources.SandOcean);
                    SandOcean.PlayLooping();
                    break;
                case 4:
                    SoundPlayer DeathWind = new SoundPlayer(global::Buscaminas.Properties.Resources.DeathWind);
                    DeathWind.PlayLooping();
                    break;
                case 5:
                    SoundPlayer Silence = new SoundPlayer(global::Buscaminas.Properties.Resources.Silence);
                    Silence.PlayLooping();
                    break;
                case 6:
                    SoundPlayer PortTown = new SoundPlayer(global::Buscaminas.Properties.Resources.PortTown);
                    PortTown.PlayLooping();
                    break;
                case 7:
                    SoundPlayer RedCanyon = new SoundPlayer(global::Buscaminas.Properties.Resources.RedCanyon);
                    RedCanyon.PlayLooping();
                    break;
                case 8:
                    SoundPlayer WhiteLandI = new SoundPlayer(global::Buscaminas.Properties.Resources.WhiteLandI);
                    WhiteLandI.PlayLooping();
                    break;
                case 9:
                    SoundPlayer WhiteLandII = new SoundPlayer(global::Buscaminas.Properties.Resources.WhiteLandII);
                    WhiteLandII.PlayLooping();
                    break;
                case 10:
                    SoundPlayer FireField = new SoundPlayer(global::Buscaminas.Properties.Resources.FireField);
                    FireField.PlayLooping();
                    break;
            }
        }

        /// Métodos para reproducir las canciones de inicio y finalizacion del juego
        private void Opening()
        {
            SoundPlayer player = new SoundPlayer(global::Buscaminas.Properties.Resources.Opening);
            player.PlayLooping();
        }

        private void Ending()
        {
            SoundPlayer player = new SoundPlayer(global::Buscaminas.Properties.Resources.Ending);
            player.PlayLooping();
        }
        
        /// <summary>
        /// Manejador de eventos del boton adelante que cambia la cancion a la siguiente
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Adelante_Click(object sender, EventArgs e)
        {
            ///le suma 1 a la pista y si es mayor a 10 regresa la variable a 1 (esto indica que solo hay 10 canciones)
            pista++;
            if (pista > 10) 
            {
                pista = 1;
            }
            ///Llama al metodo musica enviando como paramentro la variable pista
            Musica(pista);
            ///Actualiza el nombre de la cancion que esta sonando segun la variable pista
            switch (pista) 
            {
                case 1:
                    nombrecancion.Text = "♫ Mute City ♫";
                    break;
                case 2:
                    nombrecancion.Text = "♫ Big Blue ♫";
                    break;
                case 3:
                    nombrecancion.Text = "♫ Sand Ocean ♫";
                    break;
                case 4:
                    nombrecancion.Text = "♫ Death Wind ♫";
                    break;
                case 5:
                    nombrecancion.Text = "♫ Silence ♫";
                    break;
                case 6:
                    nombrecancion.Text = "♫ Port Town ♫";
                    break;
                case 7:
                    nombrecancion.Text = "♫ Red Canyon ♫";
                    break;
                case 8:
                    nombrecancion.Text = "♫ White Land I ♫";
                    break;
                case 9:
                    nombrecancion.Text = "♫ White Land II ♫";
                    break;
                case 10:
                    nombrecancion.Text = "♫ Fire Field ♫";
                    break;
            }
        }

        /// <summary>
        /// Manejador de eventos del boton atras que cambia la cancion a la anterior
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Atras_Click(object sender, EventArgs e)
        {
            ///Le resta 1 a la variable pista y si es menor a 1 regresa la variable a 10
            pista--;
            if (pista < 1)
            {
                pista = 10;
            }
            ///Llama al metodo musica enviando como paramentro la variable pista
            Musica(pista);
            ///Actualiza el nombre de la cancion que esta sonando segun la variable pista
            switch (pista)
            {
                case 1:
                    nombrecancion.Text = "♫ Mute City ♫";
                    break;
                case 2:
                    nombrecancion.Text = "♫ Big Blue ♫";
                    break;
                case 3:
                    nombrecancion.Text = "♫ Sand Ocean ♫";
                    break;
                case 4:
                    nombrecancion.Text = "♫ Death Wind ♫";
                    break;
                case 5:
                    nombrecancion.Text = "♫ Silence ♫";
                    break;
                case 6:
                    nombrecancion.Text = "♫ Port Town ♫";
                    break;
                case 7:
                    nombrecancion.Text = "♫ Red Canyon ♫";
                    break;
                case 8:
                    nombrecancion.Text = "♫ White Land I ♫";
                    break;
                case 9:
                    nombrecancion.Text = "♫ White Land II ♫";
                    break;
                case 10:
                    nombrecancion.Text = "♫ Fire Field ♫";
                    break;
            }
        }

        /// <summary>
        /// Manejador de eventos del boton puntuaciones el cual abre el form con la tabla de puntuaciones
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BotonPuntuaciones_Click(object sender, EventArgs e)
        {
            Tabla ventana = new Tabla();
            ventana.Show();
        }
    }
}

El buscaminas cuenta con 2 formularios, uno que era el que acabamos de mostrar que se llama Form1 el cual es el juego en si, otro es el formulario Tabla el cual muestra la tabla de puntuaciones y una opcion para borrar los datos
Podemos ordenar los registros haciendo click en los encabezados de la tabla
Al darle click al boton borrar puntuaciones nos preguntará si deseamos hacerlo si le damos a sí borrará los datos, si le damos a no cerrara el mensaje
le damos a si y borrara los datos de la tabla

código del formulario Tabla
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Buscaminas
{
    public partial class Tabla : Form
    {
        public Tabla()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Método que carga los datos de la tabla puntuaciones de la base de datos Buscaminas en el datagridview
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Tabla_Load(object sender, EventArgs e)
        {
            // TODO: esta línea de código carga datos en la tabla 'buscaminasDataSet.Puntuaciones' Puede moverla o quitarla según sea necesario.
            this.puntuacionesTableAdapter.Fill(this.buscaminasDataSet.Puntuaciones);

        }

        /// <summary>
        /// Manejador de eventos del boton borrar, le pregunta al usuario si desea borrar las puntuaciones advirtiendole que no se podra revertir la operacion
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void botonborrar_Click(object sender, EventArgs e)
        {
            DialogResult dialogresult = MessageBox.Show("¿Desea borrar las puntuaciones actuales?\nEsta operación no se podrá revertir", "Borrar datos", MessageBoxButtons.YesNo);
            ///Si el usuario responde si llamara al metodo borrar datos de la clase BDBuscaminas y volverá a cargar los datos en la tabla
            if (dialogresult == DialogResult.Yes)
            {
                BDBuscaminas.borrarDatos();
                this.puntuacionesTableAdapter.Fill(this.buscaminasDataSet.Puntuaciones);
            }
            /// si responde que no cierra el mensaje y vuelve a cargar los datos
            else if(dialogresult == DialogResult.No)
            {
                this.puntuacionesTableAdapter.Fill(this.buscaminasDataSet.Puntuaciones);
            }
        }

        /// <summary>
        /// Manejador de eventos del botonsalir el cual cierra el form
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void botonsalir_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

puedes descargar el proyecto Aqui