You are here

Aula 04: SOAR Tutorial 3

 

1. TankSoar

Neste tutorial vão se explorar técnicas mais avançadas do SOAR para solucionar problemas complexos particionando eles em problemas mais pequenos

1.1 Installing and Playing TankSoar

O estado inicial do jogo é apresentado na Ilustração 1.

Ilustração 1

1.2 Recursos do tanque

Cada tanque tem três recursos:

  • Health (Saúde): A saude de cada tanque é um valor de 0 até 1000. Todos os tanques começam com uma saúde de 1000. Quanto o tanque fica com saúde de 0, ele morre e é posicionado novamente em um lugar aleatório no mundo com todos os seus recursos no máximo, ou seja health 1000, energy = 1000 e missiles = 15. Quando um míssil bate num tanque a sua saúde cai 400. A saúde do tanque aumenta em 150 unidades cada vez que ele pega um cargador de saúde.

  • Energy(Energia): Cada tanque tem um máximo de 1000 unidades de energia. A energia do tanque baixa quando ele usa o radar ou quando usa o escudo. Ele perde 20 unidades de energia cada vez que use o escudo. A energia pode aumentar quando o tanque pega o carregador de energia, 250 unidades por cada carregador. A energia também cai quando o tanque é alcançado por um míssil e ele está usando o escudo, cai 250 unidades.

  • Missiles (mísseis): O tanque começa com 15 mísseis e eles decrescem cada vez que é usado um míssil. Quando o tanque pega o carregador de mísseis são acrescentados 7 mísseis.  

1.3 Sensores primários do Tanque

Cada tanque tem 6 sensores primários. A informação de cada sensor está contida na estrutura input-link. Todos os sensores estão ativados o tempo todo, menos o sensor do radar que se deve accionar. 

  • Blocked sensor: Este sensor determina se tem algum tanque ou obstáculo na vizinhança do tanque.

  • Incoming sensor: Este sensor detecta quando um míssil está se aproximando 

  • Radar sensor: Ele só funciona se o radar for ligado e o tanque tem energia suficiente para usá-lo. Ele obtêm a distancia d e a posição onde foi achado um objeto.

 

 

 

 

 

 

 

 

 

 

 

 

 

  • Rwaves sensor: Este sensor detecta se o radar de outro tanque está detectando você.

  • Smell sensor: Este sensor detecta o tanque mais cercano além de prover informação sobre quan cerca ele está e de que cor ele é.

  • Sound sensor: Este sensor detecta o tanque mais cercano que se movimento durante a última ação sempre que ele esteja 7 quadros o menos de distancia.  

1.4 Sensores secundários do Tanque

Além dos 6 sensores principais, cada tanque tem mais sensores que estão funcionando o tempo todo.

1.5 Ações do tanque

Os tanques têm muitas ações que podem realizar. Todas as ações são realizadas usando o output-link. Todas as ações podem ser realizadas ao mesmo tempo menos move e rotate.

  • Move: O tanque pode-se movimentar para frente/atrás/direita/esquerda. Também pode-se movimentar em nenhuma (none) direção, o que significa uma ação de espera.

  • Rotate: O tanque pode rotar direita ou esquerda.

  • Fire: O tanque pode enviar um míssil por decisão

  • Radar: O tanque pode ligar o desligar o radar. A ação vai falhar se o tanque não tem energia

  • Radar Range: O tanque pode cambiar o rango do radar usando o comando radar-power. O valor pode ir de 1 até 14. Quanto maior é o alcance do radar maior vai ser a energia usada.

  • Shields: O tanque pode ligar o desligar os escudos. Ligar os escudos utiliza 20 unidades de energia por decisão.

1.6 Tipo de objetos no jogo

No mapa podem aparecer os seguintes objetos:

  • Obstáculos: os obtáculos estão presentado em forma de árvore.

  • Carregadores de saúde: só existe um carregador de saúde por mapa

  • Carregador de energia: só existe um carregador de energia por mapa

  • Pacote de mísseis: Eles estão espalhador aleatoriamente no mapa.

  • Tanques: eles estão controlador por agentes SOAR

1.7 Resumo de entradas/saídas

O resumo das entradas saídas estão apresentados na Ilustração 2.

Ilustração 2

 

2. Movimento simples do tanque

Nesta parte do tutorial será criado um tanque que se movimente no mapa procurando objetos.

Inicialmente vão se propor três operadores que realizem as seguintes tarefas:

  • Move: movimentar para frente se não tem obstáculo.

  • Turn: se na frente tem obstáculo, rotar e ligar o radar com poder de 13.

  • Radar-off: se o radar está ligado e não tem nenhum objeto visível, desligar radar.

  • Se radar-off é proposto, então prefira ele a movimentar.

2.1 Operador de movimentação

O primeiro passo é abrir o projeto base em VisualSoar. O projeto base tem um operador de inicialização e toda a estrutura no DataMap de input-link e output-link. A Ilustração 3 apresenta o estado inicial do projeto.

Ilustração 3

O primeiro operador de movimentação tem a seguente regra de proposição:

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

Em Soar está regra pode ser escrita assim:

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

São utilizando as duas regras de aplicação geral do projeto do Eaters:

## General operator application rules

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

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

Assim o tanque se movimenta quatro posições antes de encontrar um bloqueio como é apresentada na Ilustração 4

 Ilustração 4

2.2 Operador de giro

Este operador vai permitir girar o tanque se ele tem um obstáculo na frente e não tem nenhum obstáculo na direita ou na esquerda. Além de isso o operador também vai ligar o radar com potência de 13.

A proposta do operador em inglês é:

## Turn operator
# 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.

No SOAR ele fica:

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

Mas a proposta assim tem problemas quando o tanque tem bloqueios na frente, na direita e na esquerda, assim é necessária uma regra para quando está possibilidade se apresente:

# Turn operator backward
# 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*backward
   (state <s> ^name tanksoar
              ^io.input-link.blocked <bl>)
   (<bl> ^forward yes
         ^left yes
         ^right yes)
-->
   (<s> ^operator <o> +)
   (<o> ^name turn
        ^actions <ac>)
   (<ac> ^rotate.direction left)}

Assim o tanque se vai movimentar indefinidamente:

2.3 Operador de desliga o radar

O operador para desligar o radar verifica se não tem um pacote de energia, de saúde, de misseis ou se não tem outro tanque na frente dele. Se não tiver nenhum destes objetos ele desliga o radar. A regra em inglês para isso é:

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

Em Soar:

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)}

Também é proposta uma regra para preferir o operador de desligar o radar do que mover o girar:

## 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 << move turn >> )
-->
   (<s> ^operator <o1> > <o2>)}

3. Wander Operator – Subgoals

Uma abordagem ao problema do tanque é dividir um objetivo muito grande em vários objetivos mais pequenos, assim diminui o número de regras a serem escritas e o programa fica mais claro. No nosso caso os pequenos objetivos (subgoals) vão ter uma jerarquia como a apresentada na Ilustração 5

Ilustração 5

Além de objetivos já apresentados o tanque tem que realizar mais tarefas, como utilizar o escudo, lembrar sonidos etc. Estas regras são independentes da jerarquia porque são propostas e aplicadas em qualquer objetivo ativo.  

3.1 Proposta do operador Wander

É possível propor um operador que movimente e gire o tanque enquanto não tenha outra coisa mais importante a fazer. Por exemplo se não tem tanque detectado pelo radar, se não tem sonido de coisas ao redor e não nada se acercando então pode propor o operador Wander

##Wander Operator Proposal
# If there is no tank detected on radar, 
# and the sound is silent, 
# and there is no incoming, 
# then propose the wander operator.

No Soar

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

3.2 Impasses e criação de estados.

O tipo de operados escrito acima é especial porque ele tem uma regra de proposta mas não tem uma regra de aplicação, é dizer, SOAR pode selecionar ele, mas não tem nenhuma regra para aplicá-lo. Este tipo de operador é chamado de operator no-change impasse. Em SOAR além do operator no-change impasse existem mais outros três tipos de Impasses:

  • O state no-change impasse é quando nenhum operador é proposto no estado atual.

  • O operator tie impasse é quando existem varios operadores propostos mas não se tem informação suficiente nas preferências dele para o SOAR escolher um.

  • O operator conflict impasse é quando existem vários operadores propostos e as preferencias deles estão em conflito

