Universidade Federal do ABC ufabc J2 Velha Uma Implementação Java do Jogo da Velha Utilizando o Algoritmo MiniMax André Filipe de Moraes Batista [email protected] Luis Fernando de Oliveira Jacintho [email protected] Disciplina de Inteligência Artificial Profo Jerônimo Pellegrini Santo André, Junho de 2008 Sumário 1 Jogos em IA 1.1 Minimax - Algoritmo de Busca Competitiva . . . . . . . . . . . . . . . . . 1 1 2 J2 Velha: Uma Abordagem Java ao Jogo da Velha 2.1 J2 Velha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Exemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 4 8 Anexos 16 A J2 Velha: Códigos A.1 Classe Velha.java . . . A.2 Classe Tabuleiro.java A.3 Classe Sucessor.java . A.4 Classe Minimax.java . . . . . . 16 16 18 20 21 . . . . 27 27 30 34 35 . . . . . . . . . . . . . . . . B J2 Velha: Novas Funcionalidades B.1 Classe Velha.java . . . . . . . B.2 Classe Tabuleiro.java . . . . B.3 Classe Sucessor.java . . . . . B.4 Classe Minimax.java . . . . . . . . . . . . . . . . . . . . . . ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Capı́tulo 1 Jogos em IA Para a maioria das pessoas o termo jogo é considerado como um passatempo do dia-a-dia. Para as crianças serve como um modo de fugir aos trabalhos de casa e entrar em um mundo virtual de infinitas possibilidades. Para os adultos o termo jogo pode invocar imagens de jogadores que procuram estratégias que lhes dêem vantagens sobre os adversários. Ou seja, o resultado do jogo é determinado pelas estratégias utilizadas pelos jogadores. O ramo da Matemática que pensa de semelhante maneira é denominado Teoria dos Jogos. A Teoria dos Jogos tem por objetivo visualizar qualquer ambiente multiagente como um jogo, desde que o impacto de cada agente sobre os outros seja, de algum modo, significativo. Os jogos são uma das áreas mais antigas desenvolvidas em Inteligência Artificial (IA). Em 1950, desde que os computadores se tornaram programáveis, o primeiro jogo de xadrez foi criado por Claude Shannon e por Alan Turing. Desde então diversos progressos ocorreram nesta área, de tal forma que os sistemas atuais são capazes de rivalizar e ganhar de um dos melhores jogadores de xadrez da história, Garry Kasparov. Em meados de 1996 ocorreu o primeiro confronto entre Garry Kasparov e o Deep Blue. Trata-se de um super computador de alta performance desenvolvido pela IBM, seu código de programação é em linguagem C e é executado no sistema operacional AIX. O resultado é uma máquina escalável capaz de calcular entre 100 e 200 bilhões de jogadas em aproximadamente 3 minutos. No primeiro confronto entre os dois, a vitória foi de Kasparov. Até que em 1997 a IBM desdobrou-se em constantes desenvolvimentos e atualizações de modo a melhorar o desempenho do Deep Blue. O resultado de tanto esforço foi que Garry Kasparov foi vencido em 1997 pelo Deep Blue. A Figura 1.1 mostra uma cena desta disputa. 1.1 Minimax - Algoritmo de Busca Competitiva Em um ambiente multiagente os agentes convivem com situações de cooperação e competição. Um ambiente competitivo é aquele em que as metas dos agentes estão em constante 1 1.1. Minimax - Algoritmo de Busca Competitiva 2 Figura 1.1: Cena de um Disputa de Kasparov versus DeepBlue . conflito. Para tais situações é preciso desenvolver técnicas de busca competitiva entre os agentes. É neste ponto que a teoria dos jogos pode auxiliar na construção de um agente racional. Em IA os jogos normalmente são de um tipo bastante especializados - algumas vezes denominados determinı́sticos de revezamento de dois jogadores de soma zero com informações perfeitas. Isto representa ambiente determinı́sticos completamente observáveis em que existem dois agentes cujas ações devem se alternar e em que os valores de utilidade no fim do jogo são sempre iguais e opostos. Por exemplo, se um jogador ganha um jogo de xadrez (+1), o outro jogador necessariamente perde (-1). Essa oposição entre as funções de utilidades dos agentes que gera a situação de competição. O MiniMax é um algoritmo de busca competitiva que seleciona a melhor ação a ser feita em uma situação ou em um jogo, onde dois jogadores se empenham em alcançar objetivos mutuamente exclusivos. Ele se aplica especialmente na busca em árvores de jogo para determinar qual a melhor jogada para o jogador atual. O algoritmo se baseia no princı́pio de que em cada jogada, o jogador irá escolher o melhor movimento possı́vel. A árvore de jogo consiste de todas as jogadas possı́veis para o jogador atual como nós filhos da raiz, e todas as jogadas disponı́veis para o próximo jogador como filhas destes nós e assim por diante, até o nı́vel que se desejar. Cada ramificação da árvore representa um movimento que o jogador pode fazer em tal momento do jogo. Uma busca mais profunda na árvore fornece mais informações sobre as possı́veis vantagens ou armadilhas e portanto resulta em uma jogada melhor. O MiniMax faz uma busca que determina todas as possı́veis continuações do jogo até o nı́vel desejado, avaliando e atribuindo um valor a cada movimento possı́vel. A busca 1.1. Minimax - Algoritmo de Busca Competitiva 3 então retorna na árvore de jogo alternando entre escolher o valor mais alto e o valor mais baixo entre os valores da jogadas em um nı́vel. O método de busca consiste na idéia de maximizar a utilidade supondo que o adversário vai tentar minimizá-la. Em termos de busca, é realiza uma busca cega em profundidade, o agente é o MAX e seu adversário é o MIN. Algoritmo 1 MINIMAX função DECISÃO-MINIMAX(estado) retorna uma ação entradas: estado, estado corrente no jogo v ← VALOR-MAX(estado) retornar a ação em SUCESSORES(estado) com valor v função VALOR-MAX(estado) retorna um valor de utilidade se TESTE-TERMINAL(estado) então retornar UTILIDADE(estado) v ← −∞ para a, s em SUCESSORES(estado) faça v ← MAX(v, VALOR-MIN(s)) retornar v função VALOR-MIN(estado) retorna um valor de utilidade se TESTE-TERMINAL(estado) então retornar UTILIDADE(estado) v←∞ para a, s em SUCESSORES(estado) faça v ← MAX(v, VALOR-MAX(s)) retornar v Se fosse o caso de se tratar de uma busca normal, bastava percorrer-se a árvore até aos nós terminais e escolher o caminho que levasse ao nó com maior valor de utilidade. Mas não é assim, visto existir outro jogador. Assim, é necessário, escolher a partir de cada nó filho, o menor valor de utilidade, e copia-lo para o nó pai, recursivamente até ao nó inicial. Este é o algoritmo MiniMax. Isto deve-se ao fato, do jogador MIN tentar minimizar o ganho do jogador MAX, pois ele tentará escolher uma jogada, dentro das possı́veis, que dê menos pontos ao jogador adversário. Na Caixa de Algoritmo 1 tem-se o algoritmo MiniMax. No Capı́tulo que segue tem-se uma implementação do Jogo da Velha utilizando a linguagem Java e o algoritmo MiniMax. Capı́tulo 2 J2 Velha: Uma Abordagem Java ao Jogo da Velha Conhecido também como “Jogo do Galo”, ou “Tic Tac Toe”, o jogo da velha é um jogo extremamente simples, que não possui grandes dificuldades para seus jogadores. Seu nome teria se originado na Inglaterra, quando nos finais de tarde, mulheres se reuniriam para conversar e bordar. A mulheres idosas, por não terem mais condições de bordar em razão da fraqueza de suas vistas, jogavam este jogo simples. O jogo da velha é um dos exemplos mais clássicos de utilização do algoritmo Minimax. O estado inicial e os movimentos válidos para cada lado definem a árvore do jogo correspondente ao jogo. A Figura 2.1 mostra parte da árvore de jogo para o jogo da velha. A partir do estado inicial, MAX tem nove movimentos possı́veis. O jogo se alterna entre a colocação de um X por MAX e a colocação de um O por MIN até que se alcance nós de folhas correspondentes a estados terminais, tais que um jogador tem três sı́mbolos em uma linha, coluna ou ainda diagonal; ou até que todos os quadrados estejam preenchidos. O número em cada nó de folha indica o valor de utilidade do estado terminal, do ponto de vista de MAX; valores altos são considerados bons para MAX e ruins para MIN. Cabe a MAX usar a árvore de busca para determinar o melhor movimento. 2.1 J2 Velha J2 Velha (Java 2 Velha) é uma implementação do Jogo da Velha desenvolvida na Linguagem Java utilizando o algoritmo MiniMax. Consiste de 4 classes, quais sejam: 1. Velha.java - Classe principal da Aplicação; 2. Minimax.java - Classe responsável em aplicar o algoritmo MiniMax; 3. Tabuleiro.java - Classe responsável pela manipulação do tabuleiro do jogo; 4 2.1. J2 Velha 5 Figura 2.1: Árvore de busca parcial para o jogo da velha . 4. Sucessor.java - Classe responsável em gerar os sucessores, utilizados no algoritmo MiniMax. O algoritmo Minimax desenvolvido no J2 Velha pode buscar por profundidade infinita (até que se encontre um estado terminal) ou por alguma profundidade determinada. A implementação da escolha de profundidade deu-se em função da complexidade do algoritmo MiniMax. Se a profundidade máxima da árvore é m e existem b movimento válidos em cada ponto, a complexidade de tempo do algoritmo MiniMax é O(bm ). Na Caixa de Código XX tem-se um trecho do algoritmo MiniMax contido na classe Minimax.java. É possı́vel comparar esta implementação com o algoritmo apresentado no Capı́tulo anterior. 3 6 Código 2.1: Implementação do Algoritmo /∗ ∗ Método de d e c i s ã o do MiniMax ∗/ p u b l i c i n t [ ] [ ] d e c i s a o m i n i m a x ( i n t [ ] [ ] tab ) { /∗ ∗ Limpa o s s u c e s s o r e s MiniMax 2.1. J2 Velha 6 ∗/ sucessores . clear () ; 9 /∗ ∗ Recebe a u t i l i d a d e máxima ∗/ i n t v = valor max ( tab , t r u e , 1 ) ; 12 15 /∗ ∗ P e r c o r r e a l i s t a em busca do p r i m e i r o s u c e s s o r com u t i l i d a d e máxima ∗/ for ( Sucessor s : sucessores ) i f ( s . u t i l i d a d e == v ) return s . tabuleiro ; 18 21 r e t u r n tab ; 24 27 30 33 36 } p u b l i c i n t valor max ( i n t [ ] [ ] tab , b o o l e a n prim , i n t p r o f ) { /∗ ∗ Se a p r o f u n d i d a d e f o r maior que a máxima ou o j o g o acabou , r e t o r n a a ∗ utilidade ∗/ i f ( p r o f++ > maxProf | | t e s t e t e r m i n a l ( tab ) ) r e t u r n u t i l i d a d e ( tab ) ; /∗ ∗ A t r i b u i o menor v a l o r de um i n t e i r o para v ( − i n f i n i t o ) ∗/ i n t v = I n t e g e r . MIN VALUE ; 39 42 45 48 51 /∗ ∗ P e r c o r r e o s não s u c e s s o r e s de MAX ∗/ f o r ( S u c e s s o r s : g e r a r s u c e s s o r e s ( tab , 1 ) ) { v = Math . max( v , v a l o r m i n ( s . t a b u l e i r o , p r o f ) ) ; s . utilidade = v; /∗ ∗ Se forem o s p r i m e i r o s s u c e s s o r e s , a d i c i o n a na l i s t a de s u c e s s o r e s ... ∗/ i f ( prim ) s u c e s s o r e s . add ( s ) ; } 2.1. J2 Velha 7 return v ; 54 } p u b l i c i n t v a l o r m i n ( i n t [ ] [ ] tab , i n t p r o f ) { /∗ ∗ Se a p r o f u n d i d a d e f o r maior que a máxima ou o j o g o acabou , r e t o r n a a ∗ utilidade ∗/ i f ( p r o f++ > maxProf | | t e s t e t e r m i n a l ( tab ) ) r e t u r n u t i l i d a d e ( tab ) ; 57 60 63 /∗ ∗ A t r i b u i +I n f i n i t o ∗/ i n t v = I n t e g e r .MAX VALUE; 66 69 /∗ ∗ P e r c o r r e o s n ó s s s u c e s s o r e s de MIN ∗/ f o r ( S u c e s s o r s : g e r a r s u c e s s o r e s ( tab , −1) ) { v = Math . min ( v , valor max ( s . t a b u l e i r o , f a l s e , p r o f ) ) ; s . utilidade = v; } 72 75 78 return v ; 81 } 2.2. Exemplos 2.2 8 Exemplos A seguir tem-se a execução de algumas jogadas. Primeiramente vamos utilizar um tabuleiro de tamanho 3x3. Para tal não se faz necessária a definição de uma profundidade máxima, pois a resposta do algoritmo é rápida. Executando o classe Velha.java tem-se a seguinte saı́da no prompt de comando: UFABC - J2VELHA Bem vindo ao Jogo! Boa Sorte! | | ---+---+--| | ---+---+--| | Sua jogada: Linha [0 - 2]: O jogador decide jogar na linha 0, coluna 1. Tem-se então o resultado da jogada do computador: ... Sua jogada: Linha [0 - 2]: 0 Coluna [0 - 2]: 1 | o | ---+---+--| | ---+---+--| | Jogada do Computador: x | o | ---+---+--| | ---+---+--| | 2.2. Exemplos 9 Sua jogada: Linha [0 - 2]: Para decidir onde jogar, o computador efetuou todo o algoritmo minimax e escolheu uma posição que lhe favoreça, ao mesmo tempo que prejudique (não agora, pode ser nas próximas jogadas) o adversário. O jogador agora decide jogar na linha 1, coluna 1. Tem-se a seguinte jogada do computador: ... Linha [0 - 2]: 1 Coluna [0 - 2]: 1 x | o | ---+---+--| o | ---+---+--| | Jogada do Computador: x | o | ---+---+--| o | ---+---+--| x | Sua jogada: Linha [0 - 2]: Observe que o computador decidiu jogar em uma posição que evita que o adversário ganhe. O jogador decide jogar na linha 0, coluna 2. Tem-se a jogada do computador: ... Linha [0 - 2]: 0 Coluna [0 - 2]: 2 x | o | o ---+---+--| o | ---+---+--| x | 2.2. Exemplos 10 Jogada do Computador: x | o | o ---+---+--| o | ---+---+--x | x | Sua jogada: Linha [0 - 2]: Observe que de qualquer forma o computador ganhará a partida. O jogador decide jogar na linha 2, coluna 2. Tem-se a vitória do computador: ... Linha [0 - 2]: 2 Coluna [0 - 2]: 2 x | o | o ---+---+--| o | ---+---+--x | x | o Jogada do Computador: x | o | o ---+---+--x | o | ---+---+--x | x | o O computador ganhou! Você pode verificar o funcionamento do jogo com um tabuleiro 4x4. Basta mudar as variáveis TAM e PROF na classe Velha. Devido à complexidade do algoritmo recomenda-se utilizar uma profundidade 5 para que o tempo de execução do mesmo seja razoável. A seguir tem-se uma partida completa utilizando um tabuleiro 4x4: 2.2. Exemplos UFABC - J2VELHA Bem vindo ao Jogo! Boa Sorte! | | | ---+---+---+--| | | ---+---+---+--| | | ---+---+---+--| | | Sua jogada: Linha [0 - 3]: 0 Coluna [0 - 3]: 0 o | | | ---+---+---+--| | | ---+---+---+--| | | ---+---+---+--| | | Jogada do Computador: o | x | | ---+---+---+--| | | ---+---+---+--| | | ---+---+---+--| | | Sua jogada: Linha [0 - 3]: 1 Coluna [0 - 3]: 0 11 2.2. Exemplos o | x | | ---+---+---+--o | | | ---+---+---+--| | | ---+---+---+--| | | Jogada do Computador: o | x | x | ---+---+---+--o | | | ---+---+---+--| | | ---+---+---+--| | | Sua jogada: Linha [0 - 3]: 2 Coluna [0 - 3]: 0 o | x | x | ---+---+---+--o | | | ---+---+---+--o | | | ---+---+---+--| | | Jogada do Computador: o | x | x | ---+---+---+--o | | | ---+---+---+--o | | | ---+---+---+--x | | | 12 2.2. Exemplos Sua jogada: Linha [0 - 3]: 1 Coluna [0 - 3]: 1 o | x | x | ---+---+---+--o | o | | ---+---+---+--o | | | ---+---+---+--x | | | Jogada do Computador: o | x | x | x ---+---+---+--o | o | | ---+---+---+--o | | | ---+---+---+--x | | | Sua jogada: Linha [0 - 3]: 2 Coluna [0 - 3]: 2 o | x | x | x ---+---+---+--o | o | | ---+---+---+--o | | o | ---+---+---+--x | | | Jogada do Computador: o | x | x | x ---+---+---+--o | o | | ---+---+---+--o | | o | ---+---+---+--x | | | x 13 2.2. Exemplos Sua jogada: Linha [0 - 3]: 1 Coluna [0 - 3]: 2 o | x | x | x ---+---+---+--o | o | o | ---+---+---+--o | | o | ---+---+---+--x | | | x Jogada do Computador: o | x | x | x ---+---+---+--o | o | o | x ---+---+---+--o | | o | ---+---+---+--x | | | x Sua jogada: Linha [0 - 3]: 2 Coluna [0 - 3]: 3 o | x | x | x ---+---+---+--o | o | o | x ---+---+---+--o | | o | o ---+---+---+--x | | | x Jogada do Computador: o | x | x | x ---+---+---+--o | o | o | x ---+---+---+--o | x | o | o ---+---+---+--x | | | x 14 2.2. Exemplos Sua jogada: Linha [0 - 3]: 3 Coluna [0 - 3]: 2 o | x | x | x ---+---+---+--o | o | o | x ---+---+---+--o | x | o | o ---+---+---+--x | | o | x Jogada do Computador: o | x | x | x ---+---+---+--o | o | o | x ---+---+---+--o | x | o | o ---+---+---+--x | x | o | x Empate! O código completo da implementação encontra-se no Anexo I. 15 Anexo A J2 Velha: Códigos A seguir tem-se a codificação completa da aplicação. Esta foi desenvolvida utilizando a IDE NetBeans e JDK 1.6. A.1 3 6 Classe Velha.java /∗ ∗ UFABC − U n v e r s i d a d e F e d e r a l do ABC ∗ MC 3303 − I n t e l i g ê n c i a A r t i f i c i a l ∗ P r o f e s s o r Jerônimo P e l l e g r i n i ∗ Alunos : ∗ André F i l i p e de Moraes B a t i s t a ∗ L uı́ s Fernando de O l i v e i r a J a c i n t h o ∗/ 9 12 /∗ ∗ CLASSE VELHA − CLASSE PRINCIPAL DA APLICACAO ∗/ 15 // B i b l i o t e c a Scanner para c a p t u r a da j o g a d a do u s u á r i o import j a v a . u t i l . Scanner ; 18 public c l a s s Velha { /∗ ∗ CONSTANTES UTILIDAZAS ∗ TAM −> Tamanho do T a b u l e i r o ∗ PROF −> P r o f u n d i d a d e máxima da b u s c a no MiniMax . Se PROF = −1 o algoritmo ∗ minimax i r á b u s c a r a té um e s t a d o t e r m i n a l . ∗/ 21 24 16 A.1. Classe Velha.java 17 s t a t i c int TAM = 3 , PROF = −1; 27 public s t a t i c void main ( S t r i n g [ ] a r g s ) { Scanner e n t = new Scanner ( System . i n ) ; // O b j e t o da C l a s s e T a b u l e i r o T a b u l e i r o t = new T a b u l e i r o (TAM) ; // O b j e t o da C l a s s e Minimax MiniMax mm = new MiniMax (TAM, PROF) ; System . out . p r i n t l n ( " UFABC - J2VELHA \nBem vindo ao Jogo !\ nBoa Sorte !\n\n ") ; // Imprime o t a b u l e i r o na Tela t . imprimir ( ) ; do { // Captura j o g a d a do u s u á r i o int l , c ; System . out . p r i n t f ( "Sua jogada :\r\ nLinha [0 - %d]: " , (TAM−1) ) ; l = ent . nextInt ( ) ; System . out . p r i n t f ( " Coluna [0 - %d]: " , (TAM−1) ) ; c = ent . nextInt ( ) ; // R e a l i z a j o g a d a do u s u á r i o t . fazerJogada ( l , c ) ; t . imprimir ( ) ; // V e r i f i c a s e não é um e s t a d o t e r m i n a l i f ( !mm. t e s t e t e r m i n a l ( t . t a b u l e i r o ) ) { // A p l i c a o a l g o r i t m o minimax ao t a b u l e i r o t . t a b u l e i r o = mm. d e c i s a o m i n i m a x ( t . t a b u l e i r o ) ; System . out . p r i n t l n ( " Jogada do Computador :" ) ; t . imprimir ( ) ; } } while ( !mm. t e s t e t e r m i n a l ( t . t a b u l e i r o ) ) ; // V e r i f i c a o ganhador , ou um empate i f (mm. ganhou ( t . t a b u l e i r o , 1 ) ) System . out . p r i n t l n ( "O computador ganhou !" ) ; e l s e i f (mm. ganhou ( t . t a b u l e i r o , −1) ) System . out . p r i n t l n ( "Voc^ e ganhou !" ) ; else System . out . p r i n t l n ( " Empate !" ) ; } 30 33 36 39 42 45 48 51 54 57 60 63 } A.2. Classe Tabuleiro.java A.2 3 6 18 Classe Tabuleiro.java /∗ ∗ UFABC − U n v e r s i d a d e F e d e r a l do ABC ∗ MC 3303 − I n t e l i g ê n c i a A r t i f i c i a l ∗ P r o f e s s o r Jerônimo P e l l e g r i n i ∗ Alunos : ∗ André F i l i p e de Moraes B a t i s t a ∗ L uı́ s Fernando de O l i v e i r a J a c i n t h o ∗/ 9 12 /∗ ∗ CLASSE TABULEIRO − REPRESENTA O TABULEIRO NO JOGO DA VELHA ∗/ 15 18 21 24 27 30 33 36 39 42 45 public c l a s s T a b u l e i r o { /∗ ∗ Vetor de c o n v e r s ã o para i m p r e s s ã o na t e l a ∗/ s t a t i c char [ ] c o n v e r s a o = { ’o’ , ’ ’ , ’x’ } ; /∗ ∗ M a t r i z do t a b u l e i r o ∗/ s t a t i c int [ ] [ ] t a b u l e i r o ; /∗ ∗ Tamanho do t a b u l e i r o ∗/ int tam ; /∗ ∗ D i v i s o r das l i n h a s na t e l a ∗/ String divisor ; /∗ ∗ O método c o n s t r u t o r r e c e b e como parametro o tamanho do t a b u l e i r o ∗/ public T a b u l e i r o ( int tam ) { t h i s . tam = tam ; t a b u l e i r o = new int [ tam ] [ tam ] ; divisor = gerarDivisor () ; } /∗ A.2. Classe Tabuleiro.java 19 ∗ Método i n v o c a d o para a j o g a d a do Jogador ∗/ public void f a z e r J o g a d a ( int l , int c ) { i f ( t a b u l e i r o [ l ] [ c ] == 0 ) t a b u l e i r o [ l ] [ c ] = −1; else System . out . p r i n t l n ( " Posicao ja ocupada , perdeu a vez!" ) ; } 48 51 54 /∗ ∗ Metodo para a i m p r e s s ã o do t a b u l e i r o na t e l a ∗/ public void i m p r i m i r ( ) { f o r ( int i = 0 ; i < tam ; i ++) { f o r ( int j = 0 ; j < tam ; j ++) { System . out . p r i n t f ( " %c %c" , c o n v e r s a o [ t a b u l e i r o [ i ] [ j ] + 1 ] , j == ( tam−1) ? ’ ’ : ’|’ ) ; } i f ( i != ( tam−1) ) System . out . p r i n t l n ( d i v i s o r ) ; } System . out . p r i n t l n ( "\r\n" ) ; } 57 60 63 66 69 72 /∗ ∗ Metodo para Gerar o D i v i s o r de Linhas . S e r v e para a u x i l i o da visualizacao ∗ g r a f i c a do t a b u l e i r o ∗/ public S t r i n g g e r a r D i v i s o r ( ) { S t r i n g d = new S t r i n g ( "\r\n" ) ; 75 78 f o r ( int i = 0 ; i < ( tam − 1 ) ; i ++) { d += " ---+" ; } 81 84 d += " ---" ; 87 return d ; } 90 } A.3. Classe Sucessor.java A.3 3 6 9 20 Classe Sucessor.java /∗ ∗ UFABC − U n v e r s i d a d e F e d e r a l do ABC ∗ MC 3303 − I n t e l i g ê n c i a A r t i f i c i a l ∗ P r o f e s s o r Jerônimo P e l l e g r i n i ∗ Alunos : ∗ André F i l i p e de Moraes B a t i s t a ∗ L uı́ s Fernando de O l i v e i r a J a c i n t h o ∗/ /∗ ∗ CLASSE SUCESSOR − GERA OS ESTADOS DO JOGO DA VELHA ∗/ 12 15 public c l a s s S u c e s s o r { int [ ] [ ] t a b u l e i r o ; int u t i l i d a d e ; /∗ ∗ Metodo C o n s t r u t o r ∗/ public S u c e s s o r ( int [ ] [ ] tab ) { /∗ ∗ Cria um novo t a b u l e i r o , b a s e a d o no que f o i p a s s a d o ∗/ int tam = tab . l e n g t h ; t a b u l e i r o = new int [ tam ] [ tam ] ; 18 21 24 27 f o r ( int i = 0 ; i < tam ; i ++) f o r ( int j = 0 ; j < tam ; j ++) t a b u l e i r o [ i ] [ j ] = tab [ i ] [ j ] ; 30 } 33 } A.4. Classe Minimax.java A.4 3 6 9 21 Classe Minimax.java /∗ ∗ UFABC − U n v e r s i d a d e F e d e r a l do ABC ∗ MC 3303 − I n t e l i g ê n c i a A r t i f i c i a l ∗ P r o f e s s o r Jerônimo P e l l e g r i n i ∗ Alunos : ∗ André F i l i p e de Moraes B a t i s t a ∗ L uı́ s Fernando de O l i v e i r a J a c i n t h o ∗/ /∗ ∗ CLASSE MINIMAX − ALGORITMO DE BUSCA COMPETITIVA ∗/ 12 15 import j a v a . u t i l . A r r a y L i s t ; import j a v a . u t i l . C o l l e c t i o n s ; 18 public c l a s s MiniMax { /∗ ∗ L i s t a de S u c e s s o r e s . Esta l i s t a Ãc armazenada u t i l i z a n d o ∗ um A r r a y L i s t ∗/ s t a t i c A r r a y L i s t <S u c e s s o r > s u c e s s o r e s = new A r r a y L i s t <S u c e s s o r > ( ) ; int tam , maxProf ; 21 24 27 30 33 36 39 42 /∗ ∗ C o n s t r u t o r r e c e b e o tamanho do t a b u l e i r o e a p r o f u n d i d a d e máxima da busca ∗/ public MiniMax ( int tam , int maxProf ) { t h i s . tam = tam ; i f ( maxProf > 0 ) t h i s . maxProf = maxProf ; else t h i s . maxProf = I n t e g e r .MAX VALUE; // Recebe o maior v a l o r de um inteiro . } /∗ ∗ Metodo de d e c i s a o do MiniMax ∗/ public int [ ] [ ] d e c i s a o m i n i m a x ( int [ ] [ ] tab ) { A.4. Classe Minimax.java 22 /∗ ∗ Limpa os s u c e s s o r e s ∗/ sucessores . clear () ; 45 48 /∗ ∗ Recebe a u t i l i d a d e máxima ∗/ int v = valor max ( tab , true , 1 ) ; 51 /∗ ∗ P e r c o r r e a l i s t a em b u s c a do p r i m e i r o s u c e s s o r com u t i l i d a d e máxima ∗/ for ( Sucessor s : s u c e s s o r e s ) i f ( s . u t i l i d a d e == v ) return s . t a b u l e i r o ; 54 57 60 return tab ; } 63 66 69 public int valor max ( int [ ] [ ] tab , boolean prim , int p r o f ) { /∗ ∗ Se a p r o f u n d i d a d e f o r maior que a máxima ou o j o g o acabou , r e t o r n a a ∗ utilidade ∗/ i f ( p r o f++ > maxProf | | t e s t e t e r m i n a l ( tab ) ) return u t i l i d a d e ( tab ) ; 72 75 78 81 84 87 /∗ ∗ A t r i b u i o menor v a l o r de um i n t e i r o para v ( − i n f i n i t o ) ∗/ int v = I n t e g e r . MIN VALUE ; /∗ ∗ P e r c o r r e os nós s u c e s s o r e s de MAX ∗/ f o r ( S u c e s s o r s : g e r a r s u c e s s o r e s ( tab , 1 ) ) { v = Math . max( v , v a l o r m i n ( s . t a b u l e i r o , p r o f ) ) ; s . utilidade = v; /∗ ∗ Se forem os p r i m e i r o s s u c e s s o r e s , a d i c i o n a na l i s t a de s u c e s s o r e s ... ∗/ i f ( prim ) A.4. Classe Minimax.java 23 s u c e s s o r e s . add ( s ) ; } 90 return v ; 93 96 99 102 } public int v a l o r m i n ( int [ ] [ ] tab , int p r o f ) { /∗ ∗ Se a p r o f u n d i d a d e f o r maior que a máxima ou o j o g o acabou , r e t o r n a a ∗ utilidade ∗/ i f ( p r o f++ > maxProf | | t e s t e t e r m i n a l ( tab ) ) return u t i l i d a d e ( tab ) ; /∗ ∗ A t r i b u i +I n f i n i t o ∗/ int v = I n t e g e r .MAX VALUE; 105 108 /∗ ∗ P e r c o r r e os nós s u c e s s o r e s de MIN ∗/ f o r ( S u c e s s o r s : g e r a r s u c e s s o r e s ( tab , −1) ) { v = Math . min ( v , valor max ( s . t a b u l e i r o , f a l s e , p r o f ) ) ; s . utilidade = v; } 111 114 117 return v ; } 120 123 126 129 132 135 /∗ ∗ Gera os s u c e s s o r e s de um j o g a d o r , a p a r t i r do e s t a d o a t u a l ∗/ public A r r a y L i s t <S u c e s s o r > g e r a r s u c e s s o r e s ( int [ ] [ ] tab , int v ) { A r r a y L i s t <S u c e s s o r > s u c = new A r r a y L i s t <S u c e s s o r > ( ) ; f o r ( int i = 0 ; i < tam ; i ++) { f o r ( int j = 0 ; j < tam ; j ++) { i f ( tab [ i ] [ j ] == 0 ) { tab [ i ] [ j ] = v ; s u c . add (new S u c e s s o r ( tab ) ) ; tab [ i ] [ j ] = 0 ; A.4. Classe Minimax.java 24 } } } 138 return s u c ; 141 144 147 } /∗ ∗ V e r i f i c a s e chegou em algum e s t a d o t e r m i n a l e c a s o a f i r m a t i v o f i n a l i z a o jogo ∗/ public boolean t e s t e t e r m i n a l ( int [ ] [ ] tab ) { return ( ganhou ( tab , 1 ) | | ganhou ( tab , −1) | | semEspaco ( tab ) ) ; } 150 153 156 159 162 165 168 171 /∗ ∗ Retorna a u t i l i d a d e ∗/ public int u t i l i d a d e ( int [ ] [ ] tab ) { i f ( ganhou ( tab , 1 ) ) return 1 ; e l s e i f ( ganhou ( tab , −1) ) return −1; else return 0 ; } /∗ ∗ V e r i f i c a s e j o g a d o r ganhou ∗/ public boolean ganhou ( int [ ] [ ] tab , int v ) { f o r ( int i = 0 ; i < tam ; i ++) i f ( ganhouLinha ( tab , i , v ) | | ganhouColuna ( tab , i , v ) ) return true ; i f ( ganhouDiag1 ( tab , v ) | | ganhouDiag2 ( tab , v ) ) return true ; 174 return f a l s e ; 177 } 180 /∗ ∗ Ganhou na s e q u e n c i a de l i n h a s ? ∗/ A.4. Classe Minimax.java 186 private boolean ganhouLinha ( int [ ] [ ] tab , int l , int v ) { f o r ( int i = 0 ; i < tam ; i ++) i f ( tab [ l ] [ i ] != v ) return f a l s e ; 189 } 183 return true ; 198 /∗ ∗ Ganhou na s e q u e n c i a de c o l u n a s ? ∗/ private boolean ganhouColuna ( int [ ] [ ] tab , int c , int v ) { f o r ( int i = 0 ; i < tam ; i ++) i f ( tab [ i ] [ c ] != v ) return f a l s e ; 201 } 192 195 return true ; 210 /∗ ∗ Ganhou na s e q u e n c i a d i a g o n a l p r i n c i p a l ? ∗/ private boolean ganhouDiag1 ( int [ ] [ ] tab , int v ) { f o r ( int i = 0 ; i < tam ; i ++) i f ( tab [ i ] [ i ] != v ) return f a l s e ; 213 } 204 207 return true ; 216 219 222 /∗ ∗ Ganhou na s e q u e n c i a d i a g o n a l s e c u n d a r i a ? ∗/ private boolean ganhouDiag2 ( int [ ] [ ] tab , int v ) { f o r ( int i = 0 ; i < tam ; i ++) i f ( tab [ ( tam−1)− i ] [ i ] != v ) return f a l s e ; return true ; 225 } 228 /∗ ∗ Nao tem mais e s p a c o s r e s t a n t e s no t a b u l e i r o . . 25 A.4. Classe Minimax.java 26 ∗/ public boolean semEspaco ( int [ ] [ ] tab ) { f o r ( int l = 0 ; l < tam ; l ++) f o r ( int c = 0 ; c < tam ; c++) i f ( tab [ l ] [ c ] == 0 ) return f a l s e ; 231 234 return true ; 237 } } Anexo B J2 Velha: Novas Funcionalidades A seguir tem-se a codificação completa de novas funcionalidades do J2 Velha. O programa agora realiza o algoritmo minimax juntamente com o mecanismo de Poda Alfa-Beta (Alpha-beta pruning). Além disto, existe a possibilidade de jogar com elementos de acaso, isto é, as peças podem deslizar em determinada jogada. Todo o código está comentado para que estas funcionalidades sejam entendidas mais facilmente. B.1 3 6 Classe Velha.java /∗ ∗ UFABC − U n v e r s i d a d e F e d e r a l do ABC ∗ MC 3303 − I n t e l i g ê n c i a A r t i f i c i a l ∗ P r o f e s s o r Jerônimo P e l l e g r i n i ∗ Alunos : ∗ André F i l i p e de Moraes B a t i s t a ∗ L uı́ s Fernando de O l i v e i r a J a c i n t h o ∗/ 9 import j a v a . u t i l . Scanner ; 12 15 public c l a s s Velha { /∗ ∗ Peças e s c o r r e g a d i a s ? ∗/ s t a t i c boolean ESCORREGA; 18 21 public s t a t i c void main ( S t r i n g [ ] a r g s ) { Scanner e n t = new Scanner ( System . i n ) ; 27 B.1. Classe Velha.java 24 27 30 33 28 System . out . p r i n t ( "Voc^ e deseja jogar com peças escorregadias ? [s/n]: " ) ; String esc = ent . nextLine ( ) ; i f ( e s c . charAt ( 0 ) == ’s’ | | e s c . charAt ( 0 ) == ’S’ ) { ESCORREGA = true ; System . out . p r i n t l n ( " Peças escorregadias ativadas ." ) ; } else { ESCORREGA = f a l s e ; System . out . p r i n t l n ( " Peças escorregadias desativadas ." ) ; } 36 39 42 45 48 51 54 57 T a b u l e i r o t = new T a b u l e i r o (ESCORREGA) ; MiniMax mm = new MiniMax (ESCORREGA) ; t . imprimir ( ) ; do { int l , c ; System . out . p r i n t f ( "Sua jogada :\r\ nLinha [0 - 3]: " ) ; l = ent . nextInt ( ) ; System . out . p r i n t f ( " Coluna [0 - 3]: " ) ; c = ent . nextInt ( ) ; t . fazerJogada ( l , c ) ; t . imprimir ( ) ; i f ( !mm. t e s t e t e r m i n a l ( t . t a b u l e i r o ) ) { long time = System . c u r r e n t T i m e M i l l i s ( ) ; t . t a b u l e i r o = mm. d e c i s a o m i n i m a x ( t . t a b u l e i r o ) ; time = System . c u r r e n t T i m e M i l l i s ( ) − time ; System . out . p r i n t l n ( " Jogada do Computador (" + time + " ms):" ) ; t . imprimir ( ) ; } } while ( !mm. t e s t e t e r m i n a l ( t . t a b u l e i r o ) ) ; 60 63 66 int u = mm. u t i l i d a d e ( t . i f (u < 0) System . out . p r i n t l n e l s e i f ( u == 0 ) System . out . p r i n t l n else System . out . p r i n t l n tabuleiro ) ; ( " Parabens ! Voce ganhou ..." ) ; ( " Empatou !" ) ; ( "Voce realmente e pior que um computador ..." ) ; B.1. Classe Velha.java 29 System . out . p r i n t l n ( "Voc^ e marcou " + mm. co ntaP onto s ( t . t a b u l e i r o , −1) + " pontos ." ) ; System . out . p r i n t l n ( "O computador marcou " + mm. co nta Ponto s ( t . t a b u l e i r o , 1 ) + " pontos ." ) ; 69 } 72 } B.2. Classe Tabuleiro.java B.2 3 6 Classe Tabuleiro.java /∗ ∗ UFABC − U n v e r s i d a d e F e d e r a l do ABC ∗ MC 3303 − I n t e l i g ê n c i a A r t i f i c i a l ∗ P r o f e s s o r Jerônimo P e l l e g r i n i ∗ Alunos : ∗ André F i l i p e de Moraes B a t i s t a ∗ L uı́ s Fernando de O l i v e i r a J a c i n t h o ∗/ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ 9 import j a v a . u t i l . A r r a y L i s t ; 12 15 18 21 24 27 30 33 public c l a s s T a b u l e i r o { /∗ ∗ Vetor de c o n v e r s ã o para i m p r e s s ã o na t e l a ∗/ s t a t i c char [ ] c o n v e r s a o = { ’o’ , ’ ’ , ’x’ } ; /∗ ∗ M a t r i z do t a b u l e i r o ∗/ s t a t i c int [ ] [ ] t a b u l e i r o ; /∗ ∗ Pe ças E s c o r r e g a d i a s ? ∗/ boolean e s c o r r e g a ; /∗ ∗ Construtor ∗ e n t r a d a : tamanho do t a b u l e i r o ∗/ public T a b u l e i r o ( boolean e s c o r r e g a ) { this . escorrega = escorrega ; t a b u l e i r o = new int [ 4 ] [ 4 ] ; } 36 39 42 45 /∗ ∗ Método i n v o c a d o para a j o g a d a do Jogador ! ∗/ public void f a z e r J o g a d a ( int l , int c ) { i f ( t a b u l e i r o [ l ] [ c ] == 0 ) { /∗ ∗ Se e s t i v e r j o g a n d o com p e c a s e s c o r r e g a d i a s . . . 30 B.2. Classe Tabuleiro.java 31 ∗/ if ( escorrega ) { /∗ ∗ V e r i f i c a os v i z i n h o s l i v r e da posi ç ão . . ∗/ A r r a y L i s t <int [] > v i z i n h o s = v i z i n h o s L i v r e s ( l , c ) ; 48 51 /∗ ∗ Se h o u ve r ao menos um v i z i n h o l i v r e , tem 20% de chance da peça ∗ escorregar . . ∗/ i f ( v i z i n h o s . s i z e ( ) > 0 && Math . random ( ) <= 0 . 2 ) { /∗ ∗ E s c o l h e um dos v i z i n h o s a l e a t o r i a m e n t e . . ∗/ int x = ( int ) ( Math . random ( ) ∗ v i z i n h o s . s i z e ( ) ) ; /∗ ∗ Transforma as c o o r d e n a d a s a t u a i s nas c o o r d e n a d a s do v i z i n h o ∗ escolhido . . ∗/ l = vizinhos . get (x) [ 0 ] ; c = vizinhos . get (x) [ 1 ] ; System . out . p r i n t l n ( "A peça escorregou e caiu na posiç~ ao : " + l + ", " + c ) ; } 54 57 60 63 66 69 } t a b u l e i r o [ l ] [ c ] = −1; 72 } else System . out . p r i n t l n ( " Posiç~ a o já ocupada , perdeu a vez!" ) ; 75 } 78 81 84 87 90 /∗ ∗ Método que v e r i f i c a s e há v i z i n h o s l i v r e s , c o n s i d e r a n d o as d i a g o n a i s ... ∗/ public A r r a y L i s t <int [] > v i z i n h o s L i v r e s ( int l , int c ) { A r r a y L i s t <int [] > v i z i n h o s = new A r r a y L i s t <int [] > ( ) ; /∗ ∗ V i z i n h o s da l i n h a a n t e r i o r , s e h o u ve r . . . ∗/ i f ( l > 0) { B.2. Classe Tabuleiro.java 93 i f ( c > 0) i f ( t a b u l e i r o [ l − 1 ] [ c −1] == 0 ) v i z i n h o s . add (new int [ ] { l −1, c −1}) ; 96 i f ( t a b u l e i r o [ l − 1 ] [ c ] == 0 ) v i z i n h o s . add (new int [ ] { l −1, c } ) ; i f ( c < 3) i f ( t a b u l e i r o [ l − 1 ] [ c +1] == 0 ) v i z i n h o s . add (new int [ ] { l −1, c +1}) ; 99 } 102 /∗ ∗ V i z i n h o s da mesma l i n h a . . . ∗/ i f ( c > 0) i f ( t a b u l e i r o [ l ] [ c −1] == 0 ) v i z i n h o s . add (new int [ ] { l , c −1}) ; 105 108 i f ( c < 3) i f ( t a b u l e i r o [ l ] [ c +1] == 0 ) v i z i n h o s . add (new int [ ] { l , c +1}) ; 111 /∗ ∗ V i z i n h o s da l i n h a p o s t e r i o r , s e h o u ve r . . . ∗/ i f ( l < 3) { i f ( c > 0) i f ( t a b u l e i r o [ l + 1 ] [ c −1] == 0 ) v i z i n h o s . add (new int [ ] { l +1, c −1}) ; 114 117 120 123 i f ( t a b u l e i r o [ l + 1 ] [ c ] == 0 ) v i z i n h o s . add (new int [ ] { l +1, c } ) ; 126 i f ( c < 3) i f ( t a b u l e i r o [ l + 1 ] [ c +1] == 0 ) v i z i n h o s . add (new int [ ] { l +1, c +1}) ; } 129 return v i z i n h o s ; 132 135 } /∗ ∗ Método para a impress ão do t a b u l e i r o na t e l a ∗/ public void i m p r i m i r ( ) 32 B.2. Classe Tabuleiro.java 33 { 138 f o r ( int i = 0 ; i < 4 ; i ++) { f o r ( int j = 0 ; j < 4 ; j ++) { System . out . p r i n t f ( " %c %c" , c o n v e r s a o [ t a b u l e i r o [ i ] [ j ] + 1 ] , j == 3 ? ’ ’ : ’|’ ) ; } i f ( i != ( 3 ) ) System . out . p r i n t l n ( "\r\n---+---+---+---" ) ; } System . out . p r i n t l n ( "\r\n" ) ; 141 144 147 } 150 } B.3. Classe Sucessor.java B.3 3 6 9 12 34 Classe Sucessor.java /∗ ∗ UFABC − U n v e r s i d a d e F e d e r a l do ABC ∗ MC 3303 − I n t e l i g ê n c i a A r t i f i c i a l ∗ P r o f e s s o r Jerônimo P e l l e g r i n i ∗ Alunos : ∗ André F i l i p e de Moraes B a t i s t a ∗ L uı́ s Fernando de O l i v e i r a J a c i n t h o ∗/ public c l a s s S u c e s s o r { int [ ] [ ] t a b u l e i r o ; int u t i l i d a d e ; /∗ ∗ Construtor ∗/ public S u c e s s o r ( int [ ] [ ] tab ) { /∗ ∗ Cria um novo t a b u l e i r o , b a s e a d o no que f o i p a s s a d o ∗/ int tam = tab . l e n g t h ; t a b u l e i r o = new int [ tam ] [ tam ] ; 15 18 21 24 f o r ( int i = 0 ; i < tam ; i ++) f o r ( int j = 0 ; j < tam ; j ++) t a b u l e i r o [ i ] [ j ] = tab [ i ] [ j ] ; 27 } } B.4. Classe Minimax.java B.4 3 6 35 Classe Minimax.java /∗ ∗ UFABC − U n v e r s i d a d e F e d e r a l do ABC ∗ MC 3303 − I n t e l i g ê n c i a A r t i f i c i a l ∗ P r o f e s s o r Jerônimo P e l l e g r i n i ∗ Alunos : ∗ André F i l i p e de Moraes B a t i s t a ∗ L uı́ s Fernando de O l i v e i r a J a c i n t h o ∗/ 9 import j a v a . u t i l . A r r a y L i s t ; 12 15 18 21 24 27 public c l a s s MiniMax { /∗ ∗ L i s t a dos nós s u c e s s o r e s ∗/ s t a t i c A r r a y L i s t <S u c e s s o r > s u c e s s o r e s = new A r r a y L i s t <S u c e s s o r > ( ) ; /∗ ∗ Jogar com p e ç a s e s c o r r e g a d i a s ? ∗/ boolean e s c o r r e g a ; /∗ ∗ Construtor ∗/ public MiniMax ( boolean e s c o r r e g a ) { this . escorrega = escorrega ; } 30 33 36 39 42 45 /∗ ∗ Método de d e c i s ã o do MiniMax ∗/ public int [ ] [ ] d e c i s a o m i n i m a x ( int [ ] [ ] tab ) { /∗ ∗ Limpa os s u c e s s o r e s ∗/ sucessores . clear () ; /∗ ∗ Recebe a u t i l i d a d e máxima ∗/ int v = valor max ( tab , I n t e g e r . MIN VALUE, I n t e g e r .MAX VALUE, true ) ; B.4. Classe Minimax.java 36 /∗ ∗ P e r c o r r e a l i s t a em b u s c a do p r i m e i r o s u c e s s o r com u t i l i d a d e máxima ∗/ for ( Sucessor s : s u c e s s o r e s ) i f ( s . u t i l i d a d e == v ) return s . t a b u l e i r o ; 48 51 return tab ; 54 57 60 63 66 } public int valor max ( int [ ] [ ] tab , int a l f a , int beta , boolean prim ) { /∗ ∗ Se a p r o f u n d i d a d e f o r maior que a máxima ou o j o g o acabou , r e t o r n a a ∗ utilidade ∗/ i f ( t e s t e t e r m i n a l ( tab ) ) return u t i l i d a d e ( tab ) ; /∗ ∗ Atribui −I n f i n i t o ∗/ int v = I n t e g e r . MIN VALUE ; 69 72 75 78 81 84 87 90 /∗ ∗ P e r c o r r e os nós s u c e s s o r e s de MAX ∗/ f o r ( S u c e s s o r s : g e r a r s u c e s s o r e s ( tab , 1 ) ) { v = Math . max( v , v a l o r m i n ( s . t a b u l e i r o , a l f a , b e t a ) ) ; s . utilidade = v; /∗ ∗ Se forem os p r i m e i r o s s u c e s s o r e s , a d i c i o n a na l i s t a de s u c e s s o r e s ... ∗/ i f ( prim ) s u c e s s o r e s . add ( s ) ; /∗ ∗ Poda Beta − Se o v a l o r f o r maior que b e t a , r e t o r n a o v a l o r . . ∗/ i f ( v >= b e t a ) return v ; /∗ ∗ Se o v a l o r f o r maior que Alfa , A l f a r e c e b e o v a l o r . . . B.4. Classe Minimax.java 37 ∗/ a l f a = Math . max( a l f a , v ) ; 93 } return v ; 96 } 99 102 105 public int v a l o r m i n ( int [ ] [ ] tab , int a l f a , int b e t a ) { /∗ ∗ Se a p r o f u n d i d a d e f o r maior que a máxima ou o j o g o acabou , r e t o r n a a ∗ utilidade ∗/ i f ( t e s t e t e r m i n a l ( tab ) ) return u t i l i d a d e ( tab ) ; /∗ ∗ A t r i b u i +I n f i n i t o ∗/ int v = I n t e g e r .MAX VALUE; 108 111 /∗ ∗ P e r c o r r e os nós s u c e s s o r e s de MIN ∗/ f o r ( S u c e s s o r s : g e r a r s u c e s s o r e s ( tab , −1) ) { v = Math . min ( v , valor max ( s . t a b u l e i r o , a l f a , beta , f a l s e ) ) ; s . utilidade = v; 114 117 120 /∗ ∗ Poda A l f a − Se o v a l o r f o r menor que a l f a , r e t o r n a o v a l o r . . . ∗/ i f ( v <= a l f a ) return v ; 123 126 /∗ ∗ Se v a l o r menor que Beta , Beta o r e c e b e . . . ∗/ b e t a = Math . min ( beta , v ) ; 129 } 132 return v ; } 135 138 /∗ ∗ Gera os s u c e s s o r e s de um j o g a d o r , a p a r t i r do e s t a d o a t u a l ∗/ B.4. Classe Minimax.java 141 144 147 150 153 156 159 162 165 168 171 174 public A r r a y L i s t <S u c e s s o r > g e r a r s u c e s s o r e s ( int [ ] [ ] tab , int v ) { A r r a y L i s t <S u c e s s o r > s u c = new A r r a y L i s t <S u c e s s o r > ( ) ; f o r ( int i = 0 ; i < 4 ; i ++) { f o r ( int j = 0 ; j < 4 ; j ++) { i f ( tab [ i ] [ j ] == 0 ) { /∗ ∗ Se e s t i v e r j o g a n d o com p e c a s e s c o r r e g a d i a s . . . ∗/ if ( escorrega ) { /∗ ∗ V e r i f i c a os v i z i n h o s l i v r e da p o s i ç ã o . . ∗/ A r r a y L i s t <int [] > v i z i n h o s = v i z i n h o s L i v r e s ( tab , i , j ) ; /∗ ∗ Se h o u ve r ao menos um v i z i n h o l i v r e , tem 20% de chance da pe ça ∗ escorregar . . ∗/ i f ( v i z i n h o s . s i z e ( ) > 0 && Math . random ( ) <= 0 . 2 ) { /∗ ∗ E s c o l h e um dos v i z i n h o s a l e a t o r i a m e n t e . . ∗/ int x = ( int ) ( Math . random ( ) ∗ v i z i n h o s . s i z e ( ) ) ; /∗ ∗ Transforma as c o o r d e n a d a s a t u a i s nas c o o r d e n a d a s do vizinho ∗ escolhido . . ∗/ i = vizinhos . get (x) [ 0 ] ; j = vizinhos . get (x) [ 1 ] ; } } tab [ i ] [ j ] = v ; s u c . add (new S u c e s s o r ( tab ) ) ; tab [ i ] [ j ] = 0 ; 177 } } 180 } 183 38 return s u c ; B.4. Classe Minimax.java 39 } 186 189 /∗ ∗ Método que v e r i f i c a s e há v i z i n h o s l i v r e s , c o n s i d e r a n d o as d i a g o n a i s ... ∗/ public A r r a y L i s t <int [] > v i z i n h o s L i v r e s ( int [ ] [ ] t a b u l e i r o , int l , int c ) { A r r a y L i s t <int [] > v i z i n h o s = new A r r a y L i s t <int [] > ( ) ; 192 195 198 /∗ ∗ V i z i n h o s da l i n h a a n t e r i o r , s e h o u ve r . . . ∗/ i f ( l > 0) { i f ( c > 0) i f ( t a b u l e i r o [ l − 1 ] [ c −1] == 0 ) v i z i n h o s . add (new int [ ] { l −1, c −1}) ; 201 i f ( t a b u l e i r o [ l − 1 ] [ c ] == 0 ) v i z i n h o s . add (new int [ ] { l −1, c } ) ; 204 i f ( c < 3) i f ( t a b u l e i r o [ l − 1 ] [ c +1] == 0 ) v i z i n h o s . add (new int [ ] { l −1, c +1}) ; 207 } 210 213 /∗ ∗ V i z i n h o s da mesma l i n h a . . . ∗/ i f ( c > 0) i f ( t a b u l e i r o [ l ] [ c −1] == 0 ) v i z i n h o s . add (new int [ ] { l , c −1}) ; 216 219 222 225 228 i f ( c < 3) i f ( t a b u l e i r o [ l ] [ c +1] == 0 ) v i z i n h o s . add (new int [ ] { l , c +1}) ; /∗ ∗ V i z i n h o s da l i n h a p o s t e r i o r , s e h o u ve r . . . ∗/ i f ( l < 3) { i f ( c > 0) i f ( t a b u l e i r o [ l + 1 ] [ c −1] == 0 ) v i z i n h o s . add (new int [ ] { l +1, c −1}) ; B.4. Classe Minimax.java 40 i f ( t a b u l e i r o [ l + 1 ] [ c ] == 0 ) v i z i n h o s . add (new int [ ] { l +1, c } ) ; 231 i f ( c < 3) i f ( t a b u l e i r o [ l + 1 ] [ c +1] == 0 ) v i z i n h o s . add (new int [ ] { l +1, c +1}) ; 234 } 237 return v i z i n h o s ; } 240 243 246 /∗ ∗ Fim de j o g o ? ∗ O j o g o só termina s e não h o u v er mais e s p a ç o para j o g a d a s . . . ∗/ public boolean t e s t e t e r m i n a l ( int [ ] [ ] tab ) { return ( semEspaco ( tab ) ) ; } 249 252 255 /∗ ∗ Retorna a u t i l i d a d e . . . ∗ Aqui a u t i l i d a d e c o n s i d e r a d a é a d i f e r e n ç a de p o n t o s e n t r e o computador e ∗ o j o g a d o r , o computador não d e s e j a apenas vencer , mas também h u m i l h a r =P ∗/ public int u t i l i d a d e ( int [ ] [ ] tab ) { int pc , u s r ; 258 pc = co ntaPo nto s ( tab , 1 ) ; u s r = co nta Ponto s ( tab , −1) ; 261 return ( pc−u s r ) ; } 264 267 270 273 /∗ ∗ V e r i f i c a s e j o g a d o r ganhou ∗/ public int co ntaP onto s ( int [ ] [ ] tab , int v ) { int p o n t o s = 0 ; f o r ( int i = 0 ; i < 4 ; i ++) { p o n t o s += c o nta L inh a ( tab , i , v ) ; B.4. Classe Minimax.java 41 p o n t o s += contaColuna ( tab , i , v ) ; 276 } 279 p o n t o s += contaDiag1 ( tab , v ) ; p o n t o s += contaDiag2 ( tab , v ) ; return p o n t o s ; 282 285 288 291 294 } /∗ ∗ Pontos na s e q u e n c i a de l i n h a s ? ∗ ∗ Método de contagem b i n á r i a . . um b y t e é d e s n e c e s s á r i o , p r e c i s a r i a apenas ∗ de 4 b i t s . . Basicamente , para cada p o s i ç ã o a t r i b u i −s e o v a l o r 1 na mesma ∗ p o s i ç ã o do b y t e , i n d i c a n d o que a l i é d e l e . No f i n a l checamos as 3 ∗ p o s s i b i l i d a d e s de marcar pontos , 4 p o s i ç õ e s v i z i n h a s ( 1 1 1 1 ) ou 3 posicoes ∗ v i z i n h a s (0111 ou 1110) . Qualquer o u t r a combinação t e r i a menos do que ∗ 3 p o s i ç õ e s v i z i n h a s e não marcariam p o n t o s . ∗/ private int c o nt a L in ha ( int [ ] [ ] tab , int l , int v ) { byte soma = 0 ; 297 f o r ( int i = 0 ; i < 4 ; i ++) i f ( tab [ l ] [ i ] == v ) soma += ( 1 << i ) ; 300 i f ( soma == 1 5 ) // 1111 return 3 ; e l s e i f ( ( soma == 7 ) | | return 1 ; else return 0 ; 303 306 ( soma == 1 4 ) ) // 0111 v 1110 } 309 312 315 318 /∗ ∗ Pontos na s e q u e n c i a de c o l u n a s ? ∗/ private int contaColuna ( int [ ] [ ] tab , int c , int v ) { int soma = 0 ; f o r ( int i = 0 ; i < 4 ; i ++) i f ( tab [ i ] [ c ] == v ) B.4. Classe Minimax.java 42 soma += ( 1 << i ) ; i f ( soma == 1 5 ) // 1111 return 3 ; e l s e i f ( ( soma == 7 ) | | ( soma == 1 4 ) ) // 0111 v 1110 return 1 ; else return 0 ; 321 324 327 330 333 } /∗ ∗ Ganhou na s e q u e n c i a d i a g o n a l ? ∗/ private int contaDiag1 ( int [ ] [ ] tab , int v ) { int soma = 0 ; int p e x t r a = 0 ; 336 f o r ( int i = 0 ; i < 4 ; i ++) i f ( tab [ i ] [ i ] == v ) soma += ( 1 << i ) ; 339 /∗ ∗ Nas duas d i a g o n a i s a s e g u i r só é p o s sı́ v e l formar s e q u e n c i a s de 3 , ∗ podendo−s e a d i c i o n a r e n t a o apenas 1 ponto . . . . ∗/ i f ( tab [ 1 ] [ 0 ] == v && tab [ 2 ] [ 1 ] == v && tab [ 3 ] [ 2 ] == v ) p e x t r a ++; i f ( tab [ 0 ] [ 1 ] == v && tab [ 1 ] [ 2 ] == v && tab [ 2 ] [ 3 ] == v ) p e x t r a ++; 342 345 348 i f ( soma == 1 5 ) return 3 + p e x t r a ; e l s e i f ( ( soma == 7 ) | | ( soma == 1 4 ) ) return 1 + p e x t r a ; else return 0 + p e x t r a ; 351 354 } 357 360 /∗ ∗ Ganhou na s e q u e n c i a d i a g o n a l ? ∗/ 363 { private int contaDiag2 ( int [ ] [ ] tab , int v ) int soma = 0 ; int p e x t r a = 0 ; B.4. Classe Minimax.java 43 366 f o r ( int i = 0 ; i < 4 ; i ++) i f ( tab [3− i ] [ i ] == v ) soma += ( 1 << i ) ; 369 /∗ ∗ Nas duas d i a g o n a i s a s e g u i r só é p o s sı́ v e l formar s e q u e n c i a s de 3 , ∗ podendo−s e a d i c i o n a r e n t a o apenas 1 ponto . . . . ∗/ i f ( tab [ 0 ] [ 2 ] == v && tab [ 1 ] [ 1 ] == v && tab [ 2 ] [ 0 ] == v ) p e x t r a ++; i f ( tab [ 1 ] [ 3 ] == v && tab [ 2 ] [ 2 ] == v && tab [ 3 ] [ 1 ] == v ) p e x t r a ++; 372 375 378 i f ( soma == 1 5 ) return 3 + p e x t r a ; e l s e i f ( ( soma == 7 ) | | ( soma == 1 4 ) ) return 1 + p e x t r a ; else return 0 + p e x t r a ; 381 384 } 387 396 /∗ ∗ Não tem mais e s p a c o s r e s t a n t e s no t a b u l e i r o . . ∗/ public boolean semEspaco ( int [ ] [ ] tab ) { f o r ( int l = 0 ; l < 4 ; l ++) f o r ( int c = 0 ; c < 4 ; c++) i f ( tab [ l ] [ c ] == 0 ) return f a l s e ; 399 } 390 393 return true ; }