Você está na página 1de 46

Solucin al Problema de MasterMind utilizando Algoritmos Genticos

Roberto Loaeza Valerio 6 de Agosto de 2007


Resumen
En este documento se presenta la implementacin de algoritmos genticos para descubrir informacin que permita jugar y ganar en el juego MasterMind. Este juego tiene un nmero mximo de jugadas (diez en este caso) antes de fallar y para generar una jugada se tienen Las acciones que pueden realizar los jugadores se listan a continuacin: Generador de cdigo (CodeMaker):

Genera una secuencia (cdigo) de cinco colores a partir del conjunto de colores originales como se muestra en la gura 2. Calica cada intento del CodeBraker en base a su cdigo y se tienen dos casos:

85

posibles se-

cuencias lo cual vuelve al juego muy complicado. Sin embargo se mostrarn resultados que muestran que al utilizar algoritmos genticos se puede ganar en un maximo de 6 jugadas.

1. Introduccin

Si el color en una posicin es el mismo en ambos (cdigo e intento) se calica con una bolita negra, estas son acumulativas, es decir si hay tres colores en la misma posicin en ambas secuencias se calica con tres bolitas negras.

MasterMind es un juego de meza para dos jugadores donde uno genera un cdigo y otro intenta descifrarlo, inventado en 1970 por Mordecai Meirowitz. El juego consta bsicamente de: Un tablero para ir jando jugadas y calicaciones como se muestra en la gura 1. Un conjunto de 8 colores con el cual se genera tanto el cdigo como el intento(jugada). Un conjunto adicional de colores para calicar, blanco y negro (su signicado se detallar ms adelante).

Si un color se encuentra en ambos (cdigo e intento) pero en diferente posicin se calica con una bolita blanca, estas son acumulativas, es decir si hay tres colores iguales en ambas secuencias pero en diferentes posiciones se calica con tres bolitas blancas.

Rompedor de cdigo (CodeBraker):

Genera una secuencia (intento) de cinco colores a partir del conjunto de colores inicial como se muestra en la gura 2, y se la da al CodeMaker para que este la calique.

El CodeBraker tiene un nmero mximo de intentos para descifrar la Un conjunto de acciones denidas para los jugadores. secuencia del CodeMaker.

Figura 2: Generador de cdigos

Teniendo en cuenta que se debe seguir patrones y se tiene un nmero mximo de intentos, una tcnica de resolverlo de forma muy ilustrativa son los algoritmos genticos aplicando los siguientes operadores geneticos: cruza, mutacin circular y transposicin. La implementacin de algoritmos genticos se realiz en Java y para una mayor portabilidad la interfaz grca de usuario (IGU) se realiz en Java + Ewe VM.

2.

Implementacin

Como la solucin esta basada algoritmos genticos es necesario contar con los siguientes mtodos: Figura 1: Tablero de MasterMind Crear Poblacin. Fitness (consistencia). Seleccionar un individuo. Calicar un individuo. Operadores Genticos (cruza, mutacin circular, transposicin, hipermutacin). La implementacin se dividio en dos paquetes: ga y masterMind; donde ga contiene las clases necesarias para implementar las funciones mencionadas anteriormente y por su parte masterMind es la interfaz grca de usuario.

Como las jugadas se generan a partir del conjunto de colores (de ocho elementos) y se tienen cinco de ellos en cada intento, existen entonces

85 (32768)

secuencias posibles para cada intento, sera muy poco viable

generar una jugada aleatoriamente y presentarsela al CodeMaker por lo que para tener xito en este juego se debe de seguir patrones basados en la respuesta que proporciona el CodeMaker. Para poder seguir un patron primero debemos tener nuestra propia funcin de evaluacin (que simular ser el CodeMaker) y a partir del primer intento cada nueva secuencia deber ser consistente con todas las anteriores para ser candidata a intento/jugada.

El cdigo fuente se lista en los apendices A y B respectivamente para ambos paquetes.

Calicacion de una jugada con respecto al cdigo secreto Se realiza en dos fases: primero se reviza por colores iguales en misma posicin (cdigo y jugada), y nalmente se reviza por colores iguales en posiciones diferentes. Fitness, es la consistencia de un individuo de la poblacin con respecto a la historia de intentos. Se calcula mediante[Merelo-Guervos]:

2.1.

Paquete ga

En este paquete se conforma por tres clases que se describen a continuacin.

2.1.1. MasterMindBase
Es el ncleo de la implementacin y cuenta con: La representacion de los colores Se hizo mediante un arrego de char: char colores[] = {'0', '1', '2', '3', '4', '5', '6', '7' } donde cada nmero representa un color especco. Longitud de la combinacion Se denio igual a cinco: static int L = 5. Crear combinacin aleatoria Esta se genera a partir del arreglo de colores y es de longitud L, la generacin es aleatoria. Cantidad de poblacin Se denio igual a 200: int nPob = 200, pero puede ser modicado mendiante las opciones de la IGU. Poblacin Se almacenan en un arreglo bidimensional: char[][] poblacion. Historia de jugadas Se almacenan en un arreglo bidimencional: char[][] historia; Numero de intentos antes de perder Se denio igual a diez: int J = 10, pero puede ser modicado mediante las opciones de la IGU. Porcentaje de mutacion (crossover, mutacion circular, transposicion) Estos porcentajes se jaron de la siguiente forma: 40, 20, 40 respectivamente. Estos pueden ser modicados mediante las opciones de la IGU.

F itness =
i=1

Abs P nb hnbi + Abs P nw hnwi

(1)

Quicksort. Algoritmo de ordenamiento, se utiliza para ordenar la poblacin de forma decreciente en base al tness.

2.1.2. MasterMindOperadoresGeneticos
MasterMindOperadoresGeneticos, implementa: Cruza Dos padres se eligen en la cruza y una porcin de cada uno se intercambia para generar dos nuevos hijos, como se muestra en la gura 3. Su cdigo se lista a continuacin. La porcin que se intercambiara es generada al azar mediante dos nmeros aleatorios. Mutacion Circular En la mutacin circular un padre se elige aleatoriamente y se intercambia un cromosoma (tambin elegido aleatoriamente) por el valor siguiente. O si ste es el ltimo valor, se sustituye por el primero correspondiente. En la gura 4 se puede apreciar este operador (donde los valores para un cromosoma son { 0, 1, 2, 3, 4, 5, 6, 7 }). Transposicion En la operacin gentica de transposicin, un padre se elige aleatoriamente y se intercambian aleatoriamente todos sus cromosomas por valores que ya contiene, es decir, slo se revuelve su cdigo. Este operador se ilustra en la gura 5.

Hipermutacion Esta operacin se realiza cuando la poblacin a pasado el nmero mximo de generaciones sin encontrar un candidato consistente y consiste en reiniciar la poblacin, es decir: la poblacin se vuelve a generar aleatoriamente.

Figura 5: Transposicin

Jugar Esta funcin se utiliz para mostrar resultados sin interfaz grca. JugarOP(Tablero, codeMaker, codeBraker) Esta funcin permite jugar MasterMind en las siguientes modalidades:

Figura 3: Cruza

CodeMaker = Humano y CodeBraker = Humano CodeMaker = Humano y CodeBraker = PC CodeMaker = PC y CodeBraker = Humano CodeMaker = PC y CodeBraker = PC

Adems de contar con la implementacin propia tambin contiene todas las implementaciones de MasterMindOperadoresGeneticos

2.2.
Figura 4: Mutacin circular

Paquete masteMind

Este paquete contiene las siguientes clases

La porcin de individuos a la que se le aplica cada uno de los operadores geneticos es heredada de MasterMindBase por lo tanto puede ser facilmente modicada mediante la IGU. Adems de contar con la implementacin propia tambin contiene todas las implementaciones de MasterMindBase

2.2.1. MasterMindGUI
Es el ncleo de la interfaz grca. Contiene un men para acceder a las opciones e iniciar juegos nuevos. Esta interfaz se puede observar en la gura 1.

2.1.3. MasterMind
Esta clase implementa las siguientes funciones:

2.2.2. GenerarCodigo
Esta clase permite interactuar al usuario para que este genere un cdigo a partir del conjunto de colores como se muestra en la gura 2.

2.2.3. Jugada
Es la interfaz para manipular el conjunto de colores y generar una secuencia.

2.2.4. Calicacin
Permite al usuario interactuar y muestra una interfaz donde el usuario puede calicar como se muestra en la gura 6.

Figura 7: Modicando valores

3.

Resultados

Figura 6: Calicando una jugada

A continuacin se muestran algunas pruebas realizadas a la implementacin. La conguracin utilizada se muestra en la tabla 1 y los resultados obtenidos en 500 corridas se muestra en la tabla 2.

2.2.5. Calicador
Es la interfaz para manipular el conjunto de colores (negro y blanco) para generar una calicacin.

Poblacin Nmero mximo de Intentos Nmero de generaciones antes de hipermutacin Porcentaje de poblacin que recibir el operador de cruza Porcentaje de poblacin que recibir el operador de transposicin Porcentaje de poblacin que recibir el operador de mutacin circular Nmero mximo de hipermutaciones

200 10 15 40 40 20 200

2.2.6. Opciones
Permmite al usuario modicar los valores con los que trabajara la aplicacin. Como se muestra en la gura 7.

2.2.7. Tablero
Implementa un tablero para jar las jugadas y calicaciones. Esta interfaz se puede observar en la gura 1.

Tabla 1: Conguracin de Prueba

Mejor Tiempo de un Juego Peor Tiempo de un Juego Mejor Jugada (intentos para ganar) Peor Jugada (intentos para ganar) Promedio de Juegos ganados Tiempo total Promedio de jugadas (intentos para ganar)

1 milisengundo 819 milisegundos 3 9 100 % de 500 juegos 20824 milisegundos 5.8

Tabla 2: Resultados de Prueba

4.

Conclusiones
Se realiz satisfactoriamente la implementacin de algoritmos genticos para resolver el juego de MasterMind. Se observ que el potencial de los algoritmos genticos es enorme, ya que de tener

85 (32768)

posibles secuencias por jugada y un mximo

de 10 jugadas, se logra ganar al 100 % en 5.8 jugadas WoW!!. La implementacin adicional de Ewe le da un nuevo alcance a las aplicaciones java, ya que se puede ejecutar en casi cualquier dispositivo computacional.

Referencias
[wiki] http://es.wikipedia.org/wiki/Mastermind.

[Merelo-Guervos] J. J. Merelo-Guervs, P. Castillo y V. M. Rivas. Findind a needle in a haystack using hints and evolutionary computation: the case of evolutionary MasterMind. Elsevier, pags. 170-179. 2006.

APENDICE A: Paquete ga

ga.MasterMindBase.java
package ga; import ewe.util.Random; /** * MasterMindBase, contiene: * <br>** La representacion de los colores * <br>** Longitud de la combinacion * <br>** Historia de jugadas * <br>** Numero de intentos antes de perder * <br>** Cantidad de poblacion * <br>** Porcentaje de mutacion (crossover, mutacion circular, transposicion) * <br>** Calificacion de una jugada con respecto al codigo secreto * <br>** Crear combinacion aleatoria * @author Roberto Loaeza Valerio * */ public class MasterMindBase { /** * Nombre de colores */ char colores[] = {'0', '1', '2', '3', '4', '5', '6', '7'}; /** * Codigo Secreto */ private char[] codigo; /** * Cantidad de colores disponibles */ private int N = 8; /** * Caracteres adicionales al codigo: D, W, B. */
7

private int cAux = 3; /** * Longitud de combinacion */ public static int L = 5; /** * Cantidad de poblacion */ public int nPob = 400; /** * Maximo numero de jugadas */ public int J = 10; /** * Numero de generaciones sin tener una combinacion consistente antes de hacer Hipermutacion */ public int nH = 15; /** * Porcentaje de cruza */ public int nC =40; /** * Porcentaje de transposicion */ public int nT = 20; /** * Porcentaje de mutacion circular */ public int nM =40; /** * Numero maximo de hipermutaciones */ public int nR = 200; /** * Jugadas Realizadas */
8

public int nJ=0; /** * Historia de jugadas realizadas */ public char[][] historia; /** * Poblacion */ private static char[][] poblacion; /** * Aleatorio */ public Random r = new Random(); /** * Crea una combinacion * @return Nueva combinacion */ private char[] crearCombinacion() { char[] ret = new char[L+cAux]; for(int i=0; i<(L+cAux); i++) ret[i]=i<L?colores[r.nextInt(N)]:ret[0]; } return ret;

/** * Es contenido un caracter C en un arreglo S, entre la posicion 0 y N * @param c Caracter * @param S Arreglo de caracteres * @param n Ultima posicion a buscar * @return True si C es contenido en S, en caso contrario False */ public boolean contiene(char c, char[] S, int n) { int i=0; while(i<=n)
9

} /** * Crea una combinacion sin colores repetidos * @return Combinacion sin colores repetidos */ public char[] crearCombinacionUniq(){ char[] ret = new char[L+cAux]; int i=0; while(i<L) { ret[i]=colores[r.nextInt(N)]; i+=existeEn(ret, ret[i], i)?0:1; } ret[i++]=ret[0]; ret[i++]=ret[0]; ret[i++]=ret[0]; return ret; /** * Obtiene la distancia entre la jugada y el codigo secreto * @param i indice de la combinacion * @return distancia entre la jugada y el codigo secreto */ private int getFitness(int i) { return poblacion[i][L+0]-48; } /** * Obtiene la cantidad de blancas (color existente pero fuera de su lugar) * @param c Combinacion * @return Cantidad de blancas */ public int getW(char[] c) { return c[L+1]-48; }
10

if(c==S[i++]) return true; return false;

/** * Obtiene la cantidad de negras (color existente en su lugar) * @param c Combinacion * @return Cantidad de negras */ public int getB(char c[]) { return c[L+2]-48; } /** * Fija la distancia entre la combinacion y el codigo * @param i Indice de la combinacion * @param w Distancia entre la combinacion y el codigo */ public void setFitness(int i, int w) { poblacion[i][L+0] = (char)(48+w); } /** * Fija la cantidad de blancas a una combinacion * @param i Indice de la combinacion * @param w Cantidad de blancas */ public void setW(int i, int w) { poblacion[i][L+1] = (char)(48+w); } /** * Fija la cantidad de negras a una combinacion * @param i Indice de la combinacion * @param w Cantidad de negras */ public void setB(int i, int w) { poblacion[i][L+2] = (char)(48+w); } /** * Clona un arreglo de tipo char * @param A Arreglo fuente * @return Clon de A */
11

public char[] clone(char[] A) { char[] ret =new char[A.length]; for(int i=0; i<A.length;i++) ret[i] = A[i]; return ret; } /** * Crea una poblacion entera * @param cantidad Cantidad de la poblacion */ public void crearPoblacion(int cantidad) { poblacion = new char[cantidad][]; for(int i=0; i<cantidad; i++) { poblacion[i] = crearCombinacionUniq(); } } /** * Imprime la poblacion * */ public void imprimirPoblacion() { for(int i=0; i<poblacion.length; i++) { System.out.println(i+" -> "+String.valueOf(poblacion[i])); } } /** * Crea el codigo secreto * */ public void crearCodigo(){ codigo = crearCombinacion(); } /** * Obtiene el codigo secreto * @return codigo secreto
12

*/ public char[] getCodigo() { return codigo; } /** * Genera un codigo secreto (manual) * @param nuevoCodigo Codigo secreto */ public void setCodigo(char[] nuevoCodigo) { codigo = nuevoCodigo; //System.out.println(historia[0]); //D(codigo, 0); //historia[0] = getIndividuo(0); //System.out.println(historia[0]); } /** * Obtiene un individuo de la poblacion * @param i Indice de la poblacion * @return Individuo i */ public static char[] getIndividuo(int i) { return poblacion[i]; } /** * Fija un individuo en la poblacion * @param i Indice de la poblacion * @param nuevoIndividuo Nuevo Individuo para el indice I */ public void setIndividuo(int i, char[] nuevoIndividuo) { poblacion[i] = nuevoIndividuo; } /** * Agrega una combinacion al historial de jugadas * @param combinacion combinacion a agregar * @return true si aun no se ha llegado al maximo de jugadas, caso contrario false */
13

public boolean agregarCombinacionHistoria(char [] combinacion) { if(nJ<J) { historia[nJ++] = clone(combinacion); return true; } else return false; } /** * Obtiene la ultima combinacion agregada a la historia * @return combinacion */ public char[] sacarUltimaCombinacionHistoria() { return historia[nJ-1]; } /** * Obtiene la historia de jugadas * @param i Indice de la jugada * @return Combinacion Jugada */ public char[] getHistoria(int i) { return historia[i]; } /** * Crea una instancia de MasterMindBase * @param np # de poblacion * @param nj # maximo de jugadas * @param nh # de generaciones antes de hipermutacion * @param nc % de cruza * @param nt % de transposicion * @param nm % de mutacion circular * @param nr # de hipermutaciones antes de game over */ public MasterMindBase(int np, int nj, int nh, int nc, int nt, int nm, int nr) { nPob = np; J = nj;
14

nH nC nT nM nR

= = = = =

nh; nc; nt; nm; nr;

crearCodigo(); crearPoblacion(nPob); historia= new char[J][]; D(getCodigo(), 0); agregarCombinacionHistoria(getIndividuo(0));

/** * Crea una instancia de MasterMindBase * */ public MasterMindBase() { crearCodigo(); crearPoblacion(nPob); historia= new char[J][]; D(getCodigo(), 0); agregarCombinacionHistoria(getIndividuo(0)); } /** * QuickSort * @param izq izquierda * @param der derecha */ public void qsm( int izq, int der) { int i = izq; int j = der; int pivote = getFitness( (izq + der) / 2); do { while (getFitness(i) < pivote) { i++; }
15

while (getFitness(j) > pivote) { j--; } if (i <= j) { char[] t = getIndividuo(i); setIndividuo(i, getIndividuo(j)); setIndividuo(j, t); i++; j--;

} } while (i <= j); if (izq < j) { qsm(izq, j); } if (i < der) { qsm(i, der); }

/** * Ordenamiento usando quicksort * */ public void sort(){ qsm(0,nPob-1); } /** * Ordenamiento usando burbuja * */ public void sort2() { for(int i = 0; i< nPob-1; i++ ) { for(int j= i+1; j<nPob; j++) { if(getFitness(i)>getFitness(j)) { char[] t = getIndividuo(i);
16

setIndividuo(i, getIndividuo(j)); setIndividuo(j, t);

/** * Elimina y cuenta las posiciones repetidas de una combinacion comparada con otra * @param A Combinacion a comparar * @param B Combinacion Principal * @return Combinacion sin colores en comun (en la misma posicion y el mismo color) */ private char[] D2(char[] A, char[] B) { char[] ret = new char[A.length]; for(int i=0; i<L; i++) { if(A[i]==B[i]) { ret[i] = ' '; B[L+1]++; } else ret[i]=A[i]; } return ret; } /** * Cuenta el numero de colores existenes pero fuera de su posicion * @param A Combinacion sin colores en su lugar * @param B Combinacion Principal */ private void D3(char[] A, char[] B) { for(int i=0; i<L; i++) { if(A[i]!=' ') { for(int j=0; j<L; j++) { if(B[i]==A[j]) { A[j] = '*'; B[L+2]++;
17

break;

/** * Calcula la cantidad de Blancas y/o Negras entre dos combinaciones * @param pi Combinacion Nueva * @param pj Combinacion de la poblacion */ public void D(char[] pi, int pj) { setB(pj, 0); setW(pj, 0); D3(D2(pi, getIndividuo(pj)), getIndividuo(pj)); } /** * Imprime la pila de jugadas (historia) * */ public void impHistoria() { for(int i=0; i<nJ; i++) System.out.println(i+" -> "+String.valueOf(getHistoria(i)).substring(0,5) ); } /** * Valor absoluto de un numero entero * @param a Numero * @return Absoluto de a */ private int abs(int a) { return a<0?(a*(-1)):a; } /** * Calcula y fija el fitness (distancia) entre la nueva generacion y la historia * @return True si existe una combinacion que cumpla con la consistencia, en caso contrario false */ public boolean fitness() {
18

} /** * Existe un caracter entre 0 y n de un arreglo * @param source Arreglo fuente * @param f Caracter buscado * @param n Limite de la busqueda * @return True si existe el caracter en la fuente, en caso contrario false */ public static boolean existeEn(char[] source, char f, int n) { for(int i=0; i<n; i++) if(source[i]==f) return true; return false; } /** * Test de MasterMind * @param args */ public static void main(String[] args) { long t1 = new java.util.Date().getTime(); long t2 = 0; long t3 =0; MasterMind g;
19

int suma; boolean ret = false; for(int i=0; i<nPob; i++ ) { suma =0; for(int j=0; j<nJ; j++) { D(getHistoria(j), i); suma += abs( getW(getHistoria(j)) - getW(getIndividuo(i)) )+ abs( getB(getHistoria(j)) - getB(getIndividuo(i)) ); } setFitness(i, suma); if(suma==0) ret = true; } return ret;

double n=0; double k=0; long mejorTiempo = 0; long peorTiempo = 0; int peorJugada = 0; int mejorJugada=0; for(int i=0; i<500; i++) { g = new MasterMind(400, 10, 15, 40, 40, 20, 200); t2 = new java.util.Date().getTime(); if(g.jugar()) { //System.out.println(i+" Ganaste\n\t" + String.valueOf(g.getCodigo()).substring(0,L) +"\n\t"+String.valu n+=g.nJ; k++; if(i==0) { mejorJugada= peorJugada= g.nJ; } else { if(mejorJugada>(g.nJ)) mejorJugada = g.nJ; if(peorJugada<(g.nJ)) peorJugada=g.nJ; } } else { // System.out.println(i+" Perdiste"); } t3 = new java.util.Date().getTime(); if(i==0) { mejorTiempo = peorTiempo = t3-t2; } else { if(mejorTiempo>(t3-t2)) mejorTiempo = t3-t2; if(peorTiempo<(t3-t2)) peorTiempo=t3-t2;
20

} } System.out.println("Cantidad Ganada = "+k); System.out.println("Promedio de ganar = "+(n/k)); System.out.println("\nTiempo transcurrido ="+(new java.util.Date().getTime()-t1) ); System.out.println("Peor Tiempo\t= "+peorTiempo); System.out.println("Mejor Tiempo\t= "+mejorTiempo); System.out.println("Peor Jugada\t= "+peorJugada); System.out.println("Mejor Jugada\t= "+mejorJugada); } }

21

ga.MasterMindOperadoresGeneticos.java
package ga; /** * MasterMindOperadoresGeneticos, implementa: <br> * 1.- Cruza <br> * 2.- Mutacion Circular <br> * 3.- Transposicion <br> * 4.- Hipermutacion <br> * @author Roberto Loaeza Valerio * */ public class MasterMindOperadoresGeneticos extends MasterMindBase{ /** * Devuelve el siguiente color * @param color Color actual * @return Colo siguiente */ private char sigColor(char color) { return color==colores[colores.length-1]?'0':(char)(color+1); } /** * Mutacion Circular * @param individuo Id del individuo * @return El mismo individuo con un cromosoma mutado */ public char[] mutacionCircular(int individuo) { char[] ret = clone(getIndividuo(individuo)); int x = r.nextInt(L); ret[x] = sigColor(ret[x]); return ret; } /** * Intercambia P[a] por P[b] * @param P Arreglo P
22

* @param a posicion a * @param b posicion b */ private void swap(char[] P, int a, int b) { char t = P[a]; P[a] = P[b]; P[b] = t; } /** * Tranposicion * @param individuo Id del individuo * @return Un nuevo individuo con su contenido transpuesto */ public char[] transposicion(int individuo) { char[] ret = clone(getIndividuo(individuo)); for(int i=0; i<L; i++) swap(ret, i, r.nextInt(L)); return ret; } /** * CrossOver A con B * @param A Padre A * @param B Padre B * @param pi inicio del rango * @param pj fin del rango * @return Un hijo con [A0, A1, ... Api] + [Api+1, Api+2, ... Apj] + [Apj+1, Apj+2, ... An] */ private char[] crossOver(char[] A, char[] B, int pi, int pj) { char[] ret = new char[A.length]; for(int i=0; i<ret.length; i++) ret[i] = ( (i>=pi)&&(i<=pj))?B[i]:A[i]; return ret; } /**
23

* Cruza dos padres * @param A Padre A * @param B Padre B * @return 2 Hijos con caracteristicas de sus padres */ public char[][] cruza(int A, int B) { int r1 = r.nextInt(L/2); int r2 = (L/2)+r.nextInt(L/2); r2= (r1+r2)==L?r2-1:r2; return new char[][] {crossOver(getIndividuo(A), getIndividuo(B), r1, r2), crossOver(getIndividuo(B), getIndividuo(A), r1, r2)}; } /** * Crea una nueva poblacion (elimina la actual) */ public void hiperMutacion() { crearPoblacion(nPob); } /** * Muta la mitad de la poblacion con los porcentajes definidos en las variables * nC(crossover), nT(transposicion), nM(mutacion circular). */ public void Mutar() { int n = (nPob/2); double[] prob = { nC/100, /* crossover*/ nT/100, /* transposition */ nM/100 /* circular mutation */ }; char r[][] = new char[2][]; for(int i=0; i<n;i++ ) { if(i<(prob[0]*n)) { r = cruza(i, i+1); setIndividuo(nPob-i-1, r[0]);
24

setIndividuo(nPob-(++i)-1, r[1]); } else if(i<(prob[0]*n)+(prob[1]*n)) setIndividuo(nPob-i-1, transposicion(i)); else setIndividuo(nPob-i-1, mutacionCircular(i));

} }

public MasterMindOperadoresGeneticos() { super(); } /** * Crea una instancia de MasterMindOperadoresGenticos * @param np # de poblacion * @param nj # maximo de jugadas * @param nh # de generaciones antes de hipermutacion * @param nc % de cruza * @param nt % de transposicion * @param nm % de mutacion circular * @param nr # de hipermutaciones antes de game over */ public MasterMindOperadoresGeneticos(int np, int nj, int nh, int nc, int nt, int nm, int nr) { super(np, nj, nh, nc, nt, nm, nr); } }

25

ga.MasterMind
package ga; import ewe.ui.Control; /** * MasterMind, con N = 8 y L = 5. * @author Roberto Loaeza Valerio * */ public class MasterMind extends MasterMindOperadoresGeneticos{ /** * Si el codeMaker es humano y no califica bien, cheater es True en caso contrario false. */ public boolean cheater=false; /** * Juega MasterMind con modalidades <br> * codeMaker = PC codeBraker = PC <br> * codeMaker = PC codeBraker = Humano <br> * codeMaker = Humano codeBraker = PC <br> * codeMaker = Humano codeBraker = Humano <br> * @param t Tablero donde se mostraran las jugadas/calificaciones * @param codeMaker 0 = PC, 1 = Humano * @param codeBraker 0 = PC, 1 = Humano * @return True si se ha logrado ganar, en caso contrario False */ public boolean jugarOP(Tablero t, int codeMaker, int codeBraker ) { int i=0, j=0; int k =0; boolean win = false; GenerarCodigo g2; if((codeBraker==0)&&(codeMaker==0)) { t.addBalls(getHistoria(0), 0, 5); t.addMiniBalls(new int[] { getB(getHistoria(0)), getW(getHistoria(0))}, 0); t.refresh();
26

} else {

nJ--; } while((k<J)&&(i<nR)) { if(j==nH) { hiperMutacion(); j=0; i++; } if(codeBraker==0) { if(fitness()) { if(codeMaker==0) { sort(); D(getCodigo(), 0); } else { if(!codeMakerCalif(null)) break; } agregarJugada(t); k++; j=0; i=0;

} else {

} if(this.getW(this.sacarUltimaCombinacionHistoria())==(L)) { win = true; break; }else { j++; sort(); Mutar(); } g2 = new GenerarCodigo();
27

if( ShowMB(g2, "Nueva Combinacion", 240, 130)==MessageBox.IDCANCEL) { break; } if(codeMaker==0) { setIndividuo(0, g2.getCodigo()); D(getCodigo(), 0); } else { if(!codeMakerCalif(g2.getCodigo())) break; } agregarJugada(t); k++; if(this.getW(this.sacarUltimaCombinacionHistoria())==(L)) { win = true; break; }

} }

if(codeMaker==1) cheater = isCheater(); return win;

/** * Agrega la siguiente jugada al tablero * @param t Tablero */ private void agregarJugada(Tablero t) { agregarCombinacionHistoria(getIndividuo(0)); t.addBalls(getHistoria(nJ-1), nJ-1, 5); t.addMiniBalls(new int[] { getB(getHistoria(nJ-1)), getW(getHistoria(nJ-1))}, nJ-1); t.refresh();
28

} /** * Crea una instancia para calificar un codigo * @param codigo Codigo a calificar * @return True si fue calificado, en caso contrario False */ private boolean codeMakerCalif(char[] codigo) { Calificador g3; if(codigo!=null) setIndividuo(0, codigo); g3 = new Calificador(getIndividuo(0)); if(ShowMB(g3, "Calificacion", 240, 180)==MessageBox.IDCANCEL) { return false; } setB(0, g3.getB()); setW(0, g3.getW()); return true; } /** * Solicita el codigo secreto y comprueba si el calificador hizo trampa * @return True para trampa, en caso contrario False */ private boolean isCheater() { GenerarCodigo g2= new GenerarCodigo(); if(ShowMB(g2 , "Codigo Secreto", 240, 130)!=MessageBox.IDCANCEL) { setCodigo(g2.getCodigo()); for(int i=0; i< nJ; i++) { setIndividuo(0,clone(getHistoria(i))); D(getCodigo(), 0); if((getB(getIndividuo(0))!=getB(getHistoria(i)))||(getW(getIndividuo(0))!=getW(getHistoria(i))) ) { return true; } } } return false;
29

} /** * Test * @return True si se ha logrado ganar, en caso contrario False */ public boolean jugar() { int i=0, j=0; int k =0; boolean win = false; while((k<J)&&(i<nR)) { if(j==nH) { //this.crearPoblacion(nPob); hiperMutacion(); j=0; i++; } if(fitness()) { sort(); D(getCodigo(), 0); agregarCombinacionHistoria(getIndividuo(0)); k++; j=0; i=0; } if(this.getW(this.sacarUltimaCombinacionHistoria())==(L)) { win = true; break; }else { j++; sort(); Mutar(); }

} return win;

30

public MasterMind() { super(); } /** * Crea una instancia de MasterMindBase * @param np # de poblacion * @param nj # maximo de jugadas * @param nh # de generaciones antes de hipermutacion * @param nc % de cruza * @param nt % de transposicion * @param nm % de mutacion circular * @param nr # de hipermutaciones antes de game over */ public MasterMind(int np, int nj, int nh, int nc, int nt, int nm, int nr) { super(np, nj, nh, nc, nt, nm, nr); } /** * Muestra un MessageBox con contenido ob * @param ob Objeto a mostrar en el MessageBox * @param titulo Titulo del MessageBox * @param ancho Ancho del MessageBox * @param alto Alto del MessageBox * @return MBOK o MBCANCEL */ private int ShowMB(Control ob, String titulo, int ancho, int alto) { MessageBox mb = new MessageBox(titulo, "",MessageBox.MBOKCANCEL); mb.doBeep=false; mb.addLast(ob); mb.setPreferredSize(ancho, alto); return mb.execute(); }

31

APENDICE B: Paquete masterMind

masterMind.MasterMindGUI.java
package masterMind; import ewe.fx.Color; public class MasterMindGUI extends Form{ char[] colores = {'0', '1', '2', '3', '4', '5', '6', '7'}; private MasterMind g; private boolean win; private SingleContainer contenedor = new SingleContainer(); // 470 70 40 // 300 70 25 private int ancho = 230, alto = 470, n =(alto-70)/40; private int np=400, nj=10, nh=15, nc=40, nt=40, nm=20, nr= 200, codeMaker = 0, codeBraker = 0; private HtmlDisplay hd = new HtmlDisplay(12,20); Dimension dimensionTablero; private Tablero tablero = new Tablero(alto-30, n); public void nuevo() { g = new MasterMind(np, nj, nh, nc, nt, nm, nr); win =g.jugarOP(tablero, codeMaker, codeBraker); tablero.addBalls(g.getCodigo(), n, 5); tablero.refresh(); String historial = win?"<h1 align=\"center\"> <font color=\"#0099FF\"> YaY ....</font></h1>":"<h1 align=\"center\"> <font color=\"#0099FF\">Buu ....</font></h1>"; if(g.cheater) historial +="<h1 align=\"center\"><br> Cheater!!! <br></h1>"; historial +="<h2> Codigo secreto = "+String.valueOf(g.getCodigo()).substring(0, 5)+"</h2>"; for(int i=0; i<g.nJ;i++) {
32

historial +="<br> "+i+" -> "+String.valueOf(g.getHistoria(i)).substring(0, 5); historial +=" ["+g.getB(g.getHistoria(i))+", "+g.getW(g.getHistoria(i))+" ]";

hd.setHtml(historial); } public MasterMindGUI () { setPreferredSize(ancho, alto); title = "MasterMind"; addLast(LoadMenus()).setCell(VCONTRACT); contenedor.setControl(new Tablero(alto-30, n)); addLast(contenedor);

} private MenuBar LoadMenus() { MenuBar mb = new MenuBar(); Menu m = new Menu(); MenuItem mSeparator =new MenuItem(); mSeparator.modifiers = MenuItem.Separator; m.addItem(new MenuItem("Nuevo ", "imgs/iconos/Nuevo.png", Color.White)); m.addItem(mSeparator); m.addItem(new MenuItem("Salir ", "imgs/iconos/Salir.png", Color.White)); mb.addMenu(m, "Archivo"); m= new Menu(); m.addItem(new MenuItem("Ver Historial ", "imgs/iconos/opciones.png", Color.White)); m.addItem(new MenuItem("Opciones ", "imgs/iconos/opciones.png", Color.White)); mb.addMenu(m, "Herramientas"); m= new Menu(); m.addItem(new MenuItem("Acerca de ", "imgs/iconos/AcercaDe.png", Color.White)); m.addItem(new MenuItem("Tips ", "imgs/iconos/Tips.png", Color.White)); mb.addMenu(m, "Ayuda"); return mb;

} public boolean handleAction(String action) { if(action.equals("Salir ")) {

33

exit(1); } else if(action.equals("Ver Historial ")) { msgHistorial(); } else if(action.equals("Opciones ")) { Opciones op = new Opciones(np, nj, nh, nc, nt, nm, nr, codeMaker, codeBraker); msgAyuda(op, "Opciones"); np = Integer.valueOf(op.nPoblacion.getText()); nj = Integer.valueOf(op.nJugadas.getText()); nh = Integer.valueOf(op.nHipermutacion.getText()); nc = Integer.valueOf(op.nCruza.getText()); nt= Integer.valueOf(op.nTransposicion.getText()); nm= Integer.valueOf(op.nMutacionCircular.getText()); nr= Integer.valueOf(op.nReset.getText()); codeMaker = op.codeMaker.selectedIndex; codeBraker = op.codeBraker.selectedIndex; } else if(action.equals("Nuevo ")) { tablero =new Tablero(alto-30, n); contenedor.setControl(tablero, true); nuevo(); msgHistorial(); } else if(action.equals("Acerca de ")) { HtmlDisplay hd = new HtmlDisplay(12,20); hd.setHtml( "<h1 align=\"center\"><font color=\"#0099FF\">MasterMind</font></h1><br>"+ "Fue escrito por <b>Roberto Loaeza Valerio</b> (2007)"+ ", sin embargo el software siempre puede ser mejorado, para esto usted (el usuario) "+ "debe informar cuando algo no funcione como debiera o cuando pueda ser mejorado "+ "enviando un reporte."); hd.modify(DisplayOnly,0); msgAyuda(hd, "Acerca de");
34

} else if(action.equals("Tips ")) { HtmlDisplay hd = new HtmlDisplay(12,20); hd.setHtml( "<h1 align=\"center\"><font color=\"#0099FF\">mMasterMind</font></h1><br>"+ "En proceso..... "+ "<br><br><h1 align=\"right\">Roberto Loaeza Valerio(2007)</h1>"); hd.modify(DisplayOnly,0); msgAyuda(hd, "Acerca de"); } return super.handleAction(action);

private void msgHistorial() { hd.modify(DisplayOnly,0); msgAyuda(new HtmlViewer(hd, HtmlViewer.DISPLAY_NO_STATUS_BAR|HtmlViewer.DISPLAY_NO_TOOL_BUTTONS|HtmlViewer.DISPLAY_NO_TA } private void msgAyuda(Control ob, String titulo) { MessageBox mb = new MessageBox(titulo, "",MessageBox.MBOK); mb.doBeep=false; mb.addLast(ob); mb.execute(getFrame(), Gui.CENTER_FRAME); }

masterMind.Calicacion.java
package masterMind; import ewe.fx.Color; public class Calificacion extends InteractivePanel{ final int incy = 40; final int incx = 35;
35

private int n = 5, m= 2; public char[] codigo= {'2', '2', '2', '2', '2'}; AniImage[] b = new AniImage[n]; AniImage[] c = new AniImage[m]; void crearColores() { Image cols =null; for(int i=0;i<m;i++) { switch(i) { case 0: cols =new Image("imgs/negrag.png"); case 1: cols =new Image("imgs/blancag.png"); case 2: cols =new Image("imgs/vacia2.png"); } cols.transparent = Color.White; c[i] = new AniImage(cols); c[i].properties |= AniImage.IsMoveable; } } public void addCodeGuess(char[] guess) { Image cols ; AniImage ai; for(int i=0; i<5; i++) { cols =new Image("imgs/"+guess[i]+".png"); cols.transparent = Color.White; ai = new AniImage(cols); ai.location.x = 15+(43*i); ai.location.y = 15; addImage(ai); } refresh(); }

break; break;

break;

36

public Calificacion(char[] code) { backGround= ewe.fx.Color.White; borderWidth = 2; borderStyle = Canvas.EDGE_SUNKEN; addCodeGuess(code); touching = new ImageList(); for(int i=0; i<n; i++) { b[i] = new AniImage(new mImage("imgs/vacia2.png",Color.White)); b[i].move(15+(i*44), 50); addImage(b[i]); touching.add(b[i]); } crearColores(); for(int i=0; i<m; i++){ c[i].move(15+(i*25), 100); addImage(c[i]); } } public int cual(AniImage A) { for(int i=0;i<m; i++) if(A==c[i]) return i; return 0; } public void droppedOn(ImageDragContext dc) { int c = cual(dc.image); for(int i=0; i<n; i++) { if (dc.draggingOver == b[i]){ b[i].change(dc.image);
37

} } }

dc.image.move(15+(25*c), 100); codigo[i] = (char)(48+c); b[i].refresh();

38

masterMind.Calicador.java
package masterMind; import ewe.ui.Form; public class Calificador extends Form{ Calificacion t ; public Calificador(char[] guess) { setPreferredSize(290, 290); t = new Calificacion(guess); addLast(t); } public int getB () { int b=0; for(int i=0; i< t.codigo.length; i++) if(t.codigo[i]=='1') b++; return b; } public int getW () { int b=0; for(int i=0; i< t.codigo.length; i++) if(t.codigo[i]=='0') b++; return b; } public char[] getCodigo() { return t.codigo; }

39

masterMind.GeneradorCodigo.java
package masterMind; import ewe.ui.Form; public class GenerarCodigo extends Form{

Jugada t = new Jugada(); public GenerarCodigo () { setPreferredSize(230, 90); addLast(t); } public char[] getCodigo() { return (String.valueOf(t.codigo)+"000").toCharArray(); } }

masterMind.Jugada.java
package masterMind; import ewe.fx.Color; public class Jugada extends InteractivePanel{ final int incy = 40; final int incx = 35; private int n =5, m =8; public char[] codigo= {'0', '0', '0', '0', '0'}; AniImage[] b = new AniImage[n]; AniImage[] c = new AniImage[m]; void crearColores() { Image cols ; for(int i=0; i<m;i++) {
40

cols =new Image("imgs/"+i+".png"); cols.transparent = Color.White; c[i] = new AniImage(cols); c[i].properties |= AniImage.IsMoveable;

public Jugada( ) { backGround= ewe.fx.Color.White; borderWidth = 2; borderStyle = Canvas.EDGE_SUNKEN; touching = new ImageList(); for(int i=0;i<n; i++) { b[i] = new AniImage(new mImage("imgs/vacia2.png",Color.White)); b[i].move(15+(i*44), 20); addImage(b[i]); touching.add(b[i]); } crearColores(); for(int i=0; i<m; i++ ) { c[i].move(15+(i*25), 50); addImage(c[i]); } } public int cual(AniImage A) { for(int i=0; i<m; i++) if(A==c[i]) return i; return 0; }

41

public void droppedOn(ImageDragContext dc) { int c = cual(dc.image); for(int i=0; i<n; i++) { if (dc.draggingOver == b[i]){ b[i].change(dc.image); dc.image.move(15+(25*c), 50); codigo[i] = (char)(48+c); b[i].refresh(); break; } } }

42

masterMind.Opciones.java
* Elaborado por: Roberto Loaeza Valerio. package masterMind; import ewe.ui.CellConstants; /** * Datos generales del proyecto * @author Roberto Loaeza Valerio * */ public class Opciones extends Editor { public mInput nJugadas, nPoblacion, nHipermutacion, nCruza, nTransposicion, nMutacionCircular, nReset; public mChoice codeMaker, codeBraker; public Opciones(int np, int nj, int nh, int nc, int nt, int nm, int nr, int cMaker, int cBraker) { InputStack is = new InputStack(); is.inputLength = 5; nPoblacion = new mInput(String.valueOf(np)); nJugadas = new mInput(String.valueOf(nj)); nHipermutacion = new mInput(String.valueOf(nh)); nCruza = new mInput(String.valueOf(nc)); nTransposicion = new mInput(String.valueOf(nt)); nMutacionCircular = new mInput(String.valueOf(nm)); nReset = new mInput(String.valueOf(nr)); codeMaker = new mChoice("PC|Humano", cMaker); codeBraker= new mChoice("PC|Humano", cBraker); is.add(codeMaker, "CodeMaker:"); is.add(codeBraker, "CodeBraker:"); is.add(nPoblacion, "Poblacion:"); is.add(nJugadas, "Maximo de jugadas:"); is.add(nReset, "N de reinicios:"); is.add(nHipermutacion, "G. antes de HiperM:");
43

is.add(nCruza, "% Cruza:"); is.add(nTransposicion, "% Transposicion:"); is.add(nMutacionCircular, "% Mutacion Circular:"); addLast(is,CellConstants.HSTRETCH,CellConstants.FILL); }

44

masterMind.Tablero.java
package masterMind; import ewe.fx.Color; public class Tablero extends InteractivePanel{ // 40 // 26 final int incy = 40; // 35 // 28 final int incx = 35; private int alto; public void addBalls(char[] c, int l, int n) { Image cols ; for(int i=0; i<n; i++) { cols =new Image("imgs/"+c[i]+"_.png"); cols.transparent = Color.White; AniImage ai = new AniImage(cols); ai.location.x = 10+(incx*i); ai.location.y = alto-incy*l; addImage(ai); } refresh(); } public void addMiniBalls(int[] bw, int l) { Image cols ; for(int i=0; i<bw[0]+bw[1]; i++) { cols =i<bw[0]?new Image("imgs/blanca.png"):new Image("imgs/negra.png"); cols.transparent = Color.White; AniImage ai = new AniImage(cols); // 180 20 180 20 // 150 15 150 15 /* */
45

} refresh();

ai.location.x = i<3?180+((incx-20)*i):180+((incx-20)*(i-3)); ai.location.y = i<3?alto+2-incy*l:alto+15-incy*l; addImage(ai);

void addHoles(int n, int l) { AniImage hole; for(int i=0; i<n; i++) { hole = new AniImage(new mImage("imgs/vacia.png",Color.White)); hole.move(10+(incx*i),alto-incy*l); addImage(hole); } refresh(); } public Tablero( int alto, int n) { this.alto = alto-30; backGround= ewe.fx.Color.White; borderWidth = 2; borderStyle = Canvas.EDGE_SUNKEN; for(int i=0;i<n; i++) addHoles(5,i);

46

Você também pode gostar