Quando o SOAR detecta um impasse automaticamente cria um estado para tentar mudar a situação atual e seja possível selecionar outro operador. Se algum operador é selecionado no novo sub-estado então ele é removido automaticamente após o operador selecionado seja aplicado.

3.3 Operador Wander e sub-operadores

Para que os operadores de movimentação (move) e giro (turn) possam atuar como sub-operadores do operador wander é necessário que quando seja criado o sub-estado ele tenha o mesmo nome do estado (wander) e que a estrutura do io seja copiado do estado para o sub-estado. Assim as duas regras que devem ser acrescentadas são:

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


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

A saída do Soar Debugger é apresentada na Ilustração 6

Ilustração 6

4. Operadores de Chase, Attack, e Retreat

 

4.1 Proposta do operador chase

Este operador deve ser selecionado quando o tanque detecta que outro tanque esta perto. As condições que se devem cumprir são:

  • O segundo tanque não é detectado pelo radar.

  • O tanque tem mísseis e energia suficiente para começar uma persecução

  • O radar de sonido detecta alguma coisa.

Estas condições em inglês são:

##Propose Chase Operator
# If the task is tanksoar, 
# and sound sensor is not silent, 
# and there is no tank on radar, 
# and energy or missiles is not low, 
# then propose the chase operator.

Aqui é útil criar uma estrutura no estado que contenha a informação quando os mísseis ou a energia estejam baixas. As regras do SOAR para fazer isto são:

sp {elaborate*state*missiles*low
   (state <s> ^name tanksoar
              ^io.input-link.missiles 0)
-->
   (<s> ^missiles-energy low)}


sp {elaborate*state*energy*low
   (state <s> ^name tanksoar
              ^io.input-link.energy <= 200)
-->
   (<s> ^missiles-energy low)}

Assim a proposta do operador Chase em SOAR é:

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

4.2 Aplicação do operador chase

A ideia é que o tanque se movimente na direção onde escute o sonido, para isso é necessário saber de onde vem o sonido. Também é desejável que o tanque deixe de perseguir outro tanque quando ele apareça no radar, porque ai o tanque pode atacar.

A regra para determinar a direção de onde vem o sonido é

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

Aqui é perguntada se o estado é chase porque os operadores de movimento e giro serão sub-operadores do operador chase.

A anterior regra copia a direção do sonido num atributo do estado chamada sound-direction.

A regra para que o tanque ligue o radar se ele está desligado é:

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

Agora são escritas as regras de aplicação dos operadores de movimentação, giro e retrocesso:

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


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

4.3 Proposta do operador attack

O propósito do operador de ataque é lançar mísseis e atingir o outro tanque. O operador de ataque deve ser selecionado quando outro tanque esteja no radar, Sem embargo se o tanque tem pouca energia ou pouca saúde, é melhor propor a retirada.

As regras de proposta em inglês são:

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

No SOAR é:

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

4.4 Aplicação do operador attack

O operador de ataque deve utilizar os operadores de e movimentação, giro e fogo

As ações a serem realizadas são:

  • Se um inimigo está no centro do radar, dispare um míssil.

  • Se o inimigo está na frente mas não no centro do radar, o tanque deve movimentar para a direita ou esquerda para poder disparar.

  • Se ele não pode se movimentar para direita ou esquerda porque está bloqueado então se movimente na frente.

  • Se o inimigo está do lado do tanque ele deve-se girar

Posposta em inglês:  

##Propose Fire-missile Operator
# If the state is attack 
# and there is a tank on radar in the center, 
# and if there are more than zero missils
# then propose the fire missile operator 
# and make it the best preference

No SOAR é:

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-missil
        ^actions.fire.weapon missile)}


##Propose Slide 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 there is an open spot in the direction of the tank, 
# then propose the slide operator in the direction of the tank.

Em SOAR é:

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>)}

Agora vai se propor o operador de movimentação na frente

##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.

Em SOAR é

sp {attack*propose*move-forward
   (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-forward
        ^actions.move.direction forward)}

Proposta do operador girar:

###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.

Em SOAR é:

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)}

4.5 Proposta do operador Retreat (Retirada)

A ação de retirada deve tirar ao tanque de uma batalha quando ele tem poucos mísseis ou pouca energia. Assim o operador de retirada deveria ser selecionado quando o tanque tem poucos mísseis e pouca energia e detecta que há um outro tanque perto.

## Propose Retreat Operator 
# 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})
-->
   (^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)}
##Propose Retreat Operator
# If the state is tanksoar 
# and the tank 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)}

 

4.6 Retreat Operator Application: State Elaboration

Para a aplicação desta regra são uteis algumas regras de elaboração:

## Retreat Operator Elaboration
## If there is a retreat state and there is a sound coming in a given direction, record that direction.
## If there is a retreat state and there is radar contact with a tank, record forward direction.
## If there is a retreat state and there is an incoming, record the direction.
## If there is a retreat state and there is radar contact with a tank that is not in the center, record
## that direction as a direction to avoid moving.

Em SOAR é 

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>)}

 

4.7 Aplicação do operador Retreat

A aplicação do operador de retirada é feita a traves do operador de movimentação. Vão-se ter em conta duas possibilidades:

## 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.
#
## Propose Wait
# If the state is named retreat 
# then propose wait, 
# and make a worst preference for it.

No SOAR é

sp {elaborate*sidestep-directions
   (state <s> ^name tanksoar)
-->
   (<s> ^side-direction <sd>)
   (<sd> ^forward right left ^backward right left
         ^right forward backward ^left forward backward)}

Finalmente la regra de aplicação do operador Retreat é:

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>)}

 

4.8 Shield Control Rules

As regras para controlar quando o escudo é usado são as seguentes:

sp {elaborate*shields-on
   (state <s> ^operator.actions <a>
              ^io.input-link <il>)
   (<il> ^incoming.<dir> yes
         ^shield-status off)
-->
   (<a> ^shields.switch on)}

sp {elaborate*shields-off
   (state <s> ^operator.actions <a>
              ^io.input-link <il>)
   (<il> -^incoming.<dir> yes
          ^shield-status on)
-->
   (<a> ^shields.switch off)}

 

4.9 Operador de espera

O operador de espera é proposto quando não tem operador disponível no estado atual.

## Propose wait for a state-no-change
sp {top-state*propose*wait
   (state <s> ^attribute state
              ^choices none
             -^operator.name wait)
-->
   (<s> ^operator <o> +)
   (<o> ^name wait)}

Até o momento a saída do SOAR Debugger é apresentada na Ilustração 7

Ilustração 7

 

5. Melhorando o detector de sonido.

Um problema que apresenta o programa até o memento é que o operador de chase só é proposto por um movimento só, porque se o tanque inimigo fica queto o sensor de sonido não vai detectar nada. Assim se vai criar uma estrutura nova onde vai-se guardar a última posição obtida do detector de sonido.

Serão necessários dois operadores para melhorar o detector de sonido, um para criar a estrutura e outro para eliminá-la  

  • Record-sound: Cria a estrutura de dados do sonido quando um novo sonido é escutado.

  • Remove-sound: Remove a estrutura de dados do sonido se a direção dele muda ou quando a gravação do sonido expirou.

 

5.1 Operador de Record-sound

A proposta do operador será feita no operador wander

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

A regra de aplicação deste operador deve verificar se o operador é record-sound e o tempo atual assim pode calcular o tempo no qual o silencio expira. Neste exemplo foi escolhido um tempo de 5.

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

Para melhorar um pouco a manipulação da direção, é criada uma regra que cria uma estrutura no estado para obter a direção do sonido em forma global, é dizer, em coordenadas no mapa. A regra é uma elaboração:

sp {elaborate*directions
   (state <s> ^superstate nil)
-->
   (<s> ^direction-map <dm>)
   (<dm> ^north <north>
         ^south <south>
         ^west <west>
         ^east <east>)
   (<north> ^right east ^left west ^backward south ^forward north)
   (<south> ^right west ^left east ^backward north ^forward south)
   (<west> ^right north ^left south ^backward east ^forward west)
   (<east> ^right south ^left north ^backward west ^forward east)}

Assim a regra de aplicação do operador record-sound pode-se melhorar assim:

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))}

Aqui é obtida a posição absoluta da fonte do sonido.

Também é cambiada a regra para obter a direção da fonte do sonido no operador chase:

sp {chase*elaborate*state*sound-direction
   (state <s> ^name chase
              ^superstate <ss>)
   (<ss> ^sound.absolute-direction <abs-sound>
         ^direction-map.<direction>.<rel-sound> <abs-sound>
         ^io.input-link.direction <direction>)
-->
   (<s> ^sound-direction <rel-sound>)}

 

5.2 Operador para eliminar a estrutura do sonido (Remove-sound Operator)

A regra de proposta do operador remove-sound pode ser escrita como:

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)}

A regra de aplicação desta regra é então:

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

Agora é possível modificar a proposta do operador chase para que utilize a estrutura criada anteriormente, a nova proposta fica então:

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

 

6. Criando um mapa (Creating a Map)

Em quanto o tanque esteja se movimentando pelo mapa é possível que ele faça um mapa que ele pode usar depois para achar os carregadores e os pacotes de saúde.

6.1 The Map Data Structure and Initialization

O mapa é uma estrutura que deve ser armazenada e atualizada enquanto o tanque este no jogo. O primeiro é inicializar a estrutura do mapa com o operador init-map.

A proposta do operador é:

sp {propose*init-map
   (state <s> ^name tanksoar)
 -{(<s> ^map.square <sq>)
   (<sq> ^x 0 ^y 0 ^east)}
-->
   (<s> ^operator <o> + >)
   (<o> ^name init-map)}

E o regra de aplicação é:

sp {apply*init-map
   (state <s> ^operator.name init-map)
-->
   (<s> ^map <m>)
   (<m> ^square <s0-0> <s0-1> <s0-2> ...)
   (<s0-0> ^x 0 ^y 0)
   (<s0-1> ^x 0 ^y 1)
   (<s0-2> ^x 0 ^y 2)
   ...
}

Também são usadas duas regras para obter qual é o quadro adjacente e quais são as bordas do mapa.

6.2 Atualizando o Mapa (Updating the Map)

Para usar e atualizar o mapa o agente deve saber em que posição do mapa está. Para isso é criada uma regra que vai-se atualizar cada vez que o tanque cambie de posição.

sp {all*map*curent-square
   (state <s> ^name tanksoar
              ^io.input-link <il>
              ^map.square <cs>)
   (<il> ^x <x>
         ^y <y>)
   (<cs> ^x <x>
         ^y <y>)
-->
   (<s> ^square <cs>)}

Com essa e mais outras regras é possível encher o mapa com toda a informação do mapa que ele obtenha do radar.

 

Atividade 3

Desenvolva um programa Soar, utilizando o mapa do ambiente, para fazer com que o tanque, além de suas atividades normais, dirija-se aos carregadores de energia e saúde, quando estiverem com baixa energia ou baixa saúde.

 

Continuando com a proposta da utilização de subgoals, é possível definir uma atividade chamada search que tenha como subgoals Move e Turn. Assim:

 

A regra de proposição do operador seria:

#Se o estado é chamado tanksoar
# e tem pouca energia
# então proponha o operador search

sp {propose*search*energy
   (state <s> ^name tanksoar
              ^missiles-energy low)
-->
   (<s> ^operator <o> + >)
   (<o> ^name search)
   (write (crlf) | Propose search energy |)}

e

#Se o estado é chamado tanksoar
# e tem pouca saude
# então proponha o operador search

sp {propose*search*saude
   (state <s> ^name tanksoar
              ^io.input-link.health < 100)
-->
   (<s> ^operator <o> + >)
   (<o> ^name search)
   (write (crlf) | Propose search health |) }

 

Depois é definida uma regra que obtenha a posição do pacote de energia:

 

sp {search*propose*move
   (state <s> ^name search
              ^map.square <sq>)
   (<sq> ^energy *yes*
         ^x <x>
         ^y <y>)
-->
   (write (crlf) | Propose search-energy em | <x> |, | <y> )
   (<s> ^operator <o> + =)
   (<o> ^name move
        ^actions.move.direction forward) } // (1)

 

Aqui teria que obter qual das 4 direções é a que minimiza a distancia entre a localização do tanque atual e a localização do pocote de energia, eu achei em calcular a distancia entre a localização do pacote de energia e as 4 posposições adjacentes ao tanque, mas não sei como fazer o calculo em SOAR e assim determinar qual direção é a melhor

A saída no Visual Soar é:

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer