Dedicado a mis proyectos en Gambas ,un lenguaje de programación parecido al Visual Basic + Java pero ampliamente mejorado y...¡¡para gnu/linux!!.La potencia del gnu/linux sumada a la facilidad del Basic



Consultas, Desarrollo de programas y petición de presupuestos:



viernes, 4 de octubre de 2013

Juego Conecta 4: Paso a Paso (2). La Clase ClassTablero(1)

Juego Conecta 4:

 Paso a Paso (2). La Clase ClassTablero(1)


En esta entrada iré comentando las clases principales y su código.
Se va a comentar el código de la version 0.0.4 (se encuentra en la entrada http://jsbsan.blogspot.com.es/2013/09/juego-conecta4-inteligencia-artificial.html , al final de la entrada en Notas: Actualizaciones 3/10/2013).



Empezamos con la clase:
ClassTablero.
Propiedades:
El tablero lo vamos a definir como una matriz de 7x7 elementos, y usaremos un array de dos dimensiones del tipo integer, para manejarlo en el programa. Lo definimos como la propiedad "casillas":



- Como detalle importante, la propiedad se define como Integer[], pero la variable privada hcasillas hay que definirla como de dos dimensiones y con el tamaño de la matriz: New Integer[7,7]

Métodos:
El método _New: 
Es el "método constructor" y  va a ser llamado cada vez que instanciamos una nueva clase ClassTablero.


- Como veis, en la definición tiene un parámetro opcional, que nos va a permitir que el método se comporte de distinta forma si le pasamos un argumento ("t" es otro objeto del tipo ClassTablero) o si no le pasamos ningún argumento (toma valor nulo).
Si el no hay argumento pasado, "t" toma el valor Null y hacemos que todos los valores de las casillas se pongan a 0. Esto indica que la casilla esta vacía. Para recorrer las casillas una a una, usamos dos bucles For...Next.
En caso contrario, que se haya pasado argumento, lo que se hace es copiar los valores del objeto "t" a la variable interna hcasillas, (que es la que usa la propiedad casillas)
Con esto conseguimos que este método nos sirva tanto para iniciar un tablero en blanco (complentamente vacio) como que nos permita copiar un tablero que nos pasen como argumento.

El método muestra:
Incialmente este médodo lo diseñe para mostar el estado del tablero en la consola (por eso hay tantas ordenes print comentadas). Pero luego lo use para que me devolviera el array de las casillas.


El método copia:
Sirve para devolver una copia de las casillas del actual objeto., Este método esta en des huso, porque su función la puede hacer también el uso del constructor _New,



El método insertaficha:
Es el encargado de colocar la ficha en el tablero, indicándole como argumentos la columna donde hay que colocar la ficha y el jugador al que pertenece. El se encarga de comprobar cual es la primera casilla de la columna que este vacia y cuando la encuentra, asigna el valor de la casilla  al jugador.



- Es importante que veais cual es el criterio seguido para insertar una ficha. Lo podemos ver en estos esquemas:
Matriz [ i, j ]: sentido de la caida de una ficha
Matriz [i , j]: ejemplo del llenado de una columna


Como podéis ver el en código, el dato de  la columna esta en el 2º termino de la matriz. Y es el primer termino de la matriz es el que va tomando los valores empezando desde el 6 hasta llegar a 0, buscando la celda que esté vacía (cuyo valor sea 0). 
Si la encuentra vacía, cambia el valor de la celda al jugador dado, y además cambia la variable ok a True, saliendo del bucle (orden Break) y devolviendo el valor de la variable ok  (Return ok).
Como veis el "relleno" matemático de las columnas del  tablero se va haciendo de derecha a izquierda.

Nota:
Como veis el tablero esta "girado" 90º hacia la izquierda, y esto es importante saberlo, para tenerlo en cuenta a la hora de representarlo gráficamente.

¿para que se usa la variable "ok"?
Si una columna esta llena,  la variable "ok", mantiene su valor inicial (definida como False), y devolverá False, esto nos va a indicar que no ha sido posible poner una ficha en dicha columna.
Si esta función no devolviera ningún valor, no sabríamos si ha sido posible insertar la ficha en el tablero.

Nota:
Como veis el tablero esta "girado" 90º hacia la izquierda, y esto es importante saberlo, para tenerlo encuenta a la hora de representarlo gráficamente.

El método deshacer:
Deshace un movimiento (quita la ficha) sabiendo la columna donde fue insertada.


-Mediante un bucle For...Next, va recorriendo la columna de izquierda a derecha (i=0,1,2,3...6), hasta encontrar una casilla que no este vacia (<>0) , y la deja vacía (me.casillas[i,columas]=0).
Una ver hecho esto, sale del bucle y del método.


Método GameOver()
Este método se encarga de indicar si se ha terminado la partida. La partida termina cuando uno de los dos jugadores ha ganado o se ha producido tablas.


- Primero lo que hace es comprobar si hay ganador que se sea igual a a "0", comparando la salida del  método .ganador() con "0". Si es asñi hace un recorrido por el tablero, para averiguar si hay casillas vacias, si es asi , devuelve "False"  , para indicar que no hay ganador.
En el caso de que haya algun ganador (1 o -1) devuelve TRUE



El método jugadasPosibles()
Es el método que nos devuelve las jugadas que son posible.





 Esto lo hace, comprobando la última fila del tablero. Como lo tenemos girado 90º matemáticamente, es la siguiente:

Las jugadas que sean posibles, se pueden devolver con un array integer[] (de una dimensión), ya que indicaría las columnas libres, que pueden ser usadas para insertar una ficha.

En la próxima entrada veremos los métodos :

  • ganador()
  • comprueba_linea()
  • comprueba_linea_Cuadro()
  • comparaCuaternaElemento()

Saludos
 
Nota:
Diferencia entre un parámetro y un argumento:
Parámetros
  • Un parámetro representa un valor que el procedimiento espera que se transfiera cuando es llamado.  La declaración del procedimiento define sus parámetros.
  • Cuando se define un procedimiento Function o Sub, se especifica una lista de parámetros entre paréntesis que va inmediatamente después del nombre de procedimiento. 
Argumentos
  • Un argumento representa el valor que se transfiere a un parámetro del procedimiento cuando se llama al procedimiento. El código de llamada proporciona los argumentos cuando llama al procedimiento.
  • Cuando se llama al procedimiento Function o Sub, se incluye una lista de argumentos entre paréntesis que van inmediatamente después del nombre del procedimiento. Cada argumento se corresponde con el parámetro situado en la misma posición de la lista.





jueves, 3 de octubre de 2013

Relato Corto: La píldora dorada

Relato Corto:

 La píldora dorada

  • Era finales del Otoño, ya estaba cerca el día. Como en otras ocasiones, nos reuníamos los compañeros para celebrarlo. Esta vez iba a ser yo el centro de atención, mi fiesta de despedida. Después de tantos años, con tan buenos ratos pasados y otros tantos malos, todo hay que decirlo, había llegado la despedida para mi. 
  • En la fiesta vi compañeros que hacia años que no recordaba ver, charlamos y bebimos, recordando los viejos tiempos, nuestro inicios, duros y largos para "coger experiencia", quedándonos horas y horas al servicio de la empresa y muchas veces por compañerismo para ayudar a nuestro amig@ a terminar los informes de fin de mes. Fueron tiempos duros pero divertidos, cenando pizzas al final de la jornada entre papeles y facturas....luego llegaron épocas más malas, cuando veíamos que se reducía la carga de trabajo, y todos los días nos mirábamos unos a otros, a ver quien le tocaba que le llegara la carta, la carta informándole de su despido inminente. 
  • Gracias a Dios, nunca me llegó a tocar, pero a otros si les llegó la carta, apenas supe de ellos después del despido, a esos les perdí de pista hace mucho tiempo, ... pero en el fondo me alegraba que no me hubiera tocado a mi. También no había razón para tener contacto con ellos, habían dejado de ser de la "Empresa", sus problemas no eran los mismos que los míos, y no tendríamos temas de conversación en común...
  • Con el paso del tiempo, el trabajo se fue convirtiéndose en rutina, hubo tiempo con muchos trabajo y otros con menos... pero yo seguía allí... en la "Empresa". Pasaron los años, como pasan los días...
  • Llegó mi Jefe a la fiesta de despedida, me saludó cordialmente, quizás como nunca lo había hecho... llevaba una pequeña caja de terciopelo rojo.. contenía mi regalo de despedida... me lo entregó diciendo en voz alta a los presentes lo agradecida que estaba la "Empresa", por la  dedicación que había tenido durante 40 años , que era un trabajador ejemplar, que había producido muchos beneficios a la "Empresa", en definitiva  estaban muy contentos conmigo y que me echarían de menos. Terminó con un abrazo efusivo.
  • Terminó la fiesta, me despedí de los comparer@s y me fui a casa. No me esperaba nadie en ella. En los anteriores 40 años, nunca nadie me había esperado, nunca tuve tiempo de mantener alguna relación con otra persona, la dedicación de mi vida había sido para la "Empresa". Entre en el dormitorio. La habitación estaba fría como siempre,   me senté en la cama. Abrí con mucho cuidado la caja de terciopelo. En su interior brillaban dos objetos,  uno era un reloj de oro, que me coloqué en la muñeca para ver como quedaba, nunca había tenido nada igual. El otro objeto era una pequeña píldora dorada. 
  • Sonreí, la "Empresa" se había portado bien conmigo hasta el último día. La cogí, me la tomé y me tumbé en la cama. Cerré los ojos..... supe que nunca más los abriría.
  •  Gracias "Empresa".. fue mi último pensamiento.



martes, 1 de octubre de 2013

Juego Conecta4: Paso a Paso (1) Estructura del Proyecto

Juego Conecta4: Paso a Paso (1)

Estructura del Proyecto


Como dije en la primera entrada de Conecta4 ( enlace ), vamos a desmembrar el código fuente para estudiarlo y explicarlo como funciona.

En primer lugar vamos a ver la estructura del código fuente del proyecto, en la siguiente imagen:
Como veis tenemos:
A) Clases:
ClassJugador (clase "padre") y ClassJugdorHumano y ClassJugadorOrdenador "que heredan de de la clase ClassJugador"
ClassJugador: Es la clase "padre" de las que van a heredar la clase ClassJugadorHumano y ClassJugadorOrdenador.
Estas últimas clases, tiene el método "juega" que se comparta de manera muy diferente si pertenece a una u otra clase. En el caso de ClassJugadorHumano, el método "juega" va a esperar que se el jugador humano pulse sobre una columna del tablero, para realizar el movimiento (si es posible) en el tablero.
Sin embargo en la clase ClassJugadorOrdenador, el método "juega", usa la función negamax del módulo "ia", para elegir la jugada a realizar.

ClassPartida:
En la versión 0.0.3, es una simple clase que se usa para iniciar una nueva partida, usando el formulario FormInicio. En principio preveo en próximas versiones, añadirle la opción de guardar y abrir partidas.

ClassTablero:
Es la clase más importante, ya que contiene:
- Propiedades:
La propiedad "casillas": Posee la información de la situación de las fichas de los jugadores (representados por 1,-1 ó 0 si esta vacía) en el tablero,  es un array integer de 2 dimensiones (7x7).
- Métodos:
New: inicia un tablero y es aprovechado para iniciar las casillas con valor "0"
Muestra: devuelve una copia del array casillas (información de fichas y jugador al que perteneces), para que sea usado en las subrutinas de dibujar el tablero. En versiones anteriores, mostraba en la consola el tablero.
Copia: devuelve una copia del array casillas, que será usado para crear tableros auxiliares.
InsertaFicha: es la función que se usa para modificar el tablero cuando se inserta una ficha. En el caso que devuelva el valor "False", se debe a que no se puede introducir una ficha en la columna que se ha indicado, y si devuelve "True", se ha colocado  correctamente y sin problemas la ficha en el tablero.
GameOver(): Comprueba si se ha producido tablas o ha ganado alguien (con la función ganador).
ganador: Comprueba si un jugador tiene 4 fichas juntas (en fila,columna o diagonal) en el tablero
comprueba_linea: Funcione que devuelve el numero de lineas de fichas que hay en el tablero de un jugador
comprueba_linea_Cuatro(): comprueba si se han formado conjunto de 4 fichas en Filas/Columnas/o Diagonal.
comparaCuaternaElemento(): Compara elementos de una array con otro, para ver si son iguales todos o no.
jugadasPosibles(): devuelve una array de las posibles jugadas que se pueden realizar. Esto significa que devuelve las columnas que no están llenas y por lo tanto podemos insertar una ficha.


B) Formularios:
FormInicio:
Simple formulario que nos muestras los iconos de gambas y conecta4, con un botón que es el que inicia una partida.

FormJugadores:
Este formulario es usado para dar a elegir el tipo de jugadores Humano o Ordenador, y si es Ordenador, indicar el nivel de inteligencia del jugador. Devuelve estos datos elegidos por el usuario, que serán usados para pasárselos al Formulario FmainTablero, para configurar asi el nuevo juego.


FmainTablero: Es el formulario donde mostraremos los datos de la partida: el tablero propiamente dicho, las fichas, el listado de jugadas que se ha realizado e información sobre la clase de jugadores (humano/ordenador / nivel). Además es donde el jugador humano interactua con el dibujo del tablero para indicar en que columna coloca su ficha.




C) Módulos
ia:
Contiene las funciones relacionadas con la Inteligencia Artificial del programa.
- La función negamax es la encargada de analizar el árbol de movimientos y quedarse con la mejor jugada (incluso hace las podas de jugadas no necesarias de calcular), y se usa de un modo "recursivo" (se llama a si misma).
El árbol de movimiento, se genera a partir del tablero inicial, donde se ven que movimientos son posibles, se ejecutan estos movimientos en tableros auxiliares (copias del original) y se vuelve a usar la función negamax de modo recursivo para analizar el nuevo tablero. Este proceso se repite hasta llegar al final de la profundidad a analizar del árbol o se encuentre un tablero donde se termine la partida. Al final devuelve la mejor jugada que puede realizar el jugador.
- La función EvaluaJugadaMov es la encargada de hacer la valoración un tablero según el jugador que se le pase, valorando el numero de veces que se produce en el tablero conjuntos de 2 fichas, 3 fichas o 4 fichas, que estén en el tablero de forma diagonal, horizontal o vertical.

main: 
Es el módulo de inicio del programa (indicado por el triangulo negro, junto a su nombre). Crea una instancia de la clase "ClassPartida", y usa su método ".inicio", con lo cual hace arrancar una partida. Además contiene la variable "maxinteger", que va a ser usada como variable global, (tendrán acceso todos los módulos y clases del programa).

Utilidades: 
Las funciones que incluye este formulario son de tipo "publicas" y de uso común  (osea muy usadas en otras aplicaciones). Pero realmente no se ha usado ninguna de ellas en esta aplicación.

E) Datos:
En esta carpeta tenemos alojados las dos imagenes que usamos en la aplicación, el icono de gambas3 y la imagen del juego conecta4


Nota:
¿seria necesario una clase Jugada o estructura que definiera la Jugada a realizar?
En concreto en este juego de conecta 4, las jugadas simplemente representa la columnas donde podemos insertar una ficha. Básicamente se trata de una información de una dimensión (valor de las columnas). Por lo tanto, una jugada es un número y se puede usar un array integer para contener la lista de jugadas posibles.

Sin embargo en otros juegos más complicados (por ejemplo las damas), las jugadas van a contener más información (coordenada donde empieza y coordenada donde termina, incluso coordenadas intermedias para indicar "saltos"). En este caso si sería necesario usar variables más complicadas (por ejemplo arrays de Variant, o estructuras o directamente clases ), para que pudieran manejar la información del movimiento de la ficha o pieza.

En la próxima entrada, entraremos más profundamente en el código.

Saludos