class Simulation: sheep_list = [] wolf = None turns_number = None sheep_number = None alive_sheep_number = None eaten_sheep_list = [] json_data = [] write_data = Data() wait_character = None def __init__(self, init_pos_limit, sheep_move_dist, wolf_move_dist, sheep_number, turns_number, wait): log = "Info level: Setting simulation details " + str(init_pos_limit) + " " + str(sheep_move_dist) + " " + \ str(wolf_move_dist) + " " + str(sheep_move_dist) + " " + str(sheep_number) + " " + str(turns_number) + " " logging.info(log) self.turns_number = turns_number self.wolf = Wolf(wolf_move_dist) self.wait_character = wait self.sheep_number = sheep_number self.alive_sheep_number = sheep_number for _ in range(sheep_number): self.sheep_list.append(Sheep(init_pos_limit, sheep_move_dist)) def start_simulation(self): log = "Debug level: start_simulation " logging.debug(log) for i in range(self.turns_number): if self.alive_sheep_number == 0: return self.sheep_move() if self.wolf_attack(): self.wolf_move() print("Tur: " + str(i) + ", wolf position: " + str("{:.3f}".format(self.wolf.x_position)) + " " + str("{:.3f}".format(self.wolf.y_position))) print("Sheep number: " + str(self.alive_sheep_number) + ",eaten sheep: " + str(self.eaten_sheep_list)) self.write_data.add_json_data(i, self.wolf.x_position, self.wolf.y_position, self.alive_sheep_number, self.sheep_list) self.write_data.add_csv_data( [str(i), str(self.alive_sheep_number)]) input_character = "" log = "Info level: End of the round" logging.info(log) while input_character != self.wait_character: input_character = input() def sheep_move(self): log = "Debug level: sheep_move " logging.debug(log) for i in range(self.sheep_number): if self.sheep_list[i].alive: self.sheep_list[i].move() def wolf_move(self): log = "Debug level: wolf_move " logging.debug(log) i = self.closed_sheep() dist = self.sheep_from_wolf_distance(self.sheep_list[i]) delta_x = abs(self.wolf.x_position - self.sheep_list[i].x_position) delta_y = abs(self.wolf.y_position - self.sheep_list[i].y_position) x = (self.wolf.wolf_move_dist * delta_x) / dist y = (self.wolf.wolf_move_dist * delta_y) / dist if self.wolf.x_position > self.sheep_list[i].x_position: x = -x if self.wolf.y_position > self.sheep_list[i].y_position: y = -y log = "Info level: Calculating wolf move" logging.info(log) self.wolf.move(x, y) return def wolf_attack(self): for i in range(self.sheep_number): if self.sheep_list[i].alive: if self.wolf.wolf_move_dist >= \ self.sheep_from_wolf_distance(self.sheep_list[i]): self.alive_sheep_number -= 1 self.eaten_sheep_list.append(i) self.sheep_list[i].alive = False self.wolf.set_wolf_position(self.sheep_list[i].x_position, self.sheep_list[i].y_position) log = "Debug level: wolf_attack " + "False" logging.debug(log) log = "Info level: Wolf can eat sheep" logging.info(log) return False log = "Info level: Wolf can't eat sheep" logging.info(log) log = "Debug level: wolf_attack " + "True" logging.debug(log) return True def closed_sheep(self): dist = 0 sheep = None for i in range(self.sheep_number): if self.sheep_list[i].alive: dist = self.sheep_from_wolf_distance(self.sheep_list[i]) sheep = i break for i in range(self.sheep_number): if self.sheep_list[i].alive: if dist > self.sheep_from_wolf_distance(self.sheep_list[i]): dist = self.sheep_from_wolf_distance(self.sheep_list[i]) sheep = i log = "Info level: Finding the closed sheep " + str(sheep) logging.info(log) log = "Debug level: closed_sheep " + str(sheep) logging.debug(log) return sheep def sheep_from_wolf_distance(self, sheep): log = "Info level: Calculating distance between the closed sheep and wolf " +\ str(sqrt((sheep.x_position - self.wolf.x_position) * (sheep.x_position - self.wolf.x_position) + (sheep.y_position - self.wolf.y_position) * (sheep.y_position - self.wolf.y_position))) logging.info(log) log = "Debug level: sheep_from_wolf_distance " + \ str(sqrt((sheep.x_position - self.wolf.x_position) * (sheep.x_position - self.wolf.x_position) + (sheep.y_position - self.wolf.y_position) * (sheep.y_position - self.wolf.y_position))) logging.debug(log) return sqrt((sheep.x_position - self.wolf.x_position) * (sheep.x_position - self.wolf.x_position) + (sheep.y_position - self.wolf.y_position) * (sheep.y_position - self.wolf.y_position))
def main(): # default params init init_pos_limit = 10.0 epochs = 50 sheep_count = 15 sheep_move_dist = 0.5 wolf_move_dist = 1.0 log_lvl = None # sheep list flock = [] # sheep positions sheep_pos = [] # file data json_data = [] csv_data = [] # path to pos.json and alive.csv files data_dir = os.getcwd() # parse arguments parser = argparse.ArgumentParser() parser.add_argument('-c', '--config', type=file_type, metavar='FILE', help='specify config file with initial values', dest='conf_file') parser.add_argument( '-d', '--dir', type=dir_type, metavar='DIR', help='specify path to catalog that holds generated files', dest='data_dir') parser.add_argument( '-l', '--log', type=int, metavar='LEVEL', help= 'choose level of logs saved in chase.log file: 10: DEBUG, 20: INFO, ' '30: WARNING, 40: ERROR, 50: CRITICAL', dest='log_lvl') parser.add_argument('-r', '--rounds', type=int, metavar='NUM', help='specify max number of iterations', dest='rounds_no') parser.add_argument('-s', '--sheep', type=int, metavar='NUM', dest='sheep_no', help='specify number of sheep in flock') parser.add_argument( '-w', '--wait', action='store_true', dest='wait_flag', help='wait for user at the end of each round to continue') parser.add_argument('-q', '--quiet', action='store_false', dest='quiet_flag', help='do not print info in terminal') args = parser.parse_args() # check optional args wait_flag = args.wait_flag print_flag = args.quiet_flag if args.data_dir: data_dir = os.path.join(data_dir, args.data_dir) if args.log_lvl: for lvl in [10, 20, 30, 40, 50]: if args.log_lvl == lvl: log_lvl = lvl if log_lvl is None: raise ValueError('no such level: ' + str(args.log_lvl)) print(os.path.join(data_dir, 'chase.log')) logging.basicConfig(filename=os.path.join(data_dir, 'chase.log'), filemode='w', level=log_lvl) if args.conf_file: config = configparser.ConfigParser() config.read(args.conf_file) terrain = config['Terrain'] movement = config['Movement'] if float(terrain['InitPosLimit']) < 0: logging.critical( 'ValueError raised: incorrect value in InitPosLimit config file' ) raise ValueError('negative value in config file: InitPosLimit') if float(movement['SheepMoveDist']) < 0: logging.critical( 'ValueError raised: incorrect value in SheepMoveDist config file' ) raise ValueError('negative value in config file: SheepMoveDist') if float(movement['WolfMoveDist']) < 0: logging.critical( 'ValueError raised: incorrect value in WolfMoveDist config file' ) raise ValueError('negative value in config file: WolfMoveDist') init_pos_limit = float(terrain['InitPosLimit']) sheep_move_dist = float(movement['SheepMoveDist']) wolf_move_dist = float(movement['WolfMoveDist']) if args.rounds_no: if args.rounds_no > 0: epochs = args.rounds_no elif log_lvl is not None: logging.warning( 'value passed by -r/--rounds is incorrect, continuing with default values' ) if args.sheep_no: if args.sheep_no > 0: sheep_count = args.sheep_no elif log_lvl is not None: logging.warning( 'value passed by -s/--sheep is incorrect, continuing with default values' ) # animals init wolf = Wolf(wolf_move_dist) for round_no in range(sheep_count): flock.append(Sheep(sheep_move_dist, round_no, init_pos_limit)) sheep_pos.append(flock[round_no].position) # log formatter epoch_log = '[{:2}]\twolf pos.: ({: >7.3f}, {: >7.3f})\tnumber of sheep: {:2}' eaten_log = ' •———— sheep [{:2}] is dead' tmp_str = epoch_log.format(0, wolf.position[0], wolf.position[1], len(flock)) if print_flag: print(tmp_str) logging.info(tmp_str) # wait for user input if wait_flag: input('Press enter to continue: ') # main loop for round_no in range(1, epochs + 1): # move and update sheep for sheep in flock: sheep.move() sheep_pos[sheep.id] = sheep.position # find closest sheep to wolf closest_sheep = flock[0] for sheep in flock: x = fun.calc_euclid_dist(wolf.position, sheep.position) if x < fun.calc_euclid_dist(wolf.position, closest_sheep.position): closest_sheep = sheep wolf.closest_sheep_pos = closest_sheep.position logging.info('sheep closest to wolf found ({})'.format( closest_sheep.position)) # eat or chase sheep if fun.calc_euclid_dist(wolf.position, closest_sheep.position) < wolf.move_dist: tmp_str = eaten_log.format(closest_sheep.id) if print_flag: print(tmp_str) logging.info(tmp_str) flock.remove(closest_sheep) sheep_pos[closest_sheep.id] = None else: wolf.move() logging.info('Wolf is chasing closest sheep') # print epoch summary tmp_str = epoch_log.format(round_no, wolf.position[0], wolf.position[1], len(flock)) if print_flag: print(tmp_str) logging.info(tmp_str) # append data to csv file csv_data.append([round_no, len(flock)]) # append data to json file cloned_sheep_pos_list = sheep_pos[:] json_data.append({ 'round_no': round_no, 'wolf_pos': wolf.position, 'sheep_pos': cloned_sheep_pos_list }) # end loop if no sheep in flock if len(flock) < 1: logging.info('simulation ended: all sheep were eaten') break # wait for user input if wait_flag and input('Press enter to continue: ') == 'exit': logging.info('simulation ended: user stopped the simulation') break # write to json file with open(os.path.join(data_dir, 'pos.json'), mode='w', newline='') as json_file: json_file.write(json.dumps(json_data, indent=4)) logging.info('data written to pos.json') # write to csv file with open(os.path.join(data_dir, 'alive.csv'), mode='w', newline='') as csv_file: writer = csv.writer(csv_file) writer.writerow(['epoch_no', 'living_sheep']) writer.writerows(csv_data) logging.info('data written to alive.csv')