domingo, 19 de enero de 2014

El entorno Visual Basic VII

Una de las aplicaciones de las primeras computadoras consistió en la exploración de las capacidades de las mismas para emular la inteligencia del hombre en cosas tales como juegos de competencia, en lo que fueron los primeros pasos dados en el campo de la inteligencia artificial. En ese entonces, se llegó a creer que con suficiente memoria y suficiente capacidad de procesamiento digital binario hasta se podría dotar a las computadoras de algo equivalente a una consciencia. Tales intenciones demostraron ser demasiado ambiciosas e ingenuas, en virtud de que las redes de procesamiento de información que distinguen a los seres vivos superiores no están basadas en circuitos digitales binarios que tengan que ser programados por alguien sino en redes neurales que van aprendiendo solas de su propio entorno y se van adaptando a su entorno según se requiera. Más aún, las redes neurales son muchísimo más tolerantes a fallos en los circuitos básicos, las neuronas, que las computadoras digitales binarias, ya que en una computadora digital puede bastar con que una instrucción binaria se lea un “0” cuando lo que tiene que ser leído es un “1” para mandar abajo la computadora paralizándola por completo, mientras que en lo que toca a los humanos hay cientos de miles de casos de personas a las cuales se les ha tenido que extirpar un tumor en el cerebro o que han recibido un balazo en la cabeza, con la consecuente destrucción de cientos de miles (o inclusive millones) de neuronas, y muchas de esas personas con el paso del tiempo se pueden recuperar logrando incluso una rehabilitación completa que deja asombrados a los médicos. Hasta la fecha, falta por ver una computadora que se pueda reparar sola de un daño parecido.

De cualquier modo, en lo que constituyeron los primeros triunfos de las computadoras digitales binarias en el campo de la inteligencia artificial, se han logrado crear máquinas capaces de jugar ajedrez y derrotar incluso hasta los campeones mundiales, además de otros juegos de diversa índole. Los videojuegos en realidad no son más que otro campo de combate en el que el hombre se enfrenta a la máquina.

En esta entrada veremos cómo se puede programar un juego sencillo recurriendo a Visual Basic, y lo haremos de manera completa en lo que será nuestro primer contacto con un proyecto terminado en Visual Basic. El juego seleccionado tal vez sea el más sencillo de todos, el Juego del Gato, en el que un adversario pone una “O” (estamos hablando de una letra “O” mayúscula, no del número cero) o una “X” en cualquiera de nueve casilleros, el otro adversario responde poniendo el símbolo contrario en uno de los ocho casilleros restantes, y el juego continúa hasta que gana el primero que pueda llenar un renglón completo o una columna completa con tres símbolos iguales.

Pero antes de entrar de lleno en los detalles de este juego hecho realidad mediante Visual Basic, haremos un breve repaso de las capacidades gráficas ofrecidas por el entorno Visual Basic para construír todo tipo de figuras en una aplicación.

Para el trazado de figuras geométricas dentro de una forma, la manera más fácil de lograrlo es recurriendo al control Shape, con el cual podemos seleccionar a través de la propiedad Shape del objeto Shape (¡no confundir el objeto o control con su propiedad!) ya sea rectángulos, cuadrados, óvalos, círculos, ventanas con bordes redondeados y cuadrados con bordes redondeados:




Los bordes de todas estas figuras geométricas se pueden trazar de cualquier color, y también pueden ser rellenadas de cualquier color, a través de las propiedades de las figuras en la ventana de Propiedades de cada objeto Shape.

Sin embargo, el objeto Shape carece de medios para el trazado de líneas o puntos dentro de una forma, para tal cosa se recurre no a objetos sino a métodos. Recuérdese que para poder acceder a los métodos de un objeto se recurre al operador punto usando una sintaxis como la siguiente:

Objeto . método

En Visual Basic, es posible extender las capacidades de un método proporcionándole argumentos del siguiente modo:

Objeto . método  argumentos

Tal vez el método más elemental para “encender” puntos individuales de color en la pantalla del monitor dentro de un objeto que se preste a ello (tal como una ventana Form) sea el método PSet, cuya sintaxis usando el operador punto es la siguiente:

Objeto . Pset  (X,Y) , color

Por ejemplo:

Form1.Pset  (1000,1000) , RGB(255,0,0)

Este enunciado pondrá un punto rojo apenas perceptible en la ventana que corresponde a una forma Form1. Si se desea obtener un punto más grande centrado en las mismas coordenadas, se puede aumentar la propiedad DrawWidth del objeto Form1 desde 1 hasta, digamos, 10.

Dentro de un mismo objeto, no es necesario hacer referencia explícita al mismo y se puede invocar directamente el método de la manera siguiente (el enunciado se puede poner dentro de un procedimiento de evento como Click() o como Load() del objeto):

PSet  (X,Y) , color

Las coordenadas (X,Y) se miden a partir de la esquina superior izquierda del objeto, en la cual está la coordenada (0,0). La coordenada X se encuentra X unidades hacia la derecha de la esquina superior izquierda, y la coordenada Y se encuentra Y unidades hacia abajo de la esquina superior izquierda. Se parece en efecto al típico sistema de coordenadas rectangulares Cartesianas, excepto que la ordenada Y va aumentando (positivamente) hacia abajo en vez de aumentar hacia arriba.

Si no nos gusta el sistema de coordenadas (X,Y) proporcionado de manera predeterminada por Visual Basic con la coordenada (0,0) puesta en la esquina superior izquierda, podemos crear dentro de una forma nuestro propio sistema de coordenadas (X,Y) relocalizando la coordenada (0,0) hacia otra posición (digamos, el centro de la ventana) usando el método Scale, de acuerdo a la sintaxis:

Objeto . Scale (X1,Y1) - (X2,Y2)

Después del método PSet para “pintar” puntos individuales en la pantalla, se tiene el método usado para el trazado de líneas rectas en la pantalla, el método Line, cuya sintaxis es la siguiente:

Objeto . Line  (X1,Y1) - (X2,Y2) , color

Este método toma dos pares de coordenadas que representan el punto inicial (X1,Y1) y el punto final (X2,Y2) de una línea recta.

Veamos ahora lo que produce el siguiente código cuando se hace clic con el Mouse en la ventana de la aplicación cuando se está ejecutando:

   Sub Form_Click ()

     Line (3000, 1000)-(3000, 3000), RGB(0, 255, 0)

     Line (500, 3000)-(2000, 3000), RGB(0, 0, 255)

     Line (800, 800)-(2500, 2500), RGB(0, 0, 0)

     Line (1400, 400)-(1800, 800), RGB(0, 0, 0), B

     Line (1500, 3500)-(2500, 4500), RGB(255, 0, 0), BF

   End Sub

Si corremos la aplicación y hacemos clic con el Mouse dentro de la ventana, obtendremos los siguientes trazos:




La primera instrucción dentro del procedimiento Sub se encarga de trazar la línea vertical de color verde que empieza desde la coordenada (3000,1000) y llega hasta la coordenada (3000,3000); obsérvese que la coordenada X se mantiene constante. La segunda instrucción se encarga de trazar la línea horizontal azul que empieza desde la coordenada (500,3000) y llega hasta la coordenada (2000,3000); obsérvese que la coordenada Y se mantiene constante. Y la tercera instrucción traza una línea diagonal negra que va desde la coordenada (800,800) hasta la coordenada (2500,2500). Por otro lado, la cuarta instrucción, en virtud del argumento B especificado al final, traza la caja de borde negro cuya esquina superior izquierda está en la coordenada (1400,400) y cuya esquina inferior derecha está en la coordenada (1800,800). La quinta instrucción también en virtud del argumento BF traza una caja de color rojo, pero la caja es rellenada con el color rojo que distingue los bordes de la caja.

Obsérvese que en el ejemplo que se acaba de dar se han especificado coordenadas proporcionadas en el rango de las miles de unidades. Sin embargo, las líneas y cajas trazadas en la pantalla de un monitor están dentro del alcance de cualquier monitor, incluso uno que tenga una resolución modesta de 800 por 600 pixeles. Si las unidades usadas en la especificación de las coordenadas no se refieren a pixeles (elementos de imagen, puntos individuales en la pantalla del monitor), ¿entonces qué tipo de unidades son? Las unidades empleadas son twips, y fueron implementadas no para la resolución limitada de los monitores de aquél entonces sino para la creciente resolución de las impresoras. En forma predeterminada (default), todos los enunciados de movimientos, dimensionado y trazados gráficos de Visual Basic usan una unidad de un twip, definida en un principio como la vigésima parte de un “punto de impresión” (en una impresora, no en una pantalla de monitor). Para dar una idea, 1,440 twips es igual a una pulgada, 567 twips es igual a un centímetro. Las medidas especifican el tamaño actual que tendrá un objeto cuando sea impreso. Las distancias en la pantalla dependerán según el tamaño del monitor.

Otro método del que nos interesa estar al tanto es el método Circle. En su expresión más sencilla, este método toma tres argumentos: un par de coordenadas que representa el centro del círculo, un número que representa el radio del círculo, y un valor de color de acuerdo a la siguiente sintaxis:

Objeto . Circle  (X,Y) , radio , color

A modo de ejemplo, si corremos el siguiente código en el procedimiento de evento Click() de una forma:

   Sub Form_Click ()

      Circle (800, 1200), 400, RGB(255, 0, 0)

      Circle (2500, 1200), 800, RGB(0, 0, 0)

      Circle (1800, 3000), 1200, RGB(0, 0, 255)

      Circle (1500, 4000), 700, RGB(0, 255, 0)

   End Sub

obtendremos lo siguiente en la pantalla del monitor:




Habiendo visto los rudimentos para el trazado de gráficos, estamos en condiciones de regresar al objetivo que nos habíamos fijado desde un principio, diseñar en Visual Basic un “Juego del Gato” que nos permita enfrentarnos a la computadora dándonos a la vez un poco de entretenimiento.

El Juego del Gato que diseñaremos en Visual Basic, cuando se abre la aplicación y se empieza con un juego nuevo, presenta el siguiente aspecto:




El usuario tiene las “X”, mientras que la computadora tiene las “O”. Conforme va evolucionando el juego, se va cubriendo el campo de la manera siguiente:




Adicionalmente, la figura del cursor del Mouse cambiará de su acostumbrada figura de flecha inclinada (cuando el cursor está colocado fuera del área del juego) a la figura “+” cuando el cursor del Mouse es movido dentro del área del juego (sombreada de color gris) con las líneas horizontales y verticales divisoras.

El diseño de este juego empieza con una forma que llamaremos RejillaG (podríamos usar RejillaGato, pero mantendremos la designación RejillaG que usa los ocho caracteres que se imponían como límite máximo para la designación del nombre de un archivo cuando los sistemas operativos DOS empezaron con tal limitante y la mantuvieron por un tiempo al efectuarse la transición del sistema operativo PC-DOS al sistema operativo Windows) en la cual colocaremos la siguiente colección sencilla de objetos (además de las rutinas Sub y las funciones que se requieren):
  • Nueve cajas de imagen
  • Una definición breve de línea de Menú
  • Un control temporizador
Todo el código para el programa estará contenido en la forma RejillaG.

La siguiente figura, además de ilustrar cómo el fondo gris -sacando un poco hacia afuera una de las nueve cajas que lo forman y en cada una de las cuales se pondrá una “X” o una “O” como parte del juego- está realmente formado por nueve cajas, se muestra también la inserción de un objeto temporizador puesto en la esquina inferior derecha:




El propósito del objeto temporizador Timer es actuar como un cronómetro que fijará intervalos de tiempo, y no será visto en ningún momento por el usuario final cuando se esté ejecutando el programa Juego del Gato, la única persona que verá el temporizador Timer es el programador al estar diseñando el programa. La propiedad más importante de este objeto es la propiedad Interval, a la cual se le asigna un número entero que representa el número de milisegundos que transcurren para el intervalo fijado. En el programa del Juego del Gato, el valor dado al Timer es 2000, o sea 2 segundos, el tiempo suficiente para que se imprima en el área de la línea del título de la forma (ventana) el mensaje “Tu ganas” o “Tu pierdes” y que lo lea el jugador, antes de que se borre tal mensaje en preparación para el siguiente juego.

Estar fijando las propiedades de cada una de las nueve cajas de imagen (algunas de las cuales son iguales tales como el color de fondo de las cajas de imagen así como sus dimensiones rectangulares) no parece ser una manera muy eficiente y rápida de trabajar. Existe una manera rápida y sencilla de crear la rejilla con las nueve cajas de imagen para el Juego del Gato, la cual consiste en empezar el proyecto con la forma y meter en la forma la primera caja de imagen que irá en la esquina superior izquierda de la rejilla, a la cual le daremos el nombre CajaG:




especificando para esto las siguientes propiedades de ambos objetos, forma y caja de imagen:


Valores asignados a las propiedades
Forma Caja de Imagen
 Height = 5340  Height = 1215
 Width = 5130  Width = 1215
 Name = RejillaG   Name = CajaG
 Caption = Juego del Gato   Left = 720
 BackColor = &H00FFFF80&   Top = 360
 BackColor =  &H00C0C0C0& 
 BorderStyle  = 0-None 
 DrawWidth = 10
 ScaleHeight = 2
 ScaleLeft = -1
 ScaleMode = 0-User
 ScaleTop = -1
 ScaleWidth = 2


Al meter la primera una caja de imagen dentro de la forma, Visual Basic le dá el nombre predeterminado Picture1. Mediante la propiedad Name, y podemos cambiar el nombre de la caja de imagen a algo más descriptivo para el Juego del Gato, como CajaG, como se ha especificado arriba en la lista de propiedades para la caja de imagen. El siguiente paso es inmediato: mediante operaciones de copiado y empastado a través de la línea de Menú de Visual Basic simplemente seleccionamos la única caja de imagen que aparece dentro de la forma y mediante una operación de copiado copiamos el objeto completo (la caja de imagen) con la sub-opción Copy y metemos con la sub-opción Paste la segunda caja de imagen que fácilmente podemos acomodar dentro de la forma a un lado de la primera caja mediante una operación sencilla de arrastre de objetos (precaución: al hacer clic con el Mouse para la operación de empastado de la segunda caja de imagen, hay que crearla con el cursor del Mouse situado fuera de la primera caja de imagen, ya que de lo contrario se estará creando una caja de imagen dentro de otra, y eso no es lo que queremos). Este truco, de poder copiar objetos completos y empastar las copias de dichos objetos dentro de una forma, se puede aplicar a todos los objetos disponibles en la Caja de Herramientas de Visual Basic. Al hacer la copia de la caja de imagen CajaG y empastar la segunda caja dentro de la forma, Visual Basic se dá cuenta de que ya tenemos una (primera) caja de imagen con el nombre CajaG, y nos dice que ya tenemos un control (objeto) llamado CajaG, preguntándonos si lo que queremos crear es un array de cajas de imagen. Respondemos afirmativamente con “Yes”, y Visual Basic cambia automáticamente el nombre de la primera caja de imagen de CajaG a CajaG(0), dándole automáticamente a la segunda caja de imagen el nombre de CajaG(1), con lo cual se ha creado ya el primer array con dos objetos cajas de imagen. Hecho esto, solo tenemos que continuar llevando a cabo el proceso de empastado poniendo de una en una las siguientes siete cajas de imagen, de izquierda a derecha y de arriba hacia abajo.

Algunos de los valores proporcionados en la tabla de arriba para las propiedades del array CajaG son de interés especial en el contexto de la programación de graficados, y son de hecho partes esenciales del diseño de la aplicación:
  • El ajuste a True de la propiedad AutoRedraw tiene como propósito asegurar que la imagen gráfica dentro de las cajas de imagen sea restaurada automáticamente siempre que ocurra algún evento que pueda borrar o alterar su contenido. Bajo este ajuste, Visual Basic crea en la memoria un mapa persistente de bits de la imagen.
  • El ajuste BackColor selecciona el color de fondo de cualquiera de las imágenes gráficas que sea trazada dentro de una caja de imagen. La manera más sencilla de ajustar esta propiedad para cualquier objeto consiste en escoger un color directamente de la ventana de Paleta de Colores.
  • La propiedad DrawWidth especifica el grosor, en pixeles, de cualquier línea o círculo que sea dibujado dentro de una caja de imagen. El valor de 10 dado a esta propiedad asegura que haya “X”s y “O”s prominentes dentro de la rejilla de juego.
  • Las cinco propiedades de escala establecen un sistema de coordenadas al interior de cada una de las cajas de imagen. Los ajustes ScaleLeft y ScaleTop ambos a -1 asignan las coordenadas (-1,-1) a la esquina superior izquierda de cada caja de imagen. Los ajustes de ScaleHeight y ScaleWidth ambos a 2 implican que la esquina opuesta (la esquina inferior derecha) tenga las coordenadas (1,1). El punto en el centro de cada caja de imagen corresponderá por lo tanto a una coordenada (0,0). Como se verá más abajo, este sistema de coordenadas simplifica el proceso de dibujar las “X”s y las “O”s dentro de las cajas de imagen (Visual Basic cambia automáticamente al modo de escala ScaleMode al valor 0-User cuando se hacen cambios en los ajustes de otras propiedades relacionadas de escala).
Como ya se mencionó más arriba, Visual Basic proporciona una alternativa para crear, en tiempo de ejecución, un sistema diferente de coordenadas dentro de cada caja de imagen, usando el método Scale para definir los domicilios de las esquinas opuestas:

Objeto . Scale (X1,Y1) - (X2,Y2)

En esta sintaxis, (X1,Y1) representa la esquina superior izquierda de la caja y (X2,Y2) representa la esquina inferior derecha. La anchura a escala se convierte en X2-X1 y la altura a escala se convierte en Y2-Y1. Podríamos, por ejemplo, incluír el siguiente bucle For en el procedimiento del cargado de la forma Form_Load():

     For i% = 0 To 8
       CajaG(i%).Scale  (-1,-1) - (1,1)
     Next i%

Este bucle tendrá el mismo efecto que los ajustes establecidos en tiempo de diseño para las propiedades ScaleLeft, ScaleTop, ScaleWidth y ScaleHeight dados arriba. Obsérvese que estamos anexando el sufijo “%” para indicarle a Visual Basic con i% que se trata de números enteros (del tipo Integer). Haremos lo mismo en otras porciones del código.

Hay otras propiedades de las cajas de imagen que retienen sus valores predeterminados durante la etapa de diseño pero que son modificados durante el tiempo de ejecución:
  • La propiedad MousePointer determina la forma que tomará el cursor del puntero del Mouse cuando es posicionado sobre algún objeto en particular.
  • La propiedad Enabled tiene un valor de True para un objeto que reconoce eventos controlados por el usuario, como un clic del Mouse efectuado en tiempo de ejecución. El programa del Juego del Gato cambia esta propiedad a False para una caja de imagen que ya tiene puesta una “X” o una “O”.
De este modo, tenemos ya un array indexado de cajas de imagen con la siguiente asignación de índices:




A continuación podemos meter un objeto temporizador Timer, el cual recibe el nombre Name de Timer1 que le es dado en forma predeterminada por Visual Basic, y el cual cambiaremos por el de Temporizador, fijando la propiedad Interval del temporizador a 2000 con lo cual le daremos un intervalo de dos segundos a la acción del temporizador. El temporizador tiene definido únicamente un procedimiento de evento, el evento Timer().

Ahora diseñaremos la linea de menú que le corresponderá al Juego del Gato, abriendo para ello la ventana para el Diseño de Menús (Menu Design) de Visual Basic con una unica opción titulada (con Caption) “Juego” y dos sub-opciones de la misma tituladas (también con Caption) “Nuevo” y “Salir”, cada una de las cuales tendrá como nombre (Name) “mnuJuego”, “JuegoNuevo” y “SalirJuego”. Le asignaremos a las opciones y sub-opciones del menú las teclas de acceso que darán un acceso más rápido oprimiento la tecla <Alt> y la tecla de acceso aplicable:

   Juego

   Nuevo

   Salir

con:

   &Juego

   &Nuevo

   &Salir

con lo cual sin necesidad de tener que acceder al menú se logra la misma acción que oprimiendo (respectivamente):

   <Alt> + <J>

   <Alt> + <N>

   <Alt> + <S>

Agregaremos además a las sub-opciones las siguientes teclas aceleradoras:

   <Ctrl> + <N>

   <Ctrl> + <S>

De este modo, tendremos algo como lo siguiente:




Si hemos hecho lo anterior al pie de la letra (ajustando desde luego los pasos de acuerdo a las idiosincrasias de la versión de Visual Basic que se esté utilizando para el diseño del programa), al inspeccionar los objetos que hay disponibles para los procedimientos de la forma veremos esto:




indicándonos que hay seis tipos de objetos:

  CajaG()
  Form
  JuegoNuevo
  mnuJuego
  SalirJuego
  Temporizador

siendo el primer objeto, desde luego, un array de objetos (cajas de imagen).

El primer procedimiento de evento que podemos declarar sin ninguna dificultad es el que corresponde a la forma cuando ésta se carga en la memoria RAM, o sea Form_Load(), abriendo con ello un juego nuevo mediante la invocación del procedimiento de evento JuegoNuevo_Click():

   Sub Form_Load()

      JuegoNuevo_Click

   End Sub

Obsérvese que para invocar un procedimiento de evento desde otro dentro de un mismo objeto no es necesario poner los paréntesis que usualmente acompañan al evento, o sea que basta con escribir Click sin tener que escribir Click().

Veremos ahora el código que se tiene que escribir para que se pueda ejecutar el programa Juego del Gato. Como ya se dijo, por simplicidad todo el código se pondrá dentro de la misma forma, y será un proyecto con un solo archivo, el archivo RejillaG.FRM (el nombre dado a la forma). En la sección general de declaraciones se declarará la variable indexada de hilera XOActual$(8), la cual llevará el rastro de lo que está en proceso actual en las nueve cajas indexadas que van numeradas desde 0 hasta 8, ya sea que su contenido se trate de una “X” o de una “O” o de una hilera vacía:

Dim XOActual$(8)

De la caja colgante que nos muestra los objetos disponibles, podemos ver que tres de los objetos corresponden a objetos creados durante la etapa del diseño del menú, lo cual es lógico porque el acceso mediante porciones de código a las opciones y sub-opciones del menú se lleva a cabo considerandolas como objetos.

Al cargarse la forma por vez primera se invoca el procedimiento de evento JuegoNuevo() para el inicio de un juego nuevo, usando (para evitar repeticiones innecesarias) el mismo código que se usa cuando el objeto JuegoNuevo de la línea del menú es puesto en marcha. De este modo, empezamos haciendo un llamado al siguiente código que estará almacenado bajo el objeto JuegoNuevo, que como ya se dijo es un objeto especificado como la sub-opción que aparece en la línea de menú del Juego del Gato, la sub-opción JuegoNuevo (usaremos comentarios en lugares apropiados del código precediéndolos con la comilla de apóstrofe usada en Visual Basic para denotar comentarios):

   Sub JuegoNuevo_Click ()

     ' Empezar un juego.

     LimpiarRejilla
     DibujarRejilla
     Randomize

     ' Limpiar los datos X-O del juego previo.

     For i% = 0 To 8
       XOActual$(i%) = ""
     Next i%

   End Sub

Al principio de un juego nuevo, el programa inicializa con un bucle toda la rejilla del juego:

     For i% = 0 To 8
       XOActual$(i%) = ""
     Next i%

Pero antes de que esto ocurra, para empezar un juego nuevo primero “limpiamos” con un procedimiento Sub titulado LimpiarRejilla() la rejilla formada por las nueve cajas de imagen, tras lo cual dibujamos con el procedimiento Sub titulado DibujarRejilla() una nueva rejilla. Finalmente, con el bucle For asignamos una hilera vacía a cada una de las nueve cajas de imagen, lo cual se encarga de borrar cualquier “X” o “O” que haya en cualquiera de los nueve lugares disponibles en la rejilla. Obsérvese que después de dibujada la rejilla con el procedimiento DibujarRejilla(), tenemos el enunciado Randomize. Este procedimiento de encarga de aleatorizar el generador de números aleatorios de Visual Basic, porque queremos que cada vez que se empiece con un juego nuevo la computadora responda de nuevas maneras sin repetir los mismos movimientos de antes aunque nosotros queramos repetir las mismas movidas, ya que de lo contrario cada juego sería completamente predecible y no habría el factor de azar que deseamos que haya cada vez que se lleve a cabo un juego enfrentándonos con la computadora, la emoción del juego radica precisamente en la aleatoriedad con la cual responda la computadora a nuestros movimientos o jugadas sin que nosotros podamos anticipar la manera en la cual responderá la computadora. Si no se usa el enunciado Randomize, entonces la función de aleatoriedad Rnd de Visual Basic regresará la misma secuencia de números pseudo-aleatorios cada vez que el programa se ejecute, y el desarrollo del juego será anticipable y predecible. Para que la secuencia de números (pseudo)aleatorios cambie cada vez que el programa se ejecute, se debe poner el enunciado Randomize al principio del programa sin darle ningún argumento.

¿Y cómo habremos de meter el código para cada procedimiento Sub que corresponda a cualquier rutina, empezando por la rutina LimpiarRejilla()? Esto depende de la versión de Visual Basic que se esté utilizando, y en ocasiones el procedimiento para ello no es bien explicado en los manuales. En el caso de Visual Basic 3.0, podemos empezar por abrir la ventana del código de procedimientos para la forma RejillaG.frm, y abrir cualquier procedimiento de evento (el que sea, no importa) como el procedimiento de evento Load() para la forma, el cual mostrará lo que ya habíamos puesto allí anteriormente:

   Sub Form_Load()

      JuegoNuevo_Click

   End Sub

Borramos ahora la porción de código que dice “Form_Load()” y la reemplazamos con “LimpiarRejilla()”, borrando también el código que aparece entre <b>Sub</b> y <b>End Sub</b>, para tener algo como lo siguiente:

   Sub LimpiarRejilla()


   End Sub

Si salimos de la ventana del procedimiento de evento Load() de la forma, Visual Basic reconocerá que lo que se tiene dentro es un procedimiento Sub con otro nombre que no corresponde al del evento Load(), y creará un procedimiento nuevo titulado “LimpiarRejilla” que será agregado a la lista de procedimientos de tipo general para la forma. De este modo, tenemos para el objeto General de la forma, en la sección de Procedimientos, dos procedimientos, el primero que corresponde a las declaraciones (declarations) en el cual se sigue encontrando la declaración del array:

Dim XOActual$(8)


y el segundo que corresponde al procedimiento Sub llamado LimpiarRejilla:




en donde meteremos el código que corresponde a dicho procedimiento Sub. El código para LimpiarRejilla() es el siguiente:

   Sub LimpiarRejilla ()

     ' Limpiar la rejilla de juego en preparacion
     ' para el siguiente juego.

     For i% = 0 To 8

       CajaG(i%).Cls
 
       ' Habilitar todas las cajas, y cambiar la forma
       ' del cursor del Mouse a una mira de tiro.

       CajaG(i%).Enabled = True
       CajaG(i%).MousePointer = 2

     Next i%

   End Sub

El único “pero” que se le puede poner a este proceso es que al usar el procedimiento de código Load() del objeto Form para crear el procedimiento Sub designado como LimpiarRejilla, el código que se había puesto previamente en Load() queda borrado. Lo que se puede hacer es usar a Load() como una etapa intermedia para ir creando los procedimientos Sub y las funciones Function para todo el programa, y habiendo terminado entonces sí podemos meter el código que debe de haber en Form_Load().

Así pues, procediendo de la misma manera, dentro de la sección para la declaración de procedimientos de procedimientos de la forma se encontrarán las otras rutinas que serán invocadas por el programa Juego del Gato conforme se vayan requiriendo:




Repasando el código del procedimiento LimpiarRejilla ():

   Sub LimpiarRejilla ()

     ' Limpiar la rejilla de juego en preparacion
     ' para el siguiente juego.

     For i% = 0 To 8

       CajaG(i%).Cls
 
       ' Habilitar todas las cajas, y cambiar la forma
       ' del cursor del Mouse a una mira de tiro.

       CajaG(i%).Enabled = True
       CajaG(i%).MousePointer = 2

     Next i%

   End Sub

vemos que el método Cls que es aplicable para cada caja con el operador punto, se encarga de “limpiar” el graficado interior de una caja dada:

     For i% = 0 To 8

       CajaG(i%).Cls

Tras esto, la propiedad Enabled de la caja es puesta a True para que con ello un clic del Mouse del usuario pueda ser reconocido:

       CajaG(i%).Enabled = True

Finalmente, la propiedad del puntero del cursor del Mouse MousePointer es fijada en un valor de 2 para la caja actual. Esto ocasiona que el puntero del cursor del Mouse tome la forma de la “cruz” de una mira telescópica de rifle, y con ello el jugador verá que el puntero del cursor del Mouse cambia a este tipo de figura cada vez que el puntero es posicionado sobre una caja que tenga este valor. Esta cruz de mira telescópica le dice al jugador que la caja está disponible:

CajaG(i%).MousePointer = 2

El código para el procedimiento DibujarRejilla() es el siguiente:

   Sub DibujarRejilla ()

     ' Dibujar la rejilla de juego

     LineasHoriz CajaG(3)
     LineasHoriz CajaG(5)
     LineasVert CajaG(1)
     LineasVert CajaG(7)
     Cuadrado CajaG(4)

   End Sub

Este procedimiento DibujarRejilla invoca tres procedimientos Sub:

   LineasHoriz()

   LineasVert()

   Cuadrado()

y tiene como propósito construir la rejilla del juego dentro de la cual el jugador humano irá poniendo las “X” y la computadora irá poniendo las “O”. Aquí es donde se le dará uso a las capacidades de las funciones gráficas de Visual Basic discutidas arriba. Los procedimientos Sub respectivos para el dibujado de la rejilla del juego son los siguientes:

Procedimiento Sub LineasHoriz:

   Sub LineasHoriz (CajaImagen As Control)

     ' Trazado de las lineas horizontales verdes
     ' en una caja

     CajaImagen.Line (-1, -1)-(1, -1), RGB(0, 255, 0)
     CajaImagen.Line (-1, 1)-(1, 1), RGB(0, 255, 0)

   End Sub

Procedimiento Sub LineasVert:

   Sub LineasVert (CajaImagen As Control)

     ' Trazado de las lineas verticales verdes en
     ' una caja.

     CajaImagen.Line (-1, -1)-(-1, 1), RGB(0, 255, 0)
     CajaImagen.Line (1, -1)-(1, 1), RGB(0, 255, 0)

   End Sub

Procedimiento Sub Cuadrado:

   Sub Cuadrado (CajaImagen As Control)

     ' Dibujar un cuadrado a la mitad de la caja.

     CajaImagen.Line (-1, -1)-(1, 1), RGB(0, 255, 0), B

   End Sub

Veamos la manera en la que trabajan  estos procedimientos de graficado, empezando por el procedimiento para el trazado de líneas horizontales:

   Sub LineasHoriz (CajaImagen As Control)

     ' Trazado de las lineas horizontales verdes
     ' en una caja

     CajaImagen.Line (-1, -1)-(1, -1), RGB(0, 255, 0)
     CajaImagen.Line (-1, 1)-(1, 1), RGB(0, 255, 0)

   End Sub

Obsérvese que al procedimiento LineasHoriz() le estamos pasando como argumento no un número entero o una hilera de caracteres sino todo un objeto (un control, declarado como control usando la sintaxis de Visual Basic para especificar a un objeto que es pasado como argumento con As):

LineasHoriz (CajaImagen As Control)

Aunque al principio nos parezca extraño, esto es algo perfectamente válido y consistente con la afirmación que se había hecho anteriormente de que podemos considerar a un objeto, por complejo que sea, como un tipo de dato.

En el código de DibujarRejilla() hacemos dos invocaciones al procedimiento LineasHoriz():

     LineasHoriz CajaG(3)
     LineasHoriz CajaG(5)

pasándole como argumento el objeto CajaG(3), y pasándole en la segunda invocación como argumento el objeto CajaG(5). Cada invocación tiene por efecto que se tracen dentro del área de trabajo dos líneas horizontales de color verde, como se muestra a continuación:




Veamos ahora la manera en la que trabaja el procedimiento para el trazado de líneas verticales:

   Sub LineasVert (CajaImagen As Control)

     ' Trazado de las lineas verticales verdes en
     ' una caja.

     CajaImagen.Line (-1, -1)-(-1, 1), RGB(0, 255, 0)
     CajaImagen.Line (1, -1)-(1, 1), RGB(0, 255, 0)

   End Sub

De nueva cuenta, obsérvese que al procedimiento LineasVert() le estamos pasando como argumento no un número entero o una hilera de caracteres sino todo un objeto (control):

LineasVert (CajaImagen As Control)

En el código de DibujarRejilla() hacemos dos invocaciones al procedimiento LineasVert():

     LineasVert CajaG(1)
     LineasVert CajaG(7)

pasándole como argumento el objeto CajaG(1), y pasándole en la segunda invocación como argumento el objeto CajaG(7). Cada invocación tiene por efecto que se tracen dentro del área de trabajo dos líneas verticales de color verde, como se muestra a continuación:




Hasta este punto, el trazado de la rejilla está incompleto, porque solo tenemos lo siguiente:




Resulta obvio que la rejilla está incompleta, y que nos falta poner justo en medio una “caja” completa de bordes verdes. Esto se logra con el procedimiento Sub:

   Sub Cuadrado (CajaImagen As Control)

     ' Dibujar un cuadrado a la mitad de la caja.

     CajaImagen.Line (-1, -1)-(1, 1), RGB(0, 255, 0), B

   End Sub

al cual le pasamos como argumento precisamente la CajaG(4):

Cuadrado CajaG(4)

Obsérvese que en la invocación del método Line estamos especificiando la opción “B” (Box) que indica que en vez de que se dibuje una línea diagonal se dibuje una caja cuyas dos esquinas contrarias sean los puntos que marquen el borde de la caja. De este modo, la ejecución del código:

Cuadrado( G(4) )

tendrá como efecto dibujar lo siguiente:




Así pues, al hacer clic en la sub-opción “JuegoNuevo” de la línea del menú se trazará la rejilla completa en la cual se llevará a cabo el Juego del Gato.

Nos falta por especificar aún otra cosa: los procedimientos gráficos para dibujar dentro de cada caja CajaG() de la rejilla ya sea una “X” (movida del jugador humano) o una “O” (respuesta de la computadora). A continuación tenemos los procedimientos Sub gráficos para dibujar dentro de cada caja de la rejilla ya sea una “X” o una “O”:

Procedimiento Sub X:

   Sub X (CajaImagen As Control)

     ' Poner una "X" dentro de una caja (movida del
     ' jugador humano).

     CajaImagen.Line (-.6, -.6)-(.6, .6), RGB(255, 0, 0)
     CajaImagen.Line (.6, -.6)-(-.6, .6), RGB(255, 0, 0)

     ' Cambiar el cursor del Mouse a su valor usual,
     ' predeterminado, e inhabilitar la caja.

     CajaImagen.MousePointer = 0

     CajaImagen.Enabled = False

   End Sub

Procedimiento Sub O:

   Sub O (CajaImagen As Control)

     ' Dibujar una "O" en una caja.

     CajaImagen.Circle (0, 0), .6, RGB(0, 0, 255)

     ' Inhabilitar la caja, y cambiar el puntero cursor
     ' del Mouse a su apariencia normal.

     CajaImagen.Enabled = False

     CajaImagen.MousePointer = 0

   End Sub

Como puede verse, a través del argumento RGB estamos especificando que para las “X” se use un “lápiz” de color rojo, mientras que para las “O” se deberá usar un lápiz de color azul. Obsérvese que para poner la “X” dentro de una CajaG hacemos el trazado de <i>dos</i> líneas diagonales recurriendo al método Line, sobreponiendo una encima de la otra:




La longitud especificada para el trazo de las “X” es un sesenta por ciento (0.6) de la longitud diagonal total dentro de cada caja de imagen:

   CajaImagen.Line (-.6, -.6)-(.6, .6), RGB(255, 0, 0)
   CajaImagen.Line (.6, -.6)-(-.6, .6), RGB(255, 0, 0)

Por otro lado, para que la computadora ponga una “O” dentro de una CajaG que esté disponible, basta con recurrir una sola vez al método Circle, usando un radio igual a 0.6 centrado en el origen (0,0) de la caja, lo cual hace que el diámetro de las “O” tenga la misma longitud diagonal que las “X”:

     CajaImagen.Circle (0, 0), .6, RGB(0, 0, 255)

Para el siguiente procedimiento Sub, con el cual el jugador humano con un clic con el Mouse hará su movida poniendo una “X” en cualquier casillero que esté disponible, hay que tener presente que el objeto CajaG ya existe como un array dentro de la forma.

   Sub CajaG_Click (Indice As Integer)

     ' Anotar la "X" del jugador en la caja seleccionada.

     X CajaG(Indice)

     XOActual$(Indice) = "X"

     ' Revisar si ya hay tres en-una-linea.

     BuscarTres "X"

     ' En caso contrario, tomar la movida de la
     ' computadora.

     If Not TodasTomadas() Then

       ' Buscar patrones de dos-en-una-linea.

       BuscarDos

       ' Checar si esta es la movida ganadora.

       BuscarTres "O"

     End If

   End Sub

De este modo, cuando el jugador hace clic con el Mouse dentro de una caja de imagen que esté disponible, el procedimiento de evento CajaG_Click() entra en acción y llama al procedimiento X para que se dibuje una “X” en la caja seleccionada. El procedimiento Click() toma nota y registra la posición de la nueva “X” en el array XOActual$():

     X  CajaG(Indice)

     XOActual$(Indice) = "X"

El procedimiento X recibe una caja de imagen como argumento-objeto, representado por CajaImagen:

   Sub X (CajaImagen As Control)

Además de dibujar una “X” dentro de la caja de imagen seleccionada, el procedimiento X cambia los ajustes de las propiedades MousePointer y Enabled para la caja de imagen seleccionada:

     CajaImagen.MousePointer = 0

     CajaImagen.Enabled = False

El valor de cero de la propiedad MousePointer produce el aspecto usual del puntero de cursor del Mouse (una pequeña flecha inclinada). En la rejilla del juego, esta flecha indica que la caja a la que se está apuntando no está disponible. Cuando Enabled es puesto a False,  el programa deja de reconocer eventos para la caja de imagen, de modo tal que el clic del jugador en una caja ya tomada no tendrá efecto alguno.

Cambios equivalentes se llevan a cabo cuando la computadora decide en dónde poner su “O” en cierta caja. Una llamada al procedimiento O dibuja una “O”, y el elemento correspondiente en el array XOActual$ es ajustado en forma apropiada en el procedimiento BuscarDos() que veremos más abajo:

   O  CajaG(MiSeleccion)
   XOActual$(MiSeleccion) = "O"

Y dentro del procedimiento O. la propiedad Enabled es reajustada a False y el puntero del Mouse MousePointer es reajustado a cero.

La estrategia de la computadora para escoger los lugares en los cuales pondrá sus “O”s incluye tres niveles de toma de decisiones:
  1. Si existe un patrón de dos “O” (alineadas) en cualquier lugar en la rejilla de juego, la computadora pone una tercer “O” en el casillero vacío, para ganar el juego.
  2. Alternativamente, si existe un patrón de dos “X” alineadas, la computadora pone una “O” en el casillero que se requiera para impedir que el jugador pueda ganar.
  3. Alternativamente, si no existe un patrón de dos alineados en la rejilla de juego, la computadora selecciona al azar una posición que esté disponible.
Estas decisiones se llevan a cabo con el siguiente procedimiento Sub llamado <b>BuscarDos</b>:

   Sub BuscarDos ()

      ' Buscar dos O's o dos X's en un renglon.

      Dim MiSeleccion As Integer

      Select Case True

         ' Buscar las O's primero.

         Case Dos(0, 1, 2, "O")
         Case Dos(3, 4, 5, "O")
         Case Dos(6, 7, 8, "O")
         Case Dos(0, 3, 6, "O")
         Case Dos(1, 4, 7, "O")
         Case Dos(2, 5, 8, "O")
         Case Dos(0, 4, 8, "O")
         Case Dos(2, 4, 6, "O")

         ' Si no hay O's, buscar las X's.

         Case Dos(0, 1, 2, "X")
         Case Dos(3, 4, 5, "X")
         Case Dos(6, 7, 8, "X")
         Case Dos(0, 3, 6, "X")
         Case Dos(1, 4, 7, "X")
         Case Dos(2, 5, 8, "X")
         Case Dos(0, 4, 8, "X")
         Case Dos(2, 4, 6, "X")

         Case Else  ' Si no hay, buscar un lugar al azar.

            MiSeleccion = ColocacionAleatoria()
            O CajaG(MiSeleccion)
            XOActual$(MiSeleccion) = "O"

      End Select

   End Sub

En este procedimiento, un largo enunciado Case primero busca dos “O” que estén alineadas en un renglón (ya sea horizontal, diagonal, o diagonal pasando por el centro de la rejilla), y en caso de no encontrar esto buscará dos “X” que estén alineadas en un renglón. Los ocho renglones de la rejilla de juego -tres horizontales, tres verticales, dos diagonales- son examinados uno a la vez con llamados a la función Dos(), una función que será especificada más abajo. He aquí tres llamados a la función Dos() cuando se está buscando primero dos “O”:

         Dos(3, 4, 5, "O")

         Dos(6, 7, 8, "O")

         Dos(0, 3, 6, "O")

La combinación Dos(3,4,5,"O") regresará un valor de True si hay dos “O” en cualquiera de dos de las cajas 3, 4 y 5 de lo que viene siendo el segundo renglón horizontal; la combinación Dos(6,7,8,"O") regresará un valor de True si hay dos “O” en cualquiera de dos de las cajas 6, 7 y 8 de lo que viene siendo el tercer renglón horizontal, y la combinación Dos(0,3,6,"O") regresará un valor de True si hay dos “O” en cualquiera de dos de las cajas 0, 3 y 6 en lo que viene siendo el primer renglón vertical que está a la derecha de la rejilla. Obsérvese que todas las ocho posibilidades están cubiertas en forma exhaustiva, tanto para las “O” como para las “X”. Y en virtud de la manera en la cual funciona la estructura de decisión Case:

   Select Case  valor de prueba

      Case ...

      Case ...

      Case Else

         ...

   End Select

basta con que una sola de las combinaciones posibles sea Verdadera para que no se ejecute la parte Else de la estructura de decisión. Sin embargo, si ninguna de las combinaciones posibles es Verdadera (True), entonces se ejecuta la parte Else:

   MiSeleccion = ColocacionAleatoria()
   O CajaG(MiSeleccion)
   XOActual$(MiSeleccion) = "O"

con la cual la computadora responde con “MiSeleccion” (un entero que puede variar entre cero y 8) invocando la rutina O para dibujar una “O” en un casillero seleccionado al azar con ColocacionAleatoria() y asignando la “O” a uno de los espacios del array de hileras XOActual$().

La declaracion de las funciones Function() en un programa Visual Basic sigue un procedimiento semejante al que fue utilizado para la declaracion de los procedimientos Sub: Teniendo abierta la ventana del código de procedimientos para la forma RejillaG.frm y teniendo abierto cualquier procedimiento de evento (el que sea, no importa) como el procedimiento de evento Load() para la forma, se borra la porción de código que dice “Form_Load()” pero se borran también las palabras clave Sub y End Sub dejando todo el interior de la ventana en blanco, escribiendo al principio la primera línea de la función:

   Function Dos (i%, j%, k%, c$) As Integer

Al pasar hacia la siguiente línea, Visual Basic automáticamente borra “End Sub” y pone “End Function” teniéndose así:

   Function Dos (i%, j%, k%, c$) As Integer


   End Function

Podemos meter a continuación entre las dos líneas <b>Function</b> y <b>End Function</b> el código que corresponde a dicha función:

   Function Dos (i%, j%, k%, c$) As Integer

      ' Checar tres cajas para un patron de
      ' dos-en-un-renglon.

      Dim TempDos As Integer

      TempDos = True

      If (XOActual$(i%) = c$) _
      And (XOActual$(j%)) = c$ And _
      (XOActual$(k%)) = "" Then

        Tomar% = k%

      ElseIf (XOActual$(i%) = c$) _
      And (XOActual$(k%) = c$) And _
      (XOActual$(j%) = "") Then

        Tomar% = j%

      ElseIf (XOActual$(j%) = c$) _
      And (XOActual$(k%) = c$) And _
      (XOActual$(i%) = "") Then

        Tomar% = i%

      Else

         TempDos = False

      End If

      ' Si se encuentra un patron de dos-en-un-
      ' renglon, tomese la caja aun sin usar
      ' del renglon

      If TempDos Then

         O CajaG(Tomar%)
         XOActual$(Tomar%) = "O"

      End If

      Dos = TempDos

   End Function

La función Dos() coloca las “O” de la computadora en el primer renglón (horizontal, vertical, diagonal) que se encuentre que tenga un patrón que permita tal cosa. Obsérvese que en virtud de que tres de las líneas de código son algo extensas, se recurrió a la convención de Visual Basic que permite repartir una misma instrucción en dos líneas de texto distintas poniendo al final de la primera línea de texto un espacio en blanco seguido de un guión bajo (cuantas veces sea neceario), lo cual es tomado como una indicación de que la instrucción Visual Basic continúa en la siguiente línea:

    If (XOActual$(i%) = c$) _

    And (XOActual$(j%)) = c$ _

    And (XOActual$(k%)) = "" Then

Sin embargo, algunas versiones de Visual Basic no permiten subdividir una instrucción demasiado larga en varias líneas, y es necesario escribir explícitamente toda una instrucción larga en una sola línea de texto:

  If (XOActual$(i%) = c$) And (XOActual$(j%)) = c$ And (XOActual$(k%)) = "" Then

Si el procedimiento BuscarDos no encuentra un patrón de “dos en un renglón”, se hace un llamado a la función ColocacionAleatoria():

   Function ColocacionAleatoria () As Integer

     ' Buscar, al azar, una caja sin usar

     Dim AzarTemp As Integer

     Do

       AzarTemp = Int(9 * Rnd)

     Loop Until CajaG(AzarTemp).Enabled

     ColocacionAleatoria = AzarTemp

   End Function

Esta rutina usa la función de Visual Basic Rnd() en una fórmula que da un entero aleatorio entre cero y 8:

Int(9 * Rnd)

La función ColocacionAleatoria() genera repetidamente estos enteros al azar en un bucle Do hasta encontrar una caja de imagen que esté disponible:

     Do

       AzarTemp = Int(9 * Rnd)

     Loop Until CajaG(AzarTemp).Enabled

Cuando este bucle encuentra una caja de imagen que esté disponible, AzarTemp es regresado como el valor de la función ColocacionAleatoria() mediante:

ColocacionAleatoria = AzarTemp

Cada vez que el jugador selecciona una caja de imagen que esté disponible, el procedimiento CajaImagen_Click hace un llamado al procedimiento BuscarTres para determinar si el jugador ha ganado el juego:

BuscarTres "X"

Del mismo modo, una invocación a BuscarTres determina si la computadora ha ganado el juego después de que ha colocado una “O” en la rejilla de juego:

BuscarTres "O"

Este es el procedimiento BuscarTres:

   Sub BuscarTres (Cual$)

      ' Buscar la rejilla por tres X's o tres O's en
      ' un renglon. (Cual$ puede ser una "X" o una "O".)

      Dim TresEncontrado As Integer

      TresEncontrado = True

      Select Case True

         Case Tres(0, 1, 2, Cual$)
         Case Tres(3, 4, 5, Cual$)
         Case Tres(6, 7, 8, Cual$)
         Case Tres(0, 3, 6, Cual$)
         Case Tres(1, 4, 7, Cual$)
         Case Tres(2, 5, 8, Cual$)
         Case Tres(0, 4, 8, Cual$)
         Case Tres(2, 4, 6, Cual$)

         Case Else  ' No hay patrones de tres en un renglon.

         TresEncontrado = False

      End Select

      If TresEncontrado Then

      ' El juego ha terminado.

      InhabilitarTodas

      ' Mostrar el mensaje final.

      If Cual$ = "X" Then
         Msg$ = "Tu ganas."
      Else
         Msg$ = "Tu pierdes."
      End If
 
      RejillaG.Caption = Msg$

      Temporizador.Enabled = True

      End If

   End Sub

El procedimiento BuscarTres utiliza una estructura Select Case, así como llamados individuales a una función llamada Tres() para examinar los renglones horizontales, verticales y diagonales en la rejilla de juego. Si el procedimiento encuentra tres “X” o tres “O” alineados, el juego se dá por terminado.

Esta es la función Tres() que como puede verse consta de una sola instrucción:

   Function Tres (i%, j%, k%, c$) As Integer

      ' Checar tres cajas buscando que haya un patron
      ' de tres en un renglon.

      Tres = (XOActual$(i%) = _c$) _
      And (XOActual$(j%) = c$) And _
      (XOActual$(k%) = c$)

   End Function

El primer paso en dar por terminado el juego consiste en inhabilitar todas las cajas de imagen, lo cual se logra con un llamado al procedimiento Sub InhabilitarTodas:

   Sub InhabilitarTodas ()

      ' Inhabilitar todas las cajas para impedir que
      ' el jugador trate de continuar con el juego.

      For i% = 0 To 8

         CajaG(i%).Enabled = False

         ' Regresar al cursor puntero predeterminado
         ' del Mouse.

         CajaG(i%).MousePointer = 0

      Next i%

   End Sub

Como puede verse arriba, la rutina InhabilitarTodas() también reajusta las propiedades Enabled y MousePointer de todas las nueve cajas de imagen.

Tras el paso anterior, el procedimiento BuscarTres prepara un mensaje anunciando los resultados del juego, e imprime el mensaje del resultado en la barra del título de la forma RejillaG:

      If Cual$ = "X" Then
         Msg$ = "Tu ganas."
      Else
         Msg$ = "Tu pierdes."
      End If
 
      RejillaG.Caption = Msg$

Con la instrucción:

If Not TodasTomadas() Then

el procedimiento CajaG_Click hace una invocación a la función TodasTomadasen la cual se ejecuta lo siguiente:

   Function TodasTomadas () As Integer

     ' Regresar un valor de True si todas las cajas
     ' ya tienen X's o O's dentro de ellas.

     Dim TodasTemp As Integer, i%

     TodasTemp = True

     For i% = 0 To 8
       If CajaG(i%).Enabled Then TodasTemp = False
     Next i%

     TodasTomadas = TodasTemp

   End Function

A continuación tenemos el procedimiento Sub para ser usado por el objeto Timer1 renombrado como Temporizador, colocado en la forma durante la etapa de diseño y cuyo único procedimiento de evento Timer() contiene el código que se muestra:

   Sub Temporizador_Timer ()

      ' Restaurar la leyenda (Caption) original
      ' después de dos segundos.

      RejillaG.Caption = "Juego del Gato"

      Temporizador.Enabled = False

   End Sub

El evento es disparado en el punto temporal en el cual el intervalo definido expira. De este modo, el procedimiento de evento Temporizador_Timer() es invocado al final de un intervalo de dos segundos, después de lo cual el control temporizador es habilitado. Más precisamente, esta es la manera en la cual un evento temporizador sencillo ocurre en un programa:
  1. El programa Visual Basic, al estarse ejecutando, cambia la propiedad Enabled del temporizador de False a True. Esto da comienzo al intervalo cronometrado.
  2. Al terminar el evento cronometrado, el procedimiento de evento Timer() es invocado.
  3. El procedimiento Timer() ejecuta una acción diseñada específicamente para el final del evento cronometrado, que en este caso es el restablecimiento del título de la ventana (forma) al texto “Juego del Gato” después de un intervalo de dos segundos en el que se mostró en la barra del título ya sea el mensaje “Tu ganas” o “Tu pierdes”. Además, el procedimiento de evento reajusta de nuevo la propiedad Enabled a False, de forma tal que no sea disparado otra vez por cuenta propia.
Estos tres pasos son repetidos cada vez que, al final de un juego, el programa activa el temporizador ajustando la propiedad Enabled a True. El código del programa está diseñado para que en caso de un empate no se muestre cambio alguno en la barra del título de la ventana.

Finalmente, terminamos el diseño del Juego del Gato con la siguiente rutina haciendo clic en la sub-opción del menú que corresponde a “Salir”:

   Sub SalirJuego_Click ()

      ' Dar por terminado el juego.

      End

   End Sub

Hecho lo anterior, podemos ejecutar el programa en forma simulada bajo Visual Basic con la finalidad de detectar errores y purgarlos (debug) checando en forma exhaustiva todas las posibilidades que se puedan presentar al estar en manos del usuario. Esta última etapa, cuando no se lleva a cabo en forma diligente, es causante de que en un programa complejo de las 20 mil posibilidades a revisar cuando no se revisan todas ellas se “cuele” un error fatal que se encuentra entre las 500 posibilidades que no se checaron, y es causante de que empresas de software que deben cuidar su prestigio se vean obligadas a pedir perdón a sus clientes enviándoles parches y remiendos sin costo alguno traduciéndose en pérdida de ganancias y pérdida de prestigio. Precisamente para evitar estos descalabros se cuenta en las grandes empresas de software con departamentos cuya única función no es programar sino checar a conciencia todas las posibilidades que se puedan dar, y aún así no se posible anticipar todas las posibilidades que se puedan presentar como lo evidencia el descubrimiento constante de agujeros que suelen ser explotados por hackers diligentes que logran encontrar los defectos que los revisores del funcionamiento del programa no pudieron encontrar.

Una vez que estamos satisfechos con el buen funcionamiento del Juego del Gato, el siguiente paso es convertir el programa en un archivo ejecutable con algun nombre llamativo como GATO.EXE. Pero antes de hacer tal cosa, es muy probable que queramos asociar el archivo ejecutable a algún mosaico gráfico (ícono) sugerente del propósito del programa ejecutable, con el cual podamos poner en marcha el programa con el solo hecho de hacer clic con el Mouse en el ícono, para lo cual tenemos que construír un ícono como el siguiente con un programa de construcción gráfica que permita especificar cada punto elemento de imagen del ícono:




Una vez que tenemos el ícono diseñado y terminado, para incorporar el ícono al programa ejecutable el entorno ofrecerá opciones para ello, tal como el selecionar la forma principal del proyecto a la cual será anclada el ícono y tras ello en la ventana de la lista de Propiedades para la forma se selecciona una propiedad llamada Icon. Haciendo clic en la cajita que corresponde a dicha propiedad aparece una ventana como la que se muestra a continuación con algún título como “Load Icon” (Cargar Icono) para llevar a cabo la selección del ícono (en varias de las versiones de Visual Basic, una vez que se haya seleccionado el ícono la elipsis o puntos suspensivos es borrada poniéndose en lugar de la elipsis el título (Icon)):




Terminado lo anterior, podemos proceder a la construcción del programa ejecutable, para lo cual la mayoría de los entornos tienen alguna opción en una línea de Menú (como Make EXE File) o en una barra de Herramientas, algo como lo siguiente:




El Juego del Gato es muy sencillo y no requiere de mucha explicación, pero en proyectos y programas más complejos será deseable y conveniente agregarle al programa un sistema informativo de Ayuda para que el usuario pueda procurar información que le pueda ayudar a resolver algún problema o aclararle alguna duda.

En los entornos de programación visual hay que estar muy atentos a las variantes que ofrecen una gama de ricas posibilidades que aumentan el repertorio de las cosas que se pueden hacer, variantes que no siempre están resaltadas claramente en la documentación y los manuales. Tómese por ejemplo el método Line usado para trazar una línea recta dentro de un objeto:

Objeto.Line  (X1,Y1) - (X2,Y2) ,  color

El enunciado abreviado:

Line - (X,Y)

tiene el efecto de dibujar una línea recta (de color negro, al omitirse la especificación para el color) desde el último punto de trazado hasta el punto (X2,Y2). Clics sucesivos del Mouse harán que se vaya trazando una sucesión de líneas rectas conectadas en sus extremos. Una manera de ver esto en acción consiste en abrir un proyecto nuevo, y usar para la forma su procedimiento de evento MouseDown(), el cual es invocado cada vez que el botón del Mouse es oprimido con el cursor de puntero del Mouse situado dentro de la ventana. Con el siguiente código:

  Sub Form_MouseDown (Button As Integer, Shift As Integer, X As Single, Y As Single)

   Line -(X, Y), RGB(0, 255, 0)

  End Sub

podemos obtener algo como lo siguiente:





En la etapa del diseño del Juego del Gato, dentro de la forma se fueron insertando de una en una nueve cajas de imagen CajaG de igual tamaño todas ellas, haciéndolas formar parte de un array indexado. Igual podríamos haber insertado una sola caja de imagen, la que corresponde al índice cero (0), y haber generado no en la etapa de diseño sino en tiempo de ejecución las ocho cajas restantes, haciéndolas formar parte de un array bi-dimensional 3x3. Pero esto último habría complicado la elaboración del código, y el tiempo ahorrado en la inserción manual de cada caja dentro de la forma habría tenido que ser adjudicado al tiempo requerido para la elaboración de un código más complejo, de modo tal que lo que se ahorra por un lado se tiene que invertir por el otro. Este es precisamente el tipo de decisiones que tiene que enfrentar cualquier programador cuando está elaborando aplicaciones reales, al igual que las decisiones que se tienen que tomar en la selección de algún algoritmo computacional para ciertos problemas cuando hay varios algoritmos disponibles con sus propias ventajas y desventajas.

La técnica usada arriba para crear la rejilla del Juego del Gato sugiere la manera en la cual podemos elaborar estructuras visuales para crear rejillas que se asemejan a las de una hoja de trabajo utilizada para darle entrada a una serie ordenada de datos. Supóngase que se quiere crear una forma como la siguiente para llevar el registro de los datos de los miembros de un club:




Al igual que como en el caso del Juego del Gato se recurrió a un array de cajas de imagen, para la creación de una forma se puede recurrir a un array de cajas de texto, lo cual podemos hacer ya que las cajas de texto al igual que las cajas de imagen son objetos, y en Visual Basic podemos crear un array de cualquier tipo de objetos, trátese de botones de opción, botones de chequeo, cajas de imagen, cajas de texto, en fin, lo que sea, siempre y cuando se trate de objetos.

Inspecccionando la estructura de la forma dada arriba, se puede observar que la caja de texto ubicada en la esquina superior izquierda y a la cual le asignaremos la coordenada (Nombre,1) tiene una longitud horizontal mayor que la caja de texto contigua a la cual le corresponde la coordenada (Telefono,1), esto en virtud de que en el espacio horizontal que se proporciona para darle entrada al dato identificado como “Nombre” se supone que se requiere una mayor extensión para la hilera de caracteres que corresponde al Nombre que la que se requiere para darle entrada al dato identificado como “Telefono”. De este modo, se requiere en la etapa de diseño la inserción en la forma de cuatro cajas de texto individuales para darle entrada al dato que corresponde a “Nombre”, para darle entrada al dato que corresponde a “Telefono”, para darle entrada al dato que corresponde a “Edad”, y para darle entrada al dato que corresponde a “Clave RFC”:




No se requiere mucha destreza para darse cuenta de que una vez formado el primer renglón de cajas de texto, con cada caja de texto poseyendo su propia longitud horizontal, a partir de cada una de estas cajas de texto se puede generar una columna completa de varias cajas de texto mediante el uso de arrays indexados. Así, el primer array indexado sería una columna de cajas de texto contiguas que corresponde a la columna de datos “Nombre”, el segundo array indexado sería una columna de cajas de texto contiguas que corresponde a la columna de datos “Telefono”, el tercer array indexado sería una columna de cajas de texto contiguas que corresponde a la columna de datos “Edad”, y el cuarto array indexado sería una columna de cajas de texto que corresponde a la columna de datos “Clave RFC”. Esto nos permite, mediante operaciones llevadas a cabo en arrays indexados, llevar a cabo operaciones de búsqueda, ordenar alfabéticamente de acuerdo al Nombre, ordenar de acuerdo a la edad, en fin, todo lo que es posible llevar a cabo en las hojas de trabajo, con la excepción de que esto no es una hoja de trabajo sino algo mucho más específico para cumplir con cierto propósito muy especial.

A continuación se muestra la identificación de algunos de los elementos de los cuatro arrays indexados:




Tal vez ahora el lector se pueda dar cuenta de que las celdas en las hojas de trabajo como Excel en realidad no son más que cajas de texto, generadas y acomodadas en una rejilla bi-dimensional, para ser manejadas como objetos dentro de la filosofía de la programación orientada a objetos, excepto que en las hojas de trabajo comerciales los objetos (cajas de texto) poseen una gran cantidad de propiedades y poseen también un buen número de procedimientos de eventos y métodos. Pero sigue siendo una programación orientada a objetos, optimizada con buenas prácticas de programación por ingenieros que tienen acumulada una experiencia considerable en la implementación de algoritmos así como en el manejo de estructuras de control y bucles.

En el ejemplo que se acaba de dar, resulta claro que cada renglón formado por cuatro cajas de texto es el registro (record) de un conjunto ordenado de datos, y los cuatro datos son los campos de cada registro. Esta es precisamente la situación que se presta a definir con la palabra reservada Type un nuevo tipo de dato que identifique a cada registro y los cuatro campos. Usando terminología descriptiva, podríamos especificar una construcción como la siguiente:

   Type Miembro

      Nombre              As String * 40
      Telefono As Integer
      Edad As Integer
      ClaveRFC         As String * 10

   End Type

Obsérvese que mientras que los campos Telefono y Edad son declarados como variables del tipo entero (Integer), indicando con ello que las cajas de texto respectivas darán entrada a números enteros, los campos Nombre y ClaveRFC son declarados como campos de hileras admitiendo el primero hasta un máximo de 40 caracteres y el segundo hasta un máximo de 10 caracteres, para lo cual se usa la sintaxis:

String * número

La facilidad con la cual se pueden construír programas de aplicación mediante entornos como Visual Basic basados en la programación orientada a objetos fue lo que llevó a Microsoft a tratar de trasplantar sus innovaciones a la construcción de páginas Web, creando lo que fue bautizado como VBScript para VBScript para hacerle competencia a las convenciones adoptadas por Netscape con el lenguaje JavaScript. Pero Microsoft fue más lejos, y para tratar de rivalizar con el lenguaje de programación Java, un lenguaje de programación orientado a objetos usado para la construcción de páginas Web con efectos dinámicos, Microsoft creó un entorno parecido al entorno Visual Basic recurriendo a la misma metodología. Se trata de un entorno bautizado como Visual J++, distribuído en forma gratuita al momento de su lanzamiento. Pero esto no solo creó dos variedades distintas de funcionamiento para las páginas Web dificultando la labor de los programadores que tenían que tener en cuenta la existencia de dos variedades distintas para recrear páginas Web en la computadora (requiriendo dos opciones de código), sino que dicho entorno fue basado en lo que los creadores del lenguaje Java consideraron como un plagio, lo cual llevó a una prolongada batalla legal que Microsoft perdió viéndose obligada a remover del mercado su entorno de programación visual J++, aunque negándose a remover de su propio navegador Internet Explorer el apoyo dado a VBScript, lo cual terminó causando numerosos problemas de compatibilidad que los navegadores Internet Explorer de Microsoft han seguido arrastrando hasta la fecha.