universidade federal da bahia implementac¸˜ao de um agente
Transcrição
universidade federal da bahia implementac¸˜ao de um agente
UNIVERSIDADE FEDERAL DA BAHIA INSTITUTO DE MATEMÁTICA DEPARTAMENTO DE CIÊNCIA DA COMPUTAÇÃO CÍCERO AUGUSTO MAGALHÃES DA SILVA NEVES IMPLEMENTAÇÃO DE UM AGENTE REATIVO UTILIZANDO REDES NEURAIS EVOLUCIONÁRIAS Salvador 2006 CÍCERO AUGUSTO MAGALHÃES DA SILVA NEVES IMPLEMENTAÇÃO DE UM AGENTE REATIVO UTILIZANDO REDES NEURAIS EVOLUCIONÁRIAS Monografia apresentada ao Curso de graduação em Ciência da Computação, Departamento de Ciência da Computação, Instituto de Matemática, Universidade Federal da Bahia, como requisito parcial para obtenção do grau de Bacharel em Ciência da Computação. Orientador: Prof Augusto Loureiro da Costa Salvador 2006 RESUMO Este trabalho visa utilizar o NEAT, um algoritmo que permite evoluir redes neurais através de algoritmos genéticos, para implementar o controle de mira de um BOT no jogo Unreal Tournament 2004. Serão mostradas as ferramentas utilizadas para realizar os experimentos e também os resultados dos mesmos. Ao final serão comentados as dificuldades enfrentadas durante a implementação de tal BOT e serão sugeridas possibilidades de trabalhos futuros com redes neurais no jogo. Palavras-chave: redes neurais, algoritmos genéticos, unreal tournament LISTA DE FIGURAS 1 Exemplo de um neurônio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2 Redes Alimentada Adiante com Múltiplas Camadas . . . . . . . . . . . . . . . 9 3 Rede Recorrente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 4 Ciclo de evolução dos algoritmos genéticos . . . . . . . . . . . . . . . . . . . 11 5 Componentes para implementação de um agente para UT usando a API JavaBot 19 6 Representação genética do genoma usado pelo NEAT . . . . . . . . . . . . . . 21 7 Mutação por adição de conexão e por adição de nó respectivamente . . . . . . 21 8 Comparação entre cromossomos para realiazação do crossover . . . . . . . . . 22 9 Fenótipo da RNA com melhor fitness no experimento de XOR . . . . . . . . . 25 10 Fenótipo da RNA com melhor fintess no experimento do Forex Trading . . . . 25 11 Mapa feito no Unreal Editor para treinamento das redes neurais . . . . . . . . . 28 12 Rede resultante de 63 gerações com uma população de 60 indivı́duos . . . . . . 31 13 Resultado conseguido com uma das redes das primeiras gerações do processo de evolução. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 14 Resultado conseguido com uma a rede da 63a geração. . . . . . . . . . . . . . 32 15 Rede obtida da 63a geração acertando o Alvo. . . . . . . . . . . . . . . . . . . 32 SUMÁRIO 1 Introdução 6 2 Conceitos 8 2.1 Redes Neurais Artificiais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2 Algoritmos Genéticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.3 Agentes Autônomos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.3.1 14 3 Ferramentas Utilizadas 16 3.1 Unreal Tournament 2004 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.1.1 UnrealScript e Mutators . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.2 GameBots e a API JavaBot . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.3 NEAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.3.1 Codificação genética . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.3.2 Mutação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.3.3 Marcação do Histórico e a operação Crossover . . . . . . . . . . . . . 21 3.3.4 Proteção de inovações através da segredação por espécies . . . . . . . 22 3.3.5 Minimização do espaço de busca através da complexificação de estruturas . . . . . . . . . . . . . . . . . . . . . . 23 NEAT4J . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.4 4 Agentes Reativos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Descrição do problema e Implementação 26 4.1 26 Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 5 6 Implementação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 Resultados e Dificuldades 31 5.1 Resultados obtidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 5.2 Dificuldades encontradas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Conclusão 34 6.1 34 Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Apêndice A -- Protocolo de mensagens trocadas através do GameBots 36 Apêndice B -- Parâmetros de configuração para o Algoritmo Genético utilizado pelo NEAT4J 45 Referências 51 6 1 INTRODUÇÃO O uso da Inteligência Artificial em jogos eletrônicos tem se mostrado de grande valia tanto para a área de IA quanto para o desenvolvimento de jogos (LAIRD; LENT, 2000). Para a área de IA os jogos de computador representam um grande laboratório, onde é possı́vel testar diferentes técnicas e pôr a prova teorias sem que seja necessário se preocupar com aspectos pouco relevantes para a área. Os jogos eletrônicos se beneficiam graças aos novos nı́veis de desafio e imersão que a IA tem trazido para esta área, tanto em jogos cujo intuito é somente divertir como nos Serious Games, jogos feitos com o objetivo de simular situações reais e permitir que atividades normalmente caracterizadas como de alto risco, possam ser executadas com a finalidade de treinamento sem o perigo de haver danos a alguém. A Neuro-Evolução tem se mostrado como uma grande promessa para a resolução problemas complexos na área de aprendizado por reforço. Esta abordagem oferece uma alternativa aos métodos estatı́sticos convencionais que tentam estimar a validade de uma determinada ação em estados particulares do mundo (STANLEY, 2004). Este trabalho tem por objetivo utilizar o NEAT (Neuro Evolution of Augmenting Topologies) na implementação de um BOT reativo para o jogo Unreal Tournament 2004. O NEAT é um algortimo que possui como função evoluir não só os pesos, mas também as estruturas de Redes Neurais Artificiais através do uso de Algoritmos Genéticos. Este algoritmo foi proposto em (STANLEY, 2004) e tem sido alvo de interesse de muitos pesquisadores além de estar sendo utilizado no desenvolvimento de jogos (NERO, 2005). As redes neurais serão utilizadas para implementar o controle de mira do BOT, que poderá prever em que posição é indicado atirar a fim de que se acerte um inimigo em movimento. A predição desta localização é um importante fator durante o combate com um oponente, principalmente se o personagem controlado estiver portando uma arma cuja munição possui uma velocidade baixa. Uma vez que não há como saber a aceleração do oponente nem a velocidade do projétil referente a arma que está sendo usada, esta predição não pode ser feita de forma determinı́stica. Será feito então o uso de redes neurais para calcular o fator tempo da 7 equação horária de Movimento Uniforme e assim obter uma localização aproximada para onde deve ser feito o disparo. A implementação deste controle de mira tornará o comportamento BOT mais convincente para um jogador humano, pois ele estará executando exatamente o que uma pessoa faria. Ao invés de tentar atirar a esmo em um inimigo, ou somente na localização atual do mesmo, o BOT tentará prever a próxima posição em que o oponente estará, de tal forma que o tiro disparado da arma chegue a tempo de interceptá-lo. A seguir, no Capı́tulo 2, será dada uma explanação dos conceitos e abordagens da Inteligência Artificial que serão utilizados neste trabalho. No Capı́tulo 3 serão apresentadas as ferramentas utilizadas para a implementação do BOT e para a obtenção da rede neural desejada, sendo explicado no Capı́tulo 4 como foi realizada a implementação dos BOTs utilizados no processo de evolução das redes neurais. Após esse capı́tulo serão comentados os resultados obtidos e as dificuldades encontradas durante a realização dos experimentos (Capı́tulo 5) e por fim, no Capı́tulo 6 será feita uma breve conclusão sobre o trabalho e algumas sugestões para trabalhos futuros serão citadas. 8 2 CONCEITOS 2.1 REDES NEURAIS ARTIFICIAIS As rede neurais artificiais (RNAs) foram inspiradas no funcionamento do cérebro humano, o qual é constituı́do por diversas células nervosas (neurônios) que conectam-se entre si através de sinapses. Segundo (HAYKIN, 2001) uma rede neural é um processador paralelamente distribuı́do de forma maciça constituı́do de unidades de processamento simples, que têm a propensão natural para armazenar conhecimento experimental e torná-lo disponı́vel para o uso. Ela se assemelha ao cérebro em dois aspectos: • O conhecimento é adquirido pela rede a partir de seu ambiente através de um processo de aprendizagem. • Forças de conexão entre neurônios, conhecidas como pesos sinápticos, são utilizadas para armazenar o conhecimento adquirido. Cada unidade de processamento, ou neurônio, é constituı́da basicamente por um conjunto de sinapses ou elos de conexão, que por sua vez possuem um peso próprio; uma função para somar os sinais de entrada, que passam pelas sinapses, ponderados por seus respectivos pesos (combinador linear); e uma função de ativação ou função restritiva que tem como objetivo limitar a amplitude da saı́da de um neurônio (Figura 1), impedindo dessa forma que o mesmo seja ativado caso o estı́mulo recebido não ultrapasse um valor de ativação pré-definido (threshold). Além disso um neurônio pode possuir também um bias aplicado externamente, com o objetivo de realizar uma transformação afim à saı́da do combinador linear (HAYKIN, 2001) . O motivo do uso de uma função de ativação nos neurônios deve-se à analogia que se faz com as sinapses que ocorrem no cérebro humano inibindo ou excitando um determinado neurônio a depender do estı́mulo recebido por este. 9 Figura 1: Exemplo de um neurônio Dá-se o nome de arquitetura de rede à maneira como os neurônios de uma RNA estão dispostos na mesma. Existe em geral três classes de arquiteturas diferentes: • Redes Alimentadas Adiante com Camada Única: se trata de uma rede em camadas que possui apenas uma camada de entrada, formada pelos nós de fonte que receberão os estı́mulos externos, conectada a uma camada de saı́da de neurônios que representam os nós computacionais dessa rede. • Redes Alimentadas Adiante com Múltiplas Camadas: difere da arquitetura anterior por possuir uma ou mais camadas ocultas entre a camada de entrada e a camada de saı́da. Esta camada oculta provê um maior poder de generalização para a rede, permitindo que esta seja capaz de extrair estatı́sticas de ordem elevada (HAYKIN, 2001). • Redes Recorrentes: ao contrário das outras duas arquiteturas que não possuem ciclos em suas estruturas, esta apresenta pelo menos um laço de realimentação influenciando bastante a capacidade de aprendizagem e o desempenho da rede. Figura 2: Redes Alimentada Adiante com Múltiplas Camadas 10 Figura 3: Rede Recorrente 2.2 ALGORITMOS GENÉTICOS Os Algoritmos Genéticos (AGs) tiveram sua origem nos trabalhos de John Holland quando este publicou em 1975 seu livro, ”Adaptation in Natural and Artificial Systems”. Os AGs baseiam-se na teoria da evolução e seleção natural de Darwin, onde os indivı́duos que melhor se adaptam ao meio em que estão inseridos têm maiores chances de sobreviver e gerar descendentes. Em vista disso a terminologia adotada no contexto de Algoritmos Genéticos vêm também da teoria da evolução natural e da genética. Cada possı́vel solução para um problema é apresentada por um indivı́duo que faz parte de uma população de soluções. Este indivı́duo é representado por um cromossomo, que por sua vez possui a codificação genética, ou genótipo, para uma possı́vel solução de um problema (fenótipo). Cada cromossomo é implementado na forma de uma lista de atributos, onde cada atributo é chamado de gene e os valores que um gene pode assumir são chamados de alelos. O uso de algoritmos genéticos se baseia na busca, em uma população de possı́veis soluções, para um problema objetivando o equilı́brio entre duas metas: o aproveitamento das melhores soluções e a exploração do espaço de busca. Uma caracterı́stica importante dos algoritmos genéticos é que o processo de busca, diferentemente de outros métodos, ocorre de forma paralela e estruturada (REZENDE, 2003). Apesar dos Algoritmos Genéticos serem aleatórios, a busca feita através deles é direcionada, uma vez que é feito o uso de informações históricas para encontrar novos pontos de busca onde são esperados melhores desempenhos. Os algoritmos genéticos operam dentro de um ciclo (Figura 4) constituı́do das seguintes etapas: 1. Geração de uma população de soluções potenciais 2. Submissão dos indivı́duos a uma função de fitness 3. Geração de uma nova população privilegiando os indivı́duos que tiveram melhor fitness 11 4. Geração de novos indivı́duos através de mutação ou crossover de alguns indivı́duos do novo grupo 5. Repetição de todo o processo até que uma quantidade de iterações seja atingida ou até que um determinado nı́vel de adaptação seja alcançado Figura 4: Ciclo de evolução dos algoritmos genéticos O método mais utilizado para a geração de uma população inicial é a inicialização aleatória. Contudo, se forem conhecidas soluções que estejam próximas a(s) solução(ões) ótima(s), não há nada que impeça que os cromossomos da população inicial sejam codificados de forma determinı́stica. A seleção dos indı́viduos mais aptos da população é feita baseada em uma função de aptidão ou função de fitness. Esta função recebe como entrada os valores do genótipo do cromossomo e fornece como resultado o quão boa uma solução codificada por um indivı́duo é para o problema que se quer resolver. Depois de feita a avaliação dos indivı́duos da população, o processo de seleção escolherá um subconjunto de indivı́duos desta população para que seja criada a próxima geração de indivı́duos, sendo que existem diferentes tipos de seleção quase todas privilegiando os indivı́duos com maior aptidão, mas não de forma exclusiva, a fim de manter-se a diversidade (REZENDE, 2003). É preciso ter cuidado para que duas ocorrências indesejáveis sejam evitadas durante o processo de seleção: um é a existência de um indivı́duo com um fitness muito maior que os outros que poderá acabar monopolizando as seleções, gerando populações com indivı́duos muito parecidos. Este evento é chamado de convergência precoce (BITTENCOURT, 2005). Outro evento indesejável é a eliminação de indivı́duos com um fitness muito baixo. A baixa aptidão desses indivı́duos provavelmente irá fazer com que eles não sejam escolhidos para gerar descendentes, o que não é aconselhável, uma vez que eles representam a fonte de diversidade das soluções. Caso sejam eliminados de forma prematura isso também irá resultar numa convergência precoce da população. 12 A importância da diversidade nas populações se dá pelo motivo das funções objetivos serem em sua maioria multimodais. Isso faz com que uma convergência precoce ocorra geralmente em um máximo local. Com a diversidade nas populações as chances de se chegar a um máximo global aumentam consideravelmente (BITTENCOURT, 2005). É importante observar que pelo fato dos AGs serem processos estocásticos, indivı́duos com boas aptidões podem ser eliminados durante o processo evolutivo ao não serem escolhidos para gerar descendentes. Esse problema é resolvido muitas vezes utilizando-se o elitismo, onde os n melhores indivı́duos sempre são selecionados para a próxima geração. O surgimento de uma nova população a partir dos indivı́duos selecionados ocorre através dos operadores genéticos, mutação e crossover . Estes operadores têm a função de gerar uma população totalmente nova mas que ao mesmo tempo carregue consigo caracterı́sticas de seus pais. O operador de mutação age modificando aleatoriamente o valor de um ou mais genes de um cromossomo, sendo que a probabilidade de um gene ser alterado por ele é chamada de taxa de mutação, e a esta normalmente são atribuı́dos valores baixos, pois o objetivo da mutação é apenas criar uma variação extra na população sem, contudo, danificar o progresso alcançado com a busca. Além disso, a mutação ajuda a contornar o problema dos mı́nimos locais por causar uma pequena alteração na direção da busca feito pelo Algoritmo Genético (REZENDE, 2003). O crossover é o operador genético predominante na geração da nova população. O conceito básico do operador de crossover é que ele realiza a troca de informações entre dois indivı́duos candidatos criando novas soluções que contêm informações combinadas destes indivı́duos. Alguns dos operadores de crossover que existem são os de um-ponto, multipontos e uniforme. O crossover de um ponto seleciona aleatoriamente um ponto de corte nos cromossomos pais e os segmentos gerados a partir deste corte são trocados dando origem a dois novos indivı́duos. Já no crossover de multipontos, como o próprio nome já diz vários pontos de corte são selecionados nos cromossomos pais e a partir daı́ troca-se o material genético entre eles. Por último tem-se o crossover uniforme que realiza a combinação genética escolhendo, através de uma probabilidade fixa, qual dos pais vai fornecer informações para cada gene do filho, isso faz com que cada gene possua uma informação independente da sua posição relativa no cromossomo. Além dos operadores genéticos e método de seleção a ser escolhido, outros parâmetros também têm grande influência no desempenho de Algoritmos Genéticos como o tamanho da população, a taxa de crossover e mutação, o intervalo de geração e o critério de parada (REZENDE, 2003). O tamanho da população afeta o desempenho de um AG pois caso essa população 13 seja muito pequena, haverá apenas uma pequena cobertura do espaço de busca, por outro lado trabalhar com populações grandes demanda mais recursos computacionais além de um tempo maior de trabalho do algoritmo. A taxa de crossover alta irá resultar no surgimento mais rápido de novas estruturas na população, o que pode inclusive causar a retirada muito rápida de indivı́duos com boas aptidões da população, enquanto que uma taxa muito baixa irá estagnar o processo de evolução. Já com a taxa de mutação ocorre o contrário: não é desejável que ela seja alta pois pode tornar o processo de busca aleatório. O ideal é que esta taxa seja baixa o suficiente para impedir que a busca fique estagnada em algum sub-espaço, além de permitir que qualquer ponto do espaço de busca seja atingido. O intervalo de geração indica a porcentagem da população que será substituı́da para a próxima geração. Um valor muito elevado para este parâmetro pode resultar na perda de estruturas com boa aptidão, enquanto que um valor baixo causará lentidão no processo evolutivo. O critério de parada indica em que momento o Algoritmo Genético deve parar, quer seja depois de um determinado número de gerações ou quando as aptidões dos melhores indivı́duos não mudarem depois de um certo tempo. 2.3 AGENTES AUTÔNOMOS A definição do que é um agente não é universal, sendo uma das mais abrangentes a seguinte segundo (REZENDE, 2003) : Um agente é uma entidade real ou virtual, capaz de agir num ambiente, de se comunicar com outros agentes, que é movida por um conjunto de inclinações (sejam objetivos individuais a atingir ou uma função de satisfação a otimizar); que possui recursos próprios; que é capaz de perceber seu ambiente (de modo limitado); que dispõe (eventualmente) de uma representação parcial deste ambiente; que possui competência e oferece serviços; que pode eventualmente se reproduzir e cujo comportamento tende a atingir seus objetivos utilizando as competências e os recursos que dispõe e levando em conta os resultados de suas funções de percepção e comunicação, bem como suas representações internas. Agentes são normalmente vistos como instrumentos integradores das técnicas de IA (REZENDE, 2003) e possuem alguns ingredientes-chave que os caracterizam: Autonomia de Decisão, Autonomia de Execução, Competência para Decidir, Existência de uma Agenda Própria, Reatividade, Adaptabilidade, Mobilidade, Personalidade, Interatividade com o Usuário, Ambiente de Atuação, 14 Comunicabilidade. Além dessas caracterı́sitcas os agentes também podem ser classificados segundo alguns eixos tais como o eixo Cognitivo, o de Foco, o de Atuação e o eixo Ambiental. No que diz respeito ao eixo Cognitivo o agente pode ser um Agente Cognitivo onde suas ações são baseadas em um planejamento feito a partir de um modelo do ambiente construı́do pelo próprio agente; ou um Agente Reativo, que somente reage a estı́mulos provocados pelo ambiente onde está. Todo agente deve possuir componentes que o auxiliem na execução ou na sugestão das ações a serem tomadas, considerando seus objetivos, ambiente e perfil de atuação. A essas três caracterı́sticas dá-se o nome de modus operandi, podendo este ser constituı́do do mapeamento da percepção sensorial do ambiente para ações ou então de um planejador de ações. 2.3.1 AGENTES REATIVOS Agentes reativos são agentes baseados no modelo de funcionamento estı́mulo-resposta e possuem as seguintes caracterı́sticas: • não há representação explı́cita do conhecimento: o conhecimento do agente é implı́cito (as suas regras de comportamento) e sua manifestação se externa através do seu comportamento; • não há representação do ambiente: o comportamento (resposta) do agente é baseado no que ele percebe (estı́mulo) a cada instante. Não há uma representação interna explı́cita do ambiente; • não há memória das ações: os agentes reativos não mantém nenhum tipo de histórico de suas ações, ou seja, o resultado de uma determinada ação passada não influencia diretamente na decisão de uma ação futura. O processo de realizar uma ação a partir de dados sensoriais normalmente é dividido em duas fases. Uma fase em que o processamento sensorial produz um vetor de caracterı́sticas e uma fase de computação da ação onde uma determinada ação é selecionada baseada nos valores contidos neste vetor. Tais caracterı́sticas são escolhidas por quem está implementando o agente de forma a manterem uma correlação com as caracterı́sticas do ambiente que são relevantes para a escolha da ação a ser tomada no estado atual do mesmo (NILSSON, 1998). As técnicas descritas neste capı́tulo servirão de base para as ferramentas que serão utilizadas neste trabalho bem como para a execução dos experimentos descritos no Capı́tulo 4. 15 3 FERRAMENTAS UTILIZADAS 3.1 UNREAL TOURNAMENT 2004 O jogo Unreal Tournament 2004 (UT2004) é um jogo tri-dimensional de tiro em primeira pessoa1 criado pela Epic Games em parceria com a Digital Extremes, e foi concebido para jogos do tipo multiplayer, permitindo que um ou mais jogadores disputem partidas em time, de forma cooperativa, ou em partidas em que cada jogador busca a realização de objetivos individuais. Cada jogador controla um personagem do jogo, sendo que um jogador pode ser tanto uma pessoa como um BOT. Um BOT é um agente autônomo que tem a função de controlar um personagem durante as partidas da forma mais parecida com um humano possı́vel, enquanto tenta cumprir seus objetivos que podem incluir matar seus oponentes e/ou proteger seus aliados no caso de uma partida cooperativa. Um BOT é alimentado pelo servidor com informações sensoriais e atua sobre o ambiente a partir de comandos dados ao personagem controlado por ele, comandos esses que incluem: olhar para uma determinada direção, andar, saltar, agachar-se e atirar. Cada personagem possui alguns dados que informam o seu status atual no jogo. Essas informações incluem a arma que está usando, quanto de munição possui (Ammo), qual o seu nı́vel de saúde (Health), qual seu nı́vel de proteção (Armor) e a sua pontuação ou pontuação do seu time (Score). Durante uma partida, é possı́vel coletar itens no ambiente que aumentem os nı́veis de Health, Armor ou de Ammo do jogador. As partidas no jogo UT2004 ocorrem em levels, cada um desses possuindo um mapa que representa o ambiente fı́sico em que os jogadores irão se enfrentar. Os objetivos para se alcançar a vitória variam para cada formato de partida existente, podendo ir da simples contagem de inimigos que foram mortos por um jogador até o cumprimento de tarefas mais complexas, como dominação de um determinado ponto do mapa em que ocorre a partida. Alguns dos tipos 1 Jogos de tiro em primeira pessoa (do inglês First Person Shooter) são jogos em que o jogador compartilha o mesmo ponto de vista e as mesmas percepções sensoriais do personagem controlado por ele, e faz uso de armas para alcançar determinados objetivos durante as partidas. 16 de partida existentes são: • Deathmatch (DM): É o mais comum dos formatos de partida existentes para jogos de tiro em primeira pessoa. O objetivo deste tipo de jogo é eliminar tantos outros jogadores quanto possı́vel até que um certo limite de tempo ou de inimigos eliminados seja atingido. Vence o jogador que tenha eliminado mais oponentes. • Team DeathMatch (TDM): neste formato de partida muito semelhante do DM, os jogadores são divididos em dois times, cada time tendo um Score próprio. O objetivo deixa de ser eliminar qualquer jogador da partida e passa a ser eliminar qualquer jogador que não esteja no seu time. O jogo UT2004 oferece uma boa diversidade de armas que podem ser utilizadas pelos jogadores durante as partidas. Cada uma dessas armas possui uma munição e velocidade de tiro especı́ficas. Cada tipo de munição existente em UT2004 possui também caracterı́sticas próprias como velocidade, quantidade de dano que causa, além dos efeitos colaterais quando esta se choca com algo no ambiente do jogo. 3.1.1 UNREALSCRIPT E MUTATORS A série de jogos Unreal Tournament (UT), incluindo aı́ o UT2004, possui uma linguagem de script, chamada UnrealScript, que pode ser utilizada para realizar alterações nas caracterı́sticas do jogo por terceiros. Tal ferramenta foi criada com o intuito de prover ao time de desenvolvimento de UT uma linguagem de programação voltada para as necessidades e nuances da programação para jogos. Ela oferece um estilo de programação simples, semelhante à linguagem Java, com checagem de erros em tempo de compilação. As principais caracterı́sticas que o UnrealScript herdou da linguagem Java são: inexistência de ponteiros, ambiente com uso de garbage collector automático, herança simples entre classes, tipagem forte e checada em tempo de compilação, sintaxe semelhante a Java e C/C++ e geração de bytecodes, permitindo o uso dos códigos gerados com ela em diferentes plataformas (ex.: Windows, Unix, Mac) (SWEENEY, 1998). Utilizando o UnrealScript é possı́vel escrever códigos para alterar determinados aspectos do jogo como armas disponı́veis e itens que podem ser coletados. A estas modificações dá-se o nome de Mutators. Os Mutators podem ser classificados como plugins que realizam uma ação em resposta a eventos que ocorram no jogo. Um exemplo de Mutator é o do tipo Arena, ao usar esse Mutator todas as armas disponı́ves em uma partida são substituı́das por um único tipo de 17 arma, todos os itens referentes a munição são substituı́dos por munição para a arma escolhida e todos os jogadores começam com esta arma e nenhuma outra mais (JBP, 2000). 3.2 GAMEBOTS E A API JAVABOT GameBots é um projeto que se iniciou no Information Sciences Institute da University of Southern California (USC-ISI) com o objetivo de permitir o uso do jogo Unreal Tournament para pesquisas na área de Inteligência Artificial. O principal produto deste projeto é uma modificação para o jogo UT que fornece a funcionalidade de se controlar personagens do jogo através de sockets (GAMEBOTS, 2002). Através desta modificação o personagem recebe informações sensoriais do jogo (servidor) através da conexão de rede e baseado nestas informações, uma aplicação cliente (BOT) pode decidir que ações o personagem irá tomar: andar, pular, falar, entre outras. Inicialmente a modificação foi feita para o jogo UT, contudo, uma implementação parcial do GameBots foi feita para Unreal Tournament 2003 (UT2003) por John Manoloviche e posteriormente terminada por Jessica Bayliss e Tim Garwood do Rochester Institute of Technology (RIT). Junto com a adaptação do GameBots para UT2003 também foi feita uma versão da modificação para UT2004, que além de corrigir alguns bugs existentes na primeira versão e adicionar novos tipos de mensagens também sofreu um processo de refactoring2 a fim de reduzir a redundância em inúmeras hierarquias de classe e deixar o código base mais fácil de se manter (RIT, 2005). O protocolo de envio e recebimento de mensagens do GameBots é bastante simples. Todas as mensagens vindas do servidor possuem a seguinte estrutura: ”Tipo arg1 val1 arg2 val2 ...”, onde Tipo indica a qual tipo de informação esta mensagem se refere; arg1 e arg2 são os atributos que podem vir junto com a mensagem dando informações adicionais (coordenadas de localização ou um valor quantificado); val1 e val2 são os respectivos valores dos atributos. Os comandos enviados ao personagem também possuem este mesmo formato. Um exemplo de uma mensagem recebida pelo servidor seria: MSG {Id Player-1} {String Attack the base!} {Location 12,23,34} (ver apêndice A). Os tipos de mensagens recebidas pelo cliente constituem dois tipos básicos: mensagens sı́ncronas e mensagens assı́ncronas. Mensagens sı́ncronas são enviadas a um cliente em lotes dentro de um intervalo de tempo pré-configurado. Tais mensagens incluem informações do que o personagem enxerga e sobre o status do mesmo. Todo lote de mensagens sı́ncronas começam 2 O termo refactoring é utilizado na área de engenharia de software para se referir ao ato de alterar o código-fonte de um programa a fim de facilitar o entendimento e manutenibilidade do mesmo, sem alterar o seu comportamento externo. 18 com uma mensagem do tipo ”BEG”contendo um valor indicando o instante de tempo do jogo em que foi enviado (timestamp). Todas as mensagens subsequentes farão parte do mesmo lote até que seja recebida uma mensagem do tipo ”END”com o mesmo timestamp da mensagem ”BEG”correspondente. Como todas essas mensagens são enviadas durante o mesmo instante de tempo de jogo, elas representam um único estado da partida (RIT, 2005). Mensagens assı́ncronas, por sua vez, representam eventos que podem ocorrer a qualquer momento do jogo, como por exemplo sofrer dano ou se chocar com uma parede. É importante notar que apesar de poder ser enviada a qualquer momento do jogo, uma mensagen assı́ncrona nunca irá ser enviada entre uma mensagem do tipo ”BEGIN”e uma do tipo ”END”de um lote de mensagens sı́ncronas (RIT, 2005) e que também não há como saber o timestamp destas mensagens. Com o intuito de facilitar a realização de pesquisas na área de Inteligência Artificial utilizando o GameBots, Andrew Marshall da USC-ISI implementou a API JavaBot. Essa API tem como principal objetivo permitir que desenvolvedores consigam programar BOTs para jogos da série UT sem se preocuparem em lidar com aspectos especı́ficos do protocolo do GameBots, como por exemplo o parsing das mensagens recebidas pelo BOT ou programação de sockets. Desta forma a programação em Java de uma aplicação que faça uso de Inteligência Artificial para trabalhar com o GameBots se torna bastante direta, sendo necessário somente implementar a interface BOT do pacote JavaBot (ROZICH, 2002). Figura 5: Componentes para implementação de um agente para UT usando a API JavaBot 19 3.3 NEAT A Neuro-Evolução é a área que estuda a evolução de RNAs (Redes Neurais Artificiais) e uma das abordagens usadas para tal é chamada de TWEANN (Topology and Weight Evolutionary Neural Networks). Esta abordagem busca evoluir tanto a topologia das redes como os pesos de suas conexões utilizando Algoritmos Genéticos (AGs). Contudo, verificam-se alguns problemas referentes ao uso de AGs para evoluir estruturas de RNAs. Entre estes problemas estão a escolha da codificação mais adequada que possua representação tanto da topologia quanto das conexões da rede e permita haver cromossomos de tamanhos diferentes; a realização do cruzamento de cromossomos que representam estruturas completamente distintas; a preservação de inovações nas estruturas, evitando que estas se percam durante o processo de evolução; e a minimização do espaço de busca de soluções. Pensando nisso, Kenneth O. Stanley propôs em (STANLEY, 2004) um algoritmo chamado NEAT (Neuro Evolving Augmenting Topologies) o qual segue princı́pios básicos que o tornam capaz de evoluir complexas estruturas de redes neurais juntamente com seus pesos. Estes princı́pios incluem a implementação de um método para detectar estruturas homólogas, proteção de inovações que venham a surgir durante o processo de evolução e minimização do espaço de busca. A codificação genética no NEAT é feita visando permitir que as estruturas das redes neurais possam ser modificadas de forma dinâmica. Este requisito é importante pois, para permitir que as estruturas se tornem cada vez mais complexas, o genoma não pode ter um tamanho fixo, senão a complexidade da sua solução poderá estar limitada ao tamanho máximo de genes permitido pela sua representação genética. 3.3.1 CODIFICAÇÃO GENÉTICA O genoma no NEAT possui duas listas de genes, uma representando os neurônios (node genes) e a outra as conexões existentes entre esses neurônios (connection genes). Cada node gene possui um número de identificação e uma indicação do seu tipo (entrada, oculto ou saı́da) e cada gene de conexão possui uma identificação global chamada Innovation Number , uma identificação dos neurônios que estão conectados, sendo um de entrada e outro de saı́da, o peso desta conexão e uma flag de habilitação, que indica se esta conexão deve ser representada no fenótipo, ou seja, na RNA. 20 Figura 6: Representação genética do genoma usado pelo NEAT 3.3.2 MUTAÇÃO A mutação em Sistemas Neuro-Evolucionários normalmente é efetuada aplicando uma perturbação, com probabilidade fixa, nos pesos das conexões, adicionando a eles um número de ponto flutuante escolhido a partir de uma distribuição uniforme de valores positivos e negativos. Utilizando a representação genética citada, o NEAT permite que outros dois tipos de mutação sejam efetuados além deste: a adição de conexões e a adição de neurônios à estrutura da rede. Na adição de conexões uma nova conexão é criada entre dois neurônios que não estavam conectados anteriormente (Figura 7). Já a adição de neurônios desabilita uma conexão préexistente entre dois neurônios e cria duas novas conexões, uma ligando um neurônio de entrada com o novo neurônio e outra ligando este neurônio ao neurônio de saı́da (ver Figura 7). A conexão estabelecida entre o neurônio de entrada e o novo neurônio recebe o valor 1 (um) para o seu peso, enquanto que a conexão entre o novo neurônio e o neurônio de saı́da permanece com o peso da conexão que foi desfeita. Essa ”divisão”de uma conexão em duas, acaba por inserir não-linearidade ao sistema, possibilitando o desenvolvimento de caracterı́sticas novas em cima do comportamento já existente, ao mesmo tempo em que não destrói a estrutura da rede pré-existente. Figura 7: Mutação por adição de conexão e por adição de nó respectivamente 21 3.3.3 MARCAÇÃO DO HISTÓRICO E A OPERAÇÃO CROSSOVER O problema de como realizar a operação de crossover é resolvido no NEAT utilizandose a marcação do histórico dos genes. Isso é possı́vel graças ao uso do Innovation Number , que representa a cronologia dos genes no sistema e permite verificar se dois genes quaisquer possuem a mesma origem histórica. Tendo esta informação, pode-se dizer a qual indivı́duo da população um gene se refere. Basicamente o Innovation Number é um contador global que é incrementado quando um novo gene surge e associado então a este gene. Enfim, para realizar o crossover é preciso comparar os genomas dos pais entre si. Cada genoma é alinhado um contra o outro, de forma que haja uma ordem crescente do Innovation Number (IN). Os genes de um pai que não possuam correspondentes (mesmo IN) no outro são chamados de disjoint, caso o outro pai possua genes mais velhos(IN baixo), ou podem ser chamados de excess caso o outro pai não possua genes mais velhos. Os genes disjoint e excess são herdados do pai com melhor fitness , mas se este for igual, eles podem ser herdados randomicamente. Genes com o mesmo IN podem ser cruzados de duas maneiras. Na primeira, o gene passado ao filho é escolhido aleatoriamente entre os pais. A outra maneira consiste em passar um gene para o filho com a média do peso dos genes dos pais. Além disso, genes desabilitados têm uma chance de serem reativados nos novos indivı́duos dando margem às redes de fazerem uso de genes mais antigos novamente. Estas marcações do histórico dos genes permitem ao NEAT realizar o crossover sem uma análise sobre a topologia da rede, pois a compatibilidade entre as estruturas é garantida apenas pelo IN. 3.3.4 PROTEÇÃO DE INOVAÇÕES ATRAVÉS DA SEGREDAÇÃO POR ESPÉCIES De acordo com Stanley (STANLEY, 2004) redes recentemente evoluı́das têm inicialmente o seu fitness diminuı́do, mas isso ocorre apenas porque a rede ainda não teve o tempo necessário para se otimizar. Aliado a isso, redes menos complexas tendem a se otimizar mais rapidamente, provocando um sério problema que consiste na perda de inovações em potencial caso o tempo necessário não seja dado à nova rede e ela seja superada por redes menores, que por sua vez podem não levar a uma solução ótima global. O NEAT realiza a segregação das soluções existentes em uma população, em espécies. Essa segregação tem como objetivo dar tempo às inovações topológicas de aprimorarem suas estruturas antes de terem que competir com um número maior de indivı́duos. Para realizar tal segregação, é feita uma medida de ”‘distância histórica”, que indica o quanto em comum dois 22 Figura 8: Comparação entre cromossomos para realiazação do crossover indivı́duos têm em termos cronológicos. Utilizando-se as marcações de histórico dos genes, a distância entre dois indivı́duos é medida através da seguinte equação: δ = c1 × NE + c2 × ND + c3 ×W Onde E representa a quantidade de genes excedentes entre os dois genomas, D é a quantidade de genes disjuntos e W a diferença de pesos média entre genes que se combinam. As constantes c1 , c2 e c3 servem para dar o devido peso para cada um desses elementos e N é a quantidade de genes no maior genoma. A organização e classificação dos genomas (cromossomos) em espécies se dá da seguinte maneira: no inı́cio do processo evolucionário é criada a espécie de número 1 e o primeiro genoma criado é designado a ela. Os cromossomos subsequentes são comparados com todos as outras espécies existentes. Caso a distância δ entre o genoma que está sendo comparado e o representante da espécie corrente for menor que um limite inferior δt , tal genoma é colocado nesta espécie. Caso o cromossomo não se classifique em nenhuma das espécies, uma nova espécie é criada para acolher este indivı́duo. Esse processo de comparação se inicia pelas espécies mais antigas, o que dá a elas a chance de receber novos indivı́duos. Dessa forma, o NEAT é capaz de identificar espécies que não apresentaram melhoramentos durante muitas gerações (onde não houve adição de novos genomas) e removê-las da população. O NEAT faz 23 uso da divisão de fitness explı́cita, onde cada indivı́duo compartilha de um fitness comum ao seu nicho, evitando que uma espécie se torne muito grande ou que um indivı́duo tome conta de toda uma espécie. Baseado nisto a cada espécie é designado um número máximo de descendentes que esta pode ter. Por fim, a escolha de quem vai ser parte da próxima geração é feita do seguinte modo: a fração de indivı́duos que teve o pior fitness é eliminada, os pais que irão gerar novos indivı́duos para a próxima geração são escolhidos aleatoriamente dos indivı́duos remanescentes e o melhor indivı́duo de cada espécie é mantido para a próxima geração. 3.3.5 MINIMIZAÇÃO DO ESPAÇO DE BUSCA ATRAVÉS DA COMPLEXIFICAÇÃO DE ESTRUTURAS A população inicial do NEAT é constituı́da por indivı́duos com a mesma estrutura topológica (todos os nós de entrada conectados a todos os nós de saı́da, sem camadas ocultas), variando somente os pesos das conexões entre um indivı́duo e outro. Através do processo de evolução novas estrututras são introduzidas através da mutação. O NEAT consegue minimizar o espaço de busca de soluções através do processo de complexificação, evitando que estruturas desnecessárias sejam introduzidas no conjunto de soluções existentes. Isto é feito mantendo na população de soluções somente as novas estruturas que se mostrem aptas através da avaliação de fitness . Desta maneira o NEAT consegue realizar uma busca através de um número mı́nimo de dimensões de pesos, reduzindo de forma significante o número de gerações necessárias para se achar uma solução e garantindo que as redes se tornarão não mais complexas do que o necessário. A este processo dá-se o nome de complexificação, O uso da complexificação, permite que o NEAT busque por espaços de soluções reduzidos, pois uma vez que uma nova estrutura é introduzida, os pesos das conexões já existentes já estão otimizados, sendo necessário apenas realizar o ajuste fino dos pesos das novas conexões para que estas trabalhem bem com as já existentes, ou vice-versa. Em outras palavras, as estruturas mais simples já existentens no inı́cio do processo evolucionário podem não satisfazer o problema de forma global, contudo soluções locais ótimas provavelmente aparecerão nesses momentos iniciais. Com o aumento da complexidade no decorrer do tempo, aumenta-se também a dimensão do espaço de soluções a ser vasculhado. Pelo fato das soluções ótimas para espaços menores de buscas, representados pelas espécies, já terem sido encontradas anteriormente, a busca em um espaço dimensionalmente maior será acelerada pois menos parâmetros precisarão ser otimizados simultaneamente. 24 3.4 NEAT4J O algoritmo criado por Kenneth Stanley possui diversas implementações para diferentes linguagens (Java, C++, C, Delphi e Matlab). Das opções existentes a implementação escolhida para realização dos experimentos foi o NEAT4J, desenvolvida em Java por Matt Simmerson (SIMMERSON, 2006). A escolha de uma implementação feita em Java do NEAT, foi direcionada por estar sendo usado neste trabalho a API JavaBot, também feita em Java, o que facilitaria a integração das duas ferramentas. Apesar de existirem outras duas implementações para a mesma linguagem (ANJI e JNEAT), o NEAT4J foi escolhido por apresentar uma maior facilidade de uso, com tutoriais disponibilizados pelo autor no site do projeto explicando como estender o framework para experimentos que não aqueles que acompanham a distribuição. Para criar um experimento o que precisa ser feito basicamente é criar uma classe que extenda a classe org.neat4j.neat.core.NeuralFitnessFunction, que representa a função de fitness a ser utilizada durante o processo de evolução do Algoritmo Genético, e implementar o método evaluate(Chromosome genotype) de acordo com a avaliação que será feita. Este método recebe como parâmetro um genótipo e a depender da implementação realizada, avalia este mesmo genótipo retornando o valor desta avaliação. Além disso é preciso criar um arquivo de configuração que será lido pela aplicação que irá gerenciar o processo de evolução (org.neat4j.neat.applications.train.NEATGATrainingManager). Este arquivo contém informações relacionadas ao processo evolucionário (probabilidade de mutação, quantidade de épocas, fitness máximo) e à estrutura inicial das redes (quantidade de nós de entrada e de saı́da). Mais detalhes podem ser vistos no Apêndice B. O framework dispõe também de uma classe (org.neat4j.neat.applications.core.NEATApplicationEngine) que permite a visualização do fenótipo de um cromossomo, ou seja, a estrutura da RNA propriamente dita (veja Figuras 9 e 10). Neste modelo de representação pode-se identificar: Nós laranjas - neurônios de saı́da da rede Nós cinzas - neurônios pertecentes à(s) camada(s) oculta(s) Nós verdes - neurônios sem conexões de entrada e agem como bias na rede Nós verdes - neurônios de entrada Arestas vermelhas - conexões diretas Arestas amarelas - conexões recorrentes que conectam dois neurônios distintos 25 Arestas azuis - conexões recorrentes que conectam um neurônio a ele mesmo Figura 9: Fenótipo da RNA com melhor fitness no experimento de XOR Figura 10: Fenótipo da RNA com melhor fintess no experimento do Forex Trading No capı́tulo seguinte será explicado como essas ferramentas foram utilizadas em conjunto para que fosse possı́vel realizar os experimentos relacionados a este trabalho bem como o ambiente necessário para a execução dos mesmos. 26 4 DESCRIÇÃO DO PROBLEMA E IMPLEMENTAÇÃO 4.1 PROBLEMA O uso de técnicas de Inteligência Artificial em jogos de computador visa fazer com que os personagens de um jogo consigam simular inteligência e erros humanos, apresentando portanto um desafio maior aos jogadores, ao mesmo tempo em que propicia uma maior imersão e melhor experiência do jogo (KISHIMOTO, 2004). No jogo UT2004, a caracterı́stica mais importante é o combate. Quando se enfrenta um adversário, o principal objetivo é tentar acertá-lo com disparos de forma a eliminá-lo da partida. Algumas armas em UT2004 como o Assault Rifle e a Machine Gun possuem projéteis com velocidades muito rápidas, fazendo com que seja possı́vel, mesmo com o oponente estando longe e se movimentando acertá-lo apenas mirando na sua localização atual. Contudo existem armas como o Rocket Launcher e a emphLink Gun cujos projéteis são sensivelmente mais lentos, tornando necessário predizer a posição futura do inimigo baseando-se na sua direção, velocidade, distância do atirador e velocidade do projétil referente à arma que está sendo usada no momento. Para se resolver este problema, o uso da fórmula para cálculo de deslocamento com velocidade constante poderia ser usada. Contudo, a velocidade de um jogador em UT2004 não é constante, podendo mudar bruscamente devido a fatores externos (ser atingido por um projétil) e internos (jogador decide mudar o rumo que estava tomando). Além disso, não é possı́vel obter também a aceleração do jogador tão pouco a velocidade do tipo de munição que está sendo usado, impossibilitando desta forma que a posição futura do inimigo possa ser calculada de forma determinı́stica. Esta predição seerá realizada através do uso de Redes Neurais Artificiais devido ao fato destas apresentarem bons resultados em tarefas de aproximação de funções (REZENDE, 2003). 27 Tais redes estarão inseridas em um agente reativo, pois a predição da posição futura do oponente deve ser feita baseada no modelo estı́mulo-resposta para que mesmo mudanças bruscas na velocidade do inimigo possam ser percebidas a tempo. 4.2 IMPLEMENTAÇÃO Como solução para o problema proposto na seção anterior foram implementados dois BOTs para o jogo UT2004 com o intuito de se construir um ambiente para realizar o processo de evolução das redes neurais proposto pelo algoritmo NEAT. Os dois BOTs foram implementados na linguagem Java e fazem uso da API JavaBOTs para se comunicarem com o servidor do jogo. Um dos BOTs, chamado de Alvo, possui a única função de se movimentar constantemente pelo ambiente a fim de prover um alvo móvel para o BOT que faz uso das redes neurais. O Alvo implementa um comportamento reativo bastante simples que pode ser descrito da seguinte forma: Se estiver correndo { não faça nada } Se não { Se viu um ponto de navegação { corra para ele } Se não { gire para a esquerda ou para a direita } O outro BOT, chamado de Atirador, também implementa um comportamento reativo simples. Sua única ação é atirar em um outro BOT caso o veja se movendo. A localização onde o Atirador deve atirar é calculada pela rede neural que está sendo usada no momento. Esta rede possui 8 nós de entrada e um de saı́da. Os três primeiros nós de entrada recebem respectivamente as coordenadas x, y e z da localizaçãoo atual do alvo que está se movimentando; os três nós seguintes recebem as componentes vetoriais da direção para a qual o Alvo se desloca (x, y e z); e os dois últimos nós recebem, respectivamente, a velocidade do BOT Alvo e distância atual entre o Atirador e o Alvo. O nó de saı́da da rede representa a componente t da seguinte fórmula que é usada para o cálculo da posição futura do alvo onde o projétil da arma irá interceptá-lo: S = S0 + v × t 28 Apesar do movimento do Alvo não ser totalmente uniforme no que diz respeito à velocidade, a Equação horária do Movimento Uniforme foi escolhida pois a aceleração e desaceleração dos personagens em UT são bastante bruscas, possibilitando que esta equação obtenha um resultado aproximado do que seria o ideal. O ambiente de treinamento utilizado foi construı́do com a ajuda do Unreal Editor, um editor de mapas de Unreal que acompanha a distribuição do jogo. Este ambiente é constituı́do basicamente de um grande espaço que possui uma extensão de 6000 unidades de medida do jogo UT para cada uma das dimensões espaciais (ver Figura 11). Neste espaço foram colocados 4 spawn points1 . Além disso foram colocados, aleatoriamente, diversos pontos de navegação para que o Alvo pudesse se movimentar na maior variedade de direções possı́veis e assim tentar recriar a situação real de jogo onde os personagens se movimentam em um número variado de direções. Figura 11: Mapa feito no Unreal Editor para treinamento das redes neurais O Atirador expressa como único comportamento, a ação de atirar em um outro jogador que estiver se movendo, sem se preocupar com outros aspectos do jogo, como quantidade de munição restante. Este BOT foi assim implementado para que durante todo o tempo em que uma rede estivesse sendo avaliada, o BOT tivesse somente a preocupação de atirar no seu alvo. Caso fosse implementado um comportamento de busca de itens, por exemplo, o tempo que uma rede teria para ser avaliada poderia estar sendo gasto com outras ações não relevantes para o processo evolucionário. Para evitar, portanto, que o Atirador acabasse sem munição e consequentemente prejudicasse a avaliação das redes, foram criados uma nova arma e um Mutator utilizando-se a linguagem UnrealScript. A arma criada é uma variação do Rocket Launcher e sua principal caracterı́stica é que após um disparo ter sido feito, a munição não é consumida, garantindo assim que o 1 Spawn points são localizações no mapa do jogo onde o personagem controlado pelo jogador surge no mapa no inı́cio da partida ou a cada vez que é morto e retorna à essa partida. 29 Atirador nunca chegue a ficar sem munição. O Mutator criado simplesmente faz com que todos os jogadores comecem com este Rocket Launcher modificado. Para realizar a avaliação de uma rede foi criada a classe UTFitnessFunction cujo método evalutate(Chromossome chromo) passa a rede neural a ser avaliada para a classe BOTLoader, responsável por conectar um BOT ao jogo. O BOTLoader então atribui essa rede ao BOT, que já está conectado no servidor do jogo, e espera 10 segundos para que ela tenha tempo de ser avaliada. Terminado este tempo, é recolhida a quantidade de acertos que o BOT conseguiu fazer com a rede neural corrente e este número de acertos é passado para o algoritmo genético do NEAT4J como o valor do fitness da mesma. Apesar do Atirador atirar somente em alvos móveis, é possı́vel que ele acabe por acertar o Alvo quando este estiver parado. Caso o Atirador tenha previsto uma posição em que o tiro chegaria normalmente atrasado, isto é, o Alvo já tenha passado por essa posição, e por não conseguir ver outro ponto de navegação o Alvo tenha se mantido parado procurando outra direção para ir, haverá a possibilidade deste tiro acertar o mesmo. Como o objetivo de todo o processo é obter uma rede neural que possibilite ao Atirador acertar alvos em movimento, somente é conferida pontuação para acertos em alvos cuja a velocidade esteja acima de zero. O método de avaliação escolhido se baseia na contagem direta de acertos que o Atirador conseguiu utilizando uma determinada rede. Uma outra forma de avaliação, que se mostraria mais eficiente, seria o favorecimento dos indivı́duos que conseguissem gerar uma saı́da que fornecesse a localização com menor erro. Com a abordagem escolhida, se tornou impraticável diferenciar uma rede que fornecesse uma localização totalmente dı́spare a aquela desejada, de uma rede cuja saı́da resultasse numa localização muito próxima do ideal, uma vez que as duas respostas não resultam em acertos, nenhuma das duas redes recebe pontuação e serão avaliadas de forma igual pelo algoritmo genético. Isto dificultou a convergência do algoritmo para uma solução ótima ou próxima da ótima. Caso a rede que forneceu uma resposta próxima da correta pudesse ser pontuada esta teria maiores chances de ser selecionada para gerar descendentes e passar para a próxima geração e desta forma tentar-se-ia chegar a uma solução através da minimização do erro. Tal abordagem somente não foi utilizada no processo de evolução das redes pois não é possı́vel conhecer de antemão qual seria a resposta correta para um determinado conjunto de entradas, nem tão pouco saber em que momento o projétil disparado alcançou sua localização de destino. Diante disto buscou-se através dos parâmetros utilizados pelo algoritmo genético do NEAT (ver apêndice B), incentivar o surgimento de inovações mais rapidamente, a fim de 30 que surgisse um indivı́duo que conseguisse fornecer saı́das corretas para alguns conjuntos de entrada e assim gerar descendentes que por sua vez poderiam chegar a uma solução melhor que o seu ancestral. Os dados de entrada passados às redes neurais encontravam-se todos em unidades de UT, variando da casa das centenas até a casa dos milhares e as funções de ativação dos neurônios das camadas de saı́da e escondidas utilizadas pelo NEAT4J são do tipo sigmóide2 e tangente hiperbólica3 respectivamente, que por sua vez fornecem resultados somente dentro do intervalo [0, 1]. Em consequência disto, durante alguns dos experimentos os resultados gerados pelas redes se restringiam a 0 ou 1, devido aos altos valores de entrada, fazendo com que o algoritmo acabasse por convergir para um mı́nimo local. Viu-se então a necessidade de diminuir esses valores para uma faixa de valores computáveis, faixa esta localizada entre os números 0 e 1 (respostas fornecidas pela função sigmóide), dividindo-os por 104 antes destes serem passados à uma rede, e multiplicando a saı́da da rede por 104 . A convergência do algortimo a partir deste momento deixou de se aproximar para um mı́nimo local e passou a não apresentar resultados significativos. Através das experiências anteriores pôde-se observar que os valores próximos do ideal para as respostas das redes estavam localizados na faixa de valores que vai de 0 a 10 aproximadamente. Para que a rede conseguisse dar respostas que chegassem a essa faixa, seria necessário que as saı́das fornecidas por elas estivessem entre os valores 10−4 e 10−3 , o que se mostrou inviável de se alcançar devido ao tempo necessário para que o processo de evolução chegasse a este nı́vel, uma vez que a maior parte dos menores valores encontrados pelas redes chegavam somente à faixa de 10−1 . Por isso, tentando acelerar o processo de convergência do algoritmo ao invés de multiplicar as saı́das das redes pelo valor de 104 , optouse por multiplicá-las por 10 somente, para assim obter uma faixa de respostas que permitisse ao processo evolucionário encontrar melhores soluções para o problema de forma mais rápida. 1 função sigmóide pode ser representada pela fórmula: P(t) = 1+e t 3 Um função tangente hiperbólica pode ser representada pela fórmula: tahn(x) = 2 Uma e2x −1 e2x +1 31 5 RESULTADOS E DIFICULDADES 5.1 RESULTADOS OBTIDOS Como resultado dos experimentos obteve-se uma rede neural recorrente e com mútliplas camadas (ver Figura 12). Esta rede apresenta ainda uma performance baixa se comparada com o esperado, sendo que a mesma calcula de forma razoável a posição ideal para distâncias médias e longas Figura 12: Rede resultante de 63 gerações com uma população de 60 indivı́duos Esta rede foi gerada após um processo de evolução com uma população de 60 indivı́duos durante 63 gerações, parâmetros muito baixos se comparados com os valores normalmente usados em algoritmos genéticos. Devido aos problemas relatados na seção seguinte, como a impossibilidade de utilizar a funcionalidade de aprendizado distribuı́do no processo de evolução, houve uma limitação na quantidade de indivı́duos e gerações a serem utilizadas dentro do tempo hábil deste trabalho. Contudo a diferença da rede obtida para uma rede no inı́cio do processo de evolução é perceptı́vel. Enquanto que os melhores indivı́duos das primeiras gerações realizavam 32 disparos para localizações muito distantes da localização do Alvo (ver Figura 13), a rede obtida ao final do experimento consegue acertar, a uma distância média, cerca de 70% a 85% dos disparos efetuados e a uma longa distância efetua disparos que acompanham de forma próxima a trajetória percorrida pelo Alvo (ver Figuras 14 e 15), mostrando que mesmo sem conseguir avaliar os indivı́duos através da minimização dos seus erros, o NEAT é capaz de achar uma solução, ainda que parcial, para aproximação da função desejada. Nas figuras 13 e 14 as linhas verdes representam as trajetórias dos tiros dados pelo Atirador. O marco azul no ambiente representa o BOT Atirador e o marco vermelho representa o BOT Alvo. Nessas duas figuras e na Figura 15 o placar na parte superior localizado ao lado esquerdo representa a quantidade de inimigos mortos pelo BOT Alvo e o placar ao lado direito indica quantas vezes o BOT Atirador matou um oponente. Figura 13: Resultado conseguido com uma das redes das primeiras gerações do processo de evolução. 33 Figura 14: Resultado conseguido com uma a rede da 63a geração. Figura 15: Rede obtida da 63a geração acertando o Alvo. 34 5.2 DIFICULDADES ENCONTRADAS Durante o desenvolvimento do projeto algumas problemas com as ferramentas utilizadas foram encontrados. No que diz respeito ao GameBots surgiram problemas relacionados ao recebimento e envio de mensagens e como o personagem reagia a elas. Os principais problemas foram: • Quando enviado o comando ”SHOOT” com o ID como parametro comandos posteriores como o ”RUNTO” apresentam anomalias • Ao contrário do que é dito no protocolo de mensagens, quando um projétil está vindo em direção a um jogador, não é enviada a mensagem do tipo ”PRJ” • O atributo ”PlayerScores” da mensagem do tipo ”GAM” contém somente a pontuação do próprio jogador • Mensagens indentificando o time do jogador também chegam com problemas Durante os experimentos foram observados alguns problemas de comunicação entre o BOT e o jogo, ocasionando muitas vezes na ausência de qualquer tipo de ação do BOT Alvo pelo fato deste não estar recebendo as informações sensoriais corretamente. Em consequência disso, muitos experimentos foram prejudicados, sendo necessário então iniciar um outro processo desde o inı́cio a cada vez que isso ocorria. Além disso, o jogo Unreal Tournament 2004 apresentou o problema de conectar um BOT do próprio jogo no instante em que um BOT implementado com o uso do JavaBOT ou um jogador humano deixava a partida. Isso impediu que a funcionalidade de aprendizado distribuı́do do NEAT4J, onde um servidor distribui indivı́duos para serem avaliados por aplicações remotas (SIMMERSON, 2006), pudesse ser integrada a tempo com o UT2004. Caso a integração da funcionalidade com o jogo pudesse ter sido feita a tempo seria possı́vel realizar experimentos com populações maiores e durante mais gerações, uma vez que mais de um indivı́duo da população de possı́veis soluções estariam sendo avaliados de forma paralela. Com relação ao NEAT4J, o único problema encontrado foi a falta da possibilidade de salvar uma determinada população para, caso o experimento fosse parado, este pudesse ser continuado posteriormente do ponto de parada. Existe o método implementado para tal, contudo ao carregar uma população salva previamente, o programa acaba por lançar uma exceção no momento em que se termina a avaliação desta população e irá ser realizada a segregação dos indivı́duos por espécie. 35 6 CONCLUSÃO Este trabalho realizou portanto, a implementação de um Bot, no jogo Unreal Tournament 2004, que faz uso de uma Rede Neural Artificial com o intuito de prever a posição ideal para se fazer um disparo com uma arma de forma que haja tempo para o projétil atingir um alvo em movimento. Tal rede foi obtida através do uso de algoritmos genéticos utilizandose o framework NEAT4J, uma implementação do algoritmo NEAT, capaz de evoluir tanto as conexões sinápticas como a estrutura topológica de redes neurais. O uso de tal algoritmo permitiu que durante os experimentos realizados, não fosse necessário haver nenhum estudo com relação à melhor topologia de rede a ser utilizada para o problema proposto, pois o processo evolucionário se encarregou de adicionar às redes somente estruturas úteis para a resolução do problema, conseguindo gerar uma rede com uma topologia complexa como a da Figura 12. Caso fosse adotada a abordagem convencional de se utilizar uma rede com estrutura fixa, seriam necessários um tempo e esforço muito maiores para se descobrir que a estrutura encontrada através do NEAT deveria ser utilizada para resolver o problema. Devido a problemas com as ferramentas utilizadas bem como à falta de determinadas informações no ambiente de simulação, a obtenção de uma rede que generalizasse de forma ideal a função desejada não foi possı́vel. Contudo, mesmo com estas dificuldades, a melhoria nas redes é facilmente percebida, mostrando que dispondo-se de mais tempo é possı́vel encontrar uma solução satisfatória para o problema proposto, colocando o NEAT como uma opção viável para o estudo e desenvolvimento da Inteligência Artificial na área de jogos eletrônicos, sendo contudo necessário escolher cuidadosamente os parâmetros a serem utilizados pelo algoritmo genético a fim de que o processo evolucionário não fique estagnado e tampouco se torne aleatório. 6.1 TRABALHOS FUTUROS Sugere-se como trabalhos futuros a realização de uma melhor integração do framework NEAT4J com o jogo UT2004, com a finalidade de se utilizar a funcionalidade de aprendizado 36 distribuı́do e também a realização de experimentos a fim de se obter uma rede através do NEAT que generalize: • a mesma função citada neste trabalho, mas que possibilite a predição da localização ideal para o disparo independente da arma que o BOT estiver portando. • uma função que indique para qual estado um BOT que utilize Máquinas de Estado Finito deve ir a depender de um conjunto de informações sensoriais. 37 APÊNDICE A -- PROTOCOLO DE MENSAGENS TROCADAS ATRAVÉS DO GAMEBOTS Mensagens sı́ncronas: BEG - inı́cio de um lote de mensangens sı́ncronas. Time - timestamp do jogo. SLF - informação do estado do personagem. Id - id única, atribuı́da pelo jogo. Rotation - para qual direção o personagem está voltado, em termos absolutos. Location - localização absoluta. Velocity - velocidade absoluta medida em unidades do jogo (unidades UT). Name - nome que é utilizado pelo personagem. Team - a qual time o jogador pertence. 255 significa que ele não está em time algum. 0-3 são os times vermelho, azul, verde e dourado respectivamente Health - quanto de Health ainda resta para o personagem. Começa em 100 e varia de 0 a 200. Weapon - qual arma o personagem está usando. Os nomes das armas incluem: ImpactHammer, Enforcer, Translocator, GESBioRifle, ShockRifle, PulseGun, Minigun2, UT FlakCannon, UT Eightball, WarheadLauncher. CurrentAmmo - o quanto de munição da arma que está usando no momento resta. Armor - o quanto de proteção o BOT possui. Começa em 0 e pode ir até 200. GAM - informação sobre o jogo. 38 PlayerScores - Lista dos scores de cada jogador da partida. Cada elemento dessa lista possui dois valores. O primeiro é o id do personagem que aquele jogador controla e o segundo o valor do score. TeamScores - funciona do mesmo jeito que a mensagen do tipo PlayerScores só que aplicada a times, onde os ids mostrados são os que identificam os times. Esse tipo de mensagen não é enviado quando a partida é no formato DeathMatch. DomPoints - lista de pontos dominados por cada time para o formato de partida Domination. HaveFlag - usado nas partidas do formato Capture The Flag (CTF) caso o personagem controlado pelo BOT esteja com a bandeira de outro time. O valor informado é o número do time dono da bandeira EnemyHasFlag - usado nas partidas de CTF caso um personagem de outro time esteja com a bandeira de outro time PLR - Informações sobre outro jogador no jogo. Mostra somente informações sobre jogadores que são visı́ves. Id - id único atribuı́da pelo próprio jogo. Rotation - para qual direção o personagem controlado pelo jogador está voltado, em termos absolutos. Location - localização absoluta. Velocity - velocidade absoluta medida em unidades do jogo (unidades UT). Team - a qual time o jogador pertence. Reachable - tem valor True se é possı́ve alcançar esse jogador diretamente, o valor será False caso contrário. Possı́veis razões para este atributo ser False é a existência de alguma espécia de obstáculo entre os dois jogadores. Weapon - que tipo de arma o personagem está segurando. NAV - informações sobre um pathnode do jogo. Pathnodes são objetos invisı́veis colocados pelo mapa do jogo que definem caminhos para os BOTs do jogo poderem se guiar, gerando um grafo totalmente conexo que abrange quase todo mapa da partida. Id - um id único atribuı́do a esse pathnode pelo próprio jogo. Location - localização absoluta. Reachable - tem valor True se o jogador pode correr diretamente para este pathnode. 39 MOV - informações sobre objetos que podem se mover (portas, elevatores, etc). Id - id único atribuı́do esse objeto móvel pelo próprio jogo. Location - localização absoluta. Reachable - tem valor True se o jogador pode correr para este objeto. DamageTrig - tem valor True se é necessário atirar no objeto para ativá-lo. Class - classe do objeto móvel. DOM - ponto de dominação de uma partida de Domination. Controller - qual time controla este ponto. FLG - informações sobre uma bandeira. (Somente em partidas de CTF). Id - id único atribuı́do a essa bandeira pelo próprio jogo. Location - localização absoluta. Holder - id do jogador que está carregando a bandeira. Team - time ao qual a bandeira pertence. Reachable - True se o jogador pode correr diretamente para ela. State - indica se a bandeira está com alguém (Held), caı́da no chão (Droped) ou está na base (Home) INV - informações sobre um objeto no chão que pode ser pego (item de inventário). Id - id único atribuı́do a esse item de inventário pelo próprio jogo. Location - localização absoluta. Reachable - tem valor True se o jogador pode correr diretamente para este objeto. Class - uma string representando o tipo do objeto. END - indica final do lote de mensagens sı́ncronas. Time - timestamp do jogo. Mensagens assı́ncronas: NFO - informações sobre o jogo logo após a conexão com o servidor ter sido estabelecida. O BOT deve esperar esta mensagem antes de enviar uma mensagen do tipo INIT para o servidor. 40 Gametype - Qual o formato da partida (BotDeathMatchPlus, BotTeamGame, BotDomination). Level - nome do mapa em que se está jogando. TimeLimit - tempo máximo que o jogo irá durar. FragLimit - número de mortes necessárias para vencer o jogo (somente para o formato BotDeathMatchPlus). GoalTeamScore - número de pontos necessários para um time ganhar a partida (BotTeamGame, BotDomination). MaxTeams - número máximo de times. Varia de 0 a (MaxTeams - 1) (BotTeamGame, BotDomination). MaxTeamSize - número máximo de jogadores por time (BotTeamGame, BotDomination) AIN - enviada pelo servidor quando o personagem adquire um novo item para o inventário. Id - id único atribuı́do a este item pelo próprio jogo. Id baseado em uma string que descreve o tipo do item. Class - uma string representando o tipo do item. VMS - mensagem recebida do canal de chat global. String - mensagem enviada por outro jogador no jogo através do canal de chat global. VMT - mensagem recebida do canal de chat privado. String - mensagem enviada por um jogador do mesmo time através do canal de chat privado do time. VMG - mensagem padrão do jogo recebida de outro jogador. Sender - id único do jogador que enviou a mensagem. Type - tipo da mensagem (comando, xingamento). Id - id da mensagem. ZCF - pés do personagem mudaram de uma zona artificial do jogo para outra. (se entrou na água ou na lava) Id - id único da zona que os pés estão agora. ZCH - a cabeça do personagem mudou de uma zona artificial para outra. Id - id único da zona que a cabeça está agora. 41 ZCB - personagem mudou de uma zona artificial para outra. Id - id único da zona que o personagem está agora. CWP - o personagem trocou de arma (através de comando ou porque a munição da arma terminou. Id - id único da nova arma, baseado no nome da arma. Class - uma string representando o tipo da arma WAL - houve colisão com uma parede. Id - id único da parede onde colidiu. Normal - normal do ângulo que o personagem colidiu. Location - localização absoluta do personagem no momento da colisão. FAL - BOT atingiu a beirada de algum lugar alto. Se o personagem estava caminhando ele não terá caı́do, caso esteja correndo já estará caindo no recebimento desta mensagem. Fell - True se caiu, False se parou na beirada. Location - localização absoluta do personagem BMP - se chocou com outro ator. Id - id único do ator (atores são outros players ou qualquer objeto que posso bloquear seu caminho). Location - localização do objeto com o qual o jogador se chocou. HRP - o BOT escutou alguém pegar algum objeto do chão. Player - id único do jogador que pegou o objeto. HRN - o BOT escutou um barulho (um outro jogador andando ou atirando, uma bala acertando o chão ou um elevador se movendo). Source - id único do ator que fez o barulho. SEE - o BOT viu um outro jogador. Mensagem gerada pelo jogo entre 1 e 2 vezes a cada segundo quando outro jogador está visı́vel. Útil quando o delay entre as mensagens sı́ncronas é muito grande É possı́vel que esteja depreciado. Id - id único do jogador, atribuı́do pelo jogo. 42 Rotation - para qual direção o jogador está voltado em termos absolutos. Location - localização absoluta do personagem controlado por este jogador. Velocity - localização absoluta em unidades de UT. Team - em qual time o jogador está. Reachable - True se o é possı́vel correr em direção a este jogador. Weapon - qual arma o personagem controlado por este jogador está usando. PRJ - projétil vindo em direção ao personagem controlado pelo BOT. Time - tempo estimado para o impacto. Direction - valor de rotação de onde o projétil está vindo. KIL - algum outro jogador morreu. Id - id único do jogador. Killer - id único do jogador que o matou, caso exista. DamageType - string descrevendo o tipo de dano que o matou. DIE - este BOT morreu. Killer - id único do jogador que o matou, caso exista. DamageType - string descrevendo o tipo de dano que o matou. DAM - o BOT sofreu dano. Damage - quantidade de dano sofrida. DamageType - string descrevendo o tipo de dano. HIT - feriu outro jogador. Id - id único do jogador ferido. Damage - quantidade de dano provocada. DamageType - string descrevendo o tipo de dano. PTH - lista de pathnodes em resposta a uma mensagem GETPATH do cliente. Id - id da mensagem idêntica a enviada pelo cliente, permitindo que este possa reconhecer a resposta da sua mensagem. 43 Multiple pathnodes - lista de pathnodes para se chegar no destino desejado na ordem em que precisam ser percorridos. RCH - resultado booleano em resposta a um comando do tipo CHECKREACH. Id - id da mensagem idêntica a enviada pelo cliente, permitindo que este possa reconhecer a resposta da sua mensagem. Reachable - True se o personagem pode se movimentar na direção informada, False caso contrário. From - localização do personagem no momento do check. FIN - enviada quando a partida termina. Commandos: INIT - mensagem enviada para que seja instanciado um personagem no jogo, deve ser enviado antes de poder executar qualquer ação e somente após receber uma mensagem do tipo NFO do servidor. Name - Nome a ser usado pelo BOT. Team - Diz o time ao qual o BOT pertencerá. SETWALK - diz se o personagem irá se movimentar andando ou correndo. Walk - Se tiver valor True o personagem irá caminhar, caso contrário irá correr. STOP - para qualquer movimentação que esteja executando. JUMP - faz o personagem pular. RUNTO - se movimenta para uma determinada localização ou alvo. Target - id único do alvo para o qual deseja se movimentar. Só irá funcionar se o alvo estiver visı́vel, caso contrário o personagem não fará nada. Location - As três coordenadas da localização para onde o personagem deve se movimentar. STRAFE - semelhante ao RUNTO, mas o personagem irá se movimentar em uma direção enquanto está olhando para outro ponto. Location - As três coordenadas da localização para onde o personagem deve se movimentar. 44 Target - id único do alvo para o qual deseja se movimentar. Só irá funcionar se o alvo estiver visı́vel. TURNTO - faz o personagem virar um valor em unidades UT ou para a direção de um alvo ou uma localização. Target - id único do alvo para o qual deseja se movimentar. Só irá funcionar se o alvo estiver visı́vel. Rotation - Valor de rotação que se deseja usar. Três coordenadas em unidades UT em valores absolutos, separadas por espaço ou vı́rgula (2pi = 65535 unidades UT). Esse argumento somente é usado se nenhum alvo for indicado. Location - As três coordenadas da localização para onde o personagem deve se virar. Somente usada se não forem passados nem alvo nem valor de rotação. ROTATE - rotaciona uma determinada quantidade de unidades UT Amount - quantidade em unidaes UT a ser rotacionada, podendo ser negativa para rotacionar no sentido anti-horário Axis - eixo sobre o qual será feita a rotação SHOOT - faz com que o personagem comece a atirar. Location - As três coordenadas da localização para onde o personagem deve atirar. Target - id único do alvo no qual o personagem deve atirar. Se o alvo estiver visı́vel o servidor irá providendicar correção da mira e irá fazer com que o personagem continue mirando no alvo. Caso não esteja visı́vel o personagem somente irá ficar atirando na localização informada. Mesmo informando o alvo ainda é necessário informar a localização. Alt - Faz com que o personagem use o disparo alternativo da arma. CHANGEWEAPON - o personagem troca de arma. Id - id único da arma que o personagem deve usar. Se for usado o valor Best será escolhida a melhor arma que ainda tenha munição. STOPSHOOT - faz o personagem parar de atirar. CHECKREACH - checa se é possı́ve se movimentar diretamente para algum lugar ou algo sem ser obstruı́do. 45 Target - id único do alvo para o qual se deseja fazer o check. É necessário que ele esteja visı́vel. Location - As três coordenadas da localização alvo. Somente usado se nenhum alvo for informado. Id - id da mensagem criada pelo BOT para ser retransmitida em resposta pelo servidor, assim é possı́vel identificar a resposta da mensagem enviada. From - localização do personagem no momento do check. GETPATH - obtém um caminho para uma localização especı́fica. É retornada uma lista ordenada de pathnodes. Location - As três coordenadas da localização para onde o personagem deve ir. Id - id da mensagem criada pelo BOT para ser retransmitida em resposta pelo servidor, assim é possı́vel identificar a resposta da mensagem enviada. MESSAGE - envia uma mensagem para todos ou somente para os integrantes do time do BOT. String - string a ser enviada. Global - Se o valor for True, envia a mensagem para todos, caso contrário somente para o time. PING - envia um PING para o servidor que irá responder com um PONG. 46 APÊNDICE B -- PARÂMETROS DE CONFIGURAÇÃO PARA O ALGORITMO GENÉTICO UTILIZADO PELO NEAT4J PROBABILITY.MUTATION - Controla a mutação das conexões e o fator da função sigmóide dos neurônios Faixa de Valores - 0 - 1 Valor usado - 0.45 PROBABILITY.CROSSOVER - Define a taxa em que indivı́duos da mesma espécie realizam o crossover Faixa de Valores - 0 - 1 Valor usado - 0.35 PROBABILITY.ADDLINK - Define a taxa em que novas conexões são adicionadas. Não leva em conta o parâmetro que lida com conexões recorrentes. Faixa de Valores - 0 - 1 Valor usado - 0.3 PROBABILITY.ADDNODE - Taxa de adição de num novo neurônio a uma conexão ativa. Faixa de Valores - 0 - 1 Valor usado - 0.5 PROBABILITY.MUTATEBIAS - Taxa em que os bias dos neurônios sofrem mutação. Faixa de Valores - 0 - 1 47 Valor usado - 0.3 PROBABILITY.TOGGLELINK - Define a taxa em que uma conexão entre dois neurônios pode mudar seu estado de ativada para desativada e vice-versa. Faixa de Valores - 0 - 1 Valor usado - 0 PROBABILITY.WEIGHT.REPLACED - Define com que frequência o peso de uma conexão é mudado aleatoriamente para outro valor. Faixa de Valores - 0 - 1 Valor usado - 0.1 EXCESS.COEFFICIENT - Coeficiente especı́fico do NEAT. Provê a importância de genes do tipo excess no cálculo de compatibilidade entre cromossomos. Faixa de Valores - = 0 Valor usado - 1 DISJOINT.COEFFICIENT - Coeficiente especı́fico do NEAT. Provê a importância de genes do tipo disjoint no cálculo de compatibilidade entre cromossomos. Faixa de Valores - >= 0 Valor usado - 1 WEIGHT.COEFFICIENT - Coeficiente especı́fico do NEAT. Provê a importância da diferença de pesos em conexões de genes no cálculo de compatibilidade entre cromossomos. Faixa de Valores - >= 0 Valor usado - 0.4 COMPATABILITY.THRESHOLD - Parâmetro que serve para definir se um cromossomo é compatı́vel com outro. Faixa de Valores - >= 0 Valor usado - 0.5 COMPATABILITY.CHANGE - Se esse valor for 0, o parâmetro COMPATABILITY.THRESHOLD permanecerá inalterável. Se for maior do que 0 este parâmetro irá modificar o COMPATABILITY.THRESHOLD dinamicamente a fim de manter o número de espécies da população equivalente ao SPECIE.COUNT. 48 Faixa de Valores - >= 0 Valor usado - 0.1 SPECIE.COUNT - Quando o COMPATABILITY.CHANGE é diferente de 0, define a quantidade de espécies que se deseja ter na população. Faixa de Valores - >= 1 Valor usado - 3 SURVIVAL.THRESHOLD - Este valor define a fração dos indivı́duos da espécie que irão realizar o crossover. Faixa de Valores - >= 0 Valor usado - 0.4 SPECIE.AGE.THRESHOLD - Indica a partir de que idade uma espécie começa a ser penalizada, multiplicando-se o seu fitness por SPECIE.OLD.PENALTY. Faixa de Valores - >= 1 Valor usado - 80 SPECIE.YOUTH.THRESHOLD - Indica até que idade uma espécie tem seu fitness multiplicado por SPECIE.YOUTH.BOOST. Faixa de Valores - >= 1 Valor usado - 10 SPECIE.OLD.PENALTY - Valor multiplicado pelo fitness de espécies que têm idade maior que o SPECIE.AGE.THRESHOLD. Faixa de Valores - >= 0 Valor usado - 0.8 SPECIE.YOUTH.BOOST - Valor multiplicado pelo fitness de espécies que têm idade menor que o SPECIE.YOUTH.THRESHOLD. Faixa de Valores - >= 0 Valor usado - 1.5 SPECIE.FITNESS.MAX - Indica até que idade uma espécie que não apresente melhoras continua na população. 49 Faixa de Valores - >= 1 Valor usado - 15 OPERATOR.XOVER - A classe do operador de crossover. Faixa de Valores Valor usado - org.neat4j.neat.core.xover.NEATCrossover OPERATOR.FUNCTION - Classe da função de fitness. Faixa de Valores - classe que realize a operação de crossover Valor usado - neat.UTFitnessFunction OPERATOR.PSELECTOR - Classe do operador de seleção de pais. Faixa de Valores - classe que implementa o método de seleção Valor usado - org.neat4j.neat.core.pselectors.TournamentSelector OPERATOR.MUTATOR - Classe do operador de mutação. Faixa de Valores - classe que implementa a operação de mutação Valor usado - org.neat4j.neat.core.mutators.NEATMutator MAX.PERTURB - Módulo do valor que pode ser aplicado como perturbação a uma conexão. Faixa de Valores - >= 0 Valor usado - 3.0 MAX.BIAS.PERTURB - Módulo do valor que pode ser aplicado como perturbação a um bias. Faixa de Valores - >= 0 Valor usado - 1.0 FEATURE.SELECTION - Define a estrutura inicial das redes. Se for true um nó de entrada é conectado a um nó de saı́da aleatoriamente até que todas as saı́das tenham uma conexão. Se for false todos nós de entrada são conectados a todos os nós de saı́da. Faixa de Valores - true ou false Valor usado - false RECURRENCY.ALLOWED - Define se as conexões recorrentes são mantidas após um cromossomo ter sofrido mutação. 50 Faixa de Valores - true ou false Valor usado - true INPUT.NODES - Quantidade de nós de entrada das redes. Faixa de Valores - >= 1 Valor usado - 8 OUTPUT.NODES - Quantidade de nós de saı́da das redes. Faixa de Valores - >= 1 Valor usado - 1 NN.CONFIG - Localização do arquivo de configuração das redes. Pode ser relativo ou absoluto. Faixa de Valores - path do arquivo de configuração Valor usado - /home/cicero/neat4j/ut/ut ga neat.net ELE.EVENTS - Determina se eventos de extinção de vida ou ELEs (Extinct Life Events) irão ocorrer ou não. Isso faz com que o limite de compatibilidade inicial seja multiplicado por 5 e os n% melhores indivı́duos serão mantidos. Faixa de Valores - true ou false Valor usado - false ELE.SURVIVAL.COUNT - Indica a porcentagem da população que irá sobreviver na ocorrência de um ELEs. Faixa de Valores - >= 0 Valor usado - 0.1 ELE.EVENT.TIME - De quanto em quanto tempo um ELE irá ocorrer. Faixa de Valores - >= 1 Valor usado - 1000 KEEP.BEST.EVER - Determina se a espécie com o melhor indivı́duo encontrado até agora será mantida mesmo depois que o valor do SPECIE.FITNESS.MAX seja excedido. Faixa de Valores - true ou false 51 Valor usado - false EXTRA.FEATURE.COUNT - Indica a quantidade extra de genes dentro de um cromossomo que não nenhum efeito topológico e são usadas para a definição de valores de entrada evoluı́dos. Faixa de Valores - >= 0 Valor usado - 0 POP.SIZE - Indica a quantidade de cromossomos que a população terá. Faixa de Valores - >= 1 Valor usado - 60 NUMBER.EPOCHS - Número de épocas que serão executadas durante o processo de evolução. Faixa de Valores - >= 1 Valor usado - 100 TERMINATION.VALUE - Valor de fitness que se alcançado fará o algoritmo parar independente de ter sido chegado ao número máximo de épocas. Faixa de Valores - >= 0 Valor usado - 100000000 NATURAL.ORDER.STRATEGY Se for true - Quanto menor o fitness, melhor é o indivı́duo. Se for false quanto maior o fitness melhor será o indivı́duo. Faixa de Valores - true ou false Valor usado - false SAVE.LOCATION - Local onde deve ser salvo o melhor indivı́duo de cada geração. Faixa de Valores - String Valor usado - /home/cicero/neat4j/ut/ut neat ga.ser 52 REFERÊNCIAS BITTENCOURT, G. Inteligência Computacional. Departamento de Automação e Sistemas, Universidade Federal de Santa Catarina, 2005. Disponı́vel em: <http://www.das.ufsc.br/gia/softcomp/>. GAMEBOTS. University of Southern California, 2002. Disponı́vel em: <http://gamebots.sourceforge.net/>. HAYKIN, S. Redes Neurais Principios e Pratica. [S.l.]: Bookman, 2001. JBP. The Mutators FAQ. Planet Unreal, 2000. Disponı́vel em: <http://www.planetunreal.com/mutation/FAQ/>. KISHIMOTO, A. Inteligência Artificial em Jogos Eletrônicos. 2004. LAIRD, J. E.; LENT, M. van. Human-level AI’s killer application: Interactive computer games. In: AAAI/IAAI. [s.n.], 2000. p. 1171–1178. Disponı́vel em: <citeseer.ist.psu.edu/laird00humanlevel.html>. NERO. 2005. Disponı́vel em: <http://www.nerogame.org/>. NILSSON, N. J. Artificial Intelligence - A New Synthesis. [S.l.]: Morgan Kaufmann, 1998. 513 p. ISBN 7-111-07438-6. REZENDE, S. O. Sistemas Inteligentes Fundamentos e Aplicacoes. [S.l.]: Manole, 2003. RIT. Gamebots. Rochester Institute of Technology - Department of Computer Science, 2005. Disponı́vel em: <http://www.cs.rit.edu/j̃db/gamebots/>. ROZICH, R. JavaBot for Unreal Tournament. [S.l.]: Projeto JavaBot, 2002. SIMMERSON, M. NEAT4J. 2006. Disponı́vel em: <http://utbot.sourceforge.net>. STANLEY, K. O. Efficient Evolution of Neural Networks through Complexification. Tese (Doutorado) — The University of Texas at Austin, Agosto 2004. SWEENEY, T. UnrealScript Language Reference. Epic MegaGames, Inc, 1998. Disponı́vel em: <http://unreal.epicgames.com/UnrealScript.htm>.