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.
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
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 é: