You are here

Relatório da Aula 04 (22/03/2013) SOAR - Tutorial 3

SUMÁRIO

1.  Introdução
2.  Objetivos
3.  Experimentos e Resultados
     3.1.  Atividade 1 - Exploração do programa TankSoar
     3.2.  Atividade 2 - Atividades propostas no Tutorial 3 do Soar
     3.3.  Atividade 3 - Desenvolvimento de um programa Soar para controle de um tanque
4.  Conclusão
5. Referências Bibliográficas
 


1.  Introdução

Este relatório apresenta a descrição das atividades e os resultados obtidos nos experimentos propostos na Aula 4 do Curso IA006 - Laboratório em Arquiteturas Cognitivas. Estes experimentos dão continuidade ao aprendizado da arquitetura cognitiva Soar e explorarão o controle de criaturas artificiais mais sofisticadas com um número de sensores e atuadores bem maior se comparado com o programa da aula anterior. Nesta aula será explorado o programa TankSoar.

Os objetivos gerais são apresentados na seção 2. A seção 3 traz a descrição dos experimentos realizados, seus objetivos específicos e os resultados obtidos em cada um deles. A seção 4 apresenta as conclusões obtidas a partir dos experimentos.
 


2.  Objetivos

A principal motivação dos experimentos desta aula é lidar com problemas mais complexos usando recursos mais adequados da arquitetura para solução de problemas. Estes recursos permitem a decomposição de uma meta em metas intermediárias (subgoals) e o tratamento no contexto do subproblema a ser resolvido.

Os objetivos principais destes experimentos são:

  • Criar agentes para o programa TankSoar a partir do estudo de programas Soar conforme o tutorial;
  • Manipular um conjunto maior de sensores e atuadores disponíveis neste jogo;
  • Fazer uso dos mecanismos de criação automática de subestados para definição de submetas;
  • Resolver o problema proposto da busca por carregadores de energia e saúde.

3.  Experimentos e Resultados


3.1.  Atividade 1 - Exploração do programa TankSoar

3.1.1. Objetivos específicos

  • Explorar o programa TankSoar para se familiarizar com o seu funcionamento;
  • Verificar os sensores e atuadores dos agentes e o ambiente disponíveis.

3.1.2. Desenvolvimento

Este experimento começa com a execução do programa TankSoar. A partir do estudo do ambiente, da especificação dos sensores e atuadores e do código-fonte do programa será possível compreender o seu funcionamento. A Figura 1 mostra a tela inicial do programa TankSoar e a execução de dois agentes com base do programa simple-bot.soar.

Figura 1 - Tela inicial do programa TankSoar
Figura 1 - Tela inicial do programa TankSoar.

O uso do programa TankSoar é semelhante ao programa Eaters. Para iniciar o programa, é necessário criar um novo agente. A criação é feita através do botão <New> na barra de botões da seção Agents. O TankSoar apresenta duas formas de controle do agente: manual, cujo controle é feito pelo usuário a partir das teclas de navegação do teclado numérico, e automático a partir de um programa Soar. A Figura 2 mostra a tela para criação de um novo agente no programa TankSoar.

Figura 2 - Tela para criação de um novo agente no TankSoar
Figura 2 - Tela de criação de um novo agente no TankSoar

Para este experimento, devem ser criados dois novos agentes a partir do programa simple-bot.soar. Após selecionado o mesmo programa para ambos os agentes, o programa pode ser iniciado. Para iniciá-lo, basta clicar no botão <Run> da seção Simulation. A Figura 3 mostra a execução do TankSoar com os dois agentes (nas cores vermelho e amarelo) controlados pelo programa simple-bot.soar.

Figura 3 - Tela do Eaters em execução com agente controlado pelo programa move-to-food.soar
Figura 3 - Tela do TankSoar em execução com dois agentes controlados pelo programa simple-bot.soar

A cada programa Soar carregado para controle do agente no TankSoar, uma instância da aplicação Soar Debugger é iniciada. A execução de uma simulação no TankSoar pode ser depurada no Soar Debugger para cada um dos agentes presentes no jogo. A Figura 4 mostra a saída do log na tela do Soar Debugger para o agente em execução na primeira simulação.

Figura 4 - Depuração da execução do agente no Eaters com o Soar Debugger
Figura 4 - Depuração da execução do agente com o Soar Debugger.

Quando o programa é executado, os agentes começam a percorrer o ambiente dependendo da estratégia definida pelas regras. No caso do programa simple-bot.soar, os comportamentos especificados são: vagar pelo ambiente (wander), perseguir o inimigo (chase), atacar o inimigo (attack) e recuar (retreat).

Para entender como o controle do agente é feito, o projeto simple-bot.vsa deve ser estudado e analisado. A Figura 5 mostra a estrutura deste projeto. É possível visualizar a hierarquia de operadores que especificam os comportamentos e as ações do agente.

Figura 5 - Código-fonte do projeto simple-bot.vsa no VisualSoar
Figura 5 - Código-fonte do projeto simple-bot.vsa no VisualSoar.

Os passos executados até aqui forneceram um contato inicial com o TankSoar e a criação de agentes baseados em um programa Soar existente (simple-bot.soar). A seguir serão apresentados os detalhes do ambiente de execução e a estrutura da memória de trabalho dos agentes para sensoreamento e atuação neste ambiente.

Recursos, sensores e atuadores do TankSoar

Cada tanque possui três recursos: nível de saúde (Health), nível de energia (Energy), míssil (Missile). Basicamente, o nível de saúde está relacionado com o número de mísseis que atingem o tanque (nível máximo: 1.000). O nível de energia está relacionado está relacionado com o uso do escudo e do radar por parte do tanque (nível máximo: 1.000). E, por fim, há um pacote de mísseis disponíveis para abater seus oponentes (15 mísseis). Os detalhes com as regras de operação de cada um desses recursos pode ser consultada em (Laird, 2012, p.98).

Cada tanque é dotado de um vasto número de sensores classificados em duas categorias: sensores primários e sensores secundários. Esta classificação divide os sensores em sensores de ambiente (informações externas relacionadas ao ambiente) e sensores de status (informações internas relacionadas ao próprio tanque).

Os sensores primários são: sensor de bloqueio (Blocked sensor), sensor de míssil (Incoming sensor), sensor radar (Radar sensor), sensor de ondas de radar (Rwaves sensor), sensor de cheiro (Smell sensor) e sensor sonoro (Sound sensor). Cada um desses sensores provê uma forma específica para obter diversas informações sobre o ambiente, tais como trechos de caminho bloqueados, míssil lançado vindo em direção, percepção de tanques inimigos nas proximidades etc. As informações fornecidas por cada um dos sensores pode ser consultada em datalhes em (Laird, 2012, p.98-99).

Os sensores secundários são: relógio (Clock), direção (Direction), energia (Energy), carregador de energia (Energy recharger), mísseis (Missiles), cor (My-color), alcance real do radar (Radar-distance), alcance desejado do radar (Radar-setting), número aleatório (Random), ressucitado (Resurrected), status do escudo de defesa (Shield-status), posição no eixo x (X) e posição no eixo y (Y). As informações fornecidas por cada um dos sensores pode ser consultada em datalhes em (Laird, 2012, p.101).

As ações previstas para cada tanque são: mover (Move), rotacionar (Rotate), disparar (Fire), ativar/desativar radar (Radar), intervalo de alcance do radar (Radar range) e ativar/desativar os escudos de defesa (Shields). Os detalhes a respeito de cada uma destas ações podem ser consultados em (Laird, 2012, p.102).

Ambiente do TankSoar

O ambiente virtual do TankSoar é formado por uma grade retangular com 14 células de altura por 14 células de comprimento. O ambiente é cercado por uma parede feita de pedras o que impede que os tanques saiam deste campo. Cada agente Soar controla um tanque. Cada tanque é dotado de recursos que permitem o sensoreamento do ambiente e pode atuar nele de alguma forma.

No ambiente, vários objetos estão espalhados. Alguns deles são obstáculos que obstruem a passagem enquanto outros são recursos disponíveis para uso por parte dos tanques. A Figura 6 rotula alguns destes objetos no ambiente.

Figura 6 - Objetos no ambiente do TankSoar. Fonte: Laird, 2012
Figura 6 - Objetos no ambiente do TankSoar. Fonte: Laird, 2012.

Os objetos presentes no ambiente são: obstáculos (Obstacles), carregador de saúde (Healthcharger), carregador de energia (Energycharger) e pacotes de mísseis (Packs of missiles). Cada objeto ocupa uma célula na grade do ambiente. Há apenas um carregador de cada tipo no campo e vários pacotes de mísseis espalhados. Os detalhes de cada um destes objetos podem ser consultados em (Laird, 2012, p.103).

Resumo da estrutura de entrada e saída (Input-link e Output-link)

A memória de trabalho de cada agente conta com extensões de entrada e saída para representação de todas as informações fornecidas pelos sensores bem com as ações a serem executadas pelos agentes no ambiente virtual. A Listagem 1 traz o resumo destas extensões (input-link e output-link).

^io                                  ^io
   ^input-link                          ^input-link
      ^blocked                                  ^rwaves
         ^backward yes/no                       ^backward yes/no
         ^forward yes/no                        ^forward yes/no
         ^left yes/no                           ^left yes/no
         ^right yes/no                          ^right yes/no
      ^incoming                                 ^smell
         ^backward yes/no                          ^color none/red/blue/purple/…
         ^forward yes/no                           ^distance none/0-28
         ^left yes/no                           ^sound silent/left/right/
         ^right yes/no                                 forward/backward
      ^radar                                    ^clock 1-N
         ^energy                                ^direction north/east/south/west
            ^distance 0-13                      ^energy 0-1000
            ^position left/center/right         ^energyrecharger no/yes
         ^health                                ^health 0-1000
            ^distance 0-13                      ^healthrecharger no/yes
            ^position left/center/right         ^missiles 0-N
         ^missiles                              ^my-color blue/red/purple/…
            ^distance 0-13                      ^radar-distance 1-14
            ^position left/center/right         ^radar-setting 1-14
         ^obstacle                              ^radar-status on/off
            ^distance 0-13                      ^random 0.0-1.0
            ^position left/center/right         ^resurrect no/yes
         ^open                                  ^shield-status on/off
            ^distance 0-13                      ^x 1-14
            ^position left/center/right         ^y 1-14
         ^tank
            ^distance 0-13
            ^position left/center/right

^io
   ^output-link
      ^move.direction left/right/forward/backward/none
      ^rotate.direction left/right
      ^fire.weapon missile
      ^radar.switch on/off
      ^radar-power.setting 1-14
      ^shields.switch on/off

Listagem 1 - Resumo das estruturas de entrada e saída (input-link e output-link) (Laird, 2012, p.104).

As estruturas apresentadas na Listagem 1 correspondem aos elementos da memória de trabalho manipuladas pelas produções e serão intensivamente utilizadas no desenvolvimento de agentes no TankSoar.

3.1.3. Resultados

Nesta atividade foi possível entrar em contato com os recursos do jogo TankSoar através da execução e simulação de uma batalha com dois agentes controlados por um mesmo programa Soar: simple-bot.soar.

Através do estudo das seções introdutórias do Tutorial 3 do Soar foi possível:

  • Familiarizar-se com o funcionamento do programa;
  • Entender os recursos, sensores e atuadores de cada agente;
  • Entender os objetos do ambiente e sua interação com os agentes;
  • Entender as estruturas de entrada e saída disponíveis para o desenvolvimento de regras;

Com o resumo do input-link e output-link em mãos, pode-se agora iniciar a próxima atividade, que vai abordar em detalhes os operadores envolvidos em cada ação dos agentes.


3.2.  Atividade 2 - Atividades propostas no Tutorial 3 do Soar

3.2.1 Objetivos específicos

  • Realizar as atividades propostas no Tutorial 3 do Soar;
  • Desenvolver um agente simples com a finalidade de controlar um tanque no TankSoar.

3.2.2. Desenvolvimento

Esta atividade fornece os subsídios necessários para a criação de um conjunto de regras capazes de fazer com que o agente atue a partir de informações percebidas no ambiente. Estas regras criam operadores que representam o comportamento do agente e as ações relacionadas com cada comportamento. Inicialmente as regras serão compostas para a criação de um agente que vagueia sem destino específico pelo ambiente. Depois, outros comportamentos serão especificados, tais como perseguir e atacar um inimigo ou recuar de acordo com estratégias simples de manutenção dos níveis de energia e saúde.

O tanque vagante (wandering tank)

Vaguear pelo campo significa mover-se pelo ambiente usando os sensores para caminhar por trechos livres de obstáculos e detectar objetos. Para tal, o tanque deve acionar o radar de modo a detectar objetos em um horizonte próximo. Como a utilização do radar consome energia, a melhor estratégia é mover-se e ligar o radar apenas quando o tanque muda de direção. A mudança de direção é necessária toda vez que o tanque for bloqueado de seguir adiante. Esta abordagem exige um esforço minímo em termos do energia mas despreza ainda a identificação dos objetos encontrados.

Uma proposta de algoritmo para esta estratégia pode ser vista a seguir:

  1. Mova-se adiante, caso não esteja bloqueado;
  2. Caso a frente esteja bloqueada, vire-se e ligue o radar com potência máxima;
  3. Desligue o radar caso ele esteja ligado e não existam objetos visíveis;
  4. Assim que o radar for desligado, mova-se adiante.

A implementação desta estratégia em produções no Soar pode ser vista na Listagem 2. A Figura 7 mostra a estrutura do projeto no VisualSoar. A Figura 8 mostra a saída do log no SoarDebugger durante a execução do agente.

## Move Operator Proposal
# If the task is tanksoar and the tank is not blocked in the forward
# direction, propose the move operator.

sp {tanksoar*propose*move
   (state <s> ^name tanksoar
              ^io.input-link.blocked.forward no)
-->
   (<s> ^operator <o> +)
   (<o> ^name move
        ^actions.move.direction forward)}

## Turn Operator Proposal
# If the tank is blocked in the forward direction, and not blocked in the
# right or left directions, then propose the turn operator for the unblocked
# direction. Also create an indifferent preference for the operator.
# This operator will also turn on the radar and set the radar-power to 13.
# If the tank is blocked in the forward direction, and in both the right or
# left directions, then propose the turn operator left.

sp {propose*turn
   (state <s> ^name tanksoar
              ^io.input-link.blocked <b>)
   (<b> ^forward yes
        ^ { << left right >> <direction> } no)
-->
   (<s> ^operator <o> + =)
   (<o> ^name turn
        ^actions <a>)
   (<a> ^rotate.direction <direction>
        ^radar.switch on
        ^radar-power.setting 13)}

sp {propose*turn*backward
   (state <s> ^name tanksoar
              ^io.input-link.blocked <b>)
   (<b> ^forward yes ^left yes ^right yes)
-->
   (<s> ^operator <o> +)
   (<o> ^name turn
        ^actions.rotate.direction left)}

## Radar-off Operator Proposal
# If the radar is on but no energy, health, missiles and tanks visible,
# then propose the radar-off operator

sp {propose*radar-off
   (state <s> ^name tanksoar
              ^io.input-link <il>)
   (<il> ^radar-status on
        -^radar.<< energy health missiles tank >>)
-->
   (<s> ^operator <o> +)
   (<o> ^name radar-off
        ^actions.radar.switch off)}

## Radar-off Search Control
# If radar-off is proposed, then prefer it to move and turn.

sp {select*radar-off*move
   (state <s> ^name tanksoar
              ^operator <o1> +
              ^operator <o2> +)
   (<o1> ^name radar-off)
   (<o2> ^name << turn move >>)
-->
   (<s> ^operator <o1> > <o2>)}

sp {apply*move*radar-off
   (state <s> ^name tanksoar
              ^operator <o>
              ^io.input-link <il>)
   (<il> ^radar-status on
        -^radar.<< energy health missiles tank >>)
   (<o> ^name << turn move >>
        ^actions <a>)
-->
   (<a> ^radar.switch off)}

## Radar-off Search Control
# If radar-off is proposed, then prefer it to move and turn.

sp {elaborate*task*tanksoar
   (state <s> ^superstate nil)
-->
   (<s> ^name tanksoar)}

sp {elaborate*state*name
  (state <s> ^superstate.operator.name <name>)
-->
  (<s> ^name <name>)
}

## Elaborate Top State

sp {elaborate*state*top-state
  (state <s> ^superstate.top-state <ts>)
-->
  (<s> ^top-state <ts>)
}

sp {elaborate*state*io
   (state <s> ^superstate.io <io>)
-->
   (<s> ^io <io>)}

## Apply Action Command

sp {apply*operator*create-action-command
   (state <s> ^operator <o>
              ^io.output-link <ol>)
   (<o> ^actions <act>)
   (<act> ^<att> <value>)
   (<value> ^<att2> <value2>)
-->
   (<ol> ^<att> <value3>)
   (<value3> ^<att2> <value2>)}

sp {apply*operator*remove-command
   (state <s> ^operator.actions
              ^io.output-link <ol>)
   (<ol> ^<att> <value>)
   (<value> ^status complete)
-->
   (<ol> ^<att> <value> -)}

Listagem 1 - Resumo das estruturas de entrada e saída (input-link e output-link) (Laird, 2012, p.104).

Figura 7 - Estrutura do projeto do Wandering Tank no VisualSoar.
Figura 7 - Estrutura do projeto do Wandering Tank no VisualSoar.

Figura 8 - Log de execução do agente que controla o Tanque Vagante
Figura 8 - Log de execução do agente que controla o Tanque Vagante.

Submetas (Subgoals)

O Soar apresenta um mecanismo interessante para solução de problemas: a decomposição de metas em submetas intermediárias. Através dele é possível resolver subproblemas em contextos específicos (subestados) de modo que os resultados obtidos possam contribuir para a solução do problema original.

Para entender como realizar a decomposição de metas, podemos nos propor a implementar os demais comportamentos previstos para o agente além do vagueio pelo campo da seção anterior. A intenção é implementar os comportamentos de perseguição, ataque e recúo. Estes comportamentos são representados por operadores de alto nível. Tais operadores são compostos por um conjunto de operadores de baixo nível que representam as ações do agente no ambiente. Juntos, ambos os tipos de operadores formarão uma hierarquia de operadores.

A criação de operador de alto nível para tratamento de comportamento específicos do agente é baseado na presência de impasses e na criação de subestados. Para entendermos como a implementação deste recurso, é necessário uma explanação sobre alguns conceitos e mecanimos internos do Soar envolvidos neste processo.

Impasses e a criação de estados

Durante o ciclo de decisão, as fases de proposta e aplicação de operadores são responsáveis pela proposição de operadores com as indicações de preferências conforme a pertinência destes operadores no estado atual e pela seleção dos operadores propostos, respectivamente. Durante este o processo de seleção de operadores, os operadores são classificados conforme as indicações de preferências atribuídas a eles durante a proposição. Em determinados casos, pode haver um conflito entre estas preferências ou mesmo a ausência de operadores propostos.

Há três tipos de impasses existentes no Soar:

  • Impasse de estado sem alteração (state no-change impasse): ocorre quando nenhum operador é proposto para o estado atual;
  • Impasse de empate entre operadores (operator tie impasse): ocorre quando múltiplos operadores são propostos mas não há indicações de preferências suficientes para escolher um entre eles.
  • Impasse de conflito de operadores (operator conflict impasse): ocorre quando múltiplos operadores são propostos e as indicações de preferências conflitam entre si.

Estas situações de impasse fazem com que novos subestados sejam criados pelo Soar a partir do estado atual com o intuito de resolvê-los neste novo contexto. Uma vez que um substado é criado, operadores podem ser selecionados e aplicados da mesma forma como no estado original. Este operadores podem tanto alterar o estado local (subestado) quanto o estado original (superestado). Estas alterações podem alterar o estado original diretamente ou pela execução de ações que vão provocamr alterações nos sensores e que, por sua, vez, vão provocar alterações no estado original.

O processamento em um subestado pode levar à criação de novas estruturas (WME's) na memória de trabalho que são parte do estado original. Estas estruturas são chamadas de resultados. É a possibilidade de criar estruturas no estado original a partir de regras que são disparadas nos substados que garantem a resolução dos impasses.

Resolução de impasses

Os impasses de estado sem alteração são resolvidos quando o operador atual não é mais preferido no estado original. Isto pode ocorrer caso o operador preferido seja retirado (as regras que fizeram a sua proposição não são mais satisfeitas) ou porque o conjunto de preferências de operadores mudam de tal forma que outro operador é preferido. Ambos os impasses, de empate e conflito entre operadores, são resolvidos quando preferências suficientes foram criadas de modo que o processo de decisão pode então selecionar um operador sem incorrer novamente nessas situações.

Quando um impasse é resolvido, os subestados são automaticamente removidos da memória de trabalho. No entanto, todos os resultados que foram criados no subestado podem ser persistidos. Este aspecto é importante porque os resultados são alterações no estado original e normalmente são eles os responsáveis pela solução do impasse.

Assim como todos os outros elementos da memória de trabalho, a persistência dos resultados devem ser determinadas pelo Soar. Mecanismos internos do Soar analisam quais estruturas devem ainda existir no superestado para o resultado produzido através da técnica de backtracking. O Soar verifica as condições das regras que foram criadas pelo resultado encontrando aquelas que foram disparadas no subestado que criaram os WMEs. Ao selecionar estas regras, ele as mantém ativas de modo formarem uma justificação. Esta justificação é essencialmente uma regra instanciada pelo Soar a partir da coleção destas regras previamente selecionadas. O Soar então analisa esta regra para determinar a persistência do resultado (i-support ou o-support). Se for i-support, a justificação é mantida até que uma das condições não sejam mais satisfeitas na memória de trabalho e então o resultado é retirado. Se a justificação for o-supported, então o resultado é mantido na memória de trabalho até que seja explicitamente removido.

Os operadores de persesguição, ataque e recúo

Os operadores de perseguição, ataque e recúo, assim como o operador de vagueio são operadores de alto nível. Estes operadores são propostos no estado original e resolvidos em subestados através de operadores de baixo nível. Estes operadores de baixo nível são as ações realizadas que atendem aos própositos dos operadores de alto nível. A relação entre todos os operadores envolvidos foram uma hierarquia como pode ser visto na Figura 9.

Figura 9 - Hierarquia de operadores para o agente no TankSoar. Fonte: Laird, 2012
Figura 9 - Hierarquia de operadores para o agente no TankSoar. Fonte: Laird, 2012.

A seguir são apresentadas as implementações de cada um destes operadores.

O operador de perseguição (chase)

O operador de perseguição deve ser selecionado quando um tanque percebe que um outro tanque está perto, mas ele não sabe onde exatamente o outro tanque está. A estratégia é utilizar o sensor de som, que fornece a informação de proximidade de outro tanque. Se o tanque detecta um tanque no radar, o operador de perseguição pode ser retirado e o operador de ataque pode então ser selecionado. Ele não perseguirá outro tanque caso não tenha mísseis ou esteja com baixa energia.

A Listagem 2 mostra a implementação da fases de proposição, elaboração e aplicação envolvendo o operador de perseguição.

### Propose chase operator

sp {propose*chase
   (state <s> ^name tanksoar
              ^io.input-link <io>
             -^missiles-energy low)
   (<io> ^sound <> silent
        -^radar.tank)
-->
   (<s> ^operator.name chase)}

### Elaborations

sp {chase*elaborate*state*sound-direction
   (state <s> ^name chase
              ^io.input-link.sound <sound>)
-->
   (<s> ^sound-direction <sound>)}

sp {chase*elaborate*radar
   :i-support
   (state <s> ^name chase
              ^operator.actions <a>
              ^io.input-link.radar-status off)
-->
   (<a> ^radar.switch on
        ^radar-power.setting 13)}

### Propose Move-forward operator if sound is forward

sp {chase*propose*move
   (state <s> ^name chase
              ^sound-direction forward
              ^io.input-link.blocked.forward no)
-->
   (<s> ^operator <o> +)
   (<o> ^name move
        ^actions.move.direction forward)}

### Propose radar operator if sound is forward
### and forward is blocked, also fire a missile

sp {chase*propose*radar
   (state <s> ^name chase
              ^sound-direction forward
              ^io.input-link.blocked.forward yes)
-->
   (<s> ^operator <o> +)
   (<o> ^name radar
        ^actions <a>)
   (<a> ^radar.switch on
        ^radar-power.setting 1
        ^fire.weapon missile)}

### Propose Turn operator if sound is not from front

sp {chase*propose*turn
   (state <s> ^name chase
              ^sound-direction {<< left right >> <direction>})
   -->
   (<s> ^operator <o> + =)
   (<o> ^name turn
        ^actions.rotate.direction <direction>)}

### Propose Turn operator if sound is from the back

sp {chase*propose*backward
   (state <s> ^name chase
              ^sound-direction backward)
   -->
   (<s> ^operator <o> +)
   (<o> ^name turn
        ^actions.rotate.direction left)}

Listagem 2 - Implementação do operador de perseguição (chase).

O operador de perseguição é proposto quando o sensor de energia não está indicando energia baixa e o sensor de som não está indicando silêncio (representando que há um tanque nas proximidades). Se a direção do som for à frente do tanque, é indicado que o tanque dê um passo adiante. No entanto, se mover-se adiante não é possível, porque há um bloqueio, é indicada a ativação do radar e o disparo de um míssil. Caso a direção do sensor de som não seja à frente, é proposto que o tanque vire-se para o lado indicado ou rotacione até inverter seu posicionamento caso o sensor indique a presença do som atrás do tanque.

Se o radar detectar a presença de um tanque, o operador de perseguição é retirado e o operador de ataque é então proposto.

O operador de ataque (attack)

O operador de ataque representa o comportamento do tanque quando este avista um inimigo e inicia disparos de mísseis no intuito de atingí-lo. Ele é selecionado assim que um outro tanque é detectado pelo radar. Entretanto, se o tanque estiver com saúde ou energia baixas, é preferível não atacar e o tanque passará a fugir do inimigo até estar em melhor condições para o combate.

A Listagem 3 mostra a implementação da fases de proposição, elaboração e aplicação envolvendo o operador de ataque.

# Propose attack operator
# If the state is the top state, 
# and there is a tank on radar, 
# and health and energy are not low, then
# propose the attack operator.

sp {propose*attack
   (state <s> ^name tanksoar
              ^io.input-link.radar.tank
             -^missiles-energy low)
-->
   (<s> ^operator <o> + =)
   (<o> ^name attack)}

# Propose Fire Operator
# If the state is attack 
# and there is a tank on radar in the center,
# then propose the fire missile operator.

sp {attack*propose*fire-missile
   (state <s> ^name attack
              ^io.input-link <il>)
   (<il> ^radar.tank.position center
         ^missiles > 0)
-->
   (<s> ^operator <o> + =)
   (<o> ^name fire-missile
        ^actions.fire.weapon missile)}

# Propose Move-Forward Operator
# If the state is attack 
# and there is a tank on radar that is not in the center, 
# and there is not a tank in the center, 
# and the tank is blocked in that direction 
# then propose move-forward.

sp {attack*propose*move
   (state <s> ^name attack
              ^io.input-link <input>)
   (<input> ^blocked.<dir> yes
            ^radar <r>)
   (<r> ^tank <t>
       -^tank.position center)
   (<t> ^position { << left right >> <dir> }
        ^distance <> 0)
-->
   (<s> ^operator <o> + =)
   (<o> ^name move
        ^actions.move.direction forward)}

# Propose Move Operator
# If the state is attack 
# and there is a tank on radar that is not in the center, 
# and there is not a tank in the center, 
# then propose the move operator in the direction of the tank.

sp {attack*propose*slide
   (state <s> ^name attack
              ^io.input-link <input>)
   (<input> ^blocked.<dir> no
            ^radar <r>)
   (<r> ^tank.position { << left right >> <dir> }
       -^tank.position center)
-->
   (<s> ^operator <o> + =)
   (<o> ^name slide
        ^actions.move.direction <dir>)}

# Propose Turn Operator
# If the state is attack 
# and there is a tank on radar that right next to the tank, 
# then propose turning in that direction and firing.

sp {attack*propose*turn
   (state <s> ^name attack
              ^io.input-link.radar.tank <tank>)
  (<tank> ^distance 0
          ^position { << left right >> <dir> })
-->
   (<s> ^operator <o> + =)
   (<o> ^name turn
        ^actions <a>)
   (<a> ^rotate.direction <dir>
        ^fire.weapon missile)}

Listagem 3 - Implementação do operador de ataque (ataque).

O operador de ataque é proposto quand o radar indica e presença do um tanque e o sensor de energia não está indicando energia baixa. Se o tanque inimigo estiver no centro do radar (bem à frente do tanque conforme a indicação do sensor), um míssivel é disparado em sua direção. Caso o tanque inimigo não esteja no centro, o tanque tentará dirigir-se àquela direção. Se a direção desejada estiver bloqueada, o tanque deve seguir à frente até encontrar uma oportunidade de movimentar-se de modo a colocar o inimigo no centro do radar. O tanque ainda, verifique se há um tanque à sua vizinhaça (nas células adjacentes, à esquerda ou à direita, da sua posição atual). Se sim, ele deve virar-se na direção no tanque e imediatamente disparar um míssil.

Caso o tanque inimigo saia do radar, o operador de ataque é retirado e um outro operador deverá ser proposto de acordo com o estado atual.

O operador de recúo (retreat)

O operador de recúo representa o comportamento do tanque quando ele está com um nível de energia baixo ou sem mísseis. É um comportamento de preservação evitando or ao combate sem as condições mínimas. O operador é proposto toda vez que ele avista um tanque no radar mas está com o nível de energia baixo ou não tem mísseis disponíveis. Assim que essas condições de energia e munição forem atentidas, o operador é retirado.

A Listagem 4 mostra a implementação da fases de proposição, elaboração e aplicação envolvendo o operador de recúo.

# If the state is tanksoar 
# and the sound sensor is not silent 
# or there is a tank on radar or there is an incoming missile, 
# and health is low or the energy is low, 
# then propose the retreat operator.

sp {propose*retreat*sound
   (state <s> ^name tanksoar
              ^missiles-energy low
              ^io.input-link.sound {<direction> <> silent})
-->
   (<s> ^operator <o> + =)
   (<o> ^name retreat)}

sp {propose*retreat*radar
   (state <s> ^name tanksoar
              ^missiles-energy low
              ^io.input-link.radar.tank)
   -->
   (<s> ^operator <o> + =)
   (<o> ^name retreat)}

sp {propose*retreat*incoming
   (state <s> ^name tanksoar
              ^missiles-energy low
              ^io.input-link.incoming.<dir> yes)
-->
   (<s> ^operator <o> + =)
   (<o> ^name retreat)}

#  If the state is tanksoar 
# and the tanks is under attack 
# but cannot not directly sense the other tank, 
# then propose the retreat operator.

sp {propose*retreat*incoming*not-sensed
   (state <s> ^name tanksoar
  ^io.input-link <io>)
         (<io> ^incoming.<dir> yes
              -^radar.tank
               ^sound silent)
-->
   (<s> ^operator <o> + =)
   (<o> ^name retreat)}

# Elaborations
# Compute enemy direction from sound, radar, or incoming

sp {elaborate*retreat*sound*direction
   (state <s> ^name retreat
              ^io.input-link.sound { <> silent <direction> })
-->
   (<s> ^direction <direction>)}

sp {elaborate*retreat*radar*front
   (state <s> ^name retreat
              ^io.input-link.radar.tank)
-->
   (<s> ^direction forward)}

sp {elaborate*retreat*incoming*direction
   (state <s> ^name retreat
              ^io.input-link.incoming.<dir> yes)
-->
   (<s> ^direction <dir>)}

sp {elaborate*retreat*radar*direction
   (state <s> ^name retreat
              ^io.input-link.radar.tank.position { <dir> <> center })
-->
   (<s> ^avoid-direction <dir>)}

# Propose Move Sidestep Operator
# If the state is named retreat 
# then propose sidestep from the direction of a detected enemy, 
# as long as that direction is not blocked, 
# is not the direction of another enemy 
# or is a direction to avoid.

sp {retreat*propose*move
   (state <s> ^name retreat
              ^direction <dir>
              ^superstate.side-direction.<dir> <ndir>
             -^direction <ndir>
             -^avoid-direction <ndir>
              ^io.input-link.blocked.<ndir> no)
-->
   (<s> ^operator <o> + =)
   (<o> ^name move
        ^actions.move.direction <ndir>)}

# Propose Wait
# If the state is named retreat then propose wait, 
# and make a worst preference for it.

sp {retreat*propose*wait
   (state <s> ^name retreat
             -^operator.name wait)
-->
   (<s> ^operator <o> + <)
   (<o> ^name wait)}

Listagem 4 - Implementação do operador de recúo (retreat).

Quando o sensor de som indicar a presença de um tanque nas proximidades e houver um tanque no radar, estando com o nível de saúde ou energia baixos, o tanque deve recuar. Ele recuará impreterivelmente quando a energia estiver baixa e há um míssil vindo em sua direção. O recúo é feita através da movimentação para o lado oposto da direção do inimigo detectada desde que não esteja bloqueado. O operador de espera é sempre proposto com sendo uma escolha para o pior caso caso se tenha uma indecisão sobre as condições de recúo. O operador de espera fará com que o tanque espere alterações no ambiente de modo que novos cenários favoráreis possam surgir para então tomar a decisão sobre a movimentação novamente.

Revisão do tanque simples (simple tank)

A implementação dos operadores de vagueio, perseguição, ataque e recúo é feita representando comportamentos simples realizados pelo tanque durante a execução do TankSoar. Estes comportamentos não levam em conta estratégias mais sofisticadas como o mapeamento dos locais já visitados e proposta de planos de ação. Ainda sim, eles fornecem um conjunto de ações capazes de fazer com que o tanque responda a eventos de modo satisfatório.

Abaixo segue uma lista de todas as condições cobertas pelos operadores desenvolvidos:

Vagueio (Wander)1. Som é silêncio, sem tanques no radar, sem mísseis vindo em sua direção;
Ataque (Attack)2. Nível de energia não é baixo, tem mísseis e há tanque no radar;
Perseguição (Chase)3. Nível de energia não é baixo, som detectado, não há tanque no radar;
Recúo (Retreat)4. Nível de energia baixo, há som detectado;
 5. Nível de energia baixo, há tanque no radar;
 6. Nível de energia baixo, há míssil vindo na direção;
 7. Som é silêncio, não há tanque no radar, há míssil vindo na direção.

Estas condições podem ser mapeadas em uma árvore de decisão que mostra as diferentes condições cobertas pelos operadores. Esta é uma abordagem de força bruta e não é recomendada quando há um número grande de diferentes condições a serem testadas. Isto porque nestes casos é constatada uma explosão combinatorial entre o número de condições existentes. A Figura 10 mostra a árvore de decisões proposta (W = vagueio, C = perseguição, A = ataque, R = recúo) .

Figura 10 - Árvore de decisão do agente no TankSoar. Fonte: Laird, 2012, p.127
Figura 10 - Árvore de decisão do agente no TankSoar. Fonte: Laird, 2012, p.127.

Ao colocar em execução o TankSoar com dois agentes baseados nas produções do tanque simples, é possível notar a proposição dos operadores discutidos e como são realizadas as ações conforme a seleção destes operadores. A Figura 11 mostra a execução do TankSoar com dois agentes em combate e a Figura 12 mostra um fragmento do monitoramento destes dois agentes no SoarDebugger.

Figura 11 - TankSoar em execução com dois agentes baseado no programa simple-bot.soar.
Figura 11 - TankSoar em execução com dois agentes baseado no programa simple-bot.soar.

Figura 12 - Monitoramento da execução de um dos agentes no TankSoar
Figura 12 - Monitoramento da execução de um dos agentes no SoarDebugger.

A melhoria da detecção de som

Uma situação constatada no agente controlado pelo programa simple-bot.soar é que ele perde o rastreamento de tanques inimigos quando estes param de se mover. O sensor de som não detecta som caso o tanque inimigo pare repentinamente mesmo este estando nas proximidades do tanque. Esta é uma situação indesejada porque o tanque passa a vagar novamente pelo ambiente quando, na verdade, há um perigo eminente.

A solução para este comportamento pode ser o armazenamento da posição indicada pelo sensor de som na memória de modo que, se passado algum tempo depois e nenhum outro som for emitido, o tanque enfim pode assumir a ausência do inimigo nas imediações. Isto pode ser realizado através da persistência de elementos da memória de trabalho que mantém as informações do sensor de som assim que detectado. Se passados alguns ciclos de execução (representados pelo clock) e nenhuma modificação foi feita no ambiente em relação ao sensor de som, estes elementos podem ser removidos da memória de trabalho.

A Listagem 5 mostra a implementação das produções envolvidas na melhoria da detecção de som por parte do agente. A gravação do som é feita durante o vagueio do tanque pelo ambiente.

# Record sound 

sp {wander*propose*record-sound
   (state <s> ^name wander
              ^io.input-link.sound <> silent)
-->
   (<s> ^operator <o> + >, =)
   (<o> ^name record-sound)}

sp {wander*apply*record-sound
   (state <s> ^operator.name record-sound
              ^io.input-link <il>
              ^superstate <ss>)
   (<il> ^sound <rel-sound>
         ^direction <direction>
         ^clock <clock>)
   (<ss> ^direction-map.<direction>.<rel-sound> <abs-sound>)
-->
   (<ss> ^sound <sd>)
   (<sd> ^absolute-direction <abs-sound>
         ^expire-time (+ <clock> 5))}

# Remove sound

sp {all*propose*remove-sound
   (state <s> ^name << wander chase retreat attack >>
              ^superstate.sound.expire-time <clock>
              ^io.input-link.clock > <clock>)
-->
   (<s> ^operator <o> + =, >)
   (<o> ^name remove-sound)}

sp {all*propose*remove-sound*changed-direction
   (state <s> ^name << wander chase retreat >>
              ^superstate <ss>
              ^io.input-link <il>)
   (<ss> ^sound.absolute-direction <abs-sound>
         ^direction-map.<direction>.<rel-sound> <abs-sound>)
   (<il> ^sound { <> silent <> <rel-sound> }
         ^direction <direction>)
-->
   (<s> ^operator <o> + =, >)
   (<o> ^name remove-sound)}

sp {all*apply*remove-sound
   (state <s> ^operator.name remove-sound
              ^superstate <ss>)
   (<ss> ^sound <sd>)
-->
   (<ss> ^sound <sd> -)}

Listagem 5 - Implementação das regras para melhoria na detecção do som do agente.

Tecnicamente este problema é resolvido através da criação de um subestado a partir de um impasse de estado sem alterações assim que o sensor de som indica presença de som nas proximidades. Quando este impasse ocorre e o subestado é criado, o operador de espera é proposto. Este operador propõe que o tanque aguarde até que alterações ocorram no ambiente de modo que seja possível detectar um novo som emitido pelo inimigo. Um contador interno é atualizado no intuito de representar o tempo em que o tanque deve ficar aguardando por modificações. Passado o tempo, o operador é retirado indicando a desistência pela percepção do som relacionado com aquele inimigo. A Figura 13 mostra a saída do log no SoarDebugger para a gravação do sim e o operador de espera.

Figura 13 - Monitoramento da execução da melhoria na detecção do som no SoarDebugger
Figura 13 - Monitoramento da execução da melhoria na detecção do som no SoarDebugger.

Um último refinamento proposto no Tutorial do Soar é a manipulação do mapa do ambiente. Este mapa é construído conforme a movimentação do agente no ambiente e pode oferecer informações valiosas para um comportamento mais sofisticado do agentes.

O Mapa do Ambiente

O mapa do ambiente pode ser armazenado na memória de trabalho do agente na medida em que ele explora o ambiente. Este mapeamento pode auxiliar o agente a ter uma exata noção dos objetos no mundo e permitir ações deliberadas em relação a eles, como dirigir-se aos carregadores de energia ou saúde, por exemplo. O programa mapping-bot.soar, disponibilizado no Tutorial do Soar, implementa a gravação do mapa do ambiente na memória de trabalho. Possíveis extensões deste programa podem implementar fazer uso destas estruturas.

Em linhas gerais, o mapa do ambiente é uma estrutura formada por elementos que representam as células pelas quais o tanque pode posicionar-se, ou que um objeto ocupa no espaço. Os células formam uma grade de 16x16 células. Cada célula é representada por uma estrutura que armazenam as informações de posicionamento (x, y) e o conteúdo da célula (o objeto que nela reside ou aberta, caso seja um caminho pode ser percorrido pelo tanque). O mapa é atualizado na medida em que o tanque percorre o ambiente através da informações do radar.

A Figura 14 mostra a estrutura do projeto mapping-bot.vsa que contém as produções que inicializam o mapa do ambiente, atualizam as informações de posicionamento e objetos presentes no mundo. A Figura 15 mostra a subestrutura da memória de trabalho que representa o mapa do ambiente na memória de trabalho.

Figura 14 - Estrutura do projeto mapping-bot.vsa
Figura 14 - Estrutura do projeto mapping-bot.vsa.

Figura 15 - Estrutura da memória de trabalho que representa o mapa do ambiente no SoarDebugger
Figura 15 - Estrutura da memória de trabalho que representa o mapa do ambiente no SoarDebugger.

Com o mapa do ambiente na memória de trabalho, novas produções no Soar podem ser criadas para explorar esta estrutura das mais diversas formas. Novas estratégias podem ser propostas dando origem a implementações mais sofisticadas dos operadores existentes ou mesmo propor novos operadores para novas situações agora possíveis.

3.2.3. Resultados

Esta atividade explorou a implementação de um conjunto de regras o Soar para controle de um agente simples, capazes de atuar de acordo com informações obtidas pelos seus sensores. Além disso, o agente é capaz de memorizar os locais mapeados pelo seu radar para uso conforme as suas necessidades.

O estudo realizado nesta atividade possibilitou:

  • o entendimento sobre a proposta e seleção de operadores de alto e baixo níveis;
  • o entendimento dos impasses dos três tipos de impasses existentes no Soar;
  • o entendimento sobre os mecanismos de criação de subestados a partir do estado original;
  • o entendimento sobre a definição de submetas e solução ne subproblemas neste contexto;

O conhecimento sobre como lidar com a definição de submetas e manipulação de subestados no Soar é de suma importância para a decomposição de um problema em subproblemas. A opção por esta abordagem pode trazer vantagens na implementação de busca por soluções em espaços de problema. Ela permite que os subproblemas sejam tratados em um contexto isolado informando os resultados para o estado original.

 


3.3.  Atividade 3 - Desenvolvimento de um programa Soar para controle de um tanque

3.3.1. Objetivos específicos

  • Desenvolver um programa Soar que, utilizando o mapa do ambiente, faça com que o tanque dirija-se aos carregadores de energia e saúde quando estiverem com baixa energia ou baixa saúde, além de realizar suas atividades normais.

3.3.2. Desenvolvimento

Para implementação de um agente capaz de usar o mapa do ambiente para dirigir-se aos carregadores de energia e saúde quando os respectivos níveis estiverem baixos, será utilizado como base o código-fonte do programa mapping-bot.soar.

A estratégia mais adequada é a implementação de um processo interno (na mente do agente) para a criação e avaliação de planos de navegação e selecionar aquele que melhor atenda ao objetivo de atingir a meta (ir até o local onde se encontra o carregador de energia ou saúde). Entretanto, a solução do problema baseando-se no planejamento será, por ora, descartada devido a ausência de conhecimentos específicos para seu tratamento no Soar. Estudos sobre planejamento e aprendizado serão realizados nas aulas subquentes do curso.

Uma abordagem alternativa é a memorização do percurso percorrido pelo agente logo após ter encontrado o carregador. Desta forma, quando detectado o nível baixo de energia ou saúde, bastaria fazê-lo percorrer o caminho de volta. Esta abordagem também será descartada porque não faz uso do mapa do ambiente em si para referenciar-se na navegação.

Solução (metáfora da bússola)

A solução do problema apresentado, considerando os limites dos mecanismos conhecidos até aqui, pode ser implementada a partir de heurísticas que levam em o conhecimento do mapa do ambiente por parte do agente e a sua direção atual para determinar o seu destino.

Em linhas gerais, ao ser detectado o nível baixo de energia ou saúde por parte do agente e uma vez que ele conhece os locais onde estão os carregadores (pelo uso do radar), ele pode ter indicações de direção para movimentação (norte, sul, leste ou oeste) e saber a taxi-distância em relação ao seu destino.

Para tratamento da situação em que os níveis de energia ou saúde estão baixos, um operador de alto nível, chamado de recharge, é especificado. Ele é proposto quando os sensores de energia e saúde indicam tais condições. A partir daí, de forma análoga aos outros operadores (vagueio, perseguição, ataque e recúo), um subestado é criado em que elaborações são feitas e operadores de baixo nível são propostos.

Basicamente, assim que detectada a necessidade de recarga, o programa compara a posição atual do agente e a posição do carregador. Regras específicas elaboram extensões do estado original no intuito de informar aos operadores de movimentação em que direção deve ser o próximo passo.

Assim, por exemplo, se o tanque estiver na posição (1, 1) e o carregador na posição (10, 10), serão indicadas as direções leste e sul, considerando as coordenadas com o par ordenado (x, y) em que x é coluna e y é a linha na grade que forma o ambiente. A célula (1, 1) é a referência mais ao norte e oeste do mapa. Se, por acaso, o tanque estiver na posição (2, 10) e o carregador na posição (3, 4), serão indicadas as direções sul e oeste. Caso ele esteja posicionado na mesma linha ou coluna do carregador, a direção não é indicada por considerar que o tanque está no eixo correto, como por exemplo: se a posição do tanque for (5, 1) e a do carregador (5, 10), a indicação será apenas sul.

A partir das indicações de direção, os operadores move e rotate devem fazer que com que o tanque mova-se adiante ou vire-se de modo a dirigir-se ao destino desejado. A Listagem 6 mostra as produções envolvidas na proposição, elaboração e aplicação dos operadores de alto e baixo níveis do comportamento de recarga.

# Propose recharge operator
# if energy or health is low,
# then propose the recharge operator.

sp {propose*recharge
   (state <s> ^name tanksoar
              ^io.input-link <il>)
   (<il> ^{ << energy health >> <eh> } <= 200)
-->
   (write (crlf) |    --: [recharge] propose*recharge: (| <eh> )
   (<s> ^operator.name recharge)
}

# Elaborate recharge square 
# if energy or health is low.

sp {recharge*elaborate*move
   :o-support
   (state <s> ^name recharge
              ^operator <o>
              ^superstate <ss>
              ^io.input-link <il>
             -^recharge-square)
   (<ss> ^map <m>)
   (<m> ^square <sq>)
   (<sq> ^{ << energy health >> <energy-health> } *yes*
         ^x <sx> ^y <sy>)
   (<il> ^x <x>  ^y <y>)
-->
   (write (crlf) |    --: [recharge] | <energy-health> | charger at (| <sx> |,| <sy> |), |
                                     | I'm at (| <x> |,| <y> |) | )
   (<s> ^recharge-square <sq>)
   (<sq> ^name <energy-health>
         ^x <sx> ^y <sy>)
}

sp {recharge*elaborate*move*east
   (state <s> ^name recharge
              ^superstate <ss>
              ^io.input-link <il>
              ^recharge-square <sq>)
   (<sq> ^x <ex>
         ^name <name>)
   (<il> ^x { <x> < <ex> } )
   (<ss> ^radar-map.east <dir>)
   (<dir> ^sx 1
          ^center.x 1)
-->
   (write (crlf) |    --: [recharge | <name> |] move east | )
   (<ss> ^recharge-dir east)
}

sp {recharge*elaborate*turn*east
   (state <s> ^name recharge
              ^superstate <ss>
              ^io.input-link <il>
              ^recharge-square <sq>)
   (<sq> ^x <ex>
         ^name <name>)
   (<il> ^x { <x> < <ex> } )
   (<ss> ^radar-map.east <dir>)
   (<dir> ^sx 1
          ^center.x <> 1)
-->
   (write (crlf) |    --: [recharge | <name> |] turn east | )
   (<ss> ^recharge-dir east)
}

sp {recharge*elaborate*move*west
   (state <s> ^name recharge
              ^superstate <ss>
              ^io.input-link <il>
              ^recharge-square <sq>)
   (<sq> ^x <ex>
         ^name <name>)
   (<il> ^x { <x> > <ex> } )
   (<ss> ^radar-map.west <dir>)
   (<dir> ^sx -1
          ^center.x -1)
-->
   (write (crlf) |    --: [recharge | <name> |] move west | )
   (<ss> ^recharge-dir west)
}

sp {recharge*elaborate*turn*west
   (state <s> ^name recharge
              ^superstate <ss>
              ^io.input-link <il>
              ^recharge-square <sq>)
   (<sq> ^x <ex>
         ^name <name>)
   (<il> ^x { <x> > <ex> } )
   (<ss> ^radar-map.west <dir>)
   (<dir> ^sx -1
          ^center.x <> -1)
-->
   (write (crlf) |    --: [recharge | <name> |] turn west | )
   (<ss> ^recharge-dir west)
}

sp {recharge*elaborate*move*north
   (state <s> ^name recharge
              ^superstate <ss>
              ^io.input-link <il>
              ^recharge-square <sq>)
   (<sq> ^y <ey>
         ^name <name>)
   (<il> ^y { <y> > <ey> } )
   (<ss> ^radar-map.north <dir>)
   (<dir> ^sy -1
          ^center.y -1)
-->
   (write (crlf) |    --: [recharge | <name> |] move north | )
   (<ss> ^recharge-dir north)
}

sp {recharge*elaborate*turn*north
   (state <s> ^name recharge
              ^superstate <ss>
              ^io.input-link <il>
              ^recharge-square <sq>)
   (<sq> ^y <ey>
         ^name <name>)
   (<il> ^y { <y> > <ey> } )
   (<ss> ^radar-map.north <dir>)
   (<dir> ^sy -1
          ^center.y <> -1)
-->
   (write (crlf) |    --: [recharge | <name> |] turn north | )
   (<ss> ^recharge-dir north)
}

sp {recharge*elaborate*move*south
   (state <s> ^name recharge
              ^superstate <ss>
              ^io.input-link <il>
              ^recharge-square <sq>)
   (<sq> ^y <ey>
         ^name <name>)
   (<il> ^y { <y> < <ey> } )
   (<ss> ^radar-map.south <dir>)
   (<dir> ^sy 1
          ^center.y 1)
-->
   (write (crlf) |    --: [recharge | <name> |] move south | )
   (<ss> ^recharge-dir south)
}

sp {recharge*elaborate*turn*south
   (state <s> ^name recharge
              ^superstate <ss>
              ^io.input-link <il>
              ^recharge-square <sq>)
   (<sq> ^y <ey>
         ^name <name>)
   (<il> ^y { <y> < <ey> } )
   (<ss> ^radar-map.south <dir>)
   (<dir> ^sy 1
          ^center.y <> 1)
-->
   (write (crlf) |    --: [recharge | <name> |] turn south | )
   (<ss> ^recharge-dir south)
}

# Propose move operator

sp {recharge*propose*move*forward
   (state <s> ^name recharge
             -^recharge-square
              ^io.input-link.blocked.forward no)
-->
   (write (crlf) |    --: [recharge] propose*move*forward | )
   (<s> ^operator <o> + =)
   (<o> ^name move
        ^actions.move.direction forward)}

sp {recharge*propose*move*dir
   (state <s> ^name recharge
              ^superstate <ss>
              ^io.input-link <il>)
   (<ss> ^recharge-dir <dir>)
   (<il> ^direction <dir>
         ^blocked.forward no)
-->
   (write (crlf) |    --: [recharge] move forward: | <dir> )
   (<s> ^recharge-dir <dir> -)
   (<s> ^operator <o> + >)
   (<o> ^name move
        ^actions.move.direction forward)
}

# Propose turn operator 

sp {recharge*propose*turn
   (state <s> ^name recharge
             -^recharge-square
              ^io.input-link.blocked <b>)
   (<b> ^forward yes
        ^ { << left right >> <dir> } no)
-->
   (write (crlf) |    --: [recharge] propose*turn | )
   (<s> ^operator <o> + =)
   (<o> ^name turn
        ^actions <a>)
   (<a> ^rotate.direction <dir>)}

sp {recharge*propose*turn*backward
   (state <s> ^name recharge
             -^recharge-square
              ^io.input-link.blocked <b>)
   (<b> ^forward yes ^left yes ^right yes)
-->
   (write (crlf) |    --: [recharge] propose*turn*backward | )
   (<s> ^operator <o> >)
   (<o> ^name turn
        ^actions.rotate.direction left)}

sp {recharge*propose*turn*dir-dif
   (state <s> ^name recharge
              ^superstate <ss>
              ^io.input-link <il>)
   (<ss> ^recharge-dir <dir>)
   (<il> ^direction <> <dir>)
-->
   (write (crlf) |    --: [recharge] turn: | <dir> )
   (<s> ^recharge-dir <dir> -)
   (<s> ^operator <o> + =)
   (<o> ^name turn
        ^actions <a>)
   (<a> ^rotate.direction left)
}

Listagem 6 - Produções relacionadas ao operador de recarga do programa compass-mapping-bot.soar.

Código-fonte do agente no VisualSoar: compass-mapping-bot.zip.

As Figuras 16, 17 e 18 mostram respectivamente a estrutura do projeto compass-mapping-bot.vsa no VisualSoar, o log no SoarDebugger que mostra a indicação de direção para cada um dos carregadores, e o agente em execução no TankSoar.

Figura 16 - Estrutura do projeto compass-mapping-bot.vsa
Figura 16 - Estrutura do projeto compass-mapping-bot.vsa.

Figura 17 - Log de execução do programa compass-mapping-bot.soar no SoarDebugger
Figura 17 - Log de execução do programa compass-mapping-bot.soar no SoarDebugger.

Figura 18 - Agente controlado pelo programa compass-mapping-bot.soar em execução no TankSoar
Figura 18 - Agente controlado pelo programa compass-mapping-bot.soar em execução no TankSoar.

3.3.3. Resultados

Nesta atividade foi feita uma tentativa de resolver o problema da ação deliberada do agente no intuito de dirigir-se aos carregadores de energia e saúde assim que os respectivos sensores indicarem um valor considerado baixo (menor que 200).

Os resultados obtidos neste experimento ainda são limitados e requerem um investigação maior no tocante à novas estruturas na memória de trabalho para conversão das indicações de direção (norte, sul, leste e oeste) em movimentos precisos realizados pelo tanque. O tratamento da situação em que obstáculos impedem a passagem do tanque não é feita da forma  ideal. O tanque, nesses casos, ainda escolhe de maneira aleatória.

Apesar dos inconvenientes, foi possível entender como funcionam os mecanismos de submetas no Soar. Neste experimento, este recurso foi utilizado na proposição do operador de alto nível recharge. Ao ser proposto, um subestado é criado para tratamento desta situação. Durante o processamento no subestado, operadores de baixo nível (move e turn) são propostos e posteriormente aplicados, fazendo com que o subproblema seja resolvido, caso os níveis de energia e saúde sejam completamente carregados.

 


4.  Conclusão

Os experimentos realizados nas atividades desta aula forneceram os subsídios para o entendimento de um importante mecanismo do Soar: a detecção de impasses e criação de subestados. Impasses ocorrem sempre quando não há conhecimento suficiente sobre as preferências para determinar que operador deve ser aplicado em um ciclo de decisão no estado atual. Para solução do impasse, um novo estado é criado a partir daquele cujo impasse foi presenciado, criando uma submeta. O resultado obtido no processamento para solução do subproblema e atingir a submeta resolvem o impasse.

A abordagem de submetas foi extensivamente utilizado nos experimentos. Ela permitiu que operadores de alto nível pudessem ser especificados de modo a representar os diversos tipos de comportamento apresentados pelo agente. Tais comportamentos são representados por um conjunto de operadores de baixo nível capazes de fazer com que o agente atue no ambiente.

Foi abordado ainda uma estratégia interessante de mapeamento do ambiente permitindo a memorização do caminho percorrido na mente do agente. Este mapa é armazenado e atualizado na memória de trabalho e pode ser usado para a implementação de comportamentos mais avançados.

Espera-se, por fim, que o comportamento do agente possa ser melhorado implementando mecanismos mais sofisticados que envolvam planejamento e aprendizado a partir de estudos a serem realizados nas próximas aulas.

 


5.  Referências Bibliográficas

Laird, John E. (2012). The Soar 9 Tutorial. Universidade de Michigan, Michigan. Disponível em: <http://web.eecs.umich.edu/~soar/downloads/Documentation/SoarTutorial/Soar%20Tutorial%20Part%201.pdf>. Acesso em: 22 março 2013.

Laird, John E. e Congdon, Clare B. (2012). The Soar User's Manual Version 9.3.2. Universidade de Michigan, Michigan. Disponível em: <http://web.eecs.umich.edu/~soar/downloads/Documentation/SoarManual.pdf>. Acesso em: 22 março 2013.

SML Quick Start Guide (2012). Getting Started with SML Integration. Disponível em: <http://code.google.com/p/soar/wiki/SMLQuickStartGuide>. Acesso em: 22 março 2013.

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer