Example #1
0
class Foraging(Model):
    
    number_of_bean = 0
    number_of_corn = 0
    number_of_soy = 0
    
    def __init__(self, width=50, height=50, torus=True, num_bug=50, seed=42, strategy=None):
        super().__init__(seed=seed)
        self.number_of_bug = num_bug
        if not(strategy in ["stick", "switch"]):
            raise TypeError("'strategy' must be one of {stick, switch}")
        self.strategy = strategy
        
        self.grid = SingleGrid(width, height, torus)
        self.schedule = RandomActivation(self)
        data = {"Bean": lambda m: m.number_of_bean,
                "Corn": lambda m: m.number_of_corn,
                "Soy": lambda m: m.number_of_soy,
                "Bug": lambda m: m.number_of_bug,
                }
        self.datacollector = DataCollector(data)
        
        # create foods
        self._populate(Bean)
        self._populate(Corn)
        self._populate(Soy)
        
        # create bugs
        for i in range(self.number_of_bug):
            pos = self.grid.find_empty()
            bug = Bug(i, self)
            bug.strategy = self.strategy
            self.grid.place_agent(bug, pos)
            self.schedule.add(bug)
    
    def step(self):
        self.schedule.step()
        self.datacollector.collect(self)
        
        if not(self.grid.exists_empty_cells()):
            self.running = False
    
    def _populate(self, food_type):
        prefix = "number_of_{}"
        
        counter = 0
        while counter < food_type.density * (self.grid.width * self.grid.height):
            pos = self.grid.find_empty()
            food = food_type(counter, self)
            self.grid.place_agent(food, pos)
            self.schedule.add(food)
            food_name = food_type.__name__.lower()
            attr_name = prefix.format(food_name)
            val = getattr(self, attr_name)
            val += 1
            setattr(self, attr_name, val)
            counter += 1
Example #2
0
class SchellingModel(Model):
    '''
    Model class for the Schelling segregation model.
    '''
    def __init__(self, height, width, density, type_pcs=[.2, .2, .2, .2, .2]):
        '''
        '''

        self.height = height
        self.width = width
        self.density = density
        self.type_pcs = type_pcs

        self.schedule = RandomActivation(self)
        self.grid = SingleGrid(height, width, torus=False)

        self.happy = 0
        self.datacollector = DataCollector(
            {"happy": lambda m: m.happy},  # Model-level count of happy agents
            # For testing purposes, agent's individual x and y
            {
                "x": lambda a: a.pos[0],
                "y": lambda a: a.pos[1]
            })

        self.running = True

        # Set up agents
        # We use a grid iterator that returns
        # the coordinates of a cell as well as
        # its contents. (coord_iter)

        total_agents = self.height * self.width * self.density
        agents_by_type = [total_agents * val for val in self.type_pcs]

        for loc, types in enumerate(agents_by_type):
            for i in range(int(types)):
                pos = self.grid.find_empty()
                agent = SchellingAgent(pos, self, loc)
                self.grid.position_agent(agent, pos)
                self.schedule.add(agent)

    def step(self):
        '''
        Run one step of the model. If All agents are happy, halt the model.
        '''
        self.happy = 0  # Reset counter of happy agents
        self.schedule.step()
        self.datacollector.collect(self)

        if self.happy == self.schedule.get_agent_count():
            self.running = False
Example #3
0
class TestSingleGrid(unittest.TestCase):
    def setUp(self):
        self.space = SingleGrid(50, 50, False)
        self.agents = []
        for i, pos in enumerate(TEST_AGENTS_GRID):
            a = MockAgent(i, None)
            self.agents.append(a)
            self.space.place_agent(a, pos)

    def test_agent_positions(self):
        """
        Ensure that the agents are all placed properly.
        """
        for i, pos in enumerate(TEST_AGENTS_GRID):
            a = self.agents[i]
            assert a.pos == pos

    def test_remove_agent(self):
        for i, pos in enumerate(TEST_AGENTS_GRID):
            a = self.agents[i]
            assert a.pos == pos
            assert self.space.grid[pos[0]][pos[1]] == a
            self.space.remove_agent(a)
            assert a.pos is None
            assert self.space.grid[pos[0]][pos[1]] is None

    def test_empty_cells(self):
        if self.space.exists_empty_cells():
            pytest.deprecated_call(self.space.find_empty)
            for i, pos in enumerate(list(self.space.empties)):
                a = MockAgent(-i, pos)
                self.space.position_agent(a, x=pos[0], y=pos[1])
        assert self.space.find_empty() is None
        with self.assertRaises(Exception):
            self.space.move_to_empty(a)

    def move_agent(self):
        agent_number = 0
        initial_pos = TEST_AGENTS_GRID[agent_number]
        final_pos = (7, 7)

        _agent = self.agents[agent_number]

        assert _agent.pos == initial_pos
        assert self.space.grid[initial_pos[0]][initial_pos[1]] == _agent
        assert self.space.grid[final_pos[0]][final_pos[1]] is None
        self.space.move_agent(_agent, final_pos)
        assert _agent.pos == final_pos
        assert self.space.grid[initial_pos[0]][initial_pos[1]] is None
        assert self.space.grid[final_pos[0]][final_pos[1]] == _agent
Example #4
0
class SchellingModel(Model):
    '''
    Model class for the Schelling segregation model.
    '''

    def __init__(self, height, width, density, type_pcs=[.2, .2, .2, .2, .2]):
        '''
        '''

        self.height = height
        self.width = width
        self.density = density
        self.type_pcs = type_pcs

        self.schedule = RandomActivation(self)
        self.grid = SingleGrid(height, width, torus=False)

        self.happy = 0
        self.datacollector = DataCollector(
            {"happy": lambda m: m.happy},  # Model-level count of happy agents
            # For testing purposes, agent's individual x and y
            {"x": lambda a: a.pos[0], "y": lambda a: a.pos[1]})

        self.running = True

        # Set up agents
        # We use a grid iterator that returns
        # the coordinates of a cell as well as
        # its contents. (coord_iter)

        total_agents = self.height * self.width * self.density
        agents_by_type = [total_agents*val for val in self.type_pcs]

        for loc, types in enumerate(agents_by_type):
            for i in range(int(types)):
                pos = self.grid.find_empty()
                agent = SchellingAgent(pos, self, loc)
                self.grid.position_agent(agent, pos)
                self.schedule.add(agent)

    def step(self):
        '''
        Run one step of the model. If All agents are happy, halt the model.
        '''
        self.happy = 0  # Reset counter of happy agents
        self.schedule.step()
        self.datacollector.collect(self)

        if self.happy == self.schedule.get_agent_count():
            self.running = False
class ConwayGameOfLifeModel(Model):
    """
    Class describing Conway's game of life using the mesa agent based model framowork
    
    The model contains a grid. In each cell of the grid there is an agent. The agent can be dead or
    alive. At each step of the simulation, the state of the agent can change.

    The model is responsible of creating the grid and handling the iteration of the simulation
    
    """
    def __init__(self, grid_height, grid_width, percentage_of_cell_alive):
        """
        Constructor
        """

        self.grid = SingleGrid(grid_width, grid_height, False)
        self.scheduler = SimultaneousActivation(self)
        self.number_of_agent = grid_width * grid_height

        # Creation of all agent
        for i in range(self.number_of_agent):

            # Randomly chooses the initial state of the agent (0 is alive and 1 is dead)
            # We use choices from the random module because it allows us to specify a distribution
            # (ie. a list of probability for each state). Choices will return a list with ne element
            # which is our state
            probability_alive = percentage_of_cell_alive / 100
            probability_dead = 1 - probability_alive
            state = choices([0, 1], [probability_dead, probability_alive])[0]

            # Creating the agent and adding it to the scheduler
            agent = CellAgent(i, state, self)
            self.scheduler.add(agent)

            # Adding the new agent to the grid
            agent_coordinates = self.grid.find_empty()
            self.grid.place_agent(agent, agent_coordinates)

        # Define if the simulation is running or not
        self.running = True

    def step(self):
        """
        Method to advance the model by one step. It will call the step method of each agent.
        We use a simultaneous scheduler which means we we'll be iterating through all the agent at
        once to determine their next state and then apply the new state
        """
        self.scheduler.step()
Example #6
0
class Foraging(Model):

    number_of_bean = 0
    number_of_corn = 0
    number_of_soy = 0

    def __init__(self,
                 width=50,
                 height=50,
                 torus=True,
                 num_bug=50,
                 seed=42,
                 strategy=None):
        super().__init__(seed=seed)
        self.number_of_bug = num_bug
        if not (strategy in ["stick", "switch"]):
            raise TypeError("'strategy' must be one of {stick, switch}")
        self.strategy = strategy

        self.grid = SingleGrid(width, height, torus)
        self.schedule = RandomActivation(self)
        data = {
            "Bean": lambda m: m.number_of_bean,
            "Corn": lambda m: m.number_of_corn,
            "Soy": lambda m: m.number_of_soy,
            "Bug": lambda m: m.number_of_bug,
        }
        self.datacollector = DataCollector(data)

        # create foods
        self._populate(Bean)
        self._populate(Corn)
        self._populate(Soy)

        # create bugs
        for i in range(self.number_of_bug):
            pos = self.grid.find_empty()
            bug = Bug(i, self)
            bug.strategy = self.strategy
            self.grid.place_agent(bug, pos)
            self.schedule.add(bug)

    def step(self):
        self.schedule.step()
        self.datacollector.collect(self)

        if not (self.grid.exists_empty_cells()):
            self.running = False

    def _populate(self, food_type):
        prefix = "number_of_{}"

        counter = 0
        while counter < food_type.density * (self.grid.width *
                                             self.grid.height):
            pos = self.grid.find_empty()
            food = food_type(counter, self)
            self.grid.place_agent(food, pos)
            self.schedule.add(food)
            food_name = food_type.__name__.lower()
            attr_name = prefix.format(food_name)
            val = getattr(self, attr_name)
            val += 1
            setattr(self, attr_name, val)
            counter += 1
Example #7
0
class Factory(Model):
    """The Factory model that maintains the state of the whole factory."""

    def __init__(self, grid_w, grid_h, n_robots):
        """Initialize factory."""
        # Initialize.
        self.orders = 0
        self.n_robots = n_robots
        self.scheduler = RandomActivation(self)
        self.grid = SingleGrid(grid_w, grid_h, torus=False)
        self.init_astar()
        # Initialize departments.
        self.machine = Machine("machine", self, self.grid.find_empty())
        self.store = Store("store", self, self.grid.find_empty())
        self.packaging = Packaging("packaging", self, self.grid.find_empty())
        self.dept_positions = [self.machine.pos, self.store.pos, self.packaging.pos]
        # Initialize robots.
        for i in range(self.n_robots):
            # Create robot.
            r = Robot(i, self)
            # Initialize random location.
            pos = self.grid.find_empty()
            self.grid.place_agent(r, pos)
            # Register with scheduler.
            self.scheduler.add(r)
        # Initialize visualization.
        plt.ion()

    def add_order(self):
        """Increment the number of orders to the factory."""
        self.orders += 1

    def step(self):
        """Advance the factory by one step."""
        # Step through factory. Check for orders.
        if self.orders > 0:
            self.store.orders += 1
            self.orders -= 1
        # Step through departments.
        self.store.step()
        self.machine.step()
        self.packaging.step()
        # Step through robots.
        self.scheduler.step()
        # Visualize.
        self.visualize()

    def init_astar(self):
        """Initialize a-star resources so that it doesn't have to calculated for each robot.

        Initialized in such a way that:
            * A diagonal paths are allowed.
            * The path calculated takes into account all obstacles in the grid.
        """
        def get_empty_neighborhood(pos):
            """A sub function to calculate empty neighbors of a point for a-star."""
            neighbors = self.grid.get_neighborhood(pos=pos, moore=True)
            return [n for n in neighbors if self.grid.is_cell_empty(n)]
        # Initialize a path finder object once for the entire factory.
        self.path_finder = astar.pathfinder(neighbors=get_empty_neighborhood,
                                            distance=astar.absolute_distance,
                                            cost=astar.fixed_cost(1))

    def find_nearest_aimless_robot(self, pos):
        """Find the nearest aimless robot to a given position in the factory."""
        def is_aimless(robot, pos):
            """Check if the robot satisfied aimless condition."""
            if robot.destination is None:
                return True
            else:
                return False

        aimless_robots = [robot for robot in self.scheduler.agents if is_aimless(robot, pos)]
        if len(aimless_robots) != 0:
            robot_distances = [astar.absolute_distance(pos, robot.pos) for robot in aimless_robots]
            nearest_index = np.argmin(robot_distances)
            return aimless_robots[nearest_index]
        else:
            return None

    def find_robot_at_position(self, pos):
        """Find robot that is at a given location in the factory that is not busy."""
        for robot in self.scheduler.agents:
            if robot.pos == pos:
                return robot
        return None

    def find_next_position_towards_destination(self, curr_pos, dest_pos):
        """Find the next empty position to move in the direction of the destination."""
        n_steps, path = self.path_finder(curr_pos, dest_pos)  # Handles non-empty locations.
        # NOTE: We cannot find a valid path to the destination when:
        #   1) The destination has an another robot located inside it, which also occurs when curr_pos and
        #       dest_pos are the same.
        #   2) The path is entirely blocked.
        #   In these cases we return the next position to be the curr_pos, in order to wait until things
        #   clear up.
        if n_steps is None or n_steps <= 0:  # No valid path to destination
            next_pos = curr_pos
            print("[MOVE] Warning: No path to destination from {} --> {}".format(curr_pos, dest_pos))
        # This mean there's a valid path to destination.
        else:
            # index 0, is the curr_pos, index 1 is the next position.
            next_pos = path[1]
        return next_pos

    def find_next_position_for_random_walk(self, curr_pos):
        """Find a valid location for a robot to just randomly walk into."""
        def is_pos_empty(pos):
            """A sub function if a cell is empty for random walking."""
            if self.grid.is_cell_empty(pos) and pos not in self.dept_positions:
                return True
            else:
                return False
        neighborhood = self.grid.get_neighborhood(curr_pos, moore=True)
        empty_neighborhood = [n for n in neighborhood if is_pos_empty(n)]
        if len(empty_neighborhood) > 0:
            next_index = np.random.randint(len(empty_neighborhood))
            next_pos = empty_neighborhood[next_index]
        else:
            next_pos = curr_pos
        return next_pos

    def visualize(self):
        """A chess board type visualization."""
        def heatmap(a):
            cMap = ListedColormap(['grey', 'black', 'green', 'orange', 'red', 'blue'])
            sns.heatmap(a, vmin=0, vmax=6, cmap=cMap, linewidths=1)
            plt.pause(0.15)
            plt.clf()

        g = np.zeros((self.grid.height, self.grid.width), dtype=int)
        g[self.store.pos] = 3
        g[self.machine.pos] = 4
        g[self.packaging.pos] = 5
        for robot in self.scheduler.agents:
            if robot.destination is None:
                g[robot.pos] = 1
            else:
                g[robot.pos] = 2

        heatmap(g)
Example #8
0
class CHModel(Model):
    """A model with some number of agents."""
    def __init__(self, width, height, random_n = 0, cow_n = 0, plan_n = 0, mc_n = 0, td_n = 0, episode_number = 0, t_mc_n = 0, old_Q_values = None):
        self.running = True 
        #self.num_agents = N
        self.grid = SingleGrid(width, height, True)
        self.schedule = RandomActivation(self)
        
        
        self.id_count = 0 #to assign each agent a unique ID
        #self.max_timesteps = 500 #max timesteps for each episode
        
        # To keep score
        self.total_cow_count = 0.0
        self.current_cow_count = 0.0
        self.score = 0.0
        self.previous_cow_count = 0.0
        
        # Save model for agent use
        self.wallLocations = [(1,5), (1,6), (1,7), (2,7), (3,7), (4,7), (5,7), (6,7), (6,6), (6,5)]
        self.goalState = [(2,5), (3,5), (4,5), (5,5), (2,6), (3,6), (4,6), (5,6)]
        self.goalTarget = (3,5) #corral "entrance" that plan agents herd towards
        self.state = None # encode state at each timestep
        
        self.number_random_agents = random_n
        self.number_cow_agents = cow_n
        self.number_plan_agents = plan_n
        self.number_monte_carlo_agents = mc_n
        self.number_td_agents = td_n
        self.number_trained_mc_agents = t_mc_n
        
        
        # load pre-trained data to add to or make new Q tables for MC Agents
        # set to false to make a new Q table
        #loadpretrained = True
        #if (loadpretrained and (not old_Q_values)):
        #    print("loading pkl file")
        #    with open('mc_q_save.pkl', 'rb') as file:
        #        self.Q_values = dill.load(file)
        
        
        # Monte Carlo Agent model save
        self.Q_table_sharing = True ## If true, agents share a Q table
        self.vision_range = 2 # How far the MC agents can see
        
        if old_Q_values: #load previous Q tables if they exist
            self.Q_values = old_Q_values
        else:
            self.Q_values = [] #no previous Q tables, so make new ones
            if (self.Q_table_sharing):
                # Just one Q table  
                self.Q_values.append(defaultdict(lambda: np.zeros(len(rl_methods.action_space))))
            else:
                #every agent gets it's own Q table
                for agent in range(self.number_monte_carlo_agents):
                    self.Q_values.append(defaultdict(lambda: np.zeros(len(rl_methods.action_space))))
        self.mc_agents = []
        
        self.episode = episode_number
        #calculate episilon based on episode
        #epsilon = 1 / i_episode
        ####### tweak episilon to get better results #######
        self.epsilon = 1.0/((episode_number/800) + 1)
        #self.epsilon = 1.0/((episode_number/8000)+1)

        
        # Place wall agents
        for i in range(len(self.wallLocations)):
            a = WallAgent(self.id_count, self)
            self.id_count += 1
            self.schedule.add(a)
            #print("placing ", a, " at ", self.corralLocations[i])
            self.grid.place_agent(a, self.wallLocations[i])
            
        # Place random agents
        for i in range(self.number_random_agents):
            a = RandomAgent(self.id_count, self)
            self.id_count += 1
            self.schedule.add(a)
            cell_location = self.grid.find_empty()
            self.grid.place_agent(a, cell_location)
            
        # Place cow agents
        for i in range(self.number_cow_agents):
            c = CowAgent(self.id_count, self)
            self.id_count += 1
            self.schedule.add(c)
            #self.cow_agent_list.append(c) #make a list of cows
            cell_location = self.grid.find_empty()
            self.grid.place_agent(c, cell_location)

        # Place plan agents
        for i in range(self.number_plan_agents):
            p = PlanAgent(self.id_count, self)
            self.id_count += 1
            self.schedule.add(p)
            cell_location = self.grid.find_empty()
            self.grid.place_agent(p, cell_location)
            
        # Place monte carlo agents
        for i in range(self.number_monte_carlo_agents):
            Q_table_to_use = None
            if (self.Q_table_sharing): # If sharing Q tables, everyone gets a copy of the same Q table 
                Q_table_to_use = self.Q_values[0]
            else:
                Q_table_to_use = self.Q_values[i] # If not sharing, everyone gets a different Q table
            m = MonteCarloAgent(self.id_count, self, Q_table_to_use, self.epsilon, vision = self.vision_range) # init MC agents with previous Q tables
            self.mc_agents.append(m) # save MC agents to retrieve Q values
            self.id_count += 1
            self.schedule.add(m)
            cell_location = self.grid.find_empty()
            self.grid.place_agent(m, cell_location)
        
        # Place trained monte carlo agents
        # open/load trained Q table
        if (self.number_trained_mc_agents > 0):
            loaded_Q = None
            with open('mc_q_save.pkl', 'rb') as file:
                loaded_Q = dill.load(file)
            if loaded_Q:
                for i in range(self.number_trained_mc_agents):
                    tm = TrainedMonteCarloAgent(self.id_count, self, loaded_Q, vision = self.vision_range)
                    self.id_count += 1
                    self.schedule.add(tm)
                    cell_location = self.grid.find_empty()
                    self.grid.place_agent(tm, cell_location)
            else:
                print("Can't load Q table for trained MC Agents")
            
        # Place TD agents
        for i in range(self.number_td_agents):
            t = TDAgent(self.id_count, self)
            self.id_count += 1
            self.schedule.add(t)
            cell_location = self.grid.find_empty()
            self.grid.place_agent(t, cell_location)
            

            
    def step(self):
        self.state = rl_methods.encode_state(self.grid)
        self.schedule.step()
        self.update_score()
        
        #print(np.matrix(self.state))
        #print("the current score is ", self.score)
        
        # Update rewards of Monte Carlo agents
        
        rewards_type = 3
        # if rewards_type is 
        ###1 use the actual current score
        ###2 use number of cows in goal
        ###3 cows in goal with penalty if cow leaves goal
        
        # how penalized do you want the agents to be for letting cow escape?
        penalty_modifier = 0.0
        # how much of a bonus for getting cows to go in the goal?
        bonus_modifier = 100.0
        # bonus for keeping cows in goal
        bonus_cows = 5.0
        
        for mcagent in self.mc_agents:
            if (rewards_type == 1):
                mcagent.update_rewards(self.score)
            elif (rewards_type == 2):
                mcagent.update_rewards(self.current_cow_count)
            elif (rewards_type == 3):
                penalty = 0.0
                bonus = 0.0
                no_cow_penalty = -1.0
                if (self.current_cow_count < self.previous_cow_count):
                    print("calculating penalty ESCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPE")
                    cows_escaped = (float(self.previous_cow_count) - float(self.current_cow_count))
                    #print("this many escaped: ", cows_escaped, ", modifier: ", penalty_modifier)
                    penalty = penalty_modifier * cows_escaped
                    #print("prev cows ", self.previous_cow_count,  ", cows ", self.current_cow_count,  ", penalty ", penalty)
                if (self.current_cow_count > self.previous_cow_count):
                    print("calculating penalty COWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW")
                    cows_gained = (float(self.current_cow_count) - float(self.previous_cow_count))
                    #print("this many escaped: ", cows_escaped, ", modifier: ", penalty_modifier)
                    bonus = bonus_modifier * cows_gained
                if (self.current_cow_count < self.number_cow_agents):
                    penalty = penalty - (no_cow_penalty * (float(self.number_cow_agents) - float(self.current_cow_count)))
                mcagent.update_rewards((self.current_cow_count * bonus_cows) - penalty + bonus)
                print("current cow count: ", self.current_cow_count, ", penalty: ", penalty, ", bonus: ", bonus, ", no cow ")
                print("total reward: ", (self.current_cow_count * bonus_cows) - penalty + bonus)
            else:
                printing("using default reward")
                mcagent.update_rewards(self.score)

    def update_score(self):
        self.previous_cow_count = self.current_cow_count
        self.current_cow_count = cow_methods.cows_in_goal(self, self.goalState)
        self.total_cow_count += self.current_cow_count
        print(self.total_cow_count, self.current_cow_count, self.schedule.time, " Episode: ", self.episode)
        self.score = self.total_cow_count / self.schedule.time
        
    def get_new_Q_values(self):
        """ Update model Q values at the end of the episode, called by run after each episode """
        new_Q = []
        
        if(self.Q_table_sharing): #If all agents are sharing Q table data
            updated_Q = None
            for agent in self.mc_agents:
                # Update the Q table then pass it on to the next agent on the team to update
                updated_Q = agent.Q_table_update(shared_Q_table = updated_Q) 
            new_Q.append(copy.deepcopy(updated_Q))
        else:
            # If all agents have their own Q tables, update and save for next episode
            for agent in self.mc_agents:
                updated_Q = agent.Q_table_update()
                new_Q.append(copy.deepcopy(updated_Q))
        return new_Q
class ReactionDiffusionModel(Model):
    """A model with some number of agents."""

    #Initialize a model that includes a grid of side-length N and one agent for each grid
    def __init__(self, N):
        #Number of agents
        self.num_agents = N * N

        #The two grids can have just one agent per cell, it is dimensions NxN, and it is toroidal
        self.oldActivatorGrid = SingleGrid(N, N, True)
        self.oldInhibitorGrid = SingleGrid(N, N, True)
        self.currentActivatorGrid = SingleGrid(N, N, True)
        self.currentInhibitorGrid = SingleGrid(N, N, True)

        #Determine how our model will pick agent to interact with
        self.schedule = RandomActivation(self)

        # Create agents
        for i in range(self.num_agents):
            #Initialize a cell with uniqueID = i
            a = Cell(i, self)

            #Add our agent to our scheduler
            self.schedule.add(a)

            #Choose a random, unoccupied cell in our grid and add our agent to it
            #position_agent stores the x and y value for each of our agents
            locationTuple = self.oldActivatorGrid.find_empty()
            if (locationTuple) == (N / 2, N / 2):
                a.act = 2 * a.act
            self.oldActivatorGrid.place_agent(a, locationTuple)
            self.oldInhibitorGrid.place_agent(a, locationTuple)
            self.currentActivatorGrid.place_agent(a, locationTuple)
            self.currentInhibitorGrid.place_agent(a, locationTuple)

    #Method to get activator values in our current activator grid
    def getActivatorGrid(self):
        activator_Grid = np.zeros((self.currentActivatorGrid.width,
                                   self.currentActivatorGrid.height))
        for cell in model.currentActivatorGrid.coord_iter():
            cell_content, x, y = cell
            activator_Grid[x][y] = cell_content.act
        return activator_Grid

    #Method to get inhibitor values in our current inhibitor grid
    def getInhibitorGrid(self):
        inhibitor_Grid = np.zeros((self.currentInhibitorGrid.width,
                                   self.currentInhibitorGrid.height))
        for cell in model.currentInhibitorGrid.coord_iter():
            cell_content, x, y = cell
            inhibitor_Grid[x][y] = cell_content.inh
        return inhibitor_Grid

    def step(self):
        #Determine what the original activator and inhibitor distributions are
        oldActivatorGrid = self.currentActivatorGrid
        oldInhibitorGrid = self.currentInhibitorGrid

        #Perform a step of the model, where we calculate all of the new concentrations
        self.schedule.step()

        #Determine the new activator and inhibitor distributions
        currentActivatorGrid = self.getActivatorGrid()
        currentInhibitorGrid = self.getInhibitorGrid()
Example #10
0
class DiseaseModel(Model):
    """
    A model with some number of agents.
    highS: Number of agents with high sociability.
    middleS: Number of agents with middle sociability.
    lowS: Number of agents with low sociability.
    width: Width of the grid.
    height: Height of the grid.
    edu_setting: If true, agents will follow a schedule and sit in classrooms,
    else they will move freely through an open grid.
    cureProb: Probability of agent getting better.
    cureProbFac: Factor of cureProb getting higher.
    mutateProb: Probability of a disease mutating.
    diseaseRate: Rate at which the disease spreads.
    """
    def __init__(self, highS, middleS, lowS, width, height, edu_setting=True,
                 cureProb=0.1, cureProbFac=2/1440, mutateProb=0.0050,
                 diseaseRate=0.38):
        super().__init__()
        self.num_agents = highS + middleS + lowS
        self.lowS = lowS
        self.middleS = middleS
        self.highS = highS
        self.initialCureProb = cureProb
        self.cureProbFac = cureProbFac
        self.mutateProb = mutateProb
        self.diseaseRate = diseaseRate
        self.edu_setting = edu_setting
        self.maxDisease = 0  # amount of mutations
        self.counter = 540  # keeps track of timesteps
        self.removed = []
        self.exit = (width - 1, floor(height / 2))
        # Check if agents fit within grid
        if self.num_agents > width * height:
            raise ValueError("Number of agents exceeds grid capacity.")

        # Create grid with random activation
        self.grid = SingleGrid(width, height, True)
        self.schedule = RandomActivation(self)

        if edu_setting:
            # Create walls
            numberRooms = 3
            self.add_walls(numberRooms, width, height)

            self.midWidthRoom = floor(width / numberRooms / 2)
            self.midHeightRoom = floor(height / numberRooms / 2)
            self.widthRoom = floor(width / numberRooms)
            self.heightRoom = floor(height / numberRooms)
            numberRows = floor((self.heightRoom) / 2)
            widthRows = self.widthRoom - 4
            location = [[] for _ in range(numberRooms * 2)]
            for i in range(numberRooms):
                for j in range(0, numberRows, 2):
                    startWidth = 2 + (i % 3) * self.widthRoom
                    for currentWidth in range(widthRows):
                        location[i] += [(startWidth + currentWidth, j)]
            for i in range(3, numberRooms * 2):
                for j in range(0, numberRows, 2):
                    startWidth = 2 + (i % 3) * self.widthRoom
                    for currentWidth in range(widthRows):
                        location[i] += [(startWidth + currentWidth,
                                         height - 1 - j)]

            # Set 3 goals per roster
            self.roster = [[location[0], location[3], location[1]],
                           [location[5], location[2], location[0]],
                           [location[4], location[1], location[5]]]

        # Create agents
        self.addAgents(lowS, 0, 0)
        self.addAgents(middleS, lowS, 1)
        self.addAgents(highS, lowS + highS, 2)

        # set up data collecter
        self.datacollector = DataCollector(
            model_reporters={"diseasepercentage": disease_collector},
            agent_reporters={"disease": "disease"})

    def heuristic(self, start, goal):
        """
        Returns manhattan distance.
        start: current location (x,y)
        goal: goal location (x,y)
        """
        dx = abs(start[0] - goal[0])
        dy = abs(start[1] - goal[1])
        return dx + dy

    def get_vertex_neighbors(self, pos):
        """
        Returns all neighbors.
        pos: current position
        """
        n = self.grid.get_neighborhood(pos, moore=False)
        neighbors = []
        for item in n:
            if not abs(item[0] - pos[0]) > 1 and not abs(item[1] - pos[1]) > 1:
                neighbors += [item]
        return neighbors

    def move_cost(self, location):
        """
        Return the cost of a location.
        """
        if self.grid.is_cell_empty(location):
            return 1  # Normal movement cost
        else:
            return 100  # Very difficult to go through walls

    def add_walls(self, n, widthGrid, heightGrid):
        """
        Add walls in grid.
        n: number of rooms horizontally
        widthGrid: width of the grid
        heightGrid: height of the grid
        """
        widthRooms = floor(widthGrid / n)
        heightRooms = floor(heightGrid / n)
        heightHall = heightGrid - 2 * heightRooms
        # Add horizontal walls
        for i in range(n - 1):
            for y in range(heightRooms):
                brick = wall(self.num_agents, self)
                self.grid.place_agent(brick, ((i + 1) * widthRooms, y))
                self.grid.place_agent(brick, ((i + 1) * widthRooms, y +
                                      heightRooms + heightHall))
        doorWidth = 2
        # Add vertical walls
        for x in range(widthGrid):
            if (x % widthRooms) < (widthRooms - doorWidth):
                brick = wall(self.num_agents, self)
                self.grid.place_agent(brick, (x, heightRooms))
                self.grid.place_agent(brick, (x, heightRooms + heightHall - 1))

    def addAgents(self, n, startID, sociability):
        """
        Add agents with a sociability.
        n: number of agents
        startID: ID of the first added agent
        sociability: sociability of the agents
        """
        disease_list = np.random.randint(0, 2, n)
        for i in range(n):
            # Set schedule for every agent if educational setting
            if self.edu_setting:
                a_roster = []
                rosterNumber = self.random.randrange(len(self.roster))
                rooms = self.roster[rosterNumber]
                for roomNumber in range(len(rooms)):
                    loc = self.random.choice(rooms[roomNumber])
                    a_roster += [loc]
                    (self.roster[rosterNumber][roomNumber]).remove(loc)
            else:
                a_roster = []

            a = DiseaseAgent(i + startID, sociability, self, disease_list[i],
                             a_roster)
            self.schedule.add(a)
            # Set agent outside grid, ready to enter, if edu setting
            # else randomly place on empty spot on grid
            if self.edu_setting:
                self.removed += [a]
                a.pos = None
            else:
                self.grid.place_agent(a, self.grid.find_empty())

    def step(self):
        """
        Continue one step in simulation.
        """
        self.counter += 1
        self.datacollector.collect(self)
        self.schedule.step()
Example #11
0
class DiseaseModel(Model):
	"""
	A model with some number of agents.
	highS: Number of agents with high sociability.
	middleS: Number of agents with middle sociability.
	lowS: Number of agents with low sociability.
	width: Width of the grid.
	height: Height of the grid.
	edu_setting: Classrooms and set schedule if true, else random free movement.
	cureProb: Probability of agent getting better.
	cureProbFac: Factor of cureProb getting higher.
	mutateProb: Probability of a disease mutating.
	diseaseRate: Rate at which the disease spreads.
	"""
	def __init__(self, highS, middleS, lowS, width, height, edu_setting=True, cureProb=0.1, cureProbFac=2/1440, mutateProb=0.0050, diseaseRate=0.38):
		super().__init__()
		self.num_agents = highS + middleS + lowS
		self.lowS = lowS
		self.middleS = middleS
		self.highS = highS
		self.initialCureProb = cureProb
		self.cureProbFac = cureProbFac
		self.mutateProb = mutateProb
		self.diseaseRate = diseaseRate
		self.edu_setting = edu_setting
		self.maxDisease = 0# amount of mutations
		self.counter = 540 # keeps track of timesteps
		self.removed = []
		self.exit = (width-1,floor(height/2))
		# Check if agents fit within grid
		if self.num_agents > width * height:
			raise ValueError("Number of agents exceeds grid capacity.")

		# Create grid with random activation
		self.grid = SingleGrid(width, height, True)
		self.schedule = RandomActivation(self)

		if edu_setting:
			# Create walls
			numberRooms = 3
			self.add_walls(numberRooms, width, height)
			self.midWidthRoom = floor(width / numberRooms / 2)
			self.midHeightRoom = floor(height / numberRooms / 2)

			# Calculate the centers of the 6 rooms
			roomLeftDown = (5 * self.midWidthRoom, self.midHeightRoom)
			roomLeftMid = (3 * self.midWidthRoom, self.midHeightRoom)
			roomLeftUp = (self.midWidthRoom, self.midHeightRoom)
			roomRightDown = (5 * self.midWidthRoom, 5 * self.midHeightRoom, )
			roomRightMid = (3 * self.midWidthRoom, 5 * self.midHeightRoom)
			roomRightUp = (self.midWidthRoom, 5 * self.midHeightRoom)

			# Set 3 goals per roster
			self.roster = [[roomLeftDown, roomLeftUp, roomRightMid], [roomRightMid, roomLeftDown, roomRightDown],
							[roomRightUp, roomRightDown, roomLeftUp]]

		# Create agents
		self.addAgents(lowS, 0, 0)
		self.addAgents(middleS, lowS, 1)
		self.addAgents(highS, lowS + highS, 2)

		self.datacollector = DataCollector(
			model_reporters={"diseasepercentage": disease_collector},
			agent_reporters={"disease": "disease"})

	def heuristic(self, start, goal):
		"""
		Returns manhattan distance.
		start: current location (x,y)
		goal: goal location (x,y)
		"""
		dx = abs(start[0] - goal[0])
		dy = abs(start[1] - goal[1])
		return dx + dy

	def get_vertex_neighbors(self, pos):
		"""
		Returns all neighbors.
		pos: current position
		"""
		n = self.grid.get_neighborhood(pos, moore=False)
		neighbors = []
		for item in n:
			if not abs(item[0]-pos[0]) > 1 and not abs(item[1]-pos[1]) > 1:
				neighbors += [item]
		return neighbors

	def move_cost(self, location):
		"""
		Return the cost of a location.
		"""
		if self.grid.is_cell_empty(location):
			return 1 # Normal movement cost
		else:
			return 100

	def add_walls(self, n, widthGrid, heightGrid):
		"""
		Add walls in grid.
		n: number of rooms horizontally
		widthGrid: width of the grid
		heightGrid: height of the grid
		"""
		widthRooms = floor(widthGrid/n)
		heightRooms = floor(heightGrid/n)
		widthHall = widthGrid - 2 * widthRooms
		heightHall = heightGrid - 2 * heightRooms
		# Add horizontal walls
		for i in range(n - 1):
			for y in range(heightRooms):
				brick = wall(self.num_agents, self)
				self.grid.place_agent(brick, ((i + 1) * widthRooms, y))
				self.grid.place_agent(brick, ((i + 1) * widthRooms, y + heightRooms + heightHall))
		doorWidth = 2
		# Add vertical walls
		for x in range(widthGrid):
			if (x % widthRooms) < (widthRooms - doorWidth):
				brick = wall(self.num_agents, self)
				self.grid.place_agent(brick, (x, heightRooms))
				self.grid.place_agent(brick, (x, heightRooms + heightHall - 1))

	def addAgents(self, n, startID, sociability):
		"""
		Add agents with a sociability.
		n: number of agents
		startID: ID of the first added agent
		sociability: sociability of the agents
		"""
		disease_list = np.random.randint(0,2,n)
		for i in range(n):
			a = DiseaseAgent(i + startID, sociability,self,disease_list[i])
			self.schedule.add(a)
			# Add the agent to a random grid cell
			location = self.grid.find_empty()
			self.grid.place_agent(a, location)
	def step(self):
		"""
		Continue one step in simulation.
		"""
		self.counter += 1
		self.datacollector.collect(self)
		self.schedule.step()
Example #12
0
class MondoModel(Model):
    """Questo รจ il mondo fatto a griglia"""
    def __init__(self, popolazione, width, height):
        super().__init__()
        self.popolazione = popolazione
        self.grid = SingleGrid(width, height, True)
        self.schedule = RandomActivation(self)
        self.points = []
        # Create agents
        for i in range(self.popolazione):
            a = PersonAgent(i, self)
            self.schedule.add(a)
            emptyspace = self.grid.find_empty()
            if emptyspace is not None:
                self.grid.place_agent(a, emptyspace)

        paziente_zero = self.schedule.agents[0]
        paziente_zero.virus = Virus(mortalita=20,
                                    tempo_incubazione=3,
                                    infettivita=70)
        paziente_zero.ttl = paziente_zero.virus.tempo_incubazione

    def step(self):
        '''Advance the model by one step.'''
        self.schedule.step()
        suscettibili = 0
        infetti = 0
        morti = 0
        immuni = 0
        for persona in self.schedule.agents:
            if persona.isAlive is False:
                morti += 1
            elif persona.isImmune is True:
                immuni += 1
            elif persona.virus is not None:
                infetti += 1
            else:
                suscettibili += 1
        self.points.append([suscettibili, infetti, morti, immuni])

    def crea_grafico(self):
        global_health_status = np.zeros((self.grid.width, self.grid.height))
        for persona, x, y in self.grid.coord_iter(
        ):  # ctrl+click per spiegare meglio

            if persona is None:
                global_health_status[x][y] = StatoCella.vuoto
            elif persona.isAlive is False:
                global_health_status[x][y] = StatoCella.morto
            elif persona.isImmune is True:
                global_health_status[x][y] = StatoCella.guarito
            elif persona.virus is not None:
                global_health_status[x][y] = StatoCella.infetto
            else:
                global_health_status[x][y] = StatoCella.suscettibile

        cmap = matplotlib.colors.ListedColormap([(0, 0, 0), (1, 0, 0),
                                                 (1, 1, 0), (0, 0, 1),
                                                 (0, 1, 0)])
        img = plt.imshow(global_health_status,
                         interpolation='nearest',
                         vmin=0,
                         vmax=4,
                         cmap=cmap)
        plt.colorbar(img, ticks=[0, 1, 2, 3, 4])
        plt.show()

    def crea_grafico_2(self):
        matplotlib.pyplot.plot(self.points)
        matplotlib.pyplot.show()
Example #13
0
class VariableSchelModel(Model):
    def __init__(self,
                 density,
                 width,
                 height,
                 satisfaction_ratio=[0.5, 0.5],
                 group_count=2,
                 group_pct=[0.5]):
        """
        Schelling's Model with a variable number of 'groups'

        :param density: density of the total number of agents, expressed as a percentage of size of model
        :param width: width of the model
        :param height: height of the model
        :param group_count: number of 'groups'
        :param satisfaction_ratio: satisfaction ratios for the different groups, takes an array of length equivalent
                                    to the number of 'groups'
        :param group_pct: group population size as a percentage of total number of agents, takes an array of length one less
                            than the number of groups. Last group percentage is calculated automatically
        """

        if isinstance(density, str):
            print('string')
        if group_count < 2:
            raise ValueError('Group Count cannot be less than 2!')
        if group_count - 1 != len(group_pct):
            raise ValueError(
                'Group Percentages and Group Count mismatch! '
                'Group percentages should be 1 less than the number of groups')
        if len(satisfaction_ratio) != group_count:
            raise ValueError(
                'Satisfaction ratio should be a list with length equivalent to the group count'
            )

        self.num_agents = int(density * (width * height))
        self.running = True
        self.grid = SingleGrid(width, height, True)
        self.schedule = RandomActivation(self)
        self.satisfaction_ratio = satisfaction_ratio
        self.reached_equilibrium = False
        self.agents = []
        self.ratio_sum = 0
        self.lowest_ratio = 1
        self.mean_ratio = 0

        id_offset = 0
        for group in range(group_count):
            if group == group_count - 1:
                # Final count (Percentage isn't explicit, have to calculate,i.e., group_pct length doesnt match ratio)
                pct = 1 - sum(group_pct)
                for i in range(id_offset,
                               id_offset + int(pct * self.num_agents)):
                    a = SchelAgent(i, self, group)
                    self.agents.append(a)
                    id_offset += 1
                    self.schedule.add(a)
                    self.grid.place_agent(a, self.grid.find_empty())
            else:
                for i in range(
                        id_offset,
                        id_offset + int(group_pct[group] * self.num_agents)):
                    a = SchelAgent(i, self, group)
                    self.agents.append(a)
                    id_offset += 1
                    self.schedule.add(a)
                    self.grid.place_agent(a, self.grid.find_empty())

        self.data_collector = DataCollector(model_reporters={
            "mean ratio value":
            lambda model: model.mean_ratio,
            "lowest ratio value":
            lambda model: model.lowest_ratio
        },
                                            agent_reporters={
                                                "coordinates":
                                                lambda agent: agent.pos,
                                                "group":
                                                lambda agent: agent.group,
                                                "satisfaction":
                                                lambda agent: agent.satisfied,
                                                "agent ratio value":
                                                lambda agent: agent.ratio
                                            })
        self.data_collector.collect(self)

    def step(self):
        self.ratio_sum = 0
        self.lowest_ratio = 1.0
        self.reached_equilibrium = True
        self.schedule.step()
        self.mean_ratio = self.ratio_sum / self.num_agents
        self.data_collector.collect(self)
        # Debugging for server charts
        # print(self.mean_ratio)
        # print(self.lowest_ratio)

        # Stops model if model reached equilibrium
        self.running = False if self.reached_equilibrium is True else True
Example #14
0
class CHModel(Model):
    """A model with some number of agents."""
    def __init__(self,
                 width,
                 height,
                 random_n=0,
                 cow_n=0,
                 plan_n=0,
                 mc_n=0,
                 td_n=0,
                 episode_number=0,
                 t_mc_n=0,
                 old_Q_values=None):
        self.running = True
        #self.num_agents = N
        self.grid = SingleGrid(width, height, True)
        self.schedule = RandomActivation(self)

        self.id_count = 0  #to assign each agent a unique ID
        self.done = False  # if all cows are herded, set to true

        # To keep score
        self.total_cow_count = 0.0
        self.current_cow_count = 0.0
        self.score = 0.0
        self.previous_cow_count = 0.0

        # Save model for agent use
        self.wallLocations = [(1, 5), (1, 6), (1, 7), (2, 7), (3, 7), (4, 7),
                              (5, 7), (6, 7), (6, 6), (6, 5)]
        self.goalState = [(2, 5), (3, 5), (4, 5), (5, 5), (2, 6), (3, 6),
                          (4, 6), (5, 6)]
        self.back_of_goal = [(2, 6), (3, 6), (4, 6), (5, 6)]
        self.front_of_goal = [(2, 5), (3, 5), (4, 5), (5, 5)]
        self.goalTarget = (3, 5
                           )  #corral "entrance" that plan agents herd towards
        self.state = None  # encode state at each timestep

        self.number_random_agents = random_n
        self.number_cow_agents = cow_n
        self.number_plan_agents = plan_n
        self.number_monte_carlo_agents = mc_n
        self.number_td_agents = td_n
        self.number_trained_mc_agents = t_mc_n

        # Monte Carlo Agent model save
        self.Q_table_sharing = True  ## If true, agents share a Q table
        self.vision_range = 2  # How far the MC agents can see

        if old_Q_values:  #load previous Q tables if they exist
            self.Q_values = old_Q_values
        else:
            self.Q_values = []  #no previous Q tables, so make new ones
            if (self.Q_table_sharing):
                # Just one Q table
                self.Q_values.append(
                    defaultdict(
                        lambda: np.zeros(len(rl_methods.action_space))))
            else:
                #every agent gets it's own Q table
                for agent in range(self.number_monte_carlo_agents):
                    self.Q_values.append(
                        defaultdict(
                            lambda: np.zeros(len(rl_methods.action_space))))
        self.mc_agents = []

        self.episode = episode_number
        #calculate episilon based on episode
        #epsilon = 1 / i_episode
        ####### tweak episilon to get better results #######
        self.epsilon = 1.0 / ((episode_number / 800) + 1)
        #self.epsilon = 1.0/((episode_number/8000)+1)

        # Place wall agents
        for i in range(len(self.wallLocations)):
            a = WallAgent(self.id_count, self)
            self.id_count += 1
            self.schedule.add(a)
            #print("placing ", a, " at ", self.corralLocations[i])
            self.grid.place_agent(a, self.wallLocations[i])

        # Place random agents
        for i in range(self.number_random_agents):
            a = RandomAgent(self.id_count, self)
            self.id_count += 1
            self.schedule.add(a)
            cell_location = self.grid.find_empty()
            self.grid.place_agent(a, cell_location)

        # Place cow agents
        for i in range(self.number_cow_agents):
            c = CowAgent(self.id_count, self)
            self.id_count += 1
            self.schedule.add(c)
            #self.cow_agent_list.append(c) #make a list of cows
            cell_location = self.grid.find_empty()
            self.grid.place_agent(c, cell_location)

        # Place plan agents
        for i in range(self.number_plan_agents):
            p = PlanAgent(self.id_count, self)
            self.id_count += 1
            self.schedule.add(p)
            cell_location = self.grid.find_empty()
            self.grid.place_agent(p, cell_location)

        # Place monte carlo agents
        for i in range(self.number_monte_carlo_agents):
            Q_table_to_use = None
            if (
                    self.Q_table_sharing
            ):  # If sharing Q tables, everyone gets a copy of the same Q table
                Q_table_to_use = self.Q_values[0]
            else:
                Q_table_to_use = self.Q_values[
                    i]  # If not sharing, everyone gets a different Q table
            m = MonteCarloAgent(self.id_count,
                                self,
                                Q_table_to_use,
                                self.epsilon,
                                vision=self.vision_range
                                )  # init MC agents with previous Q tables
            self.mc_agents.append(m)  # save MC agents to retrieve Q values
            self.id_count += 1
            self.schedule.add(m)
            cell_location = self.grid.find_empty()
            self.grid.place_agent(m, cell_location)

        # Place trained monte carlo agents
        # open/load trained Q table
        if (self.number_trained_mc_agents > 0):
            loaded_Q = None
            with open('mc_q_save.pkl', 'rb') as file:
                loaded_Q = dill.load(file)
            if loaded_Q:
                for i in range(self.number_trained_mc_agents):
                    tm = TrainedMonteCarloAgent(self.id_count,
                                                self,
                                                loaded_Q,
                                                vision=self.vision_range)
                    self.id_count += 1
                    self.schedule.add(tm)
                    cell_location = self.grid.find_empty()
                    self.grid.place_agent(tm, cell_location)
            else:
                print("Can't load Q table for trained MC Agents")

        # Place TD agents
        for i in range(self.number_td_agents):
            t = TDAgent(self.id_count, self)
            self.id_count += 1
            self.schedule.add(t)
            cell_location = self.grid.find_empty()
            self.grid.place_agent(t, cell_location)

    def step(self):
        self.state = rl_methods.encode_state(self.grid)
        self.schedule.step()
        self.update_score()

        # Update rewards of Monte Carlo agents

        reward = 0.0
        cows_in_goal = cow_methods.cows_in_goal(self, self.goalState)

        # if a cow goes into the goal, give a huge bonus and stop the cow
        if (cows_in_goal > self.previous_cow_count):
            print("New cow in the goal:", cows_in_goal)
            reward = 50
        else:
            reward = -1.0

        print("cows in goal: ", cows_in_goal, ", previous_cow_count: ",
              self.previous_cow_count, " reward: ", reward)
        for mcagent in self.mc_agents:
            mcagent.update_rewards(reward)

        if (cows_in_goal == self.number_cow_agents):
            print(
                "all cows herded in model!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
            )
            self.done = True

    def update_score(self):
        self.previous_cow_count = self.current_cow_count
        self.current_cow_count = cow_methods.cows_in_goal(self, self.goalState)
        self.total_cow_count += self.current_cow_count
        print(self.total_cow_count, self.current_cow_count, self.schedule.time,
              " Episode: ", self.episode)
        self.score = self.total_cow_count / self.schedule.time

    def get_new_Q_values(self):
        """ Update model Q values at the end of the episode, called by run after each episode """
        new_Q = []

        if (self.Q_table_sharing):  #If all agents are sharing Q table data
            updated_Q = None
            for agent in self.mc_agents:
                # Update the Q table then pass it on to the next agent on the team to update
                updated_Q = agent.Q_table_update(shared_Q_table=updated_Q)
            new_Q.append(copy.deepcopy(updated_Q))
        else:
            # If all agents have their own Q tables, update and save for next episode
            for agent in self.mc_agents:
                updated_Q = agent.Q_table_update()
                new_Q.append(copy.deepcopy(updated_Q))
        return new_Q