You are here

Relatório 4 - LIDA: Exemplos de Implementação Prática

Atividade 1

Nessa atividade, foi feito o download dos exemplos da arquitetura cognitiva LIDA e apresentado o roteiro dos exercícios das demais atividades.

Atividade 2: Tutorial Project I


Basic Agent Exercise 0:

O objetivo desse exercício é apresentar o projeto basicAgentExercises. Foram analisadas as seguintes classes:

    myagent.Run: Executa a aplicação.

    myagent.modules.ButtonEnvironment: Implementa o ambiente de botões.

    myagent.modules.ButtonSensoryMemory: Implementa o sensor de memória do agente.

    myagent.featuredetectors.ColorDetector: Detecção de cores.

    myagent.featuredetectors.ShapeDetector: Detecção de formas.

Adicionalmente, analisamos os seguintes arquivos de configuração:

    lidaConfig.properties: Arquivo de configuração principal que aponta outros arquivos de configuração necessários para construir um agente.

    basicAgent.xml: Esse arquivo define a arquitetura do agente, incluindo os módulos e processos que a aplicação irá utilizar.

    factoryData.xml: Definição dos elementos que podem ser obtidos a partir do ElementFactory. Esse arquivo define vários tipos de Nodes, Links, Strategy e Task. Estes tipos de elementos são referenciadas por nome, como por exemplo, "defaultDecay", no arquivo de declaração do agente (estes mesmos nomes também são usados por várias classes da framework para obter novos elementos).

    guiPanels.properties: Arquivo de configuração dos elementos de painel da interface gráfica.

Basic Agent Exercise 1:

Nesse exercício, vamos nos familiarizar com a interface gráfica da aplicação, bem como entender seu funcionamento. Nesse aspecto, pode ser verificado que a aplicação pressiona "Botão 1" quando encontra um quadrado vermelho no ambiente de botões e pressiona o "Botão 2" quando encontra um círculo azul. Para tal observação, foi feito uso dos botões "Start / Pause" e "Run ticks".

Pode ser notado também que a aba Logging informa o conteúdo registrado pela aplicação, segundo o filtro selecionado na caixa de combinação Logging level. Cada linha neste painel é uma entrada do log. A primeira coluna é o número da entrada; o segundo é o tick no momento do registo; em seguida, o nível de severidade; o nome do logger; e finalmente, a mensagem em si.

Na aba Configuration Files encontramos um registro do tipo chave-valor com os arquivos de configuração estudados no tópico anterior.

A figura abaixo mostra o aplicativo em execução:

LIDA Framework

Basic Agent Exercise 2:

Neste exercício, trocamos o arquivo de configuração basicAgent.xml por um arquivo cujos os sensores de cor azul e forma circular foram removidos. Foi observado que na aba "PAM Table" (PAM é acrônimo de Perceptual Associative Memory), as ativações para blue e circle permanceram inalteradas, enquanto a ativação para red e square variava com valores entre zero e um.

Na aba "Perceptual Buffer" podemos ver os valores de ativação de cada nó enquanto eles existirem no buffer. Tal existência está ligada à curva de decaimento, até o momento em que atinge o valor de zero.

Na aba "GlobalWorkspace" temos as coalisões, suas ativações e o broadcast, seguindo o modelo de teoria de Baars-Franklin.

A última tarefa consiste da alteração da interface gráfica para inclusão de uma nova aba "Current Situational Model". Para tanto, foram adicionadas as seguintes linhas no arquivo guiPanel.properties:

#name = panel title, class name, Position [A,B,C,FLOAT, TOOL], tab order, Refresh after load, parameters
csm = Current Situational Model,edu.memphis.ccrg.lida.framework.gui.panels.NodeStructurePanel,B,7,Y,Workspace.CurrentSituationalModel

Nessa nova aba, os nós aparecem e desaparecem de maneira semelhante à aba "Perceptual Buffer".

Basic Agent Exercise 3:

Nesse exercício são propostas modificações para aumentar as funcionalidades do agente. Serão adicionadas funcionalidades de CircleDetector e BlueDetector, além de um PamListener.

Foi alterado o arquivo de configuração lidaConfig.properties para utilizarmos o arquivo basicAgent_ex3.xml. As primeiras tarefas, consistem da alteração deste arquivo para contemplar os novos elementos, a saber, um módulo e o listener. O módulo foi inserido na tag <submodules>:

<submodules> 

	( ... )
	
	<module name="Environment">
		<class>myagent.modules.ButtonEnvironment</class>
		<param name="height" type="int"> 10 </param>
		<param name="width" type="int">10 </param>
		<taskspawner>defaultTS</taskspawner>
	</module>

E o listener foi inserido na tag <listener>:

<listeners>

	( ... )

	<listener>
		<listenertype>edu.memphis.ccrg.lida.pam.PamListener</listenertype>
		<modulename>PerceptualAssociativeMemory</modulename>
		<listenername>Workspace</listenername>
	</listener>

A declaração de PerceptualAssociativeMemory foi alterada para adicionar a detecção de "azul" e "círculo":

<task name="BlueDetector">
	<tasktype>ColorDetector</tasktype>
	<ticksperrun>3</ticksperrun>
	<param name="color" type="int">-16776961</param>
	<param name="node" type="string">blue</param>
</task>
<task name="CircleDetector">
	<tasktype>ShapeDetector</tasktype>
	<ticksperrun>3</ticksperrun>
	<param name="area" type="int">31</param>
	<param name="backgroundColor" type="int">-1</param>
	<param name="node" type="string">circle</param>
</task>

Finalmente, o módulo de atenção tem os Codelets inseridos, fazendo com que o agente pressione o "Button 2" em resposta a círculos azuis:

<task name="BlueCircleCodelet">
	<tasktype>BasicAttentionCodelet</tasktype>
	<ticksperrun>5</ticksperrun>
	<param name="nodes" type="string">blue,circle</param>
	<param name="refractoryPeriod" type="int">30</param>
	<param name="initialActivation" type="double">1.0</param>
</task>

Advanced Exercise 1:

Esse exercício descreve como podemos fazer para executar a framework LIDA sem sua interface gráfica. Para tanto, apenas alteramos no arquivo lidaConfig.properties o item:

lida.gui.enable=false

Opcionalmente, o log pode ser feito em arquivo ou console, escolhendo entre as seguintes opções no arquivo logging.properties:

# Arquivo
handlers= java.util.logging.FileHandler

Ou console:

# Console
handlers= java.util.logging.ConsoleHandler

Advanced Exercise 2:

Esse exercício mostra os efeitos dos ajustes do parâmetro ticksPerRun. Foi alterado o ticksPerRun para 50 e o refractoryPeriod para 300, como mostrado abaixo:

<task name="RedSquareCodelet">
	<tasktype>BasicAttentionCodelet</tasktype>
	<ticksperrun>50</ticksperrun>
	<param name="nodes" type="string">red,square</param>
	<param name="refractoryPeriod" type="int">300</param>
	<param name="initialActivation" type="double">1.0</param>
</task>

Relativamente poucas coalizões existiram na parte superior do painel "Global Workspace", quando quadrados vermelhos aparecerem. Também observamos menos coalizões em broadcast (que aparecem na parte inferior do mesmo painel).

Advanced Exercise 3:

Este exercício propõe a detecção da cor branca. Para tanto, foi criada a classe WhiteFeatureDetector:

package myagent.featuredetectors;

import edu.memphis.ccrg.lida.pam.tasks.BasicDetectionAlgorithm;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author Luiz Fernando Romanini
 */
public class WhiteFeatureDetector extends BasicDetectionAlgorithm
{
	/*
	* Size of the shape in pixels.
	*/
	private int soughtArea = 10000;

	/*
	* The white background color should be counted!
	*/
	private int backgroundColor = 0xFFFFFFFF;

	private Map< String, Object > smParams = new HashMap< String, Object >( );

	@Override
	public void init( )
	{
		super.init( );
		smParams.put( "mode", "all" );

		soughtArea = ( Integer ) getParam( "area", 1000 );
		backgroundColor = ( Integer ) getParam( "backgroundColor", 0xFFFFFFFF );
	}

	@Override
	public double detect( )
	{
		int[ ] layer = ( int[ ] ) sensoryMemory.getSensoryContent( "visual", smParams );
		int area = 0;

		for( int i = 0; i < layer.length; i++ )
		{
			if( layer[ i ] == 0xFFFFFFFF )
				++area;
		}

		if ( area == layer.length )
			return 1;

		return 0;
	}
}

O arquivo factoryData.xml foi alterado para incluir a seguinte tarefa:

<task name="WhiteDetector">
	<class>myagent.featuredetectors.WhiteFeatureDetector</class>
	<ticksperrun>5</ticksperrun>
	<associatedmodule>SensoryMemory</associatedmodule>
	<associatedmodule>PerceptualAssociativeMemory</associatedmodule>
	<param name="area" type="int">40</param>
	<param name="backgroundColor" type="int">-1</param>
	<param name="node" type="string">empty</param>
</task>

As demais alterações, foram feitas no arquivo basicAgent.xml:

	( ... )

	<!-- Adicionado o node "empty" -->
	<param name="nodes">red,blue,square,circle,empty</param>

	( ... )

	<task name="EmptyDetector">
		<tasktype>WhiteDetector</tasktype>
		<ticksperrun>3</ticksperrun>
		<param name="area" type="int">40</param>
		<param name="backgroundColor" type="int">-1</param>
		<param name="node" type="string">empty</param>
	</task>

	( ... )

	<task name="WhiteEmptyCodelet">
		<tasktype>BasicAttentionCodelet</tasktype>
		<ticksperrun>5</ticksperrun>
		<param name="nodes" type="string">white,empty</param>
		<param name="refractoryPeriod" type="int">30</param>
		<param name="initialActivation" type="double">1.0</param>
	</task>

	( ... )

	<param name="scheme.3">if white empty, release press|(white,empty)()|action.releasePress|()()|0.01</param>

	( ... )

	<param name="smm.3">action.releasePress,algorithm.releasePress</param>

Assim, pode ser observado no Logger a mensagem ":myagent.modules.ButtonEnvironment -> Button released", resultante da ativação por detecção de branco. A aba "Procedural Memory" agora apresenta uma nova linha, resultado do esquema "scheme.3", adicionado no arquivo acima:

LIDA Scheme 3

O código fonte dessa atividade pode ser encontrado aqui, e o executável, aqui.

Atividade 3: Tutorial Project II


Agent Exercise 1:

Nessa atividade, pode-se observar a diferença entre a interface gráfica da aplicação AlifeAgent e compará-la com a aplicação basicAgent. A diferença fundamental é a substituição do "Button Environment" do basicAgent pelo "AlifeEnvironment", como pode ser observado na figura abaixo:

AlifeEnvironment

Para tanto, a classe ALifeGuiPanel extendeu a classe GuiPanelImpl, sobrescrevendo os métodos initPanel( ) e refresh( ).

Adicionalmente, foi verificada a aba "PAM Graph", que desenha um grafo da memória perceptual associativa sensivelmente mais complexo que o da atividade anterior. Além disso, os nós presentes no grafo são detalhados na aba "PAM Table".

Quando executada a simulação, o agente navega pelo mundo explorando-o, enquanto sua vida se esvai. Além disso, o agente evita os macacos e come os hamburgeres quando possível. A aba "Task Queue" mostra as tarefas enfileiradas aguardando a chance de execução em "Running Tasks".

ALife Agent Exercise 2:

Inicialmente, foi alterado a quantidade disponível de hamburgueres para zero, no arquivo objects.properties. Dessa forma, o agente não se movia quando sua vida estava abaixo de 0,33. Para contornar essa situação, foi criada a class BadHealthDetector conforme abaixo:

package alifeagent.featuredetectors;

import edu.memphis.ccrg.lida.pam.tasks.BasicDetectionAlgorithm;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author Luiz Fernando Romanini
 */
public class BadHealthDetector extends BasicDetectionAlgorithm
{
	private final String modality = "";
	private Map< String, Object > detectorParams = new HashMap< String, Object >( );
	
	@Override
	public void init( )
	{
		super.init( );
		detectorParams.put( "mode", "health" );
	}
	
	@Override
	public double detect( )
	{
		double healthValue = ( Double ) sensoryMemory.getSensoryContent( modality, detectorParams );
		double activation = 0;

		if ( healthValue <= 0.33 )
			activation = 1;

		return activation;
	}
}

O arquivo alifeAgent_ex2.xml foi utilizado no lidaConfig.properties e alterado conforme abaixo:

<initialTasks>

	( ... )

	<task name="BadHealthDetector">
		<tasktype>BadHealthDetector</tasktype>
		<ticksperrun>3</ticksperrun>
		<param name="node" type="string">badHealth</param>
	</task>

	( ... )

	<task name="predatorFrontDetector">
		<tasktype>ObjectDetector</tasktype>
		<ticksperrun>3</ticksperrun>
		<param name="node" type="string">predatorFront</param>
		<param name="object" type="string">predator</param>
		<param name="position" type="int">1</param>
	</task>

O agente passou a ter dois novos detectores e duas novas capacidades perceptivas. Agora, ele detecta quando sua saúde está abaixo de 0,33 como pode ser observado na aba "Perceptual Buffer" e é capaz de se mover quando sua energia vital estiver baixa. Além disso, o agente pode fugir do predador. Isso acontece por que os esquemas já estavam listados na memória procedural, como pode ser observado abaixo:

<param name="scheme.6">move forward if bad health|(badHealth)()|action.moveAgent|()()|0.45</param>
<param name="scheme.6a">turnLeft if bad health|(badHealth)()|action.turnLeft|()()|0.1</param>
<param name="scheme.6b">turnRight if bad health|(badHealth)()|action.turnRight|()()|0.1</param>

( ... )

<param name="scheme.8">flee if predator in front|(predatorFront)()|action.flee|()()|0.2</param>
<param name="scheme.8a">flee if predator at origin|(predatorOrigin)()|action.flee|()()|0.5</param>

ALife Agent Exercise 3:

Este exercício foi trabalhado com um agente cujo alguns mecanismos de atenção foram removidos e reinseridos durante a execução da atividade. O agente inicialmente não reage ao predador, uma vez que não há um codelet de atenção ao predador. Para corrigir isso, adicionamos uma tarefa de atenção conforme abaixo:

<task name="PredatorAttentionCodelet">
	<tasktype>NeighborhoodAttentionCodelet</tasktype>
	<ticksperrun>5</ticksperrun>
	<param name="nodes" type="string">predator</param>
	<param name="refractoryPeriod" type="int">50</param>
	<param name="initialActivation" type="double">1.0</param>
</task>

Esse módulo de atenção está associado à ação flee (em português, fugir), conforme observado abaixo:

<param name="scheme.8">flee if predator in front|(predatorFront)()|action.flee|()()|0.2</param>
<param name="scheme.8a">flee if predator at origin|(predatorOrigin)()|action.flee|()()|0.5</param>

Agora, o agente é capaz de criar coalizões com os nós do tipo "predator", como pode ser observado na aba "Global Workspace".

Na tarefa seguinte, foi alterado o parâmetro initialActivation do codelet FoodAttentionCodelet para 0,01. Alterando esse parâmetro, mudamos de 100% de chance do agente reagir ao hamburguer para uma probabilidade de 1%.

Por fim, o parâmetro initialActivation do codelet FoodAttentionCodelet foi restaurado. Executamos a aplicação e alteramos o Tick duration para 40ms, diminuindo a velocidade da simulação. Também foi alterado o refractoryPeriod do codelet Good HealthCodelet para 10, aumentando a freqüência em que coalizões contendo "goodHealth" aparecem.

ALife Agent Exercise 4:

Este exercício visa a modificação de esquemas na memória procedural, utilização de inicializadores personalisados na memória perceptual associativa, e uso de estratégias de decaimento.

Inicialmente, executamos a aplicação e comprovamos que o agente não se move até que sua saúde esteja abaixo de 0,66, excetuando-se casos de ataques de predadores. Podemos mudar esse comportamento alterando o nome da ação associada ao esquema 10b de action.turnAround para action.moveAgent, como visualizado abaixo:

<param name="scheme.10b">if emptyFront turn around|(emptyFront)()|action.moveAgent|()()  |0.1</param>

Agora, personalizamos a inicialização de alguns elementos da memória perceptual associativa. O inicializador padrão, BasicPamInitializer, lê dois parâmetros chamados "nodes" e "links" e cria nós e ligações na memória perceptual associativa baseado nas especificações desses parâmetros. Os nós e ligações criados dessa maneira usam estratégias padrão de excitação e decaimento. Para obter elementos cuja estratégia segue linhas personalizadas, um inicializador personalizado é necessário. Assim, utilizaremos a classe CustomPamInitializer do pacote alifeagent.initializer. A modificação abaixo foi feita no arquivo alifeAgent_ex4.xml, previamente registrado em lidaConfig.properties. A alteração se refere à tag <PerceptualAssociativeMemory>.

<initializerclass>alifeagent.initializers.CustomPamInitializer</initializerclass>

A classe CustomPamInitializer foi alterada para criar uma nova ligação na memória perceptual associativa entre "food" e "object". Isso significa que cada nó do tipo "object" receberá uma ativação quando "food" for percebida. Além disso, ajustamos a estratégia de decaimento usada no nó "object" para "slowDecay". A estratégia de decaímento está definida no arquivo factoryData.xml e é a seguinte:

<strategy flyweight="true" name="slowDecay" type="decay">
	<class>edu.memphis.ccrg.lida.framework.strategies.LinearDecayStrategy
	</class>
	<param name="m" type="double">0.000000001</param>
</strategy>

A título de comparação, o valor padrão de decaimento era de 0,02 e foi alterado, conforme visto acima, para 0,000000001.

A classe CustomPamInitializer adaptada conforme pediu o exercício pode ser vista abaixo:

package alifeagent.initializers;

import edu.memphis.ccrg.lida.framework.Agent;
import edu.memphis.ccrg.lida.framework.initialization.FullyInitializable;
import edu.memphis.ccrg.lida.framework.shared.ElementFactory;
import edu.memphis.ccrg.lida.framework.shared.Node;
import edu.memphis.ccrg.lida.framework.strategies.DecayStrategy;
import edu.memphis.ccrg.lida.pam.BasicPamInitializer;
import edu.memphis.ccrg.lida.pam.PerceptualAssociativeMemory;
import edu.memphis.ccrg.lida.pam.PerceptualAssociativeMemoryImpl;
import java.util.Map;

public class CustomPamInitializerSolution extends BasicPamInitializer
{
	@Override
	public void initModule( FullyInitializable module, Agent agent, Map < String, ? > params )
	{
		super.initModule( module, agent, params );
		PerceptualAssociativeMemory pam = ( PerceptualAssociativeMemory ) module;

		// Obtains the ElementFactory
		ElementFactory factory = ElementFactory.getInstance( );

		// Creates a new Node in PAM labeled 'object'
		Node objectNode = pam.addDefaultNode( factory.getNode( "PamNodeImpl", "object" ) );

		// Obtains the 'rock' node from PAM
		Node child = pam.getNode( "rock" );

		// Adds a Link in PAM from 'rock' to 'object' with link category PARENT
		pam.addDefaultLink( factory.getLink( child, objectNode, PerceptualAssociativeMemoryImpl.PARENT ) );

		/* BEGIN <-- Luiz Fernando Romanini */
		// Link between "food" and "object" in PAM
		// Obtain the 'food' node from PAM
		child = pam.getNode( "food" );
		// Add a Link in PAM from 'food' to 'object' with link category PARENT
		pam.addDefaultLink( factory.getLink( child, objectNode, PerceptualAssociativeMemoryImpl.PARENT ) );

		// Change decay strategy
		DecayStrategy decayStrategy = factory.getDecayStrategy( "slowDecay" );
		objectNode.setDecayStrategy( decayStrategy );
		/* END   <-- Luiz Fernando Romanini */
	}
}

Advanced Exercise 1:

O agente não faz nada quando avista uma árvore. Entretanto, quando o agente compartilha a célula com uma árvore, está imune aos predadores. Assim, adicionaremos um attention codelet para mover o agente nesses casos, como observado abaixo:

<module name="AttentionModule">
	<class>edu.memphis.ccrg.lida.attentioncodelets.AttentionCodeletModule</class>
	<associatedmodule>Workspace</associatedmodule>
	<associatedmodule>GlobalWorkspace</associatedmodule>
	<taskspawner>defaultTS</taskspawner>
	<initialTasks>
		<task name="TreeFrontAttentionCodelet">
			<tasktype>NeighborhoodAttentionCodelet</tasktype>
			<ticksperrun>5</ticksperrun>
			<param name="nodes" type="string">tree</param>
			<param name="refractoryPeriod" type="int">50</param>
			<param name="initialActivation" type="double">1.0</param>
		</task>

	( ... )

Além disso, é necessário um esquema na memória procedural com o contexto treeFront e a ação action.moveAgent. Para executar esse exercício, foi usado comoo exemplo o esquema 10b do exercício anterior e o seguinte esquema foi adicionado, para, em caso do agente avistar uma árvore à frente, se mover:

<param name="scheme.11">if treeFront move agent|(treeFront)()|action.moveAgent |()()  |0.1</param>

Como resultado, quando o agente encontra uma árvore à sua frente, move-se para ela, como mostra a captura de tela de seleção de ação abaixo:

LIDA Action Selection

Advanced Exercise 2:

Este exercício propõe a criação de uma classe personalizada para seleção de ação. A sugestão do tutorial e que será usada nessa solução é de verificar se o comportamento está disponível no broadcast para então selecioná-lo.

A primeira alteração acontece no arquivo alifeAgent.xml. Nele, alteramos a classe de seleção de ação de edu.memphis.ccrg.lida.actionselection.BasicActionSelection para alifeagent.modules.CustomActionSelection, que será criada a partir da classe BasicActionSelection. Adicionalmente, criamos um novo listener no GlobalWorkspace. As alterações são essas:

	<module name="ActionSelection">
	    <!-- <class>edu.memphis.ccrg.lida.actionselection.BasicActionSelection</class> -->
	    <class>alifeagent.modules.CustomActionSelection</class>
	    <param name="actionSelection.ticksPerStep" type="int"> 10</param>
	    <taskspawner>defaultTS</taskspawner>
	</module>

( ... )

<listeners>
	<listener>
		<listenertype>alifeagent.modules.CustomActionSelection</listenertype>
		<modulename>GlobalWorkspace</modulename>
		<listenername>ActionSelection</listenername>
	</listener>

A nova classe, por sua vez, ficará responsável por extender FrameworkModuleImpl, além de implementar ActionSelection e ProceduralMemoryListener e pode ser vista mais abaixo.

package alifeagent.modules;

import edu.memphis.ccrg.lida.actionselection.Action;
import edu.memphis.ccrg.lida.actionselection.ActionSelection;
import edu.memphis.ccrg.lida.actionselection.ActionSelectionListener;
import edu.memphis.ccrg.lida.actionselection.Behavior;
import edu.memphis.ccrg.lida.actionselection.PreafferenceListener;
import edu.memphis.ccrg.lida.framework.FrameworkModuleImpl;
import edu.memphis.ccrg.lida.framework.ModuleListener;
import edu.memphis.ccrg.lida.framework.shared.ConcurrentHashSet;
import edu.memphis.ccrg.lida.framework.shared.ElementFactory;
import edu.memphis.ccrg.lida.framework.shared.Node;
import edu.memphis.ccrg.lida.framework.shared.NodeStructure;
import edu.memphis.ccrg.lida.framework.shared.NodeStructureImpl;
import edu.memphis.ccrg.lida.framework.strategies.DecayStrategy;
import edu.memphis.ccrg.lida.framework.tasks.FrameworkTaskImpl;
import edu.memphis.ccrg.lida.framework.tasks.TaskManager;
import edu.memphis.ccrg.lida.globalworkspace.BroadcastContent;
import edu.memphis.ccrg.lida.proceduralmemory.ProceduralMemoryListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Luiz Fernando Romanini
 */
public class CustomActionSelection
	extends FrameworkModuleImpl
	implements ActionSelection, ProceduralMemoryListener
{
	/* BEGIN <-- Luiz Fernando Romanini */
	private static final Logger logger = Logger.getLogger( CustomActionSelection.class.getCanonicalName( ) );
	/* END   <-- Luiz Fernando Romanini */
 
	private List< ActionSelectionListener > listeners = new ArrayList< ActionSelectionListener >( );
	private Set< Behavior > behaviors = new ConcurrentHashSet< Behavior >( );
	private double maxActivationThreshold;
	private DecayStrategy behaviorDecayStrategy;

	private static final int DEFAULT_REFRACTORY_PERIOD = 80;
	private int refractoryPeriodTicks;

	private static final double DEFAULT_CANDIDATE_THRESHOLD = 0.8;
	private double candidateThreshold;

	private static final double DEFAULT_REMOVAL_THRESHOLD = 0.1;
	private double defaultRemovalThreshold;

	private static final int DEFAULT_TICKS_PER_RUN = 10;
	private int ticksPerRun;

	private static final double DEFAULT_THRESHOLD_DECAY_RATE = 0.1;
	private double thresholdDecayRate;

	/* BEGIN <-- Luiz Fernando Romanini */
	NodeStructure nsBroadcast = new NodeStructureImpl( );
 
	/**
	* Default constructor
	*/
	public CustomActionSelection( )
	{
	}
 	/* END   <-- Luiz Fernando Romanini */

	/**
	* Will set parameters with the following names:
	*
	* actionSelection.refractoryperiodTicks
	* actionSelection.candidateThreshold
	* actionSelection.removalThreshold
	* actionSelection.backgroundTaskTicksPerRun
	* actionSelection.behaviorDecayStrategy
	* actionSelection.thresholdDecayRate
	*
	* @see edu.memphis.ccrg.lida.framework.FrameworkModuleImpl#init( )
	*/
	@Override
	public void init( )
	{
		refractoryPeriodTicks = ( Integer ) getParam( "actionSelection.refractoryperiodTicks", DEFAULT_REFRACTORY_PERIOD );
		candidateThreshold = ( Double ) getParam( "actionSelection.candidateThreshold", DEFAULT_CANDIDATE_THRESHOLD );
		maxActivationThreshold = candidateThreshold;

		defaultRemovalThreshold = ( Double ) getParam( "actionSelection.removalThreshold", DEFAULT_REMOVAL_THRESHOLD );

		ticksPerRun = ( Integer ) getParam( "actionSelection.backgroundTaskTicksPerRun", DEFAULT_TICKS_PER_RUN );
		taskSpawner.addTask( new BackgroundTask( ticksPerRun ) );

		ElementFactory factory = ElementFactory.getInstance( );
		String decayType = ( String ) getParam( "actionSelection.behaviorDecayStrategy", factory.getDefaultDecayType( ) );
		behaviorDecayStrategy = factory.getDecayStrategy( decayType );

		if( behaviorDecayStrategy == null )
		{
			logger.log( Level.WARNING, "factory doesn't have decay strategy" );
			behaviorDecayStrategy = factory.getDefaultDecayStrategy( );
		}

		thresholdDecayRate = ( Double ) getParam( "actionSelection.thresholdDecayRate", DEFAULT_THRESHOLD_DECAY_RATE );
	}

	@Override
	public void addListener( ModuleListener listener )
	{
		if ( listener instanceof ActionSelectionListener )
		{
			addActionSelectionListener( ( ActionSelectionListener ) listener );
		}
		else
		{
			logger.log( Level.WARNING, "Cannot add listener {1}", new Object[ ] { TaskManager.getCurrentTick( ), listener } );
		}
	}
	
	@Override
	public void addActionSelectionListener( ActionSelectionListener listener )
	{
		listeners.add( listener );
	}

	private class BackgroundTask extends FrameworkTaskImpl
	{
		public BackgroundTask( int ticksPerRun )
		{
			super( ticksPerRun );
		}
		
		@Override
		protected void runThisFrameworkTask( )
		{
			if( selectAction( ) != null )
			{
				setNextTicksPerRun( refractoryPeriodTicks );
				candidateThreshold = maxActivationThreshold;
			}
			else
			{
				candidateThreshold -= thresholdDecayRate;
				
				if( candidateThreshold < 0.0 )
					candidateThreshold = 0.0;
			}
		}
	}

	@Override
	public void receiveBehavior( Behavior b )
	{
		if( b != null )
		{
			synchronized( this )
			{
				b.setDecayStrategy( behaviorDecayStrategy );
				b.setActivatibleRemovalThreshold( defaultRemovalThreshold );
				behaviors.add( b );
			}

			logger.log( Level.FINE, "Behavior added {1}", new Object[ ] { TaskManager.getCurrentTick( ), b } );
		}
		else
		{
			logger.log( Level.WARNING, "null Behavior can not be added", TaskManager.getCurrentTick( ) );
		}
	}

	@Override
	public Action selectAction( )
	{
		Behavior behavior = chooseBehavior( );

		if ( behavior != null )
		{
			Action action = behavior.getAction( );
			logger.log( Level.FINE, "Action Selected: {1}", new Object[ ] { TaskManager.getCurrentTick( ), action } );
		
			for( ActionSelectionListener bl : listeners )
			{
				bl.receiveAction( action );
			}
			
			return action;
		}
		
		return null;
	}

	private Behavior chooseBehavior( )
	{
		/* BEGIN <-- Luiz Fernando Romanini */
		for( Behavior behavior : this.behaviors )
		{
			for( Node n : behavior.getContextNodes( ) )
			{
				if( this.nsBroadcast.containsNode( n ) )
				{
					logger.log( Level.INFO, "Behavior selected is {1}", new Object[ ] { TaskManager.getCurrentTick( ), behavior } );
					return behavior;
				}
			}
		}

		return null;
		/* END   <-- Luiz Fernando Romanini */
	}

	@Override
	public Object getModuleContent( Object... params )
	{
		if( "behaviors".equals( params[ 0 ] ) )
		{
			return Collections.unmodifiableCollection( behaviors );
		}
		
		return null;
	}

	@Override
	public void addPreafferenceListener( PreafferenceListener listener )
	{
	}

	@Override
	public void learn( BroadcastContent content )
	{
	}

	@Override
	public void receiveBroadcast( BroadcastContent bc )
	{
		/* BEGIN <-- Luiz Fernando Romanini */
		this.nsBroadcast = ( NodeStructure ) bc;
		/* END   <-- Luiz Fernando Romanini */
	}

	@Override
	public void decayModule( long ticks )
	{
		Iterator< Behavior > it = behaviors.iterator( );

		while( it.hasNext( ) )
		{
			Behavior b = it.next( );
			b.decay( ticks );
			
			if ( b.isRemovable( ) )
				it.remove( );
		}
	}
}

As principais alterações ocorreram nos métodos chooseBehavior( ) e receiveBroadcast( ) e uma captura de tela mostrando o comportamento sendo selecionado pode ser vista mais abaixo.

LIDA Custom Action Selection

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer