You are here

Aula 3 - SOAR: Tutorial 2

SOAR: Tutorial 2

 

Objetivo

Desenvolver o Tutorial 2 do Soar. Particularmente, utilizar o Soar para controlar o comportamento de criaturas artificiais bem simples, chamados "Eaters", em um jogo do tipo Pacman.  Para tanto, deverá ser utilizado o mecanismo de estados e operadores do Soar para diagnosticar o estado de cada Eater, propor diferentes tipos de ações a cada instante e mostrar como o Soar faz para interfacear com o jogo, para controlá-lo de fato.

Relatório das Atividades

 

Atividade 1

O programa do jogo (${soar_home}/Eaters.sh) foi executado, com uma criatura sendo controlado por um agente Soar com as regras definidas em move-to-food.soar. Ao executar, a criatura se move para uma célula adjacente que contenha comida, independentemente to tipo de comida (normal ou bonus). A escolha da célula é aleatória. A memória de trabalho da criatura contém informações, obtidas pela interface de entrada, sobre as células adjacentes até duas posições de distância da posição corrente, em todas as direções. Após a execução, a criatura para quando não há mais comida nas células ao seu redor. A Fig. 1.1 mostra o cenário do jogo e a Fig. 1.2 mostra a tela do Debbuger com as mensagens "state no-change" indicando a situação.

Eaters: move-to-food
Fig. 1.1 - Cenário da execução com a produção move-to-food

 

Fig. 1.2 - Debbuger mostrando o ponto de travamento

Como o estado atual da criatura é considerado nas regras?
As informações sobre as células adjacentes são relativas à posição corrente da criatura e são acessadas pela estrutura io.input-link 

Como a decisão de ação escolhida é aplicada de fato ao jogo?
O movimento é aplicado ao jogo pelo envio de um comando move <direção> através da interface de saída da criatura.

Como esse conjunto de regras escolhe a direção para a qual a criatura deve se mover?
A direção é escolhida aleatoriamente dentre as movimentações propostas para células adjacentes que contenham comida.

Para que serve a regra apply*move-to-food*remove-move?
A regra apply*move-to-food*remove-move remove da etsrutura io.output-link o comando recém executado, evitando a sua reexecução.

O que aconteceria se ela não existisse?
A criatura se moveria apenas em uma direção.

Quais são as limitações desse programa?
A criatura se move apenas se houver comida em uma célula imediatamente adjacente, ficando "travada" quando cercada por células sem comida.

O que seria necessário fazer para que ele não ficasse paralizado, depois de um tempo?
Uma possível solução seria escolher uma direção e continuar movendo-se até encontrar comida ou uma parede. E, para evitar travamento, escolher outra direção para mover-se quando encontrar uma parede.

Atividade 2

Executado o jogo com uma criatura com regras move-north.soar. A criatura se movimenta apenas na direção norte.

Executado o jogo com uma criatura com regras move-north-2.soar. A criatura se movimenta uma posição a cada vez na direção norte até encontrar um bloqueio.

Foi observada a operação da regra que remove o comando anterior no output-link quando uma nova regra é disparada (Fig. 2.1).

Fig. 2.1 - Produção move-to-north-2 destacando a remoção do operador move do io.output-link


Foi observada a utilização da mudança no ponto de parada na execução passo a passo do Debugger.

Foi executado o jogo com uma criatura com regras move-to-food-2.soar, que acrescenta a monitoração da direção do movimento as regras move-to-food.soar. O resultado da execução é mostrado nas Fig. 2.2 e Fig. 2.3.
Também foi observada a utilização de shortcuts e extensions.

Fig. 2.2 - Cenário da execução com a produção move-to-food-2.

 

Fig. 2.3 - Debbuger mostrando o trace da execução de move-to-food-2 (" Direction: <direction>").

 

A seção "Debugging Soar Programs" apresenta recursos e técnicas para identificar e resolver erros na elaboração de programas para o Soar. Os erros são classificados em semânticos, quando a lógica das regras contém incorreções, e sintáticos, quando ocorrem erros na digitação dos elementos que descrevem as regras para o Soar. Esses últimos podem ser resolvidos com auxílio dos recursos do Visual Soar.
Dentre os recursos que podem ser usados para identificar e resolver problemas de semântica temos:
a inclusão de comandos para escrever elementos (write) na ação das regras;
o uso de comandos em tempo de execução (print, wmes, matches e preferences).

Atividade 3

A seção 6 do tutorial apresenta a construção de um operador de movimento generalizado, introduzindo o conceito e o uso de preferências.
A generalização se dá através da substituição do teste da estrutura io.input-link.my-location.<dir>.content de << normalfood bonusfood >> por { <content> <> wall }. O uso de preferências é destacado na Fig. 3.1 e o resultado da execução é mostrado nas Fig. 3.2 e 3.3, mostrando que essa generalização evita o "travamento" da criatura que agora consome toda a comida disponível.

Fig. 3.1 - Destaque do uso de preferências na produção move (generalized move).

 

Fig. 3.2 - Cenário da execução com a produção move (generalized move).

 

Fig. 3.3 - Debbuger ao final da execução com a produção move (generalized move).

A seção 7 do tutorial apresenta a construção de um operador de movimento avançado que evita o movimento errático de ir e vir quando cercado por células sem comida. Isso é obtido com o uso de estruturas peristentes na memória de trabalho; uma contendo o oposto de cada direção (Fig. 3.4) e outra armazenando a última direção para a qual a criatura se moveu (Fig. 3.5). Essas estruturas são usadas na proposição de movimento, testando a ausência da última direção igual ao sentido oposto da direção proposta ou através de preferência que rejeita qualquer operador que proponha o movimento na direção oposta da atual (Fig. 3.6).

Fig. 3.4 - Estrutura persistente que contém os opostos para cada direção.

 

Fig. 3.5 - Criação e remoção da estrutura que armazena a última direção.

 

Fig. 3.6 - Teste da estutura contendo a última direção oposta à direção atual (acima) e preferência rejeitando operador que proponha movimento na direção oposta da atual (embaixo).

A seção 8 apresenta a construção de um operador de salto simples e, em seguida, um mais completo com movimento e salto, propondo a simplificação dos operadores de forma a usar apenas uma regra para aplicar e remover os operadores move e jump.

Também propõe uma alternativa para a cobertura das múltiplas combinações de movimento e salto em função do conteúdo das células de destino. Para isso é criada uma estrutura persistente na memória de trabalho contendo um mapeamento entre os operadores e os pontos obtidos com o movimento para células com cada conteúdo possível. Esses valores são então usados para comparar e selecionar entre operadores propostos.

O código completo é mostrado na Fig. 3.7.

Fig. 3.7 - Produção jump-and-move.

 

Atividade 4

Uma possível abordagem para o desenvolvimento de um conjunto de regras Soar que sistematicamente busque por comida quando estiver cercado por células sem comida, utilizando a estrutura top-state descrita na seção 10 do tutorial, é evitar que a criatura se mova aleatoriamente, preferindo manter a última direção. Para isso foi explorado o recurso de preferências, inserindo o código select*move*prefer*last-direction na produção advanced-move (Fig. 4.1). A nova produção foi chamada de systematic-move e pode ser obtida através do link systematic_eater.zip.

Fig. 4.1 - Produção systematic-eater, destacando select*move*prefer*last-direction.

 

A inclusão dessa preferência torna a movimentação da criatura mais sistemática, evitando as mudanças aleatórias de direção tanto na presença de células com comida quanto na situação em que não há comida nas células adjacentes.

Para melhorar a performance foi acrescentado o comando jump, substituindo a proposição e aplicação do comando move por versões genéricas (Fig. 4.2)

Fig. 4.2 - Produção systematic-eater destacando propose*jump, apply*move*jump e apply*move*jump*remove-name.

 

Também foram incluídas outras preferências para melhorar a performance evitando saltos desnecessários (select*move*jump*prefer-move-to-foodselect*move*avoid-jump-to-last-direction), uma vez que implicam em custo mais alto, mas preferindo o salto para uma célula com comida em vez de mover-se para uma célula vazia (select*move*jump*prefer-jump). Essa última evita que a criatura permaneça movendo-se sempre na última direção quando há células com comida duas posições ao lado (este ou oeste). Os códigos estão destacados na Fig. 4.3.

Fig. 4.2 -Produção systematic-eater destacando select*move*jump*prefer-move-to-food,  select*move*avoid-jump-to-last-direction e select*move*jump*prefer-jump.

A execução da produção systematic-move mostrou que o total de passos é reduzido se comparado com as produções advanced-eaterjump-and-move sem, e em geral, obter uma melhor pontuação, o que se deve ao menor número de movimentos por células vazias. O resultado da execução isolada é mostrado nas Fig. 4.3 e Fig. 4.4, cenário e Debbuger, respectivamente. O resultado da execução simultânea das três produções, de forma a permitir uma comparação, é mostrado nas Fig. 4.5, Fig. 4.6, Fig. 4.7 e Fig. 4.8.

Fig. 4.3 Execução isolada da produção systematic-move.

 

Fig. 4.4 - Execução isolada da produção systematic-move mostrando os últimos movimentos no Debbuger.

 

 

Fig. 4.5 - Execução competitiva das produções advanced-move (vermelho), jump-and-move (azul) e sistematic-move (amarelo).

 

Fig. 4.6 - Debbuger para a produção advanced-move na execução competitiva.

 

Fig. 4.7 - Debbuger para a produção jump-and-move na execução competitiva.

 

Fig. 4.7 - Debbuger para a produção sistematic-move na execução competitiva.

No entanto essa abordagem não garante sempre o consumo de toda a comida disponível. Quando só restam algums células com comida na região central do ambiente, a criatura se movimenta somente nas células mais externas.

Uma possível solução seria forçar a mudança de direção após um certo número de movimentos na mesma direção. Mas uma implementação que funcione ainda não foi obtida.

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer