jueves, 10 de noviembre de 2011

Buscaminas inteligente

Es la culminación del programa hecho en C# que juega solo al buscaminas, ya que logré que gane en 1 segundo en el modo experto.

Ahora paso a explicar los puntos importantes que permitieron aumentar el rendimiento.

Casi todo el cambio lo apliqué a la capa de reconocimiento, ya que es la encargada de traer las imágenes y eso es lo pesado del proceso. Lo primero que hice es que en lugar de leer pixel por pixel de la pantalla (un proceso muy lento) que lea toda una línea y luego que vaya leyendo esa línea.

ReconocimientoDePantalla.CreaBitmapRecorte(0, 0, ancho, 1);
Bitmap bmpLineaHorizontal = ReconocimientoDePantalla.ObtieneRecorteDePantalla(0, y_linea);


A la clase estática ReconocimientoDePantalla le agregué el siguiente código:

private static Bitmap bitmapRecorte;

        public static void CreaBitmapRecorte(int x1, int y1, int x2, int y2)
        {
            bitmapRecorte = new Bitmap(x2 - x1, y2 - y1, PixelFormat.Format32bppPArgb);
        }

        public static Bitmap ObtieneRecorteDePantalla(int x1, int y1)
        {
            using (Graphics gdest = Graphics.FromImage(bitmapRecorte))
            {
                using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
                {
                    IntPtr hSrcDC = gsrc.GetHdc();
                    IntPtr hDC = gdest.GetHdc();
                    int retval = BitBlt(hDC, 0, 0, bitmapRecorte.Width, bitmapRecorte.Height, hSrcDC, x1, y1, (int)CopyPixelOperation.SourceCopy);
                    gdest.ReleaseHdc();
                    gsrc.ReleaseHdc();
                }
            }
            return bitmapRecorte;
        }

Y en la clase BuscaMinasDeCarga le cambié el método siguiente:


private bool BusquedaDeCuadro(ref int ini_x, ref int ini_y, ref int nCuadrosX, ref int nCuadrosY)
        {
            // Para el ancho de la pantalla
            int ancho = Screen.PrimaryScreen.Bounds.Width;
            // Para el alto de la pantalla
            int alto = Screen.PrimaryScreen.Bounds.Height;
            int incrementoY = alto / lineasHorizontales;
            int inicioY = 150;
            int ladoX = 0;
            int ladoY = 0;

            for (int y_linea = inicioY; y_linea < alto; y_linea += incrementoY)
            {
                int indice = 0;
                bool encuentraX = false;
                bool encuentraY = false;
                int x_encontrado = 0;
                int y_encontrado = 0;
                ReconocimientoDePantalla.CreaBitmapRecorte(0, 0, ancho, 1);
                Bitmap bmpLineaHorizontal = ReconocimientoDePantalla.ObtieneRecorteDePantalla(0, y_linea);
                for (int x = 0; x < ancho; x++)
                {
                    //int color = ReconocimientoDePantalla.LeeUnPixelDePantalla(x, y_linea);
                    int color = bmpLineaHorizontal.GetPixel(x, 0).ToArgb();
                    if (coloresIniciales[indice] == color)
                    {
                        indice++;
                        if (indice >= coloresIniciales.Length)
                        {
                            encuentraX = true;
                            x_encontrado = x;
                            break;
                        }
                    }
                    else
                    {
                        indice = 0;
                    }
                }
                if (encuentraX)
                {
                    y_encontrado = y_linea;
                    ReconocimientoDePantalla.CreaBitmapRecorte(0, 0, 1, alto);
                    Bitmap bmpLineaVertical = ReconocimientoDePantalla.ObtieneRecorteDePantalla(x_encontrado, 0);
                    for (int y = y_linea; y >= 0; y--)
                    {
                        //int color = ReconocimientoDePantalla.LeeUnPixelDePantalla(x_encontrado, y);
                        int color = bmpLineaVertical.GetPixel(0, y).ToArgb();
                        if (color == colorRodea)
                        {
                            y_encontrado = y;
                        }
                        else
                        {
                            if (color == colorTope)
                            {
                                encuentraY = true;
                            }
                            break;
                        }
                    }
                    if (encuentraY)
                    {
                        for (int i = y_encontrado; i < alto; i++)
                        {
                            //int color = ReconocimientoDePantalla.LeeUnPixelDePantalla(x_encontrado, i);
                            int color = bmpLineaVertical.GetPixel(0, i).ToArgb();
                            if (color != colorRodea)
                            {
                                ladoY = i - y_encontrado;
                                break;
                            }
                        }
                        ReconocimientoDePantalla.CreaBitmapRecorte(0, 0, ancho, 1);
                        bmpLineaHorizontal = ReconocimientoDePantalla.ObtieneRecorteDePantalla(0, y_encontrado);
                        for (int i = x_encontrado; i < ancho; i++)
                        {
                            //int color = ReconocimientoDePantalla.LeeUnPixelDePantalla(i, y_encontrado);
                            int color = bmpLineaHorizontal.GetPixel(i, 0).ToArgb();
                            if (color != colorRodea)
                            {
                                ladoX = i - x_encontrado;
                                break;
                            }
                        }
                    }
                }
                if (encuentraX && encuentraY)
                {
                    int n1 = ladoX - bordeX;
                    int n2 = ladoY - BordeY;
                    if ((n1 % ladoDelCuadroX) != 0)
                    {
                        continue;
                    }
                    if ((n2 % ladoDelCuadroY) != 0)
                    {
                        continue;
                    }
                    ini_x = x_encontrado;
                    ini_y = y_encontrado;
                    nCuadrosX = n1 / ladoDelCuadroX;
                    nCuadrosY = n2 / ladoDelCuadroY;
                    if ((nCuadrosX < 1) || nCuadrosY < 1)
                    {
                        continue;
                    }
                    return true;
                }
            }
            return false;
        }

También modifiqué el método Reconocimiento, para que lea un cuadro de la pantalla un poco más rápido


public void Reconocimiento()
        {
            if (actualizando) { return; }
            actualizando = true;
            int x = 0, y = 0, nX = 0, nY = 0;
            if (BusquedaDeCuadro(ref x, ref y, ref nX, ref nY))
            {
                posIniDePantallaX = x;
                posIniDePantallaY = y;
                if ((matriz.GetLength(0) != nX) || (matriz.GetLength(1) != nY))
                {
                    InicializaMatriz(nX, nY);
                }
                ReconocimientoDePantalla.CreaBitmapRecorte(x, y, x + (nX * ladoDelCuadroX) + bordeX, y + (nY * ladoDelCuadroY) + bordeY);
                Bitmap bmp = ReconocimientoDePantalla.ObtieneRecorteDePantalla(x, y);

                //Bitmap bmp = ReconocimientoDePantalla.ObtieneRecorteDePantalla(x, y, x + (nX * ladoDelCuadroX) + bordeX, y + (nY * ladoDelCuadroY) + bordeY);
                byte[, ,] imagen = ImagenConverterBMP.byteArrayToMatrix(ImagenConverterBMP.imageToByteArray(bmp));
                CargaMatrizDeElementos(imagen, posIniXdeCuadro, posIniYdeCuadro, anchoElemento, altoElemento, ladoDelCuadroX, ladoDelCuadroY);
                encuentraCuadro = true;
            }
            else
            {
                encuentraCuadro = false;
            }

            actualizando = false;
        }

Otra de las modificaciones sobre la capa de reconocimiento es que en lugar de comparar todo cuadro correspondiente al elemento, hice que compare solo algunos puntos, con eso es suficiente como para identificarlos, aunque al final no afecta demasiado el rendimiento pero suma.


private TipoDeElemento BuscaElemento(byte[, ,] mat)
        {
            for (int i = 0; i < lImagenesDeElementos.Count; i++)
            {
                byte[, ,] mat1 = lImagenesDeElementos[i];
                bool q = false;
                for (int y = 0; y < mat.GetLength(1); y+=3)
                {
                    for (int x = 0; x < mat.GetLength(0); x+=3)
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            if (mat[x, y, j] != mat1[x, y, j])
                            {
                                q = true;
                                break;
                            }
                        }
                        if (q) break;
                    }
                    if (q) break;
                }
                if (!q)
                {
                    int k = 0;
                    foreach (TipoDeElemento te in Enum.GetValues(typeof(TipoDeElemento)))
                    {
                        if (k == i)
                        {
                            return te;
                        }
                        k++;
                    }
                    return TipoDeElemento.error;
                }
            }
            return TipoDeElemento.error;
        }


Las modificaciones sobre la capa de negocio son mínimas, sin embargo tienen un efecto muy positivo, en definitiva hago que en lugar de realizar una jugada por reconocimiento, haga todas las que pueda, para eso nada más tengo que quitar el retorno cuando encuentra la jugada, lo mismo para el método avanzado.


private bool BuscaVacio()
        {
            bool encuentra = false;
            for (int i = lDeIncognitas.Count - 1; i >= 0; i--)
            {
                ElementoBM ebm = lDeIncognitas[i];
                foreach (ElementoBM el in ebm.EVecinos)
                {
                    if (el != null)
                    {
                        if (el.EsNumero)
                        {
                            if (DeterminaSiEsVacio(el)) 
                            {
                                if (realizaJugadaMultiple)
                                {
                                    encuentra = true;
                                }
                                else
                                {
                                    return true; 
                                }
                            }
                        }
                    }
                }
            }
            return encuentra;
        }






miércoles, 2 de noviembre de 2011

Programa para ganar en el Buscaminas

Buscaminas Experto




Descargar código fuente (Visual Studio 2005)


Como funciona




El programa lo que hace es reconocer el Buscaminas en la pantalla (se podría reconocer directamente como control, pero no es esa la idea) y luego calcula la jugada y envía órdenes al mouse para que la ejecute, el movimiento es tan rápido que es imperceptible, devolviendo el mouse a la posición original. Todo lo hace en una fracción de segundo, logrando varias jugadas por segundo.




Como pueden ver en la imagen, el tiempo que le llevó en resolverlo completo es mucho menor del que una persona puede lograr, unos 14 segundos, todo un récord.



Código fuente del proyecto


El proyecto está diseñado en capas, una de reconocimiento, la capa de negocio (es la encargada de realizar las jugadas) y la capa de presentación (contiene el WinForm que nos permite controlar y configurar el programa)

Cada capa es un proyecto contenido en una misma solución.


Capa de reconocimiento

Es un proyecto de librería de clases, que se encarga de saber en que lugar de la pantalla se encuentra ubicado el Buscaminas, reconocer los cuadros y dar las órdenes para el movimiento del mouse.

using System;
using System.Collections.Generic;
using System.Text;

using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using FW;
using ReconocimientoDeFormas2D;

namespace ControlInteligente
{
    /// <summary>
    /// Es la clase que contiene el dato del buscaminas de la capa del reconocimiento
    /// </summary>
    public class BuscaMinasDeCarga
    {
        #region Control del mause

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);

        private const int MOUSEEVENTF_LEFTDOWN = 0x02;
        private const int MOUSEEVENTF_LEFTUP = 0x04;
        private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
        private const int MOUSEEVENTF_RIGHTUP = 0x10;
        private const int MOUSEEVENTF_MIDDLEDOWN = 0x20;
        private const int MOUSEEVENTF_MIDDLEUP = 0x40;
        private const int MOUSEEVENTF_MOVE = 0x01;
        private const int MOUSEEVENTF_ABSOLUTE = 0x8000;

        #endregion

        private string[] strImagenes;

        private TipoDeElemento[,] matriz;

        public TipoDeElemento[,] Matriz
        {
            get { return matriz; }
            set { matriz = value; }
        }

        private int altoElemento;
        private int anchoElemento;
        private List<byte[, ,]> lImagenesDeElementos;

        private byte[, ,] matAux;

        private int lineasHorizontales;
        /// <summary>
        /// es la cantidad de líneas que se repiten para encontrar
        /// la imagen
        /// </summary>
        public int LineasHorizontales
        {
            get { return lineasHorizontales; }
            set { lineasHorizontales = value; }
        }

        private bool actualizando;

        private bool encuentraCuadro;
        /// <summary>
        /// Indica si se reconoció un cuadro de juego
        /// </summary>
        public bool EncuentraCuadro
        {
            get { return encuentraCuadro; }
            set { encuentraCuadro = value; }
        }

        private int posIniDePantallaX;

        public int PosIniDePantallaX
        {
            get { return posIniDePantallaX; }
            set { posIniDePantallaX = value; }
        }

        private int posIniDePantallaY;

        public int PosIniDePantallaY
        {
            get { return posIniDePantallaY; }
            set { posIniDePantallaY = value; }
        }

        #region Datos para el reconocimiento

        private int[] coloresIniciales;
        /// <summary>
        /// Es la secuencia de colores iniciales
        /// </summary>
        public int[] ColoresIniciales
        {
            get { return coloresIniciales; }
            set { coloresIniciales = value; }
        }

        private int colorRodea;
        /// <summary>
        /// Es el color que rodea al cuadro a reconocer
        /// </summary>
        public int ColorRodea
        {
            get { return colorRodea; }
            set { colorRodea = value; }
        }

        private int colorTope;
        /// <summary>
        /// Es el color tope
        /// </summary>
        public int ColorTope
        {
            get { return colorTope; }
            set { colorTope = value; }
        }

        private int ladoDelCuadroX;
        /// <summary>
        /// Es el tamaño en píxel de cada cuadrado
        /// </summary>
        public int LadoDelCuadroX
        {
            get { return ladoDelCuadroX; }
            set { ladoDelCuadroX = value; }
        }

        private int ladoDelCuadroY;
        /// <summary>
        /// Es el tamaño en píxel de cada cuadrado
        /// </summary>
        public int LadoDelCuadroY
        {
            get { return ladoDelCuadroY; }
            set { ladoDelCuadroY = value; }
        }

        private int bordeX;
        /// <summary>
        /// Es el borde x del cuadro del juego
        /// </summary>
        public int BordeX
        {
            get { return bordeX; }
            set { bordeX = value; }
        }

        private int bordeY;
        /// <summary>
        /// Es el borde y del cuadro del juego
        /// </summary>
        public int BordeY
        {
            get { return bordeY; }
            set { bordeY = value; }
        }

        private int posIniXdeCuadro;

        public int PosIniXdeCuadro
        {
            get { return posIniXdeCuadro; }
            set { posIniXdeCuadro = value; }
        }

        private int posIniYdeCuadro;

        public int PosIniYdeCuadro
        {
            get { return posIniYdeCuadro; }
            set { posIniYdeCuadro = value; }
        }

        #endregion

        private void ObtieneMatriz(byte[, ,] imagen, byte[, ,] mat, int posX, int posY)
        {
            for (int y = 0; y < altoElemento; y++, posY++)
            {
                int _posX = posX;
                for (int x = 0; x < anchoElemento; x++, _posX++)
                {
                    mat[x, y, 0] = imagen[_posX, posY, 0];
                    mat[x, y, 1] = imagen[_posX, posY, 1];
                    mat[x, y, 2] = imagen[_posX, posY, 2];
                }
            }
        }

        private TipoDeElemento BuscaElemento(byte[, ,] mat)
        {
            for (int i = 0; i < lImagenesDeElementos.Count; i++)
            {
                byte[, ,] mat1 = lImagenesDeElementos[i];
                bool q = false;
                for (int y = 0; y < mat.GetLength(1); y++)
                {
                    for (int x = 0; x < mat.GetLength(0); x++)
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            if (mat[x, y, j] != mat1[x, y, j])
                            {
                                q = true;
                                break;
                            }
                        }
                        if (q) break;
                    }
                    if (q) break;
                }
                if (!q)
                {
                    int k = 0;
                    foreach (TipoDeElemento te in Enum.GetValues(typeof(TipoDeElemento)))
                    {
                        if (k == i)
                        {
                            return te;
                        }
                        k++;
                    }
                    return TipoDeElemento.error;
                }
            }
            return TipoDeElemento.error;
        }

        /// <summary>
        /// Carga la matriz
        /// </summary>
        /// <param name="imagen">trae la imagen completa a reconocer</param>
        /// <param name="iniX">posición x del primer elemento</param>
        /// <param name="iniY">posición y del primer elemento</param>
        /// <param name="anchoE">ancho del elemento</param>
        /// <param name="altoE">alto del elemento</param>
        /// <param name="escX">distancia x entre elementos</param>
        /// <param name="escY">distancia y entre elementos</param>
        private void CargaMatrizDeElementos(byte[, ,] imagen, int iniX, int iniY, int anchoE, int altoE, int escX, int escY)
        {
            if (((iniX + (escX * matriz.GetLength(0))) > imagen.GetLength(0)) ||
                ((iniY + (escY * matriz.GetLength(1))) > imagen.GetLength(1)) ||
                (anchoElemento != anchoE) || (altoElemento != altoE))
            {
                throw new System.ApplicationException("Se intenta acceder fuera de los límites del Bitmap");
            }

            for (int y = 0; y < matriz.GetLength(1); y++)
            {
                for (int x = 0; x < matriz.GetLength(0); x++)
                {
                    ObtieneMatriz(imagen, matAux, iniX + (escX * x), iniY + (escY * y));
                    matriz[x, y] = BuscaElemento(matAux);
                }
            }
        }

        private void AgregaUnElemento(Bitmap bmp)
        {
            if ((bmp.Width != anchoElemento) || (bmp.Height != altoElemento))
            {
                throw new System.ApplicationException("El Bitmap no es correcto");
            }
            byte[, ,] img = ImagenConverterBMP.byteArrayToMatrix(ImagenConverterBMP.imageToByteArray(bmp));
            lImagenesDeElementos.Add(img);
        }

        private void CargaImagenes()
        {
            strImagenes = new string[] {
                "contBombas1",
                "contBombas2",
                "contBombas3",
                "contBombas4",
                "contBombas5",
                "contBombas6",
                "contBombas7",
                "contBombas8",
                "bomba",
                "bomba2",
                "bandera",
                "vacio",
                "incognita" 
            };

            CargaImagenes(strImagenes);
        }

        private void CargaImagenes(string[] strImagenes)
        {
            string ruta = @"../../../imagenes/";
            //string ruta = @"imagenes/";//es necesario copiar la carpera imágenes en BIN
            for (int i = 0; i < strImagenes.Length; i++)
            {
                Bitmap bmp = new Bitmap(ruta + strImagenes[i] + ".bmp");
                AgregaUnElemento(bmp);
            }
        }

        private void CargaDatosIniciales()
        {
            int color1 = Color.FromArgb(255, 255, 255).ToArgb();
            int color2 = Color.FromArgb(192, 192, 192).ToArgb();
            int color3 = Color.FromArgb(128, 128, 128).ToArgb();
            coloresIniciales = new int[10] { 
                color1, color1, color1, 
                color2, color2, color2, color2, color2, color2,
                color3
            };
            colorRodea = color3;
            colorTope = color2;
            ladoDelCuadroX = anchoElemento + 1;
            ladoDelCuadroY = altoElemento + 1;
            bordeX = 5;
            bordeY = 5;
            posIniXdeCuadro = 4;
            posIniYdeCuadro = 4;
        }

        /// <summary>
        /// Busca un cuadro de juego en la pantalla, y dice el lugar inicial
        /// y la cantidad de cuadrados.
        /// </summary>
        /// <param name="ini_x"></param>
        /// <param name="ini_y"></param>
        /// <param name="nCuadrosX"></param>
        /// <param name="nCuadrosY"></param>
        /// <returns></returns>
        private bool BusquedaDeCuadro(ref int ini_x, ref int ini_y, ref int nCuadrosX, ref int nCuadrosY)
        {
            // Para el ancho de la pantalla
            int ancho = Screen.PrimaryScreen.Bounds.Width;
            // Para el alto de la pantalla
            int alto = Screen.PrimaryScreen.Bounds.Height;
            int incrementoY = alto / lineasHorizontales;
            int inicioY = 10;
            int ladoX = 0;
            int ladoY = 0;

            for (int y_linea = inicioY; y_linea < alto; y_linea += incrementoY)
            {
                int indice = 0;
                bool encuentraX = false;
                bool encuentraY = false;
                int x_encontrado = 0;
                int y_encontrado = 0;
                for (int x = 0; x < ancho; x++)
                {
                    int color = ReconocimientoDePantalla.LeeUnPixelDePantalla(x, y_linea);
                    if (coloresIniciales[indice] == color)
                    {
                        indice++;
                        if (indice >= coloresIniciales.Length)
                        {
                            encuentraX = true;
                            x_encontrado = x;
                            break;
                        }
                    }
                    else
                    {
                        indice = 0;
                    }
                }
                if (encuentraX)
                {
                    y_encontrado = y_linea;
                    for (int y = y_linea; y >= 0; y--)
                    {
                        int color = ReconocimientoDePantalla.LeeUnPixelDePantalla(x_encontrado, y);
                        if (color == colorRodea)
                        {
                            y_encontrado = y;
                        }
                        else
                        {
                            if (color == colorTope)
                            {
                                encuentraY = true;
                            }
                            break;
                        }
                    }
                    if (encuentraY)
                    {
                        for (int i = y_encontrado; i < alto; i++)
                        {
                            int color = ReconocimientoDePantalla.LeeUnPixelDePantalla(x_encontrado, i);
                            if (color != colorRodea)
                            {
                                ladoY = i - y_encontrado;
                                break;
                            }
                        }
                        for (int i = x_encontrado; i < ancho; i++)
                        {
                            int color = ReconocimientoDePantalla.LeeUnPixelDePantalla(i, y_encontrado);
                            if (color != colorRodea)
                            {
                                ladoX = i - x_encontrado;
                                break;
                            }
                        }
                    }
                }
                if (encuentraX && encuentraY)
                {
                    int n1 = ladoX - bordeX;
                    int n2 = ladoY - BordeY;
                    if ((n1 % ladoDelCuadroX) != 0)
                    {
                        continue;
                    }
                    if ((n2 % ladoDelCuadroY) != 0)
                    {
                        continue;
                    }
                    ini_x = x_encontrado;
                    ini_y = y_encontrado;
                    nCuadrosX = n1 / ladoDelCuadroX;
                    nCuadrosY = n2 / ladoDelCuadroY;
                    if ((nCuadrosX < 1) || nCuadrosY < 1)
                    {
                        continue;
                    }
                    return true;
                }
            }
            return false;
        }

        public void Reconocimiento()
        {
            if (actualizando) { return; }
            actualizando = true;
            int x = 0, y = 0, nX = 0, nY = 0;
            if (BusquedaDeCuadro(ref x, ref y, ref nX, ref nY))
            {
                posIniDePantallaX = x;
                posIniDePantallaY = y;
                if ((matriz.GetLength(0) != nX) || (matriz.GetLength(1) != nY))
                {
                    InicializaMatriz(nX, nY);
                }
                Bitmap bmp = ReconocimientoDePantalla.ObtieneRecorteDePantalla(x, y, x + (nX * ladoDelCuadroX) + bordeX, y + (nY * ladoDelCuadroY) + bordeY);
                byte[, ,] imagen = ImagenConverterBMP.byteArrayToMatrix(ImagenConverterBMP.imageToByteArray(bmp));
                CargaMatrizDeElementos(imagen, posIniXdeCuadro, posIniYdeCuadro, anchoElemento, altoElemento, ladoDelCuadroX, ladoDelCuadroY);
                encuentraCuadro = true;
            }
            else
            {
                encuentraCuadro = false;
            }

            actualizando = false;
        }

        /// <summary>
        /// Pone el cursor del mause sobre un cuadrado 
        /// y presiona clic izquierdo
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void RealizaJugada(int x, int y)
        {
            Point pAnterior = Cursor.Position;
            int mX = (posIniDePantallaX + posIniXdeCuadro) + (ladoDelCuadroX * x) + (ladoDelCuadroX >> 1);
            int mY = (posIniDePantallaY + posIniYdeCuadro) + (ladoDelCuadroY * y) + (ladoDelCuadroY >> 1);
            Cursor.Position = new Point(mX, mY);
            mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, mX, mY, 0, 0);

            Cursor.Position = pAnterior;
        }

        public void ReiniciaJuego()
        {
            Point pAnterior = Cursor.Position;
            int x = (matriz.GetLength(0) * ladoDelCuadroX) >> 1;
            int mX = (posIniDePantallaX + posIniXdeCuadro) + x;
            int mY = (posIniDePantallaY + posIniYdeCuadro) - ((ladoDelCuadroY * 3) >> 1);
            Cursor.Position = new Point(mX, mY);
            mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, mX, mY, 0, 0);

            Cursor.Position = pAnterior;
        }

        public void InicializaMatriz(int ancho, int alto)
        {
            matriz = new TipoDeElemento[ancho, alto];
        }

        private void Inicializa(int ancho, int alto, int anchoE, int altoE)
        {
            lImagenesDeElementos = new List<byte[, ,]>();
            anchoElemento = anchoE;
            altoElemento = altoE;
            matAux = new byte[anchoElemento, altoElemento, 3];
            CargaImagenes();
            CargaDatosIniciales();
            LineasHorizontales = 10;
            matriz = new TipoDeElemento[ancho, alto];
            actualizando = false;
            encuentraCuadro = false;
        }

        public BuscaMinasDeCarga(int ancho, int alto)
        {
            int anchoE = 15;
            int altoE = 15;
            Inicializa(ancho, alto, anchoE, altoE);
        }

        public BuscaMinasDeCarga(int ancho, int alto, int anchoE, int altoE)
        {
            Inicializa(ancho, alto, anchoE, altoE);
        }
    }
}


using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace ControlInteligente
{
    public static class ReconocimientoDePantalla
    {
        [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);

        private static Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);

        public static int LeeUnPixelDePantalla(int x, int y)
        {
            using (Graphics gdest = Graphics.FromImage(screenPixel))
            {
                using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
                {
                    IntPtr hSrcDC = gsrc.GetHdc();
                    IntPtr hDC = gdest.GetHdc();
                    int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, x, y, (int)CopyPixelOperation.SourceCopy);
                    gdest.ReleaseHdc();
                    gsrc.ReleaseHdc();
                }
            }

            return screenPixel.GetPixel(0, 0).ToArgb();
        }

        public static Bitmap ObtieneRecorteDePantalla(int x1, int y1, int x2, int y2)
        {
            Bitmap bitmap = new Bitmap(x2 - x1, y2 - y1, PixelFormat.Format32bppPArgb);
            Graphics graphic = Graphics.FromImage(bitmap);
            graphic.CopyFromScreen(x1, y1, 0, 0, bitmap.Size);

            return bitmap;
        }
    }
}

FW (Framework del proyecto)


Es un proyecto de tipo librería de clases que es accedido por todas las capas, y brinda funcionalidad para convertir los Bitmap en forma eficiente, más dos enumeraciones.




using System;
using System.Collections.Generic;
using System.Text;


namespace FW
{
    public enum ModoDeJuego
    {
        fin_victoria,
        fin_derrota,
        en_juego,
        inicio,
        error
    }
}


using System;
using System.Collections.Generic;
using System.Text;

namespace FW
{
    /// <summary>
    /// Es el tipo de elemento de cada uno de los cuadrados
    /// </summary>
    public enum TipoDeElemento
    {
        contBombas1,
        contBombas2,
        contBombas3,
        contBombas4,
        contBombas5,
        contBombas6,
        contBombas7,
        contBombas8,
        bomba,
        bomba2,
        bandera,
        vacio,
        incognita,
        error
    }
}


using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;

namespace ReconocimientoDeFormas2D
{
    public static class ImagenConverterBMP
    {
        public static byte[] imageToByteArray(System.Drawing.Image imageIn)
        {
            MemoryStream ms = new MemoryStream();
            imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
            return ms.ToArray();
        }

        public static Image byteArrayToImage(byte[] byteArrayIn)
        {
            MemoryStream ms = new MemoryStream(byteArrayIn);
            Image returnImage = Image.FromStream(ms);
            return returnImage;
        }

        public static byte[, ,] byteArrayToMatrix(byte[] byteArrayIn)
        {
            int cont = 4;
            int compresion = 0;
            for (int i = 0; i < 4; i++)
            {
                compresion |= byteArrayIn[cont++] << (8 * i);
            }
            cont = 10;
            int inicioEncabezado = 0;
            for (int i = 0; i < 4; i++)
            {
                inicioEncabezado |= byteArrayIn[cont++] << (8 * i);
            }
            cont = 18;
            int ancho = 0;
            for (int i = 0; i < 4; i++)
            {
                ancho |= byteArrayIn[cont++] << (8 * i);
            }
            int alto = 0;
            for (int i = 0; i < 4; i++)
            {
                alto |= byteArrayIn[cont++] << (8 * i);
            }
            cont = 28;
            int profundidadDeColor = 0;
            for (int i = 0; i < 4; i++)
            {
                profundidadDeColor |= byteArrayIn[cont++] << (8 * i);
            }

            byte[, ,] mat = new byte[ancho, alto, 3];
            cont = inicioEncabezado;
            for (int j = alto - 1; j >= 0; j--)
            {
                for (int i = 0; i < ancho; i++)
                {
                    mat[i, j, 0] = byteArrayIn[cont++];
                    if (profundidadDeColor == 8) continue;
                    mat[i, j, 1] = byteArrayIn[cont++];
                    if (profundidadDeColor == 16) continue;
                    mat[i, j, 2] = byteArrayIn[cont++];
                    if (profundidadDeColor == 24) continue;
                    cont++;
                    
                }
                if (profundidadDeColor == 24)
                {
                    if ((ancho % 2) != 0) { cont++; }
                    if ((alto % 2) != 0) { cont++; cont++; }
                }
            }

            return mat;
        }

        public static Bitmap matrixToBitmap(byte[, ,] matriz)
        {
            Bitmap bmp = new Bitmap(matriz.GetLength(0), matriz.GetLength(1));
            for (int j = 0; j < bmp.Height; j++)
            {
                for (int i = 0; i < bmp.Width; i++)
                {
                    bmp.SetPixel(i, j, Color.FromArgb(matriz[i, j, 2], matriz[i, j, 1], matriz[i, j, 0]));
                }
            }

            return bmp;
        }
    }
}


Capa de negocio


using System;
using System.Collections.Generic;
using System.Text;

namespace JuegoInteligente
{
    public class ElementoBM
    {
        private int posX;
        /// <summary>
        /// Posición x del elemento
        /// </summary>
        public int PosX
        {
            get { return posX; }
            set { posX = value; }
        }
        private int posY;
        /// <summary>
        /// Posición y del elemento
        /// </summary>
        public int PosY
        {
            get { return posY; }
            set { posY = value; }
        }

        private int nMinasRodea;

        public int NMinasRodea
        {
            get { return nMinasRodea; }
            set { nMinasRodea = value; }
        }
        private bool esBomba;

        public bool EsBomba
        {
            get { return esBomba; }
            set { esBomba = value; }
        }
        private bool esIncognita;

        public bool EsIncognita
        {
            get { return esIncognita; }
            set { esIncognita = value; }
        }
        private bool esNumero;

        public bool EsNumero
        {
            get { return esNumero; }
            set { esNumero = value; }        
        }

        private bool esBandera;

        public bool EsBandera
        {
            get { return esBandera; }
            set { esBandera = value; }
        }

        private bool esVacio;

        public bool EsVacio
        {
            get { return esVacio; }
            set { esVacio = value; }
        }

        private ElementoBM[] eVecinos;

        public ElementoBM[] EVecinos
        {
            get { return eVecinos; }
            set { eVecinos = value; }
        }

        private int numeroBombasRodea;

        public int NumeroBombasRodea
        {
            get { return numeroBombasRodea; }
            set { numeroBombasRodea = value; }
        }
        private int numeroIncognitasRodea;

        public int NumeroIncognitasRodea
        {
            get { return numeroIncognitasRodea; }
            set { numeroIncognitasRodea = value; }
        }

        public void CargaVecino(int ind, ElementoBM vecino)
        {
            eVecinos[ind] = vecino;
        }

        public ElementoBM()
        {
            nMinasRodea = 0;
            esBomba = false;
            esIncognita = false;
            esNumero = false;
            esBandera = false;
            eVecinos = new ElementoBM[8];
        }
    }
}


using System;
using System.Collections.Generic;
using System.Text;
using FW;
using ReconocimientoDeFormas2D;

namespace JuegoInteligente
{
    public class BuscaMinas
    {
        private int resultadoComp;
        public int ResultadoComp
        {
            get { return resultadoComp; }
        }
        private bool encuentraCuadro;
        public bool EncuentraCuadro
        {
            get { return encuentraCuadro; }
        }
        private int contador1;
        public int Contador1
        {
            get { return contador1; }
        }
        private int contador2;
        public int Contador2
        {
            get { return contador2; }
        }
        private int posX_mause;
        public int PosX_mause
        {
            get { return posX_mause; }
        }
        private int posY_mause;
        public int PosY_mause
        {
            get { return posY_mause; }
        }
        private ModoDeJuego modo;
        public ModoDeJuego Modo
        {
            get { return modo; }
        }
        private int[,] matrizInt;
        public int[,] MatrizInt
        {
            get { return matrizInt; }
        }
        private TipoDeElemento[,] matriz;
        private ElementoBM[,] matrizDeProceso;
        private List<ElementoBM> lDeIncognitas;
        private ControlInteligente.BuscaMinasDeCarga bmc;

        private bool modoRelampago;
        public bool ModoRelampago
        {
            get { return modoRelampago; }
            set { modoRelampago = value; }
        }

        private int limiteDeModoRelampago;

        public int LimiteDeModoRelampago
        {
            get { return limiteDeModoRelampago; }
            set { limiteDeModoRelampago = value; }
        }

        /// <summary>
        /// Es la matriz que permite determinar si se modificó
        /// </summary>
        private TipoDeElemento[,] matrizActual;

        private Random rdm;

        public void CargaMatriz(TipoDeElemento[,] mat)
        {
            matriz = new TipoDeElemento[mat.GetLength(0), mat.GetLength(1)];
            bool tieneBomba = false;
            bool todoVacio = true;
            bool tieneBandera = false;
            bool tieneError = false;
            for (int y = 0; y < mat.GetLength(1); y++)
            {
                for (int x = 0; x < mat.GetLength(0); x++)
                {
                    if (mat[x, y] == TipoDeElemento.bomba)
                    {
                        tieneBomba = true;
                    }
                    if (mat[x, y] == TipoDeElemento.bomba2)
                    {
                        tieneBomba = true;
                    }
                    if (mat[x, y] != TipoDeElemento.incognita)
                    {
                        todoVacio = false;
                    }
                    if (mat[x, y] == TipoDeElemento.bandera)
                    {
                        tieneBandera = true;
                    }
                    if (mat[x, y] == TipoDeElemento.error)
                    {
                        tieneError = true;
                    }
                    matriz[x, y] = mat[x, y];
                }
            }
            if (tieneError)
            {
                modo = ModoDeJuego.error;
                return;
            }
            if (tieneBandera)
            {
                modo = ModoDeJuego.fin_victoria;
                return;
            }
            if (tieneBomba)
            {
                modo = ModoDeJuego.fin_derrota;
                return;
            }
            if (todoVacio)
            {
                modo = ModoDeJuego.inicio;
                return;
            }
            modo = ModoDeJuego.en_juego;
        }

        public void InicializaMatrizDeProceso()
        {
            for (int y = 0; y < matrizDeProceso.GetLength(1); y++)
            {
                for (int x = 0; x < matrizDeProceso.GetLength(0); x++)
                {
                    ElementoBM ebmAux = new ElementoBM();
                    matrizDeProceso[x, y] = ebmAux;
                    ebmAux.PosX = x;
                    ebmAux.PosY = y;
                    if (matriz[x, y] == TipoDeElemento.incognita)
                    {
                        ebmAux.EsIncognita = true;
                        lDeIncognitas.Add(ebmAux);
                    }
                    else if (matriz[x, y] == TipoDeElemento.bomba)
                    {
                        ebmAux.EsBomba = true;
                    }
                    else if (matriz[x, y] == TipoDeElemento.vacio)
                    {
                        ebmAux.EsVacio = true;
                    }
                    else if (matriz[x, y] == TipoDeElemento.bandera)
                    {
                        ebmAux.EsBandera = true;
                    }
                    else
                    {
                        ebmAux.EsNumero = true;
                        if (matriz[x, y] == TipoDeElemento.contBombas1)
                        {
                            ebmAux.NMinasRodea = 1;
                        }
                        else if (matriz[x, y] == TipoDeElemento.contBombas2)
                        {
                            ebmAux.NMinasRodea = 2;
                        }
                        else if (matriz[x, y] == TipoDeElemento.contBombas3)
                        {
                            ebmAux.NMinasRodea = 3;
                        }
                        else if (matriz[x, y] == TipoDeElemento.contBombas4)
                        {
                            ebmAux.NMinasRodea = 4;
                        }
                        else if (matriz[x, y] == TipoDeElemento.contBombas5)
                        {
                            ebmAux.NMinasRodea = 5;
                        }
                        else if (matriz[x, y] == TipoDeElemento.contBombas6)
                        {
                            ebmAux.NMinasRodea = 6;
                        }
                        else if (matriz[x, y] == TipoDeElemento.contBombas7)
                        {
                            ebmAux.NMinasRodea = 7;
                        }
                        else if (matriz[x, y] == TipoDeElemento.contBombas8)
                        {
                            ebmAux.NMinasRodea = 8;
                        }
                    }
                }
            }
            for (int y = 0; y < matrizDeProceso.GetLength(1); y++)
            {
                for (int x = 0; x < matrizDeProceso.GetLength(0); x++)
                {
                    if (((x - 1) >= 0) && ((y - 1) >= 0))
                    {
                        matrizDeProceso[x, y].CargaVecino(0, matrizDeProceso[x - 1, y - 1]);
                    }
                    if ((y - 1) >= 0)
                    {
                        matrizDeProceso[x, y].CargaVecino(1, matrizDeProceso[x, y - 1]);
                    }
                    if (((x + 1) < matrizDeProceso.GetLength(0)) && ((y - 1) >= 0))
                    {
                        matrizDeProceso[x, y].CargaVecino(2, matrizDeProceso[x + 1, y - 1]);
                    }
                    if ((x - 1) >= 0)
                    {
                        matrizDeProceso[x, y].CargaVecino(3, matrizDeProceso[x - 1, y]);
                    }
                    if ((x + 1) < matrizDeProceso.GetLength(0))
                    {
                        matrizDeProceso[x, y].CargaVecino(4, matrizDeProceso[x + 1, y]);
                    }
                    if (((x - 1) >= 0) && ((y + 1) < matrizDeProceso.GetLength(1)))
                    {
                        matrizDeProceso[x, y].CargaVecino(5, matrizDeProceso[x - 1, y + 1]);
                    }
                    if ((y + 1) < matrizDeProceso.GetLength(1))
                    {
                        matrizDeProceso[x, y].CargaVecino(6, matrizDeProceso[x, y + 1]);
                    }
                    if (((x + 1) < matrizDeProceso.GetLength(0)) && ((y + 1) < matrizDeProceso.GetLength(1)))
                    {
                        matrizDeProceso[x, y].CargaVecino(7, matrizDeProceso[x + 1, y + 1]);
                    }
                }
            }

        }

        /// <summary>
        /// Devuelve:
        /// 0 si son iguales
        /// 1 si tienen distinto contenido pero el mismo tamaño
        /// 2 si tienen distinto tamaño o alguna de las dos son null
        /// 
        /// </summary>
        /// <param name="mat2"></param>
        /// <param name="mat2"></param>
        /// <returns></returns>
        private int ComparaMatrices(TipoDeElemento[,] mat1, TipoDeElemento[,] mat2)
        {
            if (mat1 == null) { return 2; }
            if (mat2 == null) { return 2; }
            if ((mat1.GetLength(0) != mat2.GetLength(0)) || (mat1.GetLength(1) != mat2.GetLength(1)))
            {
                return 2;
            }
            for (int i = 0; i < mat1.GetLength(0); i++)
            {
                for (int j = 0; j < mat1.GetLength(1); j++)
                {
                    if (mat1[i, j] != mat2[i, j]) { return 1; }
                }
            }
            return 0;
        }

        private void CopiaMatriz(TipoDeElemento[,] matDestino, TipoDeElemento[,] matSalida)
        {
            for (int i = 0; i < matSalida.GetLength(0); i++)
            {
                for (int j = 0; j < matSalida.GetLength(1); j++)
                {
                    matDestino[i, j] = matSalida[i, j];
                }
            }
        }

        #region Proceso de jugada

        private void ActualizaMatrizDeProceso()
        {
            for (int i = lDeIncognitas.Count-1; i >=0 ; i--)
            {
                ElementoBM ebm = lDeIncognitas[i];
                if (matriz[ebm.PosX, ebm.PosY] != TipoDeElemento.incognita)
                {
                    ebm.EsIncognita = false;
                    if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.vacio)
                    {
                        ebm.EsVacio = true;
                    }
                    else if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.bomba)
                    {
                        ebm.EsBomba = true;
                    }
                    else if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.bandera)
                    {
                        ebm.EsBandera = true;
                    }
                    else 
                    {
                        ebm.EsNumero = true;
                        if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.contBombas1)
                        {
                            ebm.NMinasRodea = 1;
                        }
                        else if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.contBombas2)
                        {
                            ebm.NMinasRodea = 2;
                        }
                        else if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.contBombas3)
                        {
                            ebm.NMinasRodea = 3;
                        }
                        else if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.contBombas4)
                        {
                            ebm.NMinasRodea = 4;
                        }
                        else if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.contBombas5)
                        {
                            ebm.NMinasRodea = 5;
                        }
                        else if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.contBombas6)
                        {
                            ebm.NMinasRodea = 6;
                        }
                        else if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.contBombas7)
                        {
                            ebm.NMinasRodea = 7;
                        }
                        else if (matriz[ebm.PosX, ebm.PosY] == TipoDeElemento.contBombas8)
                        {
                            ebm.NMinasRodea = 8;
                        }
                    }
                    lDeIncognitas.RemoveAt(i);
                }
            }
        }

        private void DeterminaSiEsBomba(ElementoBM ebm)
        {
            int cont = 0;
            foreach (ElementoBM el in ebm.EVecinos)
            {
                if (el != null)
                {
                    if (el.EsIncognita) { cont++; }
                    if (el.EsBomba) { cont++; }
                }
            }
            if (cont == ebm.NMinasRodea)
            {
                foreach (ElementoBM el in ebm.EVecinos)
                {
                    if (el != null)
                    {
                        if (el.EsIncognita)
                        {
                            el.EsIncognita = false;
                            el.EsBomba = true;
                        }
                    }
                }
            }
        }

        private bool BuscaBombas()
        {
            for (int i = lDeIncognitas.Count - 1; i >= 0; i--)
            {
                ElementoBM ebm = lDeIncognitas[i];
                foreach (ElementoBM el in ebm.EVecinos)
                {
                    if (el != null)
                    {
                        if (el.EsNumero)
                        {
                            DeterminaSiEsBomba(el);                                                        
                        }
                    }
                }
            }
            bool tieneBomba = false;
            for (int i = lDeIncognitas.Count - 1; i >= 0; i--)
            {
                ElementoBM ebm = lDeIncognitas[i];
                if (ebm.EsBomba)
                {
                    lDeIncognitas.RemoveAt(i);
                    tieneBomba = true;
                    continue;
                }
            }
            return tieneBomba;
        }

        private bool DeterminaSiEsVacio(ElementoBM ebm)
        {
            int cont = 0;
            bool contieneIncognita = false;
            foreach (ElementoBM el in ebm.EVecinos)
            {
                if (el != null)
                {
                    if (el.EsBomba) { cont++; }
                    else
                    {
                        if (el.EsIncognita) { contieneIncognita = true; }
                    }
                }
            }
            if ((cont == ebm.NMinasRodea) && (contieneIncognita))
            {
                foreach (ElementoBM el in ebm.EVecinos)
                {
                    if (el != null)
                    {
                        if (el.EsIncognita)
                        {
                            //Realiza jugada
                            this.posX_mause = el.PosX;
                            this.posY_mause = el.PosY;
                            bmc.RealizaJugada(el.PosX, el.PosY);
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        private bool BuscaVacio()
        {
            for (int i = lDeIncognitas.Count - 1; i >= 0; i--)
            {
                ElementoBM ebm = lDeIncognitas[i];
                foreach (ElementoBM el in ebm.EVecinos)
                {
                    if (el != null)
                    {
                        if (el.EsNumero)
                        {
                            if (DeterminaSiEsVacio(el)) { return true; }
                        }
                    }
                }
            }
            return false;
        }

        private void BuscaVacioAleatorioAvanzado()
        {
            if (lDeIncognitas.Count == 0)
            {
                return;
            }


        }

        /// <summary>
        /// Método simple, que puede ser mejorado con 
        /// una lista de probabilidades
        /// </summary>
        private void BuscaVacioAleatorio()
        {
            if (lDeIncognitas.Count == 0)
            {
                return;
            }
            int pos = rdm.Next(lDeIncognitas.Count - 1);
            this.posX_mause = lDeIncognitas[pos].PosX;
            this.posY_mause = lDeIncognitas[pos].PosY;
            bmc.RealizaJugada(lDeIncognitas[pos].PosX, lDeIncognitas[pos].PosY);
        }

        private void ActualizaMatrizInt()
        {
            for (int y = 0; y < matrizInt.GetLength(1); y++)
            {
                for (int x = 0; x < matrizInt.GetLength(0); x++)
                {
                    if (matrizDeProceso[x, y].EsIncognita)
                    {
                        matrizInt[x, y] = 10;
                    }
                    else if (matrizDeProceso[x, y].EsBomba)
                    {
                        matrizInt[x, y] = 11;
                    }
                    else if (matrizDeProceso[x, y].EsVacio)
                    {
                        matrizInt[x, y] = 12;
                    }
                    else if (matrizDeProceso[x, y].EsNumero)
                    {
                        matrizInt[x, y] = matrizDeProceso[x, y].NMinasRodea;
                    }
                }
            }
        }
        
        private void RealizaJugada()
        {
            ActualizaMatrizDeProceso();
            ActualizaMatrizInt();
            while (BuscaBombas()) { }
            if (this.modoRelampago)
            {
                int tam = matriz.GetLength(0) * matriz.GetLength(1);
                int rel;
                if (tam == lDeIncognitas.Count)
                {
                    rel = 1000000;
                }
                else
                {
                    rel = tam / (tam - lDeIncognitas.Count);
                }
                if (rel > limiteDeModoRelampago)
                {
                    BuscaVacioAleatorio();
                    return;
                }
            }
            if (!BuscaVacio())
            {
                BuscaVacioAleatorio(); 
            }
        }

        #endregion

        public void BuscaJugada()
        {
            contador1++;
            bmc.Reconocimiento();
            encuentraCuadro = bmc.EncuentraCuadro;
            if (bmc.EncuentraCuadro)
            {
                contador2++;
                int comp = ComparaMatrices(bmc.Matriz, matrizActual);
                resultadoComp = comp;
                CargaMatriz(bmc.Matriz);
                if (comp != 0)
                {
                    if (comp == 2)
                    {
                        if (bmc.Matriz == null)
                        {
                            return;
                        }
                        matrizActual = new TipoDeElemento[bmc.Matriz.GetLength(0), bmc.Matriz.GetLength(1)];
                        ReDimensiona(bmc.Matriz.GetLength(0), bmc.Matriz.GetLength(1));
                    }
                    CopiaMatriz(matrizActual, bmc.Matriz);
                    if (comp == 1)
                    {
                        if (modo == ModoDeJuego.inicio)
                        {
                            ReDimensiona(bmc.Matriz.GetLength(0), bmc.Matriz.GetLength(1));
                        }
                    }
                    if ((modo == ModoDeJuego.inicio) || (modo == ModoDeJuego.en_juego))
                    {
                        RealizaJugada();
                    }
                }
                else
                {
                    if (modo == ModoDeJuego.inicio)
                    {
                        RealizaJugada();
                    }
                }
            }
        }

        public void ReiniciaJuego()
        {
            matrizActual = null;
            bmc.ReiniciaJuego();
        }

        public void NuevaCarga()
        {
            matrizActual = null;
        }

        private void ReDimensiona(int ancho, int alto)
        {
            lDeIncognitas.Clear();
            modo = ModoDeJuego.en_juego;
            matriz = new TipoDeElemento[ancho, alto];
            CargaMatriz(bmc.Matriz);
            matrizDeProceso = new ElementoBM[ancho, alto];
            InicializaMatrizDeProceso();
            matrizInt = new int[ancho, alto];
        }

        private void Inicializa(int ancho, int alto)
        {
            lDeIncognitas = new List<ElementoBM>();
            modo = ModoDeJuego.en_juego;
            matriz = new TipoDeElemento[ancho, alto];
            matrizDeProceso = new ElementoBM[ancho, alto];
            InicializaMatrizDeProceso();
            bmc = new ControlInteligente.BuscaMinasDeCarga(ancho, alto);
            rdm = new Random();
            contador1 = 0;
            contador2 = 0;
            matrizInt = new int[ancho, alto];
            limiteDeModoRelampago = 4;
        }

        public BuscaMinas()
        {
            Inicializa(10, 10);
        }

        public BuscaMinas(int ancho, int alto)
        {
            Inicializa(ancho, alto);
        }
    }
}


Capa de presentación


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BuscaMinasExperto
{
    public partial class Form1 : Form
    {
        private JuegoInteligente.BuscaMinas bm;
        private bool actualiza;
        private bool reiniciarJuego;

        public Form1()
        {
            InitializeComponent();
        }

        private void ActualizaTimer()
        {
            this.linkLabel1.Enabled = !timer1.Enabled;
            if (timer1.Enabled)
            {
                button1.BackColor = System.Drawing.Color.MistyRose;
                button1.Text = "Detener";
                button1.ForeColor = System.Drawing.Color.Red;
            }
            else
            {
                button1.BackColor = System.Drawing.Color.LightCyan;
                button1.Text = "Iniciar";
                button1.ForeColor = System.Drawing.Color.Blue;
            }
        }

        private void ReiniciaJuegoPrincipal()
        {
            reiniciarJuego = true;
            timer1.Enabled = true;
            ActualizaTimer();
        }

        private void RealizaJugada()
        {
            bm.BuscaJugada();
            if ((bm.Modo == FW.ModoDeJuego.fin_derrota) || (bm.Modo == FW.ModoDeJuego.fin_victoria))
            {
                timer1.Enabled = false;
                ActualizaTimer();
            }
            if (checkBox1.Checked)
            {
                if (bm.Modo == FW.ModoDeJuego.fin_derrota)
                {
                    ReiniciaJuegoPrincipal();
                }
            }
            if ((bm.Modo == FW.ModoDeJuego.fin_derrota) || (bm.Modo == FW.ModoDeJuego.fin_victoria))
            {
                if (bm.Modo == FW.ModoDeJuego.fin_derrota)
                {
                    this.lbl_resultado.Text = "Fin de juego";
                }
                else
                {
                    this.lbl_resultado.Text = "Ganaste!";
                }
            }
            else
            {
                this.lbl_resultado.Text = "";
            }
        }

        private void EjecutaReloj()
        {
            if (reiniciarJuego)
            {
                if (bm.Modo != FW.ModoDeJuego.inicio)
                {
                    bm.ReiniciaJuego();
                }
                if (bm.Modo != FW.ModoDeJuego.fin_derrota)
                {
                    reiniciarJuego = false;
                }
            }
            RealizaJugada();
            ImprimeCuadro();
        }

        private void ImprimeCuadro()
        {
            pictureBox1.Visible = bm.EncuentraCuadro;
            Bitmap bmp = new Bitmap(bm.MatrizInt.GetLength(0) << 3, bm.MatrizInt.GetLength(1) << 3);
            Graphics g = Graphics.FromImage(bmp);
            g.Clear(Color.LightGray);
            for (int y = 0; y < bm.MatrizInt.GetLength(1); y++)
            {
                for (int x = 0; x < bm.MatrizInt.GetLength(0); x++)
                {
                    Color col = Color.Gray;
                    if (bm.MatrizInt[x, y] == 11)
                    {
                        col = Color.Red;
                    }
                    else if (bm.MatrizInt[x, y] == 12)
                    {
                        col = Color.Yellow;
                    }
                    else
                    {
                        col = Color.FromArgb(0, 0, 255 >> (bm.MatrizInt[x, y]));
                    }
                    Pen pe = new Pen(col);
                    g.FillRectangle(pe.Brush, x << 3, y << 3, 4, 4);
                }
            }
            g.Dispose();
            if ((pictureBox1.Height != bmp.Height) || (pictureBox1.Width != bmp.Width))
            {
                pictureBox1.Height = bmp.Height;
                pictureBox1.Width = bmp.Width;
            }
            pictureBox1.Image = bmp;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (actualiza) { return; }
            actualiza = true;
            EjecutaReloj();
            actualiza = false;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            timer1.Enabled = !timer1.Enabled;
            ActualizaTimer();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ActualizaTimer();
            bm = new JuegoInteligente.BuscaMinas();
            actualiza = false;
            reiniciarJuego = false;
            this.linkLabel1.Links.Add(new LinkLabel.Link(0, 40, "www.elementalsoft.blogspot.com"));
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            checkBox2.Enabled = checkBox1.Checked;
            if (checkBox1.Checked)
            {
                bm.ModoRelampago = checkBox2.Checked;
            }
            else
            {
                bm.ModoRelampago = false;
            }
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            timer1.Interval = (int)numericUpDown1.Value;
        }

        private void checkBox2_CheckedChanged(object sender, EventArgs e)
        {
            bm.ModoRelampago = checkBox2.Checked;
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            // Determine which link was clicked within the LinkLabel.
            this.linkLabel1.Links[linkLabel1.Links.IndexOf(e.Link)].Visited = true;

            // Display the appropriate link based on the value of the 
            // LinkData property of the Link object.
            string target = e.Link.LinkData as string;

            // If the value looks like a URL, navigate to it.
            // Otherwise, display it in a message box.
            if (null != target && target.StartsWith("www"))
            {
                System.Diagnostics.Process.Start(target);
            }
            else
            {
                MessageBox.Show("Item clicked: " + target);
            }
        }
    }
}