Buscar

Estruturas de Dados - Árvores para Jogos

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 36 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 36 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 36 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

CIC/UnB - Estruturas de 
dados
Árvores para jogos
Árvores
● O conceito de árvores genéricas que 
vimos recentemente pode ser usado para 
a implementação de programas que 
lidam com jogos, nos quais os 
movimentos são feitos alternadamente 
por cada jogador, tais como o jogo da 
velha e o xadrez
Árvores
● O jogo da velha por exemplo, é jogado 
por dois jogadores em um tabuleiro 
(board) bem simples de 9 casas:
| |
---------------------
| |
---------------------
| |
Árvores
● Um jogador é representado por X e o 
outro por O e o objetivo é fechar uma 
linha, ou coluna ou diagonal:
| |
---------------------
| X |
---------------------
| |
Árvores
● Um jogador é representado por X e o 
outro por O e o objetivo é fechar uma 
linha, ou coluna ou diagonal:
| |
---------------------
| X |
---------------------
| | O
Árvores
● Um jogador é representado por X e o 
outro por O e o objetivo é fechar uma 
linha, ou coluna ou diagonal:
| |
---------------------
| X |
---------------------
 X | | O
Árvores
● Um jogador é representado por X e o 
outro por O e o objetivo é fechar uma 
linha, ou coluna ou diagonal:
| | O
---------------------
| X | 
---------------------
 X | | O
Árvores
● Um jogador é representado por X e o 
outro por O e o objetivo é fechar uma 
linha, ou coluna ou diagonal:
| | O
---------------------
| X | X
---------------------
 X | | O
Árvores
● Um jogador é representado por X e o 
outro por O e o objetivo é fechar uma 
linha, ou coluna ou diagonal:
| O | O
---------------------
| X | X
---------------------
 X | | O
Árvores
● Um jogador é representado por X e o 
outro por O e o objetivo é fechar uma 
linha, ou coluna ou diagonal:
| O | O
---------------------
 X | X | X
---------------------
 X | | O X ganha!
Árvores
● Dada uma configuração do tabuleiro, são 
possíveis várias jogadas (para X): 
X| O | 
----------
 | X |
----------
 | | O 
X| O | X X| O | X| O | X| O | X| O |
---------- ---------- ---------- ----------- ----------
 | X | | X | X | X | | X | X| X |
---------- ---------- ---------- ----------- ----------
 | | O | | O | X | O X| | O | | O 
Árvores
● Cada configuração (ou posição) de 
tabuleiro pode ser representada assim:
struct treenode {
 char board[3][3];
 int turn;
 struct treenode *son;
 struct treenode *next;
};
Árvores
●
A
B C D
E F G
 
De forma semelhante à árvore genérica vista na aula anterior
s i n
s i n
s i n
s i n s i n
s i n s i n
A
B DC
E F G
Árvores
● Neste nó temos duas informações, a 
saber, board[3][3] e turn (veremos adiante)
board[0][0] board[0][2]
| O | O
---------------------
 X | X | X
---------------------
 X | | O 
board[2][0] board[2][2]
 board[1][1] board[2][1] 
Árvores
● A escolha de uma jogada requer uma 
estratégia de avaliação.
● Uma primeira abordagem seria verificar 
quantas linhas, colunas ou diagonais 
ainda estão disponíveis para serem 
fechadas pelos jogadores (X ou 0) e optar 
pelo caminho que dará mais chances ao 
jogador da vez.
Árvores
● Dada uma configuração do tabuleiro, são 
possíveis várias jogadas (para X): 
X| O | 
----------
 | X |
----------
 | | O 
X| O | X X| O | X| O | X| O | X| O |
---------- ---------- ---------- ----------- ----------
 | X | | X | X | X | | X | X| X |
---------- ---------- ---------- ----------- ----------
 | | O | | O | X | O X| | O | | O
 2 2 2 2 1 
Árvores
● O que é interessante é que, pelo critério 
anterior, a jogada que tem menos valor é 
melhor! Isso acontece em jogos já adiantados, e 
não se observou que a última opção X fechará o 
jogo... O critério proposto sozinho funciona bem 
apenas no início do jogo.
● Deve-se então acrescentar outro critério mais 
forte, que atribui o valor 9 (maior que qualquer 
outra possibilidade) para opções que 
terminarão o jogo.
Árvores
● Uma estratégia mais inteligente (para X):
 
X| O | 
----------
 | X |
----------
 | | O 
X| O | X X| O | X| O | X| O | X| O |
---------- ---------- ---------- ----------- ----------
 | X | | X | X | X | | X | X| X |
---------- ---------- ---------- ----------- ----------
 | | O | | O | X | O X| | O | | O
 2 2 2 9 9 
Árvores
● Que as duas últimas são melhores nós vemos, 
mas a questão é como fazer o computador 
“ver“ isso.
● Nos vemos porque já estamos olhando para a 
próxima jogada. Podemos fazer isso também, e 
não necessariamente vendo todas as 
possibilidades adiante.
● Podemos definir, portanto, um nível n de 
previsão para verificar as próximas n jogadas
Árvores
● Veja para o nodo inicial com duas jogadas adiante:
 | | 
 ------------
 | |
 ------------
 | | 
 1 
 X| | | | | X | 
 ---------- ---------- ----------
 | | | X | | | 
 ---------- ---------- ----------
 | | | | | | 
●
● -1 1 -2 
●
●
X| O | X| | X| | X| | X | | O| | | O | O| X | | X | | X | | X | | X | 
---------- ---------- ---------- ----------- ---------- ----------- ---------- ---------- ----------- ---------- ------------ -------------
 | | | | | | | | | O | | X | | X | | | O| | | | | | | O | 
---------- ---------- ---------- ----------- ---------- ---------- ---------- ---------- ----------- ---------- ------------ --------------
 | | O| | | O | | | O | | | | | | | | | | O| | | O | | | 
 1 0 1 0 -1 1 2 -1 0 -1 0 -2 
Árvores
● Esse método é chamado de estratégia Mini-max
● Seus princípios são:
– na folha utiliza-se uma função estatística para 
averiguar a melhor opção (no nosso caso 
nossas possibilidades – possibilidades “dele“) 
– na nossa escolha vale o caminho de maior 
valor
– na escolha “dele“ vale o de menor valor (ele 
optará pelo pior para nós...)
Árvores
● Em outras palavras, nossa estratégia é 
defensiva e nós optamos pelo caminho “menos 
pior“, quando é nossa vez de optar e “ele“ opta 
pelo pior caminho para nós
● Na estratégia são calculados os valores das 
folhas e os valores são projetados para cima na 
árvore, dependendo de quem vai fazer a 
jogada: se for ele, sobe o pior valor, se formos 
nós, sobe o melhor valor, daí o nome Mini-max
Árvores
● Na árvore do exemplo a profundidade é três – dá para 
escolher o primeiro passo olhando o segundo como folha.
● É possível aprofundarmais e garantir melhor 
desempenho
● Na função de avaliação por mim implementada com 3 
passos adiante consegue-se um bom desempenho e os 
dois jogadores empatam, com 6057 avaliações de folhas
● Com 8 passos, X ganha, e são feitas 409110 avaliações 
(deve haver um bug na implementação, caso contrário 
eles empatariam)
Árvores
● A abordagem para a implementação de um simulador de 
jogo é a seguinte:
– gera-se uma árvore para a raiz do jogo, que 
representa o tabuleiro vazio
– a partir da raiz produz-se as subárvores das três 
escolhas possíveis (são mais que três, mas há várias 
equivalências...)
– para cada subárvore gera-se suas possíveis escolhas
– a árvore completa terá a profundidade de consulta 
escolhida – na figura-exemplo, 3.
Árvores
● A abordagem... (continuação):
– Estando pronta a árvore a partir da raiz faz-se o 
calculo do melhor caminho a partir da mesma
– Feita a escolha, cria-se uma árvore de consulta com a 
profundidade escolhida, agora partindo da 
configuração de tabuleiro da escolha feita – na 
simulação, a segunda escolha será feita pelo outro 
jogador, que agora pode ver uma posição adiante.
– Após a jogada de 'O', nova avaliação será feita, agora 
para 'X', gerando as subárvores a partir da 
configuração das duas jogadas prévias. E assim vai...
Árvores
● A arquitetura de implementação deve permitir várias 
montagens:
– Simulação do jogo completo
– Fazer o computador jogar como 'X' e ter uma interface 
para um humano jogar como 'O'
– Vice-versa
– Estrutura para aconselhamento
– Estrutura para justificativa da jogada
Árvores
● Para dar flexibilidade, o projeto deve ser modular e 
possuir vários procedimentos ou funções que permitam a 
montagem das facilidades requeridas
● A minha implementação de teste faz a simulação do jogo 
completo
● O programa principal inicia gerando o tabuleiro vazio (em 
board) e buscando a melhor opção para o jogador X
● Ele faz isso chamando a função nextmove, que recebe o 
tabuleiro board, calcula a melhor opção, devolvida no 
tabuleiro newboard:
 nextmove(board,4,player,newboard);
Árvores
● O cerne do procedimento main vai a seguir:
 for (k=9;k>0;k--) {
 if (player=='O') player='X';
 else player='O';
 printf("\nProximo movimento player %c:\n\n",player);
 nextmove(board,4,player,newboard); // 4 é a profundidade de consulta
 printboard(newboard);
 ...
 for (i=0;i<3;i++) for (j=0;j<3;j++) board[i][j]=newboard[i][j];
 }
● Repare que nextmove é chamada até 9 vezes, alternando o player entre X e 
O
● Após cada chamada, o newboard é impresso e atribuido a board, para ser o 
ponto de partida para a próxima chamada
● Em verdade é preciso verificar outras coisas antes de chamar de novo, tais 
como ver se o jogador ganhou a partida... veja o código completo:
Árvores
● for (k=9;k>0;k--) {
● if (player=='O') player='X';
● else player='O';
● printf("\nProximo movimento player %c:\n\n",player);
● nextmove(board,4,player,newboard);
● printboard(newboard);
● contamov++;
● winner=somebodywon(newboard);
● if (winner!=' ') {
● if (winner=='V')
● printf("\nDeu velha!...\n\n");
● else 
● printf("\n%c ganhou!\n\n",winner);
● printf("\n\nTotal de movimentos:%d\n",contamov);
● printf("Total de avaliações:%d\n\n",contaeval);
● return (0);
● }
● for (i=0;i<3;i++) for (j=0;j<3;j++) board[i][j]=newboard[i][j];
● }
Árvores
● printboard e somebodywon são procedimentos relativamente simples
● printboard gera um visão do tabuleiro, bem simples, em uma shell
● somebodywon verifica condições de término, isso é, se algum dos jogadores 
fechou uma linha, coluna ou diagonal, ou se não há mais jogadas possíveis, 
isso é, o jogo deu “velha“ (empatou)
● Segue printboard, só para ilustrar:
void printboard(char board[][3]){
 int i,j;
 for (i=0;i<3;i++) {
 for (j=0;j<3;j++)
 if (j<2) printf(" %c |",board[i][j]);
 else printf(" %c ",board[i][j]);
 if (i<2) printf("\n --------\n");
 else printf("\n");
 }
}
Árvores
● O procedimento nextmove tem uma missão bem mais complexa: ele usa o 
procedimento buildtree, para criar uma árvore a partir do board e depois 
aplica bestbranch para encontrar a melhor opção de desvio (que filho de 
board é o mais indicado)
● A saída de nextmove é newboard, o filho escolhido
void nextmove(char board[][3], int looklevel, char player, 
 char newboard[][3]) {
 NODEPTR ptree, best;
 int i, j, value;
 ptree=buildtree(board,looklevel);
 bestbranch(ptree, player, &best, &value);
 for (i=0; i<3; i++)
 for (j=0; j<3; j++)
 newboard[i][j]=best->board[i][j];
}
Árvores
● O procedimento buildtree, criar o nodo raiz, a partir do board e chama o 
procedimento expand, que se chama recursivamente para ir criando e 
linkando os filhos, netos, etc., da raiz dependendo da profundidade. 
NODEPTR buildtree(char board[][3],int looklevel) {
 NODEPTR ptree;
 int i,j;
 //cria a raiz e inicializa
 ptree=malloc(sizeof(struct treenode)); 
 for (i=0; i<3; i++)
 for (j=0; j<3; j++)
 ptree->board[i][j]=board[i][j];
 //por definicao a raiz é um nó do jogador +
 ptree->turn=1;
 ptree->son=NULL;
 ptree->next=NULL;
 expand(ptree, 0, looklevel); // cria o resto da arvore do jogo
 return (ptree);
}
Árvores
● O procedimento expand, é mostrado a seguir. Seu segredo é chamar 
generate, que vai criando e linkando, um a um, os filhos do nodo p, que foi 
passado como parâmetro. Feito isso, para cada filho, verifica se a 
profundidade máxima não foi atingida, e chama expand de novo!
void expand(NODEPTR p, int plevel, int depth) {
 NODEPTR q;
 if (plevel<depth) { // p nao esta'no nivel maximo
 q=generate(p->board);
 p->son=q;
 while (q!=NULL) { // percorre a lista de nos
 if (p->turn==1)
q->turn=-1;
 else
q->turn=1;
 q->son=NULL;
 expand(q, plevel+1, depth);
 q=q->next;
 }
 }
}
Árvores
● O procedimento generate é capcioso – é preciso gerar um irmão para cada 
casa em branco do tabuleiro – irmão de quem? depende do número de 
casas marcadas...
NODEPTR generate(char board[][3]){
 NODEPTR list,new;
 int i,j,marcados=0;
 char marca='X';
 list=NULL;
 for (i=0;i<3;i++)
 for (j=0;j<3;j++)
 if (board[i][j]!=' ') marcados++;
 if (marcados%2) marca='O'; // numero de marcas impar -> O
 for (i=0;i<3;i++)
 for (j=0;j<3;j++)
 if (board[i][j]==' ') {
new=malloc(sizeof(struct treenode));
new->next=list;
list=new;
int m,n;
for (m=0; m<3; m++)
 for (n=0; n<3; n++)
 list->board[m][n]=board[m][n];
list->board[i][j]=marca;
 }
 return (list);
}
Árvores
● Pronto! Não, agora já temos nossa arvore de possibilidades, mas falta 
avaliá-la. Para isso temos bestbranch:
void bestbranch(NODEPTR pnode, char player, NODEPTR *pbest, int *pvalue) {
 NODEPTR p, pbest2;
 int val;
 if (pnode->son==NULL) { // pnode é folha
 *pvalue=evaluate(pnode->board, player);
 *pbest=pnode;
 }
 else { // não é folha, atravessar a lista de filhos...
 p=pnode->son;
 bestbranch(p, player, pbest, pvalue);
 *pbest=p;
 if (pnode->turn==-1) *pvalue=-*pvalue;
 p=p->next;
 while (p!=NULL) {
 bestbranch(p,player, &pbest2, &val);
 if (pnode->turn==-1) val=-val;
 if (val>*pvalue) {
*pvalue=val;
*pbest=p;
 }
 p=p->next;
 }
 if (pnode->turn==-1) *pvalue=-*pvalue;
 }
}
Árvores
● E bestbranch precisa de evaluate, para avaliar as folhas e ir 
subindo com os valores de melhores escolhas; evaluate é 
relativamente simples, mas é longa. Vou mostrar só umas partes:
int evaluate(char board[][3], char player) {
● char other;
● int i,j,k, b,totplayer=0,totother=0;
●contaeval++;
● if (player=='X') other='O';
● else other='X';
● for (i=0;i<3;i++) { // soma linhas livres para player
● k=0;b=0;
● for (j=0;j<3;j++){
● if ((board[i][j]==player)|(board[i][j]==' ')) k++;
● if (board[i][j]==' ') b++;
● }
● if (k==3) {
● if (b==1) return (6);
● totplayer++;
● }
● }
● for (i=0;i<3;i++) { // soma linhas livres para other
● k=0;b=0;
● for (j=0;j<3;j++) {
● if ((board[i][j]==other)|(board[i][j]==' ')) k++;
● if (board[i][j]==' ') b++;
● }
● if (k==3) {
● if (b==1) return(-8);
● totother++;
● }
● }

Outros materiais