You are here

Atividade 4

Advanced Eaters

 

    Nesta atividade, será desenvolvido um agente, criatura eater, mais complexo. A criatura fará uma busca sistemática por comida quando estiver cercada por células sem comida. Isso será realizado por meio da escrita de novas regras, baseadas nas regras já desenvolvidas para as criaturas eaters.

    Esta nova produção será chamada seek-move. Conforme discutido anteriormente, a produção jump-and-move continha regras para selecionar entre dois operadores, move e jump, criando preferências para a seleção de um ou outro operador. Essas preferências também selecionavam a direção do movimento, através da análise do conteúdo da célula destino proposta pelo operador proposto. Foram criados valores para cada conteúdo de uma célula, conforme a estrutura abaixo:

sp {init*elaborate*name-content-value
   (state <s> ^type state)
   -->
   (<s> ^name-content-value <c1> <c2> <c3> <c4> <c5>    
                       <c6> <c7> <c8>)
   (<c1> ^name move ^content empty ^value 0)
   (<c2> ^name move ^content eater ^value 0)
   (<c3> ^name move ^content normalfood ^value 5)
   (<c4> ^name move ^content bonusfood ^value 10)
   (<c5> ^name jump ^content empty ^value -5)
   (<c6> ^name jump ^content eater ^value -5)
   (<c7> ^name jump ^content normalfood ^value 0)
   (<c8> ^name jump ^content bonusfood ^value 5)}

    Analisando os valores definidos acima, a criatura eater possui a tendência de se mover para células com comida do tipo bônus, e, uma tendência para saltar quando não houver comida nas células adjacentes à criatura e houver uma célula com comida bônus a duas células de distância. Obviamente, pode ocorrer da criatura ter a mesma preferência para saltar ou se mover (operadores com valor 5), além da criatura evitar células vazias ou que contenham outra criatura.

    Após testar essa produção, jump-and-move, a criatura apresenta o comportamento de sempre consumir comida do tipo bônus, quando detectada (a uma ou duas células de distância). Não havendo esse tipo de comida, ao seu redor, a criatura exibirá o comportamento de consumir comida do tipo normal. Obviamente, a criatura sempre evitará mover-se para células com parede.

    O problema dessa produção ocorre quando não há células com comida ao redor da criatura, ou seja, quando existe comida no ambiente mas indisponível para os sensores da criatura. Neste caso, a criatura apresenta um comportamento aleatório, sem uma preferência por mover-se numa dada direção. Analisando a estrutura de dados acima, para este caso, a criatura não realiza saltos e apenas move-se aleatóriamente pelas células ao seu redor. Num dado momento, os movimentos aleatórios da criatura poderão levá-la próxima a células com comida, ativando novamente os comportamentos de consumo de comida.

    A produção seek-move criará uma estratégia, inteligente, para direcionar a criatura para regiões que contenham comida, ao invés da criatura apenas mover-se aleatóriamente. Essa solução terá que considerar o fato da criatura possuir sensores de curto alcance, não sendo possível investigar células muito distantes da criatura. A nova produção será baseada nas regras desenvolvidas para a produção jump-and-move. Os passos seguintes demonstram como esse problema será solucionado, apesar das limitações sensoriais da criatura.

1º Passo: detectar a situação

    Usando a produção jump-and-move, após vários movimentos da criatura, ocorreu o caso em que a criatura fica cercada por células vazias. A tela do SOAR Debugger exemplifica essa situação:

    Pode-se observar que os operadores propostos possuem apenas valores 0 ou -5. Os valores negativos foram causados pela proposição de operadores do tipo jump, sendo que os demais valores foram devidos a operadores do tipo move. Essa situação poderia ser usada para propor um novo operador, seek, que busca sistematicamente por células com comida. No entanto, alguns desses valores poderiam representar operadores do tipo jump que propõem que a criatura salte para células com comida normal, havendo células vazias ao seu redor. Esse caso particular será desconsiderado, por enquanto.

    Para detectar essa situação particular, ou seja, a ausência de células com comida ao redor da criatura, será proposto o uso de uma variável, no-food. Essa variável, aliás, seu valor, será usada para testar a ocorrência dessa situação peculiar. Para isso, será criada uma nova estrutura de dados na memória de trabalho, conforme abaixo:

sp {init*state*no-food
   (state <ss> ^type state)
   -->
   (<ss> ^no-food <f>)
   (<f> ^direction north
          ^value 5)}

    Essa variável é inicializada com o valor 5 (^value 5) e direção norte (^direction north). Durante a elaboração dos operadores, regra elaborate*operator*value, são associados valores (de acordo com o conteúdo de cada célula) aos operadores propostos (move e jump). Conforme mencionado anteriormente, será necessário testar o valor de cada um dos operadores propostos, a fim de detectar a situação desejada. Porém, uma melhor estratégia seria armazenar o valor (<o> ^value <lval>) e a direção (<o> ^actions.move.direction <ldir>) do último operador aplicado, conforme regra abaixo:

sp {apply*record*last*operator
   (state <s> ^operator <o>
                   ^no-food <f>)
   (<f> ^direction <dir>)
   (<f> ^value <val>)
   (<o> ^actions.move.direction <ldir>)
   (<o> ^value <lval>)
-->
   (<f> ^direction <ldir>
          ^direction <dir> -)
   (<f> ^value <lval>
          ^value <val> -)}

    Essa regra apenas armazena o valor (<f> ^value <val>) e a direção (<f> ^direction <dir>) do último operador do tipo move selecionado (<o> ^actions.move.direction <ldir>). Analisando, novamente, a estrutura de dados ^name-content-value pode-se concluir que o operador move é o único operador que será selecionado durante a situação de ausência de comida. As ações dessa regra são criar novos valores na variável ^no-food e remover os valores anteriores.

