ou nao. No nosso caso, as estrategias basicas do Othello ja nos garantem uma engine razoavel. Geralmente o nivel de sofisticacao e a forca de uma engine refletem a complexidade da heuristica utilizada. O programa Logistello, por exemplo, contem milhares de variaveis e padroes pre-calculados, alem de utilizar tecnicas estatisticas e A.I. para refinar parametros. Apos implementar o framework basico do nosso programa, voce pode facilmente extender a heuristica, tomando o cuidado de testar cada nova funcao separadamente. Outra ideia e criar um pequeno framework para testar a forca do programa, o ideal seria utilizar uma engine superior como adversaria. A maioria das engines de xadrez adotam o protocolo UCI para se comunicar com uma GUI. No nosso caso, Othello Engine Protocol parece ser a solucao.O codigo Vamos comecar definindo a estrutura basica do jogo, criando as classes OthelloBoard e OthelloMove. Na parte de AI, a classe MinimaxOthello e responsavel por executar o algoritmo em uma Thread separada, e a classe OthelloHeuristics define metodos para avaliar a 'qualidade' de determinada posicao. Finalmente, criamos uma GUI para testar a engine. Maos a obra!Classe OthelloBoard Essa classe define apenas 4 variaveis de instancia: emptyCells, phase, currentPlayer e cells. A variavel phase nos diz em qual fase do jogo estamos (abertura, meio-jogo ou final) e sera utilizada na hora de avaliar a posicao, pois cada fase exige uma estrategia diferente. A array cells representa as casas do tabuleiro 8 x 8, e sera interpretada como uma array 10 x 10, onde as 2 linhas e colunas extras irao facilitar a listagem de lances possiveis. Por fim, currentPlayer representa o jogador que tem a vez, e emptyCells nos diz quantas casas estao vazias. Os metodos makeMove e undoMove serao utilizados no Minimax para gerar a arvore de posicoes, evitando criar milhares de copias do objeto OthelloBoard que sera passado ao algoritmo. O metodo getAllMoves retorna uma lista com todos os lances possiveis para determinado jogador, iterando sobre as celulas e invocando getFlips para cada casa vazia. Para checar um possivel fim de jogo, checkEnd verifica se nao existem mais casas vazias, ou se nenhum dos jogadores possui lances validos. O restante do codigo sao getters, setters, ou metodos triviais como cloneBoard. E fundamental entender como lidamos com a situacao em que um jogador nao possui lances disponiveis: getAllMoves retorna um lance 'vazio', que e processado normalmente dentro de makeMove ou undoMove, assim nao precisamos tratar deste caso especial ao implementar o algoritmo. Segue a classe:import java.util.*;public class OthelloBoard{ public static final int BLACK = 1; public static final int WHITE = 2; public static final int EMPTY = 0; public static final int WALL = 5; public static final int BOARD_SIZE = 8; public static final int[] DIRECTIONS = { -11, -10, -9, -1, 1, 9, 10, 11}; public static final int PHASE_OPENING = 100; public static final int PHASE_MIDGAME = 200; public static final int PHASE_ENDGAME = 300; private int emptyCells; private int phase; private int currentPlayer = BLACK; private int[] cells = new int[(BOARD_SIZE + 2) * (BOARD_SIZE + 2)]; public OthelloBoard() { this.currentPlayer