No post anterior implementamos o cliente Tetris e testamos sua funcionalidade. Na segunda parte da serie, daremos inicio a parte realmente interessante: AI. Para comecar, a classe TetrisAgent sera discutida em detalhes, ja que os agentes serao a populacao alvo do algoritmo. Alem disso iremos implementar duas classes que definem habilidades importantes: listar os lances possiveis e avaliar as posicoes resultantes.Classe TetrisAgent Essa e a classe base de nossa AI, e representa um jogador de Tetris. Tres variaveis de instancia sao definidas: genes, clearedRowsArray e fitness. A habilidade de cada agente e expressa atraves de seus genes, onde cada slot da array genes corresponde ao peso atribuido a determinada caracteristica da grade. Sabemos por experiencia que determinados padroes da grade resultam em maiores chances de efetuar pontos, ou menores chances de game over. No nosso caso, a array genes corresponde ao peso de 7 caracteristicas distintas: 3 delas sao consideradas basicas e 4 sao menos obvias. Dentre as basicas temos: numero de linhas feitas, altura maxima da pilha e numero de buracos nao conectados. As outras sao: numero de buracos conectados, numero de blocos acima de buracos, numero de pocos e nivelacao da grade. Para efeito de testes, setamos genes com valores arbitrarios entre 0 e 1, mas fique a vontade para alterar esses valores e observar o efeito no desempenho do agente. Como comentado acima, os valores finais serao determinados pela GA. Nossa tarefa consiste em determinar o melhor agente possivel, ou seja, quais sao os valores de genes que resultam no maior numero medio de linhas removidas? Durante a execucao da GA, cada agente jogara uma sequencia de n games aleatorios, onde a sequencia de pecas em cada um dos n games sera fixada semeando-se o gerador numerico. Em outras palavras, todos os agentes enfrentarao os mesmo jogos, o que nos permite balancear o efeito de um agente ruim receber uma sequencia boa de pecas, ou um agente bom receber uma sequencia ruim de pecas. A variavel fitness quantifica as definicoes acima, e sera calculada como a media de linhas removidas durante os n games. Finalmente, temos a variavel clearedRowsArray, que ira armazenar o numero de linhas removidas em cada jogo. Repare que estamos utilizando uma ArrayList sincronizada, pois como comentado no post anterior, cada game sera executado em uma Thread separada, e tambem sabemos que o mesmo agente sera compartilhado por n Threads. O problema em utilizar uma ArrayList padrao surge ao invocar agent.clearedRowsArray.add(linhas): caso duas Threads se esbarrem nesse metodo, uma ConcurrentModificationException seria lancada, terminando o programa. Os metodos definidos sao simples: eval recebe uma array com a configuracao da grade, e utiliza os metodos estaticos da classe BoardEvaluator para avaliar a posicao. A interface Comparable e implementada, nos permitindo ordenar agentes facilmente, o que sera util mais adiante. O metodo estatico randomAgent simplesmente cria um novo agente e seta seus genes com valores randomicos entre 0 e 1. Por fim, cloneAgent retorna um novo objeto com os mesmos genes do agente que invoca o metodo. Segue a classe TetrisAgent: import java.util.ArrayList;import java.util.Collections;import java.util.List;import java.util.Random;public class TetrisAgent implements