Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from collections import namedtuple
- from collections import OrderedDict
- from enum import Enum
- from matplotlib import pyplot as plt
- import os
- import csv
- import numpy as np
- import copy
- import random
- import time
- from datetime import datetime
- import struct
- import pickle
- import sys
- import levyrandom2
- # test merge
- class slicee:
- """
- Creates an array of start and stop value pairs (with optional step
- values)
- Examples
- --------
- >>> slicee()[0, 1:2, ::5, ...]
- # '(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
- References
- ----------
- .. [#] Python's slice notation. StackOverflow.
- http://stackoverflow.com/questions/509211/explain-pythons-slice-notation
- """
- def __getitem__(self, item):
- return item
- dateStr = time.strftime('%Y-%m-%d')
- #timeStr = time.strftime('%Y-%m-%d_%H_%M_%S')
- timeStr = datetime.utcnow().strftime('%Y-%m-%d%H%M%S%f')[:-3]
- #timeStr1 = datetime.utcnow()
- #timeStr = timeStr1.strftime('%Y-%m-%d%H%M%S%f')[:-3]
- # set model parameters
- # give your data set directory a name
- dataSetName = "Finitefood" #+ dateStr
- #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- n_cells = 10000 #400 # must be square number
- if len(sys.argv) > 2:
- n_bots = int(sys.argv[2]) #169
- else:
- n_bots = 4
- print(type(n_bots))
- #max_food_per_cell = 100
- if len(sys.argv) > 1:
- max_food_per_cell = int(sys.argv[3]) #1
- else:
- max_food_per_cell = 100
- food_cell_value = "max_food_per_cell" # "random", "max_food_per_cell"
- plot_output = True # plots and saves output data
- show_plot = True # displays data during simulation
- #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- trophallaxis = False
- FiniteFood = True
- #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- scavenge = False
- food_map_style = "new_map" # "existing_map", "new_map", "central_map", "density_map"
- bot_map_style = "central_map" # "existing_map", "new_map", "random_map", "central_map"
- maxTimesteps = 5000
- food_tick = 1
- if food_map_style == "density_map": # specify teh density and the number of blocks
- food_map_density = 10 # % of density in each block
- blocks = 1 # number of block in n_cells
- if food_map_style == "central_map": # set dimensions of food patches:
- if len(sys.argv) > 1:
- n_food_cells = int(sys.argv[1]) #1
- else:
- n_food_cells = 100
- if food_map_style == "new_map": # set dimensions of food patches:
- if len(sys.argv) > 1:
- food_offset = int(sys.argv[1]) #1
- else:
- food_offset = 45
- bot_length = n_bots ** (1/2)
- half_bot_length = bot_length/2
- n_cells_length = n_cells ** (1/2)
- mid_n_cells = n_cells_length/2
- bot_edge_plus = mid_n_cells + half_bot_length
- bot_edge_neg = mid_n_cells - half_bot_length
- food_in_edge_plus = bot_edge_plus + food_offset
- food_in_edge_neg = bot_edge_neg - food_offset
- food_out_edge_plus = food_in_edge_plus + food_tick
- food_out_edge_neg = food_in_edge_neg - food_tick
- food_y_range = slicee()[food_out_edge_neg:food_in_edge_plus,
- food_in_edge_plus:food_out_edge_plus,
- food_in_edge_neg:food_out_edge_plus,
- food_out_edge_neg:food_in_edge_neg,]
- food_x_range = slicee()[food_out_edge_neg:food_in_edge_neg,
- food_out_edge_neg:food_in_edge_plus,
- food_in_edge_plus:food_out_edge_plus,
- food_in_edge_neg:food_out_edge_plus,]
- # food_y_range = slicee()[1:2, 4:6,]
- # food_x_range = slicee()[1:2, 4:6,]
- # food_y_range = slicee()[3:7, ]
- # food_x_range = slicee()[3:7, ]
- if bot_map_style == "new_map": # set dimensions of bot groups:
- bot_y_range = slicee()[3:7, ]
- bot_x_range = slicee()[3:7, ]
- if bot_map_style == "random":
- set_number_agents_on_food = True
- n_agents_on_food = 2
- if bot_map_style == "existing_map": # input names of previous data
- bot_map_dir = "Data.07-02-2016_00_25_05_JST"
- bot_map_name = "Data.07-02-2016_00_25_05_JST"
- if food_map_style == "existing_map": # input names of previous data
- food_map_dir = "Data.07-02-2016_00_25_05_JST"
- food_map_name = "Data.07-02-2016_00_25_05_JST"
- BOT_DEFAULTS = OrderedDict([
- ("base_metabolism", 0),
- ("EinPerFeed", 10),
- ("eff_E_conversion", 1),
- ("t_steps_to_charge", 5),
- ("Estored_init", 100),
- ("Estored_max", 100),
- ("Eth_battFull", 0),
- ("Ecost_perStep", 1),
- #("Ecost_perFeed", 1),
- ("senseRange", 1),
- ("scavenge", scavenge),
- ("trophallaxis", trophallaxis),
- ("trophMode", "cautious"), # "equalise", "cautious", "selfless"
- ])
- """dict: Default values for bot construction."""
- SENSING_TARGETS = {
- "neighbour_on_food": 2,
- "neighbour_not_on_food": 1,
- "neighbouring_recruits" : 1,
- "neighbouring_bot" : 1,
- "neighbouring_space": 0,
- }
- """dict: Potential targets that can be sensed."""
- # random seed is random choice.
- # replace with set values for repeated output
- seed_np = np.random.randint(0, 2**32 - 1)
- seed_random = np.random.randint(0, 2**32 - 1)
- np.random.seed(seed_np)
- random.seed(seed_random)
- # the path to where you want to save the data
- dirname = os.path.abspath(os.path.join(
- os.path.dirname( __file__ ),
- '../../..',
- "simulation_results"
- + "/" + "self_energy_robot"
- + "/" + "test11"
- + "/" + "levy3"
- ))
- path = dirname + "/" + dataSetName
- # give each run in the data set a unique name
- dataName = "Bots" + str(n_bots) + "Boundary" + str(food_offset) + "Maxfood" + str(max_food_per_cell) + "_" + timeStr #Maxfood = initial max_food_per_cells
- #print("current data", dataName)
- # directory to store plot data
- os.makedirs(path
- + "/" + dataName
- + "/" + "plot_data")
- # os.makedirs(path + "/"
- # + dataName
- # + "/figures")
- dataTimestep = path \
- + "/" + dataName \
- + "/" + dataName + '_dataTimestep.csv'
- dataSummary = path \
- + "/" + dataName \
- + "/" + dataName + '_dataSummary.csv'
- data_per_timestep = ["count",
- "total_food",
- "food_patches",
- "bots_with_Estored",
- "area_covered",
- "total_steps_taken",
- "max_energy_stored",
- "trophallaxis",
- ]
- count = 1
- trophallaxis_value = 0
- start_time = time.time()
- Location = namedtuple("Location", ("x", "y"))
- class Food(object):
- """
- Maps the distribution of food over the grid space.
- Parameters
- ----------
- food_map_style :
- The name of the method used to construct the food map.
- n_cells : int
- The number of cells with individual values that the food map is
- discretised into.
- max_food_per_cell : int
- The maximum value of a food cell.
- Attributes
- ----------
- n_cells : int
- The number of cells with individual values that the food map is
- discretised into.
- map_size : int
- The length of each side of the food map in grid cells.
- max_food_per_cell : int
- The maximum value of a food cell.
- food_map : array
- The distribution of food across the grid space, food cell discretisation.
- """
- def __init__(self):
- self.food_map_style = food_map_style
- self.n_cells = n_cells
- self.map_size = int(np.sqrt(self.n_cells))
- self.max_food_per_cell = max_food_per_cell
- self.food_map = np.zeros(self.n_cells)
- if food_map_style == "existing_map":
- self.food_map = np.load(food_map_dir
- + "/" + food_map_name
- + "/" + food_map_name
- + "_food.npy")
- map_name = food_map_name
- elif food_map_style == "new_map":
- self._new_map()
- map_name = dataName
- elif food_map_style == "central_map":
- self._central_map()
- map_name = dataName
- elif food_map_style == "density_map":
- self._density_map(food_map_density, blocks)
- map_name = dataName
- food_map_name = path \
- + "/" + dataName \
- + "/" + map_name
- np.save(food_map_name + "_food", self.food_map)
- self.total_food_initial = np.sum(self.food_map)
- self.total_food_cells_initial = len(np.argwhere(self.food_map))
- def _new_map(self):
- self.food_map = np.reshape(self.food_map,
- (self.map_size,self.map_size))
- for y, x in zip(food_y_range, food_x_range):
- self.food_map[y, x] = 1
- self.populate_food_cells()
- def _central_map(self):
- self.food_map = np.reshape(self.food_map,
- (self.map_size, self.map_size))
- _midpoint = int(self.map_size / 2)
- _sideLength = int(np.sqrt(n_food_cells))
- _lower = int(_midpoint - (_sideLength / 2))
- _upper = int(_lower + _sideLength)
- _range = np.arange(_lower, _upper, 1)
- for y in _range:
- for x in _range:
- self.food_map[y, x] = 1
- self.populate_food_cells()
- def _density_map(self, food_map_density, blocks):
- self.food_map = np.reshape(self.food_map,
- (self.map_size, self.map_size))
- self.cells_per_block = int(n_cells / blocks)
- self.blocks_per_side = int(np.sqrt(blocks))
- self.block_size = int(self.map_size / self.blocks_per_side)
- self.n_food_cells = int(self.cells_per_block * food_map_density / 100)
- for col in range(self.blocks_per_side):
- for row in range(self.blocks_per_side):
- self.food_cells = np.random.choice(self.cells_per_block, self.n_food_cells, replace=False)
- for cell in self.food_cells:
- self.food_map[(col * self.block_size) + (cell % self.block_size),
- (row * self.block_size) + (cell // self.block_size)] = 1
- self.populate_food_cells()
- def populate_food_cells(self):
- # food on each cell
- y, x = np.nonzero(self.food_map)
- for X, Y in zip(x, y):
- if food_cell_value == "max_food_per_cell":
- self.food_map[X, Y] = self.max_food_per_cell
- if food_cell_value == "random":
- # set limits
- food_cell_values = range(BOT_DEFAULTS['Ein_perFeed'],
- self.max_food_per_cell + 1,
- BOT_DEFAULTS['Ein_perFeed'])
- self.food_map[X, Y] = random.choice(food_cell_values)
- def __repr__(self):
- return "Food({}, {}, {})".format(
- self.n_cells,
- self.map_size,
- self.max_food_per_cell,)
- def __str__(self):
- return str(self.food_map)
- class Bot(object):
- """
- Robot agents that travel around the grid space, eating the food.
- Parameters
- ----------
- base_metabolism : int
- The base energy consumption per simulation step of each bot
- Eremoved_perFeed : int
- The amount of energy removed from the cell on the food map on which a
- bot is located every time athe bot feeds.
- Estored_init : float
- The amount of stored energy the bots are initialised with.
- Ecost_perStep : int
- The energy consumed by the bot for each step of size = one grid space.
- Eth_explore : int
- The stored energy threshold that prompts bots to change their task from
- storing energy to exploring.
- Eth_store : int
- The stored energy threshold that prompts bots to change their task from
- exploring to storing energy and aggregating bots around the
- source of energy.
- Eth_troph : int
- The stored energy threshold at which the bot begins sharing energy with
- others.
- Eth_battFull : int
- The stored energy threshold at which a bot stops receiving energy during
- trophallaxis.
- Estored_max : int.
- The value of stored energy above which feeding does not increase the
- senseRange, : int
- The thickness of the border around the bot location within which
- cells are considered in sensing operations.
- troph : logical
- Boolean value for whether bots feed each other or not.
- trophMode : string
- The name of the method the bot uses when feeding bots.
- eff_E_conversion : int
- The efficency (maximum = 1) with which energy removed from the food
- map is converted to stored energy.
- t_steps_to_charge : int
- The number of time steps the bot takes for the converted energy to be
- stored.
- Attributes
- ----------
- base_metabolism : int
- The base energy consumption per simulation step of each bot
- Eremoved_perFeed : int
- The amount of energy removed from the cell on the food map on which a
- bot is located every time athe bot feeds.
- Estored_init : float
- The amount of stored energy the bots are initialised with.
- Ecost_perStep : int
- The energy consumed by the bot for each step of size = one grid space.
- Eth_explore : int
- The stored energy threshold that prompts bots to change their task from
- storing energy to exploring.
- Eth_store : int
- The stored energy threshold that prompts bots to change their task from
- exploring to storing energy and aggregating bots around the
- source of energy.
- Eth_troph : int
- The stored energy threshold at which the bot begins sharing energy with
- others.
- Eth_battFull : int
- The stored energy threshold at which a bot stops receiving energy during
- trophallaxis.
- Estored_max : int.
- The value of stored energy above which feeding does not increase the
- senseRange, : int
- The thickness of the border around the bot location within which
- cells are considered in sensing operations.
- troph : logical
- Boolean value for whether bots feed each other or not.
- trophMode : string
- The name of the method the bot uses when feeding bots.
- eff_E_conversion : int
- The efficency (maximum = 1) with which energy removed from the food
- map is converted to stored energy.
- t_steps_to_charge : int
- The number of time steps the bot takes for the converted energy to be
- stored.
- E_stored : float
- The cumulative energy from feeding less the energy consumed.
- location : tuple
- The x, y coordinates of the bot location on the bot map.
- new_location_generator : string
- The name of the method the bot uses to choose a new location on the bot
- map to attempt to move to.
- new_location_generators : dictionary
- The possible new location generators used by the bot.
- target_location : tuple
- The x, y coordinates of the location on the bot map that the bot will
- attempt to move to.
- bots : list
- A list of all the bots that exist.
- sense_kernel : array
- A kernel of values used in bot operations that "sense" the cells
- surrounding the bot, with the footprint of the sensed cells equal to the
- dimensions of the kernel.
- """
- bots = []
- sense_kernel = np.zeros((2 * BOT_DEFAULTS["senseRange"]+ 1) ** 2)
- sense_kernel[(len(sense_kernel) // 2)] = 1
- kernel_size = np.sqrt(len(sense_kernel))
- sense_kernel = np.reshape(sense_kernel, (kernel_size, kernel_size))
- def __init__(self, world, *,
- base_metabolism,
- EinPerFeed,
- eff_E_conversion,
- t_steps_to_charge,
- Estored_init,
- Estored_max,
- Eth_battFull,
- Ecost_perStep,
- #Ecost_perFeed,
- senseRange,
- trophallaxis,
- scavenge,
- trophMode):
- self.base_metabolism = base_metabolism
- self.EinPerFeed = EinPerFeed
- self.Estored_init = Estored_init
- self.Ecost_perStep = Ecost_perStep
- self.Eth_battFull = Eth_battFull
- self.Estored_max = Estored_max
- self.senseRange = senseRange
- self.troph = trophallaxis
- self.scavenge = scavenge
- self.trophMode = trophMode
- self.eff_E_conversion = eff_E_conversion
- self.t_steps_to_charge = t_steps_to_charge
- self.Estored = Estored_init
- self.location = None
- self.new_location_generator = "levy_jump"
- self.target_location = None
- self.bots.append(self)
- self.new_location_generators = {
- "random": self.new_location_random,
- "levy": self.new_location_levy,
- "levy_jump": self.new_location_levy_jump,
- }
- #self.id = None
- #self.levylastdirection = ['0', '0']
- #self.levycumulativedir = [0, 0]
- self.levylastdirection = [0, 0]
- self.levystepsleft = 0
- def sense(self, map, sensing_target):
- """
- Checks if bot has any neighbouring bots/ spaces/ recruits
- Parameters
- ----------
- map : array
- The map whose contents are being sensed.
- sensing_target : string
- The item to search for.
- Returns
- -------
- list[Location]
- The location of all neighbours of the target type.
- """
- # top left hand corner of kernel = i, j
- i = self.location.y - BOT_DEFAULTS["senseRange"]
- j = self.location.x - BOT_DEFAULTS["senseRange"]
- k = np.shape(Bot.sense_kernel)[0]
- neighbours = np.argwhere(map[i:i + k, j:j + k]
- - Bot.sense_kernel ==
- SENSING_TARGETS[sensing_target])
- return [Location(x + j, y + i) for y, x in neighbours]
- def evaluate_neighbours(self, world):
- """
- Parameters
- ----------
- world : object
- The world in which the bots live
- Returns
- -------
- """
- location = self.location
- #print(self.location)
- neighbours = self.sense(np.ma.make_mask(world.bot_map) * 1,
- "neighbouring_bot")
- # print("neighbours", neighbours)
- #
- # print(world.food_map)
- #
- # bot_on_food = bool((np.ma.make_mask(world.food_map) * 1))
- # [location[::-1]])
- #
- # print(bot_on_food)
- #
- # print("on food", bot_on_food)
- if neighbours == []:
- pass
- else:
- return neighbours # neighbours_food
- # CATAGORISES NEIGHOURS BY FOOD VALUE OF CURRENT LOCATION
- # neighbours = {}
- #
- # bot_not_on_food_map = np.ma.make_mask(world.bot_map) * 1 - \
- # np.ma.make_mask(world.food_map) * 1
- #
- # bot_on_food_map = np.ma.make_mask(world.bot_map) * 1 + \
- # np.ma.make_mask(world.food_map) * 1
- #
- # if bot_on_food:
- # neighbours["bot1_neighbour0"] = \
- # self.sense(bot_not_on_food_map, "neighbour_not_on_food")
- #
- # neighbours["bot1_neighbour1"] = \
- # self.sense(bot_on_food_map, "neighbour_on_food")
- #
- # else: # if food cell empty
- #
- # neighbours["bot0_neighbour0"] = \
- # self.sense(bot_not_on_food_map,"neighbour_not_on_food")
- #
- # neighbours["bot0_neighbour1"] = \
- # self.sense(bot_on_food_map, "neighbour_on_food")
- def new_location_random(self, location, world):
- """
- Determines the next space the bot will attempt to move to.
- Parameters
- ----------
- initial_location: named tuple
- The x,y coordinates of position the bot will attempt to move from.
- Returns
- -------
- new_location : name tuple
- The x,y coordinates of next position the bot will attempt to move to.
- """
- new_location = Location(
- np.clip(location.x + random.randint(-1, 1),
- BOT_DEFAULTS["senseRange"],
- world.bot_map_size - 1
- - BOT_DEFAULTS["senseRange"]),
- np.clip(location.y + random.randint(-1, 1),
- BOT_DEFAULTS["senseRange"],
- world.bot_map_size - 1
- - BOT_DEFAULTS["senseRange"]))
- return new_location
- """Levy related functions"""
- """
- def switch_levy_direction(self, direction):
- if direction == '+1':
- new_direction = '-1'
- elif direction == '-1':
- new_direction = '+1'
- return(new_direction)
- def generate_levy_values(self, mode, levycumthreshold):
- if mode == '+':
- axis = random.randint(0, 1)
- # 0: move along x
- # 1: move along y
- if axis == 0:
- new_direction_x = levyrandom.direction(self.levylastdirection[0])
- new_direction_y = '0'
- elif axis == 1:
- new_direction_x = '0'
- new_direction_y = levyrandom.direction(self.levylastdirection[1])
- elif mode == 'x':
- new_direction_x = levyrandom.direction(self.levylastdirection[0])
- new_direction_y = levyrandom.direction(self.levylastdirection[1])
- elif mode == '*':
- axis = random.randint(0, 3)
- # 0: move along x
- # 1: move along y
- # 2,3: move along a diagonal
- if axis == 0:
- new_direction_x = levyrandom.direction(self.levylastdirection[0])
- new_direction_y = '0'
- elif axis == 1:
- new_direction_x = '0'
- new_direction_y = levyrandom.direction(self.levylastdirection[1])
- elif axis > 1:
- new_direction_x = levyrandom.direction(self.levylastdirection[0])
- new_direction_y = levyrandom.direction(self.levylastdirection[1])
- self.levycumulativedir[0] += int(new_direction_x)
- self.levycumulativedir[1] += int(new_direction_y)
- if abs(self.levycumulativedir[0]) > levycumthreshold:
- self.levycumulativedir[0] = 0
- new_direction_x = self.switch_levy_direction(new_direction_x)
- if abs(self.levycumulativedir[1]) > levycumthreshold:
- self.levycumulativedir[1] = 0
- new_direction_y = self.switch_levy_direction(new_direction_y)
- self.levylastdirection = [new_direction_x, new_direction_y]
- return(self.levylastdirection)
- """
- def new_location_levy(self, location, world):
- """
- Determines the next space the bot will attempt to move to.
- Parameters
- ----------
- initial_location: named tuple
- The x,y coordinates of position the bot will attempt to move from.
- Returns
- -------
- new_location : name tuple
- The x,y coordinates of next position the bot will attempt to move to.
- """
- if self.levystepsleft == 0:
- levystep = levywalk.direction()
- new_direction = levystep[0]
- self.levylastdirection = new_direction
- self.levystepsleft = levystep[1] -1
- else:
- new_direction = self.levylastdirection
- self.levystepsleft -= 1
- new_location = Location(
- np.clip(location.x + new_direction[0],
- BOT_DEFAULTS["senseRange"],
- world.bot_map_size - 1
- - BOT_DEFAULTS["senseRange"]),
- np.clip(location.y + new_direction[1],
- BOT_DEFAULTS["senseRange"],
- world.bot_map_size - 1
- - BOT_DEFAULTS["senseRange"]))
- return new_location
- def new_location_levy_jump(self, location, world):
- """
- Determines the next space the bot will attempt to move to.
- Parameters
- ----------
- initial_location: named tuple
- The x,y coordinates of position the bot will attempt to move from.
- Returns
- -------
- new_location : name tuple
- The x,y coordinates of next position the bot will attempt to move to.
- """
- levystep = levyrandom2.direction()
- new_direction = [levystep[0][0] * levystep[1], levystep[0][1] * levystep[1]]
- new_location = Location(
- np.clip(location.x + new_direction[0],
- BOT_DEFAULTS["senseRange"],
- world.bot_map_size - 1
- - BOT_DEFAULTS["senseRange"]),
- np.clip(location.y + new_direction[1],
- BOT_DEFAULTS["senseRange"],
- world.bot_map_size - 1
- - BOT_DEFAULTS["senseRange"]))
- return new_location
- """/Levy related functions"""
- def move(self, world, location):
- """
- Moves the bot and updates the stroed energy accordingly.
- Parameters
- ----------
- new_location : Location
- The location to move to.
- """
- # stop charging
- world.charge_map[location] = 0
- # select a target neighbouring lement address to move to
- new_location = self.new_location_generators[
- str(self.new_location_generator)](
- location,
- world)
- world.bot_map[location[::-1]].update_maps(world,
- new_location,
- location)
- world.Acovered_map += np.copy(np.ma.make_mask(world.bot_map) * 1)
- #world.Acovered_map += np.ma.make_mask(world.bot_map) * 1
- def update_maps(self, world, new_location, initial_location):
- maps_to_update = [world.move_map,
- world.Estored_map,
- world.bot_map]
- for _map in maps_to_update:
- if isinstance(_map[initial_location[::-1]], Bot):
- new_value = _map[initial_location[::-1]]
- else:
- new_value = np.clip((_map[initial_location[::-1]]
- - BOT_DEFAULTS["Ecost_perStep"]),
- 0, float("inf"))
- # DOES NOT MOVE
- if world.bot_map[new_location[::-1]]:
- _map[initial_location[::-1]] = new_value
- # MOVES
- elif world.bot_map[new_location[::-1]] == None:
- _map[new_location[::-1]] = new_value
- # delete initial location
- if isinstance(_map[initial_location[::-1]], Bot):
- _map[initial_location[::-1]] = None
- else:
- _map[initial_location[::-1]] = 0
- def trophallaxis(self, world, neighbours):
- #print("TROPHALAXIS")
- # TODO: create a trophallaxis map of robots engagaed in trophallaxis so
- # that only one exchnage happens per robot per timestep
- global trophallaxis_value
- if world.charge_map[self.location[::-1]] != 0:
- # random.shuffle(neighbours)
- # for neighbour in neighbours:
- neighbour = random.choice(neighbours)
- Eself = world.Estored_map[self.location[::-1]]
- Eneighbour = world.Estored_map[neighbour[::-1]]
- Eaverage = float((Eself + Eneighbour)/2)
- trophallaxis_value += 1
- print(trophallaxis_value, "sharinggggggggggggggggggggggggggggg")
- world.Estored_map[self.location[::-1]] = Eaverage
- world.Estored_map[neighbour[::-1]] = Eaverage
- # E = float(Eself/ 2)
- # world.Estored_map[self.location[::-1]] -= E
- # world.Estored_map[neighbour[::-1]] += E
- else:
- pass
- def __repr__(self):
- return "Bot({})".format(self.location)
- def __str__(self): # real signature unknown
- """ Return str(self). """
- return "x{}_y{}".format(self.location[0],self.location[1])
- class World(object):
- """
- Maps the distribution of food over the grid space.
- Parameters
- ----------
- n_bots : int
- The number of bots
- _food : Food
- The food distribution across the grid space.
- Attributes
- ----------
- n_bots : int
- The number of bots
- _food : object
- The food distribution across the grid space.
- n_cells :
- The number of food cells
- map_size : int
- The length of each side of the food map.
- food_map : np.ndarray
- The distribution of food across the grid space.
- bot_map : np.ndarray
- The distribution of bots across the grid space.
- bot_map_size : int
- The length of each side of the bot map in grid units.
- _bots_on_food : int
- The number of bots located on a cell containing food
- Estored_map : np.ndarray
- The map of cumulative energy stored by each bot.
- Acovered_map : np.ndarray
- A map showing the number of times a grid cell has been entered by a bot.
- trajectory_coords : Dictionary
- Coordinates of start and end point that define robots motion vector
- """
- def __init__(self, food):
- self.n_bots = n_bots
- self._food = food
- self.n_cells = n_cells
- self.map_size = self._food.map_size
- self.food_map, self.food_map_size = self._pad_map(
- self._food.food_map, BOT_DEFAULTS['senseRange'])
- if show_plot == True:
- plt.matshow(self.food_map)
- plt.show()
- self.bot_map = None
- self.bot_map_size = None
- self._populate_bot_map()
- self._bots_on_food = None
- self.Estored_map = np.ma.make_mask(self.bot_map) \
- * BOT_DEFAULTS["Estored_init"]
- self.Acovered_map = np.copy(np.ma.make_mask(self.bot_map) * 1)
- self.move_map = np.zeros_like(self.food_map)
- self.charge_map = np.zeros_like(self.food_map)
- # self.charge_map = np.multiply(np.ma.make_mask(self.bot_map) * 1,
- # np.ma.make_mask(self.food_map) * 1)
- self.trajectory_coords = {"x_preMove" : [],
- "y_preMove" : [],
- "y_postMove": [],
- "x_postMove": [],
- }
- def _pad_map(self, map_to_pad, _border_width):
- """
- Creates a border of width = _border_width around the map as dictated by
- the sense range of the bots. This is to prevent errors generated by
- functions that use elements of a window around each bot, the size of
- which is defined by the robot sense range.
- Parameters
- ----------
- map_to_pad : array
- The map to pad.
- _border_width :
- The border width in grid units
- """
- _full_map_size = int(self.map_size + 2*_border_width)
- if map_to_pad.dtype == object:
- _full_map = np.empty(
- (_full_map_size) ** 2, dtype=object).reshape(_full_map_size,
- _full_map_size)
- else:
- _full_map = np.zeros(
- (_full_map_size) ** 2).reshape(_full_map_size,_full_map_size)
- _full_map[_border_width:(
- _border_width + self.map_size),
- _border_width:(_border_width + self.map_size)] = map_to_pad
- return _full_map, _full_map_size
- def _populate_bot_map(self):
- """
- Distributes n_bots start locations randomly over the grid space
- """
- # TODO: if random seed gives you same starting map, this can be removed
- if bot_map_style == "existing_map":
- self.bot_map = np.load(path
- + "/" + bot_map_dir
- + "/" + bot_map_name + "bots.npy")
- self.bot_map_size = np.shape(self.bot_map)[0]
- bot_map_name = path \
- + "/" + dataName \
- + "/" + bot_map_name
- else:
- if bot_map_style == "new_map":
- self._new_map()
- if bot_map_style == "central_map":
- self._central_map()
- if bot_map_style == "random_map":
- self._random_map()
- if set_number_agents_on_food == True:
- while len(self._bots_on_food) != n_agents_on_food:
- self._random_map()
- # give each bot a location identity
- y, x = np.where(self.bot_map)
- for Y, X in zip(y, x):
- self.bot_map[Y, X].location = Location(X, Y)
- bot_map_name = (path
- + "/" + dataName
- + "/" + dataName)
- np.save(bot_map_name + "_bots", self.bot_map)
- if show_plot == True:
- plt.matshow(np.ma.make_mask(self.bot_map)*1)
- plt.show()
- def _random_map(self):
- self.bot_map = np.empty(self.n_cells, dtype=object)
- for i in range(self.n_bots):
- self.bot_map[i] = Bot(self, **BOT_DEFAULTS)
- np.random.shuffle(self.bot_map)
- self.bot_map = np.reshape(self.bot_map, (self.map_size, self.map_size))
- self.bot_map, self.bot_map_size = self._pad_map(
- self.bot_map, BOT_DEFAULTS['senseRange'])
- self._bots_on_food = np.where(
- (np.ma.make_mask(self.bot_map) * 1 > 0) &
- (self.food_map > 0))
- #print(len(self._bots_on_food))
- def _new_map(self):
- self.bot_map = np.empty(self.n_cells, dtype=object)
- self.bot_map = np.reshape(self.bot_map, (self.map_size, self.map_size))
- for y, x in zip(bot_y_range, bot_x_range):
- self.bot_map[y,x] = Bot(self, **BOT_DEFAULTS)
- self.bot_map, self.bot_map_size = self._pad_map(
- self.bot_map, BOT_DEFAULTS['senseRange'])
- def _central_map(self):
- #print("central")
- self.bot_map = np.empty(self.n_cells, dtype=object)
- self.bot_map = np.reshape(self.bot_map, (self.map_size, self.map_size))
- _midpoint = int(self.map_size/2)
- _sideLength = int(np.sqrt(self.n_bots))
- _lower = int(_midpoint-(_sideLength/2))
- _upper = int(_lower + _sideLength)
- _range = np.arange(_lower, _upper,1)
- for y in _range:
- for x in _range:
- self.bot_map[y,x] = Bot(self, **BOT_DEFAULTS)
- self.bot_map, self.bot_map_size = self._pad_map(
- self.bot_map, BOT_DEFAULTS['senseRange'])
- def save_data_per_timestep(self):
- # log data to data per cycle
- with open(dataTimestep, 'a') as c:
- writer = csv.writer(c)
- writer.writerow([
- # count
- count,
- # total food
- np.sum(self.food_map),
- # number of food patches
- len(np.where(self.food_map)[0]),
- # _bots_with_Estored
- len(np.where(self.Estored_map)[0]),
- # total area covered
- len(np.where(self.Acovered_map)[0]),
- # total steps taken
- np.sum(self.Acovered_map),
- # maximum on board eergy
- np.amax(self.Estored_map),
- # trophallaxis
- trophallaxis_value,
- ])
- def save_data_init(self, food_map):
- """
- Creates a dictionary of the attributes of the World object and the
- default properties of the bots. Saves these and other global data and
- parameters of the World object to a new csv file.
- Defines heaidings of parameters to be recorded at each timestep to a new
- csv file and calculates and saves initial values.
- Parameters
- ----------
- food_map : array
- An attribute to remove from the dictionary.
- """
- attributes = food_map.__dict__
- del attributes["food_map"]
- attributes.update(BOT_DEFAULTS)
- # write data to data summary
- with open(dataSummary, 'w') as s:
- writer = csv.writer(s)
- for key, value in attributes.items():
- writer.writerow([key, value])
- writer.writerow(["n_food_cells_initial",
- len(np.where(self.food_map)[0])])
- writer.writerow(["n_bots", self.n_bots])
- writer.writerow(["FiniteFood", FiniteFood])
- writer.writerow(["food_map_style", food_map_style])
- writer.writerow(["bot_map_style", bot_map_style])
- writer.writerow(["maxTimesteps", maxTimesteps])
- writer.writerow(["seed np:", seed_np])
- writer.writerow(["seed random:", seed_random])
- # setup data per timestep file
- with open(dataTimestep, 'w') as c:
- writer = csv.writer(c)
- writer.writerow(data_per_timestep)
- self.save_data_per_timestep()
- def save_data_food_finished(self):
- """
- Creates a dictionary of the attributes of the World object and the
- default properties of the bots. Saves these and other global data and
- parameters of the World object to a new csv file.
- Defines heaidings of parameters to be recorded at each timestep to a new
- csv file and calculates and saves initial values.
- Parameters
- ----------
- food_map : array
- An attribute to remove from the dictionary.
- """
- lst = ['time_food_finished', "bots_with_Estored_food_finished" ]
- with open(dataSummary, 'rt') as f:
- reader = csv.reader(f, delimiter=',')
- for row in reader:
- print(row)
- for l in lst:
- if row[0] == l:
- print(lst)
- lst.remove(l)
- if lst != []:
- print(lst)
- with open(dataSummary, 'a') as s:
- writer = csv.writer(s)
- writer.writerow(["time_food_finished", count])
- writer.writerow(["bots_with_Estored_food_finished",
- len(np.where(self.Estored_map)[0])])
- def save_data_final(self):
- """
- Creates a dictionary of the attributes of the World object and the
- default properties of the bots. Saves these and other global data and
- parameters of the World object to a new csv file.
- Defines heaidings of parameters to be recorded at each timestep to a new
- csv file and calculates and saves initial values.
- Parameters
- ----------
- food_map : array
- An attribute to remove from the dictionary.
- """
- with open(dataSummary, 'a') as s:
- writer = csv.writer(s)
- writer.writerow(["count", count])
- writer.writerow(["total_food_final", np.sum(self.food_map)])
- writer.writerow(["n_food_cells_final",
- len(np.where(self.food_map)[0])])
- writer.writerow(["bots_with_Estored_final",
- len(np.where(self.Estored_map)[0])])
- writer.writerow(["total_area_covered",
- len(np.where(self.Acovered_map)[0])])
- writer.writerow(["total_steps_taken",
- np.sum(self.Acovered_map)])
- lst = ['time_food_finished', "bots_with_Estored_food_finished"]
- with open(dataSummary, 'rt') as f:
- reader = csv.reader(f, delimiter=',')
- for row in reader:
- print(row)
- for l in lst:
- if row[0] == l:
- print(lst)
- lst.remove(l)
- if lst != []:
- print(lst)
- with open(dataSummary, 'a') as s:
- writer = csv.writer(s)
- # writer.writerow(["time_food_finished"],0)
- # writer.writerow(["bots_with_Estored_food_finished"],0)
- writer.writerow(["time_food_finished"])
- writer.writerow(["bots_with_Estored_food_finished"])
- AcoveredFinal = np.where(self.Acovered_map)
- np.save(path \
- + "/" + dataName \
- + "/" + dataName + '_AcoveredFinal',
- np.where(self.Acovered_map))
- #print("done")
- def plot(self):
- global count
- plot_dir = path \
- + "/" + dataName \
- + "/" + "plot_data"\
- + "/"
- plot_layer_names = ["_food", "_Estored", "_bots", "_bots_loc" ]
- plot_layers = [self.food_map,
- self.Estored_map,
- self.bot_map,
- (np.ma.make_mask(self.bot_map)*1)]
- for name, layer in zip(plot_layer_names, plot_layers):
- y, x = np.where(layer)
- nonzero = zip(y, x)
- with open(plot_dir
- + name
- + str(count).zfill(4)
- + '.txt', 'w+') as outf:
- for r, k in nonzero:
- if name == "_bots":
- outf.write(
- '{:d}{:d}{!s:s}\n'.format(
- r, k, layer[r, k]))
- else:
- outf.write(
- '{:d} {:d} {:g}\n'.format(
- r, k, layer[r, k]))
- for i in self.trajectory_coords:
- with open(plot_dir
- + "_trajectory_coords"
- + str(count).zfill(4)
- + '.csv', 'w') as f:
- writer = csv.writer(f)
- for row in self.trajectory_coords.items():
- writer.writerow(row)
- def feed(self):
- """
- Decreases food map by 1 unit in every grid cell newly occupied by a bot
- Updates the bot charging map with new occupants
- Updates the stored energy map of all charging bots with the energy from
- feeding divided by the number of time steps taken to charge.
- """
- Ein = BOT_DEFAULTS["EinPerFeed"]
- eff = BOT_DEFAULTS["eff_E_conversion"]
- t_steps = BOT_DEFAULTS["t_steps_to_charge"]
- chargePerTimetep = float(Ein * eff / t_steps)
- new_bots_on_food = np.where((np.ma.make_mask(self.bot_map)*1>0)
- & (self.food_map > 0)
- & (self.charge_map == 0))
- if FiniteFood == True:
- self.food_map[new_bots_on_food] -= BOT_DEFAULTS["EinPerFeed"]
- np.clip(self.food_map, 0, float("inf"), self.food_map)
- if BOT_DEFAULTS["scavenge"] == True:
- self.charge(new_bots_on_food, chargePerTimetep)
- def charge(self, new_bots_on_food, chargePerTimetep):
- """
- Updates the stored energy map of all charging bots with the energy from
- feeding divided by the number of time steps taken to charge.
- """
- # self.charge_map[np.where(self.charge_map)] += chargePerTimetep
- #
- # self.charge_map[new_bots_on_food] += chargePerTimetep
- self.charge_map[np.where(self.charge_map)] += 1
- self.charge_map[new_bots_on_food] += 1
- self.move_map = np.multiply(
- self.move_map,
- np.invert(np.ma.make_mask(self.charge_map)) * 1)
- t_steps = BOT_DEFAULTS["t_steps_to_charge"]
- self.Estored_map = np.clip(np.where(self.charge_map,
- self.Estored_map + chargePerTimetep,
- self.Estored_map),
- 0,
- BOT_DEFAULTS["Estored_max"],
- self.Estored_map)
- ### ****PRINT
- # print("charged")
- # print(self.charge_map)
- # print("charged")
- # print(self.charge_map[self.charge_map >= t_steps])
- # print("")
- # print("move")
- # print(self.move_map)
- # print("")
- # print("stored")
- # print(self.Estored_map)
- # print("")
- self.charge_map[self.charge_map >= t_steps] = 0
- def base_metabolism(self):
- """
- """
- np.clip(
- self.Estored_map - BOT_DEFAULTS["base_metabolism"],
- 0, float("inf"), self.Estored_map)
- def functioning_bots(self):
- functioning_bots = []
- incapacitated_bots = self.incapacitated_bots()
- _bots = np.copy(self.bot_map)
- _bots[incapacitated_bots] = None
- _functioning_bots = np.argwhere(_bots)
- for _functioning_bot in _functioning_bots:
- bot = self.bot_map[_functioning_bot[0],
- _functioning_bot[1]]
- functioning_bots.append(bot)
- return functioning_bots
- def incapacitated_bots(self):
- incapacitated_bots = np.where((self.bot_map != None)
- and self.Estored_map == 0)
- return incapacitated_bots
- def store_trajectory(self, preORpost, *initialPosition):
- if preORpost == "pre":
- y, x = np.nonzero(self.move_map)
- y=list(y)
- x=list(x)
- self.trajectory_coords["x_" + preORpost + "Move"].extend(x)
- self.trajectory_coords["y_" + preORpost + "Move"].extend(y)
- else:
- y, x = np.where(self.bot_map == initialPosition)
- self.trajectory_coords["x_" + preORpost + "Move"].append(x[0])
- self.trajectory_coords["y_" + preORpost + "Move"].append(y[0])
- return y,x
- def energy_transfer(self):
- # energy in
- self.feed()
- # energy out
- self.base_metabolism()
- functioning_bots = self.functioning_bots()
- if functioning_bots != []:
- random.shuffle(functioning_bots)
- for bot in functioning_bots:
- neighbours = bot.sense(
- np.ma.make_mask(self.bot_map) * 1,
- "neighbouring_bot")
- neighbours = bot.evaluate_neighbours(self)
- if bool(neighbours) == False:
- pass
- else:
- if BOT_DEFAULTS["trophallaxis"] == True:
- bot.trophallaxis(self, neighbours)
- def movement(self):
- # self.move_map = np.where(
- # self.Estored_map >= BOT_DEFAULTS["Eth_battFull"],
- # self.Estored_map,
- # self.move_map)
- # self.move_map = np.where(self.charge_map >= t_steps,
- # self.Estored_map,
- # self.move_map)
- # print("charge mode")
- # print(self.charge_map)
- # print("charge mode")
- # print(np.invert(self.charge_map))
- self.move_map = np.where(
- (self.Estored_map >= BOT_DEFAULTS["Eth_battFull"]) &
- (self.charge_map == 0),
- self.Estored_map,
- 0)
- # print("move")
- # print(self.move_map)
- # print("")
- # print(self.move_map)
- # print("")
- # print(self.charge_map)
- # print("")
- # print(self.food_map)
- # print("")
- # print("MoveMap")
- # print(self.move_map[45:55 , 45:55] )
- # print("EstoredMap")
- # print(self.Estored_map[45:55 , 45:55] )
- # print("ChargeMap")
- # print(self.charge_map[45:55 , 45:55])
- # self.Estored_map >= BOT_DEFAULTS["Eth_battFull"],
- # self.Estored_map,
- # self.move_map)
- #self.move_map = np.where(np.ma.make_map)
- if self.move_map.any():
- # print("move_map")
- # print(self.move_map)
- # log the initial position of each bot
- pre_move_y, pre_move_x = self.store_trajectory("pre")
- moving_bots = self.bot_map[pre_move_y, pre_move_x]
- # shuffle the bots
- # print(pre_move_y)
- # print(pre_move_x)
- # print("")
- bots_to_shuffle = list(zip(pre_move_y, pre_move_x))
- random.shuffle(bots_to_shuffle)
- pre_move_y, pre_move_x = zip(*bots_to_shuffle)
- # print(pre_move_y)
- # print(pre_move_x)
- # print("bots")
- # print(np.ma.make_mask(self.bot_map) * 1)
- # print("")
- # move all the bots
- for Y, X in zip(pre_move_y, pre_move_x):
- # print(Y,X)
- self.bot_map[Y, X].move(self, Location(X, Y))
- # print(np.ma.make_mask(self.bot_map)*1)
- # log the final position of each bot
- for initPosition in moving_bots:
- post_move_y, post_move_x = \
- self.store_trajectory("post", initPosition)
- # update each Bot instance location attribute to current position.
- self.bot_map[post_move_y[0],
- post_move_x[0]].location \
- = Location(post_move_x[0], post_move_y[0])
- def main():
- global count
- global trophallaxis_value
- # distributes food
- food_map = Food()
- # populate world with agents
- world = World(food_map)
- #print("food_map", world.food_map)
- world.save_data_init(food_map)
- if plot_output:
- world.plot()
- # loop until all bots incapacitated by hunger OR all food removed
- # while (np.any(world.Estored_map) and np.any(world.food_map)):
- while (np.any(world.Estored_map)
- #and np.any(world.food_map)
- and count <= maxTimesteps):
- # print(world.bot_map[45:55, 45:55])
- # print(np.ma.make_mask(world.bot_map[45:55, 45:55]) * 1)
- count += 1
- print("count", count, "=", trophallaxis_value)
- trophallaxis_value = 0
- world.energy_transfer()
- world.movement()
- world.save_data_per_timestep()
- if plot_output:
- world.plot()
- if np.any(world.food_map) == False:
- print("all food eaten")
- world.save_data_food_finished()
- if count >= maxTimesteps:
- print("exceeded maximum timesteps")
- else:
- print("all bots dead")
- world.save_data_final()
- if __name__ == '__main__':
- try:
- main()
- finally:
- print("end")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement