1. O Jogo
Transcrição
1. O Jogo
Algoritmo do Kalah Romildo Martins da S Bezerra Julho 2001 Índice 1. O Jogo .........................................................................................3 2. Mudanças para Implementação .......................................................3 3. O Algoritmo ..................................................................................4 3.1 MINIMAX..................................................................................4 3.2 Poda Alpha-Beta .......................................................................4 3.3 Estrutura Utilizada.....................................................................5 4. A linguagem .................................................................................5 5. Análises .......................................................................................5 5.1 Análise de Jogadas x Altura da Árvore no MINIMAX .......................5 5.2 Cálculo do tempo de resposta no MINIMAX...................................7 5.3 Análise da Poda Alpha-Beta ........................................................9 5.4 Discussão sobre a função utilidade ..............................................9 6. Anexos .......................................... Erro! Indicador não definido. 1. O Jogo O objetivo do jogo é colocar pelo menos metade das pedras (18) em seu Kalah. Uma jogada consiste em escolher um buraco não-vazio, tomar todas as pedras na mão e distribuí-las, em sentido anti-horário. Na distribuição devem ser incluídos os buracos do jogador e os do oponente, além do Kalah do jogador. Já o Kalah do oponente não recebe pedras. O que acontece a seguir depende de onde a última pedra é colocada: • Caso a distribuição termine no Kalah do jogador, este tem o direito de jogar novamente. Por exemplo (é a vez do jogador superior e o buraco escolhido está marcado pelo símbolo “*”) * (3 3 3 3 3 3) 0 0 (3 3 3 3 3 3) • (4 4 0 3 3 3) 1 0 (3 3 3 3 3 3) Caso a distribuição termine em um buraco de um jogador que não contenha nenhuma pedra, a última pedra e todas as pedras que se encontrarem no buraco do oponente, diretamente em frente ao buraco onde terminou a distribuição, são colocadas no Kalah do jogador. Por exemplo (a vez é do jogador inferior) (6 6 1 4 6 0) 1 4 (3 3 3 0 0 1) * (6 6 1 0 6 0) 1 9 (0 4 4 0 0 1) Duas condições terminam o jogo: • se um dos jogadores obtiver mais da metade das peças em seu Kalah, ele é o vencedor; • caso todos os buracos de um jogador fiquem vazios, o outro jogador coloca todas as peças restantes em seu Kalah e a vitória é daquele que tiver mais pedras no Kalah. 2. Mudanças para Implementação Com o objetivo de tornar justa a medição do tempo de uma jogada, optei por remover a condição de jogada adicional do jogo original. Isto também torna possível visualizar árvores de busca de alturas maiores, beneficiando a analise do Kalah. 3. O Algoritmo Para a implementação e análise do Jogo do Kalah utilizei o MINIMAX e a Poda Alpha-Beta. 3.1 MINIMAX O MINIMAX é utilizado num jogo de dois jogadores (MIN e MAX). Um jogo pode ser a definido como um problema de busca com os componentes abaixo: • Estado inicial - inclui o tabuleiro e o dono da próxima jogada • Um conjunto de operadores - define os movimentos legais do jogo • Um teste terminal - determina o término do jogo • A função utilidade - fornece um valor numérico à saída do jogo. Geralmente 1 para vitória, 0 para empate e -1 para derrota Para decidir qual o melhor movimento, o algoritmo deve seguir as etapas: • Gerar toda árvore do jogo até os estados terminais • Aplicar a função utilidade aos nós terminais • Nos nós superiores, usar a função utilidade dos estados terminais propagando os valores até o nó raiz • Quando a propagação destes valores chegar a um ponto onde seja a vez de MAX jogar, este deverá optar por maximizar sua jogada A complexidade deste algoritmo é O(bm) onde b é a quantidade de movimentos a cada instante (no caso do Kalah 6) e m a altura da árvore. Nota-se que este algoritmo é teoricamente perfeito em relação a qualidade das jogadas, porém o custo associado degrada o desempenho do algoritmo fazendo com que ele nào seja usado em jogos reais. No Kalah, será utilizado a proposta feita por Shannon. O algoritmo cortará a árvore de busca em um nível e aplicará a função heurística de avaliação, denominada de função utilidade, às folhas da árvore. Mas nos perguntamos : Que função heurística usar? No caso do Kalah está sendo usada uma função que calcula a diferença entre os "Kalares". A escolha desta função será discutida no capítulo 5. 3.2 Poda Alpha-Beta Com o objetivo de recorrer a uma quantidade menor de nós de busca utilizamos o algoritmo Poda Alpha-Beta. A Poda deve retornar o mesmo movimento que o MINIMAX, mas sem levar em consideração todos os nós da árvore. Para fazer isto, o algoritmo baseia-se no princípio de que se um jogador tem uma melhor escolha num nó pai ou outro do mesmo nível do pai de n, este nó n nunca será alcançado, logo podemos podá-lo. A eficiência da Poda depende da ordem que seus filhos serão acessados. Mas em todos os casos o custo ainda continua exponicial, O(bd/2). 3.3 Estrutura Utilizada Foi utilizada uma estrutura denominada tab_struct que contém quatorze (14) posições de números inteiros distribuídos da seguinte forma : 00 (13 12 11 10 09 08) 07 (01 02 03 04 05 06) Para visualizar o código completo em linguagem C, vide Anexo 1. 4. A linguagem A linguagem C foi escolhida para implementação do Kalah devido as seguintes características : • Velocidade - O código produzido pelo compilador C tende a ser muito eficiente chegando ater velocidade próxima ao seu equivalente assembly. • Suporte a programação modular - Com isso a quantidade de tempo exigida para compilar/executar um programa grande ou que exija processamento é menor. • Eficiência de memória - Os programas em C tendem a ser muito eficientes no tratamento da memória. Certamente um item muito importante para o algoritmo do Kalah usando Poda Alpha-Beta de altura variável. A portabilidade também é uma característica importante, mas não foi colocada aqui, pois foram usadas bibliotecas fora do padrão ANSI C. Com pequenas modificações o algoritmo pode ser compilado em qualquer plataforma. 5. Análises 5.1 Análise de Jogadas x Altura da Árvore no MINIMAX O algoritmo do Kalah mostrou-se bastante eficiente para árvores de altura maior ou igual a seis (h >= 6). Com h=6 o tempo de resposta para essa altura é de 0,1099 segundos e com essa altura é possível vencer uma grande quantidade de jogadores. Vamos analisar a tabela a seguir: Jogada Altura 1 Altura 2 Altura 3 Altura 4 Altura 5 Altura 6 Altura 7 6 2 4 5 6 1 4 3 1x1 3x1 8x2 9x4 10x6 12x7 13x7 16x12 0x1 6x1 7x4 11x5 12x6 13x7 15x7 16x8 0x1 1x1 9x2 12x4 13x5 13x6 16x6 18x7 0x1 1x1 9x2 12x4 13x5 13x6 16x6 18x7 0x1 1x1 9x2 12x4 13x5 13x6 16x6 18x7 0x1 1x1 9x2 12x4 13x5 13x6 16x6 18x7 0x1 1x1 9x2 12x4 13x5 13x6 16x6 18x7 Observa-se que neste caso, a partir da altura 3, as jogadas passam a convergir. Com uma grande quantidade de testes, concluímos que em mais de 92% dos casos as jogadas tendem a convergir a partir da altura 5. Podemos denominar este fenômeno de convergência de jogadas. Outro fenômeno interessante é verificado na tabela a seguir : Jogada Altura 1 Altura 2 Altura 3 Altura 4 Altura 5 Altura 6 Altura 7 5 2 1 3 4 5 6 2 1x1 3x5 5x6 6x13 7x15 9x16 11x18 12x18 0x1 6x2 9x3 10x4 11x5 11x6 11x7 13x8 0x1 1x6 2x6 3x13 4x16 4x17 9x18 9x19 0x1 1x6 2x6 3x13 4x16 4x17 9x18 9x19 0x1 6x2 9x3 10x4 11x5 11x6 12x7 19x9 0x1 6x2 9x3 10x4 11x5 11x6 12x7 19x9 0x1 6x2 9x3 10x4 11x5 11x6 12x7 19x9 Teoricamente a medida que aumentamos a altura da árvore de busca as jogadas conduziriam a um melhor resultado do computador. Nesta tabela acima observa-se que para as alturas 3 e 4, o caminho da árvore percorrido pelas jogadas não conduzem a um melhor resultado, isso é denominado como anomalia da Poda. Podemos atribuir este problema à duas causas: • Análise incorreta - Critica-se este modo de avaliação da altura da árvore x eficiência do algoritmo, pois com cada altura diferente as jogadas tendem a mudar, logo dificilmente um jogador entraria com a mesma sequ6encia de dados para jogos com alturas diferentes, simplesmente pelo fato de que o estado dos tabuleiros são também diferentes. • Falha da função de avaliação - A função criada retorna a diferença entre o Kalah do computador e do jogador. Talvez exista uma função mais sofisticada baseada na distribuição das pedras no tabuleiro e retorne um resultado mais eficaz. Sinceramente não acredito muito nesta hipótese. A função de avaliação será discutida em outro tópico deste capítulo. Depois destas análises recomendo a execução do jogo com altura 7. 5.2 Cálculo do tempo de resposta no MINIMAX Vencer o jogo é o mais importante. Entretanto devemos analisar o custo para tal. Usamos as seguintes linhas de código para medir o tempo : else { // computador joga start = clock(); tab = kalah_arvore (tab, altura, 0); end = clock(); printf("\nMinha Jogada "); exibe_tab(tab); placar(tab); printf("\nExecutado em %.4f segundos", (end-start)/(CLK_TCK)); } A função clock retorna o número de pulsos no relógio do sistema desde o início da execução do programa. Ao subtrairmos end por start, calculamos a quantidade de pulsos para executar a função kalah_arvore. E quando dividimos pela macro CLK_TCK o valor de retorno é em segundos Com o uso desse algoritmo acima, obtivemos a seguinte tabela abaixo usando um Processador AMD K6-2 400MHz e 64Mb de RAM. Altura Tempo (s) Razão* 1 2 3 4 5 6 7 8 9 10 11 12 0,0000 0,0000 0,0000 0,0000 0,0349 0,1099 1,0989 3,9011 39,9451 136,9780 1379,5604 4840,3846 3,1490 9,9991 3,5501 10,2395 3,4292 10,0715 3,5087 * Corresponde a quantidade de vezes que o tempo de uma altura n é maior que o de uma altura n-1 Observe que a razão 6-5, 8-7, 10-9 e 12-11 giram em torno de 3. Enquanto 7-6, 9-8 e 11-10 estão na faixa de 10. Com o uso do software matemático Maple 5.1 e os conceitos de interpolação numérica, achamos uma função que representa esta razão e montamos a tabela a seguir. Altura Tempo (s) Razão Aproximação* Erro** 1 2 3 4 5 6 7 8 9 10 11 12 0,0000 0,0000 0,0000 0,0000 0,0349 0,1099 1,0989 3,9011 39,9451 136,9780 1379,5604 4840,3846 3,1490 9,9991 3,5501 10,2395 3,4292 10,0715 3,5087 3,4093 10,1034 3,4093 10,1034 3,4093 10,1034 3,4093 8,27% 1,04% -3,97% -1,33% -0,58% 0,32% -2,83% * Corresponde à razão calculada por interpolação ** Indica o erro da nova razão calculada Partindo do princípio de que teríamos memória infinita, podemos estimar os futuros valores do tempo de resposta do algoritmo do Kalah. Altura 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Novos Tempos 0,0000 s 0,0000 s 0,0000 s 0,0000 s 0,0349 s 0,1100 s 1,0990 s 3,9013 s 39,9454 s 136,9798 s 22,99 min 1,34 h 13,58 h 1,93 dias 19,50 dias 66,47 dias 1,84 anos 6,27 anos 63,38 anos 216,07 anos 2,18 milênios Caso o Kalah fosse um jogo com limitação de tempo, bastava apenas criar uma função que parasse de construir a árvore de busca no momento de passar a jogada, e depois a função kalah_arvore retornava o melhor valor. Ainda continuo recomendando a execução com altura 7. Quando o aplicativo executava utilizando árvores maiores que nove (9) o seguinte código era emitido pelo compilador. ---------------------------------------------------------------------------UNHANDLED EXCEPTION 0C at 00A7:2FC3 Error code: 00008934 CX=0000 SI = 0000 DI = 09C9 BP = 0F98 SP = 0F8E CS = 00A7 Limit =FFFF DS = 009F Limit =FFFF ES = 00E7 Limit =FFFF SS = 0087 Limit =0FFF - 00A7:2FBB 00 00 00 00 00 00 00 0F 00 02 00 05 00 00 00 04 DX=0000 You may hit [T] to terminate, but you should reboot as soon as possible, as the system is unstable ... 5.3 Análise da Poda Alpha-Beta 5.4 Discussão sobre a função utilidade A função utilidade escolhida é a diferença entre os dois kalah. Como saber se esta função é a melhor função? Teoricamente ela seria a melhor função se conduzir o algoritmo para os mesmos caminhos do MINIMAX. Como implementar a árvore inteira do jogo não foi possível, nunca saberemos o quanto esta função utilidade é boa. Nota-se que a função escolhida não representa a real situação do jogo por não levar em consideração a distribuição das pedras no kalah. Dessa distribuição verificamos: • A importância de manter o maior número de pedras no seu lado, pois se o oponente não tiver jogadas por falta de pedras, todas as pedras do seu lado, irão para o seu kalah. • A importância de manter os primeiros buracos sempre ocupados, para que o computador nunca consiga acabar sua distribuição em kalah seu vazio. Como garantir que o oponente não vença adotando o critério de acabar a distribuição das pedras em um buraco vazio no lado dele do tabuleiro e vença o jogo? Temos que distribuir o peso dessas funções, mas como faremos isso? Talvez nunca conseguiremos fazer uma função heurística imbatível, pois ela deveria analisar todos os tipos de estratégia dos oponentes.
Documentos relacionados
Trabalho de Implementação – Jogo Reversi 1 Introdução
Neste trabalho foram utilizadas 2 heurísticas para avaliação das possíveis jogadas. A primeira foi proposta por [1] e é apresentada na seção 1.3.1. A segunda heurística foi proposta pela equipe de ...
Leia mais