Esta atividade corresponde em desenvolver o tutorial 4 do Soar.
Neste tutorial é proposto a soluçao do problema dos missionários e canibais, cuja descrição é a seguinte:
Estão numa margem de um rio 3 missionários, 3 canibais e um barco que pode levar até 2 pessoas. O objetivo é transportar todos as 6 pessoas para o outro lado, nunca deixando em nenhuma margem um número mairo de canibais do que missionários.
Tal como o problema do jarro de água, corresponde a uma resolução que ocorre simplesmente por busca interna.
O tutorial propõe uma sequência para resolução do problema usando o Soar, que é descrita a seguir.
Descrição do problema
O primeiro passo proposto é decrever o problema em suas características, respondendo a algumas questões:
- Como representar os estados: posição dos canibais e missionários nas 2 margens do rio, posição do barco. A regra Soar de inicialização do problema ilustra a representação escolhida:
- Estado inicial: todos os 3 canibais e 3 missionários estão do mesmo lado do rio; na implementação, foi escolhida a margem esquerda como a inicial.
- Regras de proposição de operadores: mover até 2 pessoas - em qualquer combinação - para o outro lado do rio.
- Regra para reconhecimento do estado final: todos as 6 pessoas atravessaram o rio. Na regra de inicialização existe a criação de uma extensão "^desired" com o "gabarito" do estado final. Esse "gabarito" é constantemente verificado pela regra representada abaixo, que dispara sempre que a representação do estado atual é alterada:
- Regras para reconhecimento de estados inválidos: quando, em qualquer uma das margens do rio, o número de canibais for maior que o número de missionários. Essa condição inválida é constantemente verificada toda vez que o estado atual é alterado, através da seguinte regra:
Regras para operadores
Segundo o tutorial, é um objetivo conseguir mapear a DESCRIÇÃO DO PROBLEMA em um espaço de conhecimento distinto da forma de RESOLUÇÃO, ou seja, dos operadores.
Por esse motivo são propostos operadores que não testam diretamente o estado resultante de maneira a poder evitar estados inválidos, como é possível verificar nos operadores abaixo:
Uma vez que os operadores simplesmente verificam as condições mínimas para a sua aplicação - no caso, número suficiente de canibais e missionários, assim como a presença do barco - a cada passo de resolução do problema serão propostos inúmeros operadores, muitos dos quais levarão a estados inválidos ou a estados já trabalhados em passos anteriores. Mas como não se deseja evitar esses operadores diretamente em suas regras de proposição, todos eles devem ter a preferência de indiferença e regras adicionais precisarão ser criadas para essas situações problemáticas.
Para a situação de estaods inválidos, é necessário retornar ao estado anterior, aplicando a operação inversa da operação que levou até o problema; isso pode ser feito de uma maneira bastante interessante, através da seguinte regra de seleção:
Como a cada rodada todos os operadores possíveis são propostos, para aplicar o operador inverso no caso de falha ("^failure *yes*") basta indicá-lo como o mais preferencial, que é justamente o que as regras acima fazem.
Algo similar pode ser feito para se evitar que seja aplicada a regra inversa num caso em que o estado atingido foi válido, o que causaria um atraso na resolução do problema:
Neste caso, as regras que desfazem o operador anterior são marcadas como as menos prioritárias.
Um detalhe importante é que a criação dos operadores deve se preocupar com o fato de que como será necessário manipulá-los a partir de outras regras, de forma a direcionar o resultado, quanto mais genéricos forem os operadores, menor será o trabalho dessa manipulação, a qual poderá fazer uso de regras comuns. Isso é bem ilustrado no caso da regra de aplicação dos operadores:
Dessa maneira, embora aparentemente não seja necessária a inclusão o barco dentro do operador, a máxima generalização é possível com a sua inclusão: é possível assim aplicar em paralelo o operador para cada tipo de elemento sendo transportado de uma vez só: quando o barco for movido de um lado para o outro, todos os operadores propostos vão deixar de valer - daí a necessidade de fazer tudo em paralelo.
O atributo "types" é necessário para a etapa da INVERSÃO do operador, uma vez que não é possível saber diretamente se o movimento é para 1 ou 2 tipos de pessoas, já que a aplicação dos operadores é feita tipo a tipo.
Conclusão
Em 10 testes o programa desenvolvido resolveu o problema com uma média de 185,4 ciclos do Soar; a resolução mais rápida levou 40 ciclos e a mais demorada levou 306 ciclos.
A partir da execução deste tutorial é possível listar os seguintes pontos:
- Separação da descrição do problema da resolução do problema.
- Explora novamente abordagem de resolução dos problemas a partir da exploração de todas as possibilidades.
- Explora a criação de regras compactas.
Exemplo de resolução do problema