Skip to content
Snippets Groups Projects
Commit ebd26ff6 authored by shinedday's avatar shinedday
Browse files

Rewrite most core

parent 322a9b35
Branches
Tags
No related merge requests found
Showing with 347 additions and 366 deletions
......@@ -7,12 +7,10 @@ from typing import List
import sys
import pathlib
sys.path.insert(0, str(pathlib.Path(__file__).parent))
from pyAmakCore.exception.override import ToOverrideWarning
from pyAmakCore.enumeration.agent_phase import Phase
from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy
class Agent:
......@@ -116,7 +114,7 @@ class Agent:
"""
return -inf
def _get_most_critical_neighbor(self, including_me: bool = False) -> 'Agent':
def get_most_critical_neighbor(self, including_me: bool = False) -> 'Agent':
"""
Convenient method giving the most critical neighbor at a given moment
"""
......@@ -135,10 +133,6 @@ class Agent:
return criticalest[randint(0, len(criticalest) - 1)]
"""
Cycle
"""
def on_cycle_begin(self) -> None:
"""
this method is called by each agent at the start of their cycle
......@@ -169,24 +163,7 @@ class Agent:
"""
ToOverrideWarning("on_act")
def _phase1(self) -> None:
"""
this is the first phase of a cycle
"""
self.on_perceive()
self.__criticality = self.compute_criticality()
self._next_phase()
def _phase2(self) -> None:
"""
this is the second phase of a cycle
"""
self.on_decide()
self.on_act()
self.__criticality = self.compute_criticality()
self._next_phase()
def _next_phase(self):
def next_phase(self):
next_phase = {
Phase.INITIALIZING: Phase.PERCEPTION,
Phase.PERCEPTION: Phase.PERCEPTION_DONE,
......@@ -196,32 +173,6 @@ class Agent:
}
self.__phase = next_phase.get(self.__phase)
def run(self) -> None:
"""
Full cycle of an agent
"""
self._next_phase()
execution_policy = self.__amas.get_execution_policy()
if execution_policy == ExecutionPolicy.TWO_PHASES:
if self.__phase == Phase.PERCEPTION:
self.on_cycle_begin()
self._phase1()
return
if self.__phase == Phase.DECISION_AND_ACTION:
self._phase2()
self.on_cycle_end()
return
if execution_policy == ExecutionPolicy.ONE_PHASE:
self.on_cycle_begin()
self._phase1()
self._next_phase()
self._phase2()
self.on_cycle_end()
def __eq__(self, other: 'Agent') -> bool:
# check class
if other is None:
......
"""
Amas Class
"""
from threading import Thread
from typing import List
import sys
import pathlib
sys.path.insert(0, str(pathlib.Path(__file__).parent))
from pyAmakCore.classes.tools.loggable import Loggable
from pyAmakCore.classes.schedulable import Schedulable
from pyAmakCore.classes.scheduler import Scheduler
from pyAmakCore.classes.tools.schedulable import Schedulable
from pyAmakCore.classes.environment import Environment
from pyAmakCore.classes.agent import Agent
from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy
......@@ -24,43 +21,54 @@ class Amas(Schedulable, Loggable):
Amas Class
"""
def __init__(self, environment: Environment) -> None:
def __init__(self,
environment: Environment,
execution_policy: ExecutionPolicy = ExecutionPolicy.ONE_PHASE
) -> None:
Schedulable.__init__(self)
Loggable.__init__(self)
self.__environment: Environment = environment
self.__agents: List[Agent] = []
self.__agent_to_add: List[Agent] = []
self.__agent_to_remove: List[Agent] = []
self.__nbrcycle: int = 0
self.__execution_policy: ExecutionPolicy = execution_policy
self.__scheduler: Scheduler = Scheduler()
self.__scheduler.add_schedulable(self)
self.__environment.add_scheduler(self.__scheduler)
self.__execution_policy: ExecutionPolicy = ExecutionPolicy.ONE_PHASE
self.on_initialization()
self.on_initial_agents_creation()
def add_pending_agent(self) -> List[Agent]:
"""
add pending agent into agent and return added agents
"""
for agent in self.__agent_to_add:
if agent not in self.__agents:
self.__agents.append(agent)
self.__agent_to_add = []
for agent in self.__agents:
agent.compute_criticality()
self.__agents.append(agent)
tmp = self.__agent_to_add
self.__agent_to_add = []
return tmp
self.on_initialization()
def remove_pending_agent(self):
"""
add pending agent into agent and return removed agents
"""
for agent in self.__agent_to_remove:
self.__agents.remove(agent)
tmp = self.__agent_to_remove
self.__agent_to_remove = []
# tell scheduler that init is done
self.give_token_syncro()
return tmp
def add_agent(self, agent: 'Agent') -> None:
"""
add agent in the amas agents list without duplicate
"""
if agent in self.__agents or agent in self.__agent_to_add:
if agent in self.__agents:
return
if agent in self.__agent_to_add:
return
self.__agent_to_add.append(agent)
......@@ -75,6 +83,10 @@ class Amas(Schedulable, Loggable):
"""
remove agent from amas
"""
if agent not in self.__agents:
return
if agent in self.__agent_to_remove:
return
self.__agent_to_remove.append(agent)
def get_environment(self) -> Environment:
......@@ -95,104 +107,9 @@ class Amas(Schedulable, Loggable):
"""
return self.__execution_policy
def set_execution_policy(self, execution_policy: ExecutionPolicy) -> None:
"""
set system execution_policy
"""
self.__execution_policy = execution_policy
def get_cycle(self) -> int:
"""
return nbr of cycle
"""
return self.__nbrcycle
def on_initial_agents_creation(self) -> None:
"""
This method is called at the end of __init__()
"""
ToOverrideWarning("on_initial_agents_creation")
def synchronization(self) -> None:
"""
Unlock Scheduler and wait for his response
"""
self.give_token_syncro()
self.__scheduler.take_amas_token()
def cycle(self) -> None:
"""
Main behavior of Amas
"""
print("Cycle : ", self.__nbrcycle)
for agent in self.__agent_to_add:
if agent not in self.__agents:
self.__agents.append(agent)
self.__agent_to_add = []
self.on_cycle_begin()
self.synchronization()
threads = []
# suffle
for agent in self.__agents:
current_thread = Thread(target=agent.run)
threads.append(current_thread)
current_thread.start()
for thread in threads:
thread.join()
if self.__execution_policy == ExecutionPolicy.TWO_PHASES:
threads = []
for agent in self.__agents:
current_thread = Thread(target=agent.run)
threads.append(current_thread)
current_thread.start()
for thread in threads:
thread.join()
self.synchronization()
self.on_cycle_end()
for agent in self.__agent_to_remove:
if agent in self.__agents:
self.__agents.remove(agent)
self.__agent_to_remove = []
self.to_csv(self.get_cycle(), self.get_agents())
self.__nbrcycle += 1
def put_token(self) -> None:
"""
Tell scheduler to start
"""
self.__scheduler.give_semaphore_start_stop()
def take_token(self) -> None:
"""
Tell scheduler to stop
"""
self.__scheduler.take_semaphore_start_stop()
def exit_program(self) -> None:
"""
exit the program at the end of the cycle
"""
self.put_token()
self.__scheduler.exit_bool = True
def set_sleep(self, sleep_time) -> None:
"""
set sleep between 2 cycles
"""
self.__scheduler.sleep_time = sleep_time
def start(self) -> None:
"""
launch the system
"""
self.__scheduler.run()
......@@ -3,10 +3,10 @@ Environment class
"""
import sys
import pathlib
sys.path.insert(0, str(pathlib.Path(__file__).parent))
from pyAmakCore.classes.schedulable import Schedulable
from pyAmakCore.classes.scheduler import Scheduler
from pyAmakCore.classes.tools.schedulable import Schedulable
class Environment(Schedulable):
......@@ -16,34 +16,4 @@ class Environment(Schedulable):
def __init__(self) -> None:
super().__init__()
self.__scheduler: Scheduler = None
self.on_initialization()
# tell scheduler that init is done
self.give_token_syncro()
def add_scheduler(self, scheduler: Scheduler) -> None:
"""
set scheduler pointer to scheduler
add add self to schedulables of scheduler
"""
self.__scheduler = scheduler
self.__scheduler.add_schedulable(self)
def synchronization(self) -> None:
"""
Unlock Scheduler and wait for his response
"""
self.give_token_syncro()
self.__scheduler.take_environment_token()
def cycle(self) -> None:
"""
Main behavior of Environment
"""
self.on_cycle_begin()
self.synchronization()
# mileu de cycle
self.synchronization()
self.on_cycle_end()
from pyAmakCore.exception.override import ToOverrideWarning
from pyAmakCore.classes.agent import Agent
from pyAmakCore.classes.amas import Amas
from pyAmakCore.classes.environment import Environment
from pyAmakCore.classes.scheduler import Scheduler
class SimpleAgent(Agent):
"""
test
"""
class SimpleAmas(Amas):
"""
test
"""
def on_initial_agents_creation(self) -> None:
for i in range(10):
self.add_agent(SimpleAgent(self))
class SimpleEnv(Environment):
"""
test
"""
import time
start_time = time.time()
ToOverrideWarning.enable_warning(False)
env = SimpleEnv()
amas = SimpleAmas(env)
scheduler = Scheduler(amas)
scheduler.start()
scheduler.run()
print("--- %s seconds ---" % (time.time() - start_time))
......@@ -2,13 +2,19 @@
Scheduler class
"""
from threading import Semaphore, Thread
from time import sleep
from typing import List
from time import sleep
import sys
import pathlib
sys.path.insert(0, str(pathlib.Path(__file__).parent))
from pyAmakCore.classes.schedulable import Schedulable
from pyAmakCore.classes.scheduler_tool.agent_thread import AgentThread
from pyAmakCore.classes.amas import Amas
from pyAmakCore.classes.scheduler_tool.schedulable_thread import SchedulableThread
from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy
class Scheduler:
......@@ -16,104 +22,156 @@ class Scheduler:
Scheduler class, to make sure that environment and amas are always sync together
"""
def __init__(self) -> None:
def __init__(self, amas) -> None:
self.amas: Amas = amas
# List of all schedulables
self.__schedulables: List[Schedulable] = []
# Sleep timer between 2 cycle
self.sleep_time: float = 0
self.exit_bool = False
# Semaphore for start/stop button
self.__semaphore_start_stop: Semaphore = Semaphore(0)
# Semaphore to tell amas to continue
self.__semaphore_amas: Semaphore = Semaphore(0)
# Semaphore to tell environment to continue
self.__semaphore_environment: Semaphore = Semaphore(0)
self.semaphore_start_stop = Semaphore(0)
self.exit_bool = False
self.schedulables = []
self.schedulables_threads = []
def add_schedulable(self, schedulable: Schedulable) -> None:
"""
add schedulable in schedulables list
"""
self.__schedulables.append(schedulable)
self.add_schedulables(amas)
self.add_schedulables(amas.get_environment())
def take_amas_token(self) -> None:
"""
make amas wait here for a token
"""
self.__semaphore_amas.acquire()
self.agents: List[AgentThread] = []
self.agents_thread: List[Thread] = []
def give_amas_token(self) -> None:
"""
unlock amas
"""
self.__semaphore_amas.release()
self.sleep_time = 0
def take_environment_token(self) -> None:
"""
make environment wait here for a token
"""
self.__semaphore_environment.acquire()
self.execution_policy = ExecutionPolicy.ONE_PHASE
def add_schedulables(self, schedulable):
schedulable_thread = SchedulableThread(schedulable)
self.schedulables.append(schedulable_thread)
current_thread = Thread(target=schedulable_thread.run)
self.schedulables_threads.append(current_thread)
current_thread.start()
def add_agent(self, agent):
agent_thread = AgentThread(agent)
self.agents.append(agent_thread)
current_thread = Thread(target=agent_thread.run)
self.agents_thread.append(current_thread)
current_thread.start()
def add_agents(self):
added_agent = self.amas.add_pending_agent()
for agent in added_agent:
self.add_agent(agent)
def remove_agents(self):
removed_agent = self.amas.remove_pending_agent()
for agent in removed_agent:
for i in range(len(self.agents)):
if agent == self.agents[i].agent:
self.agents[i].exit_bool = True
self.agents[i].is_waiting.release()
self.agents_thread[i].join()
def give_environment_token(self) -> None:
self.agents.remove(self.agents[i])
self.agents_thread.remove(self.agents_thread[i])
def wait_schedulables(self) -> None:
"""
unlock environment
wait for all schedulable to release a token
"""
self.__semaphore_environment.release()
for i in range(len(self.schedulables)):
SchedulableThread.action_done.acquire()
def take_semaphore_start_stop(self) -> None:
def start_schedulables(self) -> None:
"""
Scheduler will wait for a token to start the cycle
wait for all schedulable to release a token
"""
self.__semaphore_start_stop.acquire()
for schedulable in self.schedulables:
schedulable.is_waiting.release()
def give_semaphore_start_stop(self) -> None:
def wait_agents(self) -> None:
"""
give a token to unlock Scheduler
wait for all agent to release a token
"""
self.__semaphore_start_stop.release()
for i in range(len(self.agents)):
AgentThread.action_done.acquire()
def syncro_schedulable(self) -> None:
def start_agents(self) -> None:
"""
wait for all schedulable to release a token
wait for all agent to release a token
"""
for schedulable in self.__schedulables:
schedulable.take_token_syncro()
for agent in self.agents:
agent.is_waiting.release()
def first_part(self):
self.add_agents()
self.amas.to_csv(self.amas.get_cycle(), self.amas.get_agents())
self.start_schedulables()
# on cycle begin
self.wait_schedulables()
def main_part(self):
self.start_agents()
# all agent run
if self.execution_policy == ExecutionPolicy.TWO_PHASES:
self.wait_agents()
# agents are doing phase 2
self.start_agents()
self.wait_agents()
def last_part(self):
self.start_schedulables()
# on cycle end
self.wait_schedulables()
self.remove_agents()
def run(self) -> None:
"""
main part of amak core
"""
# wait that all schedulable are ready
self.syncro_schedulable()
while not self.exit_bool:
print("Cycle : ", self.amas.get_cycle())
self.__semaphore_start_stop.acquire()
self.semaphore_start_stop.acquire()
if self.exit_bool:
self.close_childs()
return
self.__semaphore_start_stop.release()
self.semaphore_start_stop.release()
threads = []
for schedulable in self.__schedulables:
current_thread = Thread(target=schedulable.run)
threads.append(current_thread)
current_thread.start()
self.first_part()
self.main_part()
self.last_part()
# on cycle begin
self.syncro_schedulable()
self.give_amas_token()
self.give_environment_token()
sleep(self.sleep_time)
self.close_childs()
# main part of the cycle
self.syncro_schedulable()
self.give_amas_token()
self.give_environment_token()
def close_childs(self):
# on cycle end
for agent in self.agents:
agent.exit_bool = True
agent.is_waiting.release()
for thread in threads:
thread.join()
for thread in self.agents_thread:
thread.join(0)
sleep(self.sleep_time)
SchedulableThread.exit_bool = True
for schedulable in self.schedulables:
schedulable.is_waiting.release()
for thread in self.schedulables_threads:
thread.join(0)
def exit_program(self):
self.exit_bool = True
self.semaphore_start_stop.release()
def start(self):
self.semaphore_start_stop.release()
def stop(self):
self.semaphore_start_stop.acquire()
def set_sleep(self, sleep_time: int):
self.sleep_time = sleep_time
"""
Class Agent thread
"""
from threading import Semaphore
import sys
import pathlib
sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
from pyAmakCore.classes.agent import Agent
from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy
class AgentThread:
"""
thread class used to thread agent
"""
action_done = Semaphore(0)
execution_policy = ExecutionPolicy.ONE_PHASE
def __init__(self, agent: Agent):
self.agent = agent
self.is_waiting = Semaphore(0)
self.exit_bool = False
def phase1(self) -> None:
"""
this is the first phase of a cycle
"""
self.agent.on_perceive()
self.agent.__criticality = self.agent.compute_criticality()
self.agent.next_phase()
def phase2(self) -> None:
"""
this is the second phase of a cycle
"""
self.agent.on_decide()
self.agent.on_act()
self.agent.__criticality = self.agent.compute_criticality()
self.agent.next_phase()
def run(self):
while not self.exit_bool:
self.is_waiting.acquire()
if self.exit_bool:
return
self.agent.next_phase()
self.agent.on_cycle_begin()
self.phase1()
if AgentThread.execution_policy == ExecutionPolicy.TWO_PHASES:
AgentThread.action_done.release()
self.is_waiting.acquire()
self.agent.next_phase()
self.phase2()
self.agent.on_cycle_end()
AgentThread.action_done.release()
"""
thread class for Schedulable
"""
from threading import Semaphore
import sys
import pathlib
sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
from pyAmakCore.classes.tools.schedulable import Schedulable
class SchedulableThread:
"""
thread class used to thread schedulable
"""
action_done = Semaphore(0)
exit_bool = False
def __init__(self, schedulable: Schedulable):
self.schedulable = schedulable
self.is_waiting = Semaphore(0)
def run(self):
while not SchedulableThread.exit_bool:
self.is_waiting.acquire()
if SchedulableThread.exit_bool:
return
self.schedulable.on_cycle_begin()
SchedulableThread.action_done.release()
# les agents cycles
self.is_waiting.acquire()
self.schedulable.on_cycle_end()
self.schedulable.cycle()
SchedulableThread.action_done.release()
"""
Schedulable interface
"""
from threading import Semaphore
import sys
import pathlib
sys.path.insert(0, str(pathlib.Path(__file__).parent))
sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
from pyAmakCore.exception.override import ToOverrideWarning
......@@ -13,7 +12,16 @@ class Schedulable:
"""
Class Schedulable
"""
syncro_semaphore: Semaphore = Semaphore(0)
def __init__(self):
self.__nbr_cycle = 0
def get_cycle(self) -> int:
return self.__nbr_cycle
def cycle(self) -> None:
self.__nbr_cycle += 1
def on_initialization(self) -> None:
"""
......@@ -32,27 +40,3 @@ class Schedulable:
This method will be executed at the end of each cycle
"""
ToOverrideWarning("on_cycle_end")
def run(self) -> None:
"""
Method that will be called by the thread
"""
self.cycle()
def cycle(self) -> None:
"""
Main behavior of the Schedulable
"""
ToOverrideWarning("cycle")
def take_token_syncro(self) -> None:
"""
Scheduler will wait here that the schedulable release a token
"""
self.syncro_semaphore.acquire()
def give_token_syncro(self) -> None:
"""
Unlock scheduler
"""
self.syncro_semaphore.release()
from pyAmakCore.exception.override import ToOverrideWarning
from pyAmakCore.classes.agent import Agent
from pyAmakCore.classes.amas import Amas
from pyAmakCore.classes.environment import Environment
from pyAmakCore.classes.agent import Agent
from pyAmakCore.classes.scheduler import Scheduler
class SimpleAgent(Agent):
......@@ -16,7 +18,7 @@ class SimpleAmas(Amas):
def on_initial_agents_creation(self) -> None:
for i in range(10):
self.add_agent(Agent(self))
self.add_agent(SimpleAgent(self))
class SimpleEnv(Environment):
......@@ -24,13 +26,18 @@ class SimpleEnv(Environment):
test
"""
import time
start_time = time.time()
ToOverrideWarning.enable_warning(False)
env = SimpleEnv()
amas = SimpleAmas(env)
amas.put_token()
scheduler = Scheduler(amas)
amas.start()
scheduler.start()
scheduler.run()
print("--- %s seconds ---" % (time.time() - start_time))
"""
......
......@@ -11,9 +11,6 @@ class SimpleAgent(Agent):
def set_criticality(self, i):
self._Agent__criticality = i
def get_most_critical(self, i):
return self._get_most_critical_neighbor(i)
class TestAgentGetMostCriticalNeighbor(TestCase):
"""
......@@ -44,7 +41,7 @@ class TestAgentGetMostCriticalNeighbor(TestCase):
n4.set_criticality(25)
agent.add_neighbour(n4)
self.assertEqual(agent.get_most_critical(False), n4)
self.assertEqual(agent.get_most_critical_neighbor(False), n4)
def test_get_most_critical_neighbor_with_self(self):
Agent.reset_agent()
......@@ -70,7 +67,7 @@ class TestAgentGetMostCriticalNeighbor(TestCase):
n4.set_criticality(25)
agent.add_neighbour(n4)
self.assertEqual(agent.get_most_critical(True), agent)
self.assertEqual(agent.get_most_critical_neighbor(True), agent)
if __name__ == '__main__':
......
"""
test that run of Agent work as intended
"""
from unittest import TestCase, main
from pyAmakCore.classes.agent import Agent
from pyAmakCore.classes.amas import Amas
from pyAmakCore.classes.environment import Environment
from pyAmakCore.enumeration.agent_phase import Phase
from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy
class TestAgentRun(TestCase):
"""
Test class for Agent run
"""
def test_run_one_phase(self):
"""
test that run() work and return correct phase with 1 phase policy
"""
Agent.reset_agent()
environment = Environment()
amas = Amas(environment)
amas.set_execution_policy(ExecutionPolicy.ONE_PHASE)
agent = Agent(amas)
self.assertEqual(agent.get_phase(), Phase.INITIALIZING)
agent.run()
self.assertEqual(agent.get_phase(), Phase.DECISION_AND_ACTION_DONE)
agent.run()
self.assertEqual(agent.get_phase(), Phase.DECISION_AND_ACTION_DONE)
def test_run_two_phase(self):
"""
test that run() work and return correct phase with 2 phase policy
"""
Agent.reset_agent()
environment = Environment()
amas = Amas(environment)
amas.set_execution_policy(ExecutionPolicy.TWO_PHASES)
agent = Agent(amas)
self.assertEqual(agent.get_phase(), Phase.INITIALIZING)
agent.run()
self.assertEqual(agent.get_phase(), Phase.PERCEPTION_DONE)
agent.run()
self.assertEqual(agent.get_phase(), Phase.DECISION_AND_ACTION_DONE)
if __name__ == '__main__':
main()
......@@ -45,24 +45,24 @@ class TestAmasAgents(TestCase):
agent3 = Agent(amas)
# add 1 agent
amas.add_agent(agent1)
amas.cycle()
amas.add_pending_agent()
self.assertEqual(amas.get_agents(), [agent1])
# don't remove previous agent
amas.add_agent(agent2)
amas.cycle()
amas.add_pending_agent()
self.assertEqual(amas.get_agents(), [agent1, agent2])
# add agent in good order
amas.add_agent(agent3)
amas.cycle()
amas.add_pending_agent()
self.assertEqual(amas.get_agents(), [agent1, agent2, agent3])
# don't add duplicate
amas.add_agent(agent1)
amas.add_agent(agent2)
amas.add_agent(agent3)
amas.cycle()
amas.add_pending_agent()
self.assertEqual(amas.get_agents(), [agent1, agent2, agent3])
def test_add_agents(self) -> None:
......@@ -80,16 +80,16 @@ class TestAmasAgents(TestCase):
agent5 = Agent(amas)
amas.add_agents([agent1, agent2, agent3, agent4, agent5])
amas.cycle()
amas.add_pending_agent()
self.assertEqual(amas.get_agents(), [agent1, agent2, agent3, agent4, agent5])
amas = SimplerAmas(environment)
amas.add_agents([agent1, agent2])
amas.cycle()
amas.add_pending_agent()
self.assertEqual(amas.get_agents(), [agent1, agent2])
amas.add_agents([agent1, agent2, agent4, agent2, agent3])
amas.cycle()
amas.add_pending_agent()
self.assertEqual(amas.get_agents(), [agent1, agent2, agent4, agent3])
def test_remove_agent(self) -> None:
......@@ -101,22 +101,23 @@ class TestAmasAgents(TestCase):
agent3 = Agent(amas)
amas.remove_agent(agent2)
amas.cycle()
amas.remove_pending_agent()
self.assertEqual(amas.get_agents(), [])
amas.add_agents([agent1, agent2, agent3])
amas.add_pending_agent()
amas.remove_agent(agent2)
amas.cycle()
amas.remove_pending_agent()
self.assertEqual(amas.get_agents(), [agent1, agent3])
amas.remove_agent(agent2)
amas.cycle()
amas.remove_pending_agent()
self.assertEqual(amas.get_agents(), [agent1, agent3])
amas.remove_agent(agent1)
amas.remove_agent(agent3)
amas.cycle()
amas.remove_pending_agent()
self.assertEqual(amas.get_agents(), [])
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment