Análise do Gerador de Mapas de jogos Match-Three
Neste trabalho, iremos verificar o desempenho do gerador de mapas com diferentes métodos de Seleção e Crossover. Para isso, será implementados diferentes métodos e será comparado o tempo de processamento de cada combinações de métodos e o número de gerações.
Inicializando pacotes e bot
Iremos importar alguns pacotes que usaremos (ou não)
E não esquecer do qd_bot
que será capaz de avaliar os mapas gerados.
Gerador de Mapas Match-Three
Agora, precisamos definir a Classe que gera mapas. Esse gerador utiliza um Algoritmo Genético para gerar um mapa que possua um desempenho médio desejado.
TENTE NÃO MEXER NO CÓDIGO DO BLOCO ESCONDIDO, POR FAVOR
Definição dos Métodos de Seleção
Diferentes métodos podem ser definidos, mas TODOS precisam seguir a mesma formatação.
def meu_metodo_de_selecao(populacao: [dict()]) -> [dict()]:
# define o método
return lista_de_escolhidos
O método definido irá receber uma lista de individuos (população). Cada individuo terá este formato a seguir:
{
'level': str, # String do mapa
'evaluate': float, # Valor de desempenho médio do mapa
'distance': float, # Distância desse desempenho para o desejado
}
O valor de fitness a ser utilizado é o distance
e ele deve ser _minimizado. (QUANTO MENOR, MELHOR)
O método de seleção irá escolher uma DUPLA de "pais" para ser utilizados no método de crossover. Logo, será retornado uma lista contendo os pais. [p1, p2]
Caso precise de uma luz, verifique o método _standard_selection
do gerador
Definição dos Métodos de Crossover
Diferentes métodos podem ser definidos, mas TODOS precisam seguir a mesma formatação.
def meu_metodo_de_crossover(pais: [dict()]) -> [dict()]:
# define o método
return lista_de_cruzados
O método definido irá receber a lista de individuos selecionados (seleção). Os individuos irão ter a mesma formatação da população, mas contendo um quantidade menor. Para o retorna da função, o cruzamento pode gerar apenas um filho ou vários. Independente da quantidade, retorna uma lista com os filhos, onde cada indivíduo terá o mesmo formato de dicionário, com 2 valores setados.
{
'level': str, # String do mapa
'evaluate': 0, # Não foi calculado, então diz que é 0
'distance': 1, # Maior distância possível
}
A string do mapa possui o seguinte formato:
M,P,T
X,X,X,X,X,X,X,X,X
X,X,X,X,X,X,X,X,X
X,X,X,X,X,X,X,X,X
X,X,X,X,X,X,X,X,X
X,X,X,X,X,X,X,X,X
X,X,X,X,X,X,X,X,X
X,X,X,X,X,X,X,X,X
X,X,X,X,X,X,X,X,X
Y,Y,Y,Y,Y,Y,Y,Y,Y
O que significa cada letra e seus possiveis valores:
- M - Quantidade de movimentos (vária de 5 até 30)
- P - Pontuação necessária (vária de 0 até 140,000)
- T - Tipo de peças diferentes (vária de 4 até 6)
- X - Indica qual peça vai nesta posição (vária de 0 até 3)
- Y - Igual a X, mas sem o valor 3, pois esse tipo de peça não pode ficar na ultima posição.
Então, a saída será dada pela lista: [c1, c2, ..., cn]
Lembrando que a entrada é dada por uam lista de tamanho 2
. Todos os métodos definidos irão necessitar que o crossover seja feita em uma dupla mas a função pode gerar uma quantidade indeterminada de filhos.
Caso precise de uma luz, verifique o método _standard_crossover
do gerador
Rodada de Testes das combinações de Métodos de Seleção e Crossover
Para fazer esses testes, escolhemos quais métodos serão utilizados E avaliamos os resultados em 10 iterações. Sendo assim, conseguimos analisar algumas métricas para decidir qual será a melhor combinação.
Primeiramente, definimos as funções utilizadas para os testes.
Agora, podemos definir quais métodos de seleção e crossover serão usados nos testes. Os targets já estão definidos por padrão como parâmetro dos métodos.
Cada teste irá gerar um arquivo csv com o seguinte nome:
{NOME_SELECAO}_{NOME_CROSSOVER}_{TARGET}.csv
Lembre-se de adicionar apenas o nome das funções definidas!
Exemplo:
def minha_funcao(x, y):
return x + y
def outra_funcao(y, z):
return y ** z
lista_funcao = [minha_funcao, outra_funcao]