2º Passo: selecionar um operador

    A estrutura de dados ^no-food e a regra apply*record*last*operator permitem armazenar as informações do último operador (do tipo move) selecionado. De posse dessas informações, agora é possível detectar a situação desejada (ausência de comida ao redor da criatura) e propor a seleção de um operador para resolver esse problema. A figura, a seguir, exibe os resultados dessa operação:

    Foram criadas regras, de teste, adicionais para exemplificar a detecção da situação desejada e a proposição de um operador. Pode-se ver, na figura, a estrutura da variável no-food (F1). Essa variável está, atualmente, armazenando os valores do último operador selecionado, ou seja: direção leste (^direction east) e valor 10 (^value 10). Os textos "MOVE" indicam quando a criatura está se movendo segundo suas estratégias normais de movimentação. Esses textos indicam quando algum atributo (^direction ou ^value) da variável no-food foi alterado. Pode-se observar (figura) que a criatura estava se movendo normalmente (MOVE) quando uma nova estratégia de movimentação foi proposta (texto SEEK). Conforme descrito anteriormente, essa situação será proposta sempre que o valor de um operador selecionado for inferior ao valor 5 (^no-food.value < 5). O texto "SEEK" indica o início do comportamento "alterado" da criatura. O próximo texto "MOVE" indica que a criatura encontrou, novamente, alguma comida ao seu redor e, portanto, não é mais necessário permanecer nesse estado comportamental alterado. A criatura volta para sua estratégia de movimentação normal.

3º Passo: criar uma estratégia

    Ao invés de propor um novo operador para modificar o comportamento da criatura, optou-se por utilizar os operadores já definidos previamente. Quando detectada a situação de ausência de comida, o operador move tem maior preferência de seleção. Sendo assim, optou-se por utilizar esse mesmo operador para gerar um novo comportamento da criatura. Foi testada a opção de se utilizar o operador jump. No entanto, a solução se mostrou muito ineficiente, além de muito custosa (cada salto da criatura consome pontos). A regra abaixo define a estratégia escolhida para solucionar o problema da ausência de comida ao redor da criatura:

sp {select*seek*move
   (state <s> ^operator <o> +
                   ^no-food <f>)
   (<f> ^value < 5)
   (<o> ^actions.move.direction <dir>)
   (<f> ^direction <ldir> <> <dir>)
-->
   (<s> ^operator <o> <)}

    A regra select*seek*move é disparada quando a situação desejada, ausência de células de comida ao redor da criatura, é detectada. Nessa situação, o último operador selecionado possui um valor inferior a 5 (^no-food.value < 5). A nova estratégia determina que, nessa situação, a criatura deverá continuar se movendo na mesma direção do último operador selecionado. Isso é realizado reduzindo a preferência (<s> ^operator <o> <) dos operadores propostos que não tem a mesma direção (<f> ^direction <ldir> <> <dir>) do último operador selecionado. Notar que somente os operadores do tipo move (<o> ^actions.move.direction <dir>) serão testados. Normalmente, nessa situação (<f> ^value < 5) somente esse tipo de operador será proposto.

    Colocando essa abordagem em prática, a criatura exibe um comportamento de se mover numa mesma direção à procura de novas células com comida. Ao encontrar, novamente, comida ao seu redor, a criatura volta ao seu estado comportamental normal. Ou seja, uma preferência por células com comida bônus e a preferência de escolher células com comida em detrimento das células vazias.

Resultados

    Essa produção, seek-move, demonstrou resultados satisfatórios. A criatura praticamente não fica presa em regiões com ausência de células com comida. Se comparada com a produção utilizada anteriormente, jump-and-move, a criatura leva bem menos tempo (conforme as próximas figuras) para consumir toda a comida disponível no seu ambiente:

Produção jump-and-move

Produção seek-move

    As figuras anteriores exibem os resultados, número de movimentos efetuados (World count), realizados por duas criaturas usando cada uma das produções propostas. As figuras exibem situações, aleatórias, que foram favoráveis para o uso das produções propostas: 592 movimentos para a produção jump-and-move e 300 movimentos para a produção seek-move. Em média, são necessários por volta de 700 movimentos e 300 movimentos, respectivamente, para as criaturas com as produções jump-and-move e seek-move conseguirem consumir praticamente toda comida disponível no seu ambiente.

    Portanto, a produção seek-move mostrou resultados satisfatórios: em média, a metade dos movimentos necessários para a outra produção. Uma característica dessa criatura, com a produção seek-move (não exibida na figura), é que a criatura exibe um comportamento de "seguir" as paredes do seu ambiente à procura de células com comida. Cada vez que a criatura confronta uma parede, a direção do movimento é alterada. Isso deve-se às condições da regra select*seek*move. A existência de uma célula com parede (wall) impede a proposição de algum operador naquela direção. Não havendo esse tipo de operador proposto, as condições da regra (<f> ^direction <ldir> <> <dir>) forçam todos os operadores propostos (em outras direções) a terem uma mesma preferência de seleção. E um novo operador proposto, numa nova direção, será selecionado aleatóriamente.

OBS: O código da produção seek-move está disponível no link: seek-move.soar

 

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer