def simulate(batch_file, preset_filename, bot_paths, seed, override_settings, *queues_sizes): result_queue = queues_sizes[0][0] result_queue._internal_size = queues_sizes[0][1] StateHandler.instance.shared_info = { "result_queue": result_queue, } send_queues = [q for q, _ in queues_sizes[1::2]] for i, (_, size) in enumerate(queues_sizes[1::2]): send_queues[i]._internal_size = size recv_queues = [q for q, _ in queues_sizes[2::2]] for i, (_, size) in enumerate(queues_sizes[2::2]): recv_queues[i]._internal_size = size Randomiser.createGlobalRandomiserWithSeed(seed) preset_file = find_abs(preset_filename, allowed_areas=preset_locations()) with open(preset_file, "r") as f: config = yaml.safe_load(f) config["settings"] = config.get("settings", {}) recursive_merge(config["settings"], override_settings) config["robots"] = config.get("robots", []) + bot_paths initialiseFromConfig(config, send_queues, recv_queues)
def initWithKwargs(self, **kwargs): self.in_error = False self.first_launch = True batch = kwargs.get("batch_file", None) self.batch = batch if batch is None: # We are simply viewing the bots to edit or manage. self.bot_keys = [] else: self.key_index = 0 with open(batch, "r") as f: b_config = yaml.safe_load(f) preset = b_config["preset_file"] fname = find_abs(preset, allowed_areas=preset_locations()) with open(fname, "r") as f: p_config = yaml.safe_load(f) self.bot_keys = p_config["bot_names"] self.bot_values = [None] * len(self.bot_keys) self.preview_images = [None] * len(self.bot_keys) self.bot_select_index = 0 self.select_enable = False self.code_enable = False self.edit_enable = False self.remove_enable = False self.bot_index = -1 self.next = kwargs.get("next", None) self.next_kwargs = kwargs.get("next_kwargs", {}) super().initWithKwargs(**kwargs)
def run_sim(sim_path, edit=False): from ev3sim.validation.batch_files import BatchValidator if not BatchValidator.validate_file(sim_path): return raise_error( f"There is something wrong with the sim {sim_path}, and so it cannot be opened or used." ) if not edit: return [ScreenObjectManager.SCREEN_SIM], [{ "batch": sim_path, }] import importlib with open(sim_path, "r") as f: conf = yaml.safe_load(f) with open(find_abs(conf["preset_file"], preset_locations())) as f: preset = yaml.safe_load(f) if "visual_settings" not in preset: return raise_error("This preset cannot be edited.") mname, cname = preset["visual_settings"].rsplit(".", 1) klass = getattr(importlib.import_module(mname), cname) return [ScreenObjectManager.SCREEN_SETTINGS], [{ "file": sim_path, "settings": klass, "allows_filename_change": True, "extension": "sim", }]
def clickSimBots(self, preset): abs_path = find_abs(preset, allowed_areas=preset_locations()) with open(abs_path, "r") as f: preset_config = yaml.safe_load(f) sim_path = find_abs(preset_config["sim_location"], allowed_areas=batch_locations()) ScreenObjectManager.instance.pushScreen( ScreenObjectManager.SCREEN_BOTS, batch_file=sim_path, )
def clickSimSettings(self, preset): import importlib abs_path = find_abs(preset, allowed_areas=preset_locations()) with open(abs_path, "r") as f: preset_config = yaml.safe_load(f) sim_path = find_abs(preset_config["sim_location"], allowed_areas=batch_locations()) mname, cname = preset_config["visual_settings"].rsplit(".", 1) klass = getattr(importlib.import_module(mname), cname) ScreenObjectManager.instance.pushScreen( ScreenObjectManager.SCREEN_SETTINGS, file=sim_path, settings=klass, allows_filename_change=False, extension="sim", )
def fromOptions(options): if "filename" in options: import yaml fname = find_abs(options["filename"]) with open(fname, "r") as f: config = yaml.safe_load(f) return fromOptions(config) if "class_path" not in options: raise ValueError( "Your options has no 'class_path' or 'filename' entry (Or the file you reference has no 'class_path' entry')" ) import importlib if isinstance(options["class_path"], str): mname, cname = options["class_path"].rsplit(".", 1) klass = getattr(importlib.import_module(mname), cname) else: from importlib.machinery import SourceFileLoader module = SourceFileLoader( "not_main", find_abs(options["class_path"][0], preset_locations())).load_module() klass = getattr(module, options["class_path"][1]) topObj = klass(*options.get("args", []), **options.get("kwargs", {})) # Add any settings for this interactor, if applicable. # This only works for package presets. Not workspace ones. if "settings_name" in options: name = options["settings_name"] if "settings_defn" not in options: raise ValueError( f"Expected a settings object to add with group name {name}") mname, cname = options["settings_defn"].rsplit(".", 1) obj = getattr(importlib.import_module(mname), cname) SettingsManager.instance.addSettingGroup(name, obj) # We need to remove this setting once the simulation ends, so save the name. topObj._settings_name = name return topObj
def spawnTiles(self): self.tiles = [] for i, tile in enumerate(self.TILE_DEFINITIONS): self.tiles.append({}) import yaml path = find_abs(tile["path"], allowed_areas=preset_locations()) with open(path, "r") as f: t = yaml.safe_load(f) self.maxZpos = 0 base_pos = np.array(tile.get("position", [0, 0])) # Transfer to rescue space. base_pos = [base_pos[0] * self.TILE_LENGTH, base_pos[1] * self.TILE_LENGTH] base_rotation = tile.get("rotation", 0) * np.pi / 180 flip = tile.get("flip", False) for obj in t["elements"]: rel_pos = np.array(obj.get("position", [0, 0])) if flip: rel_pos[0] = -rel_pos[0] if obj.get("name", "") == "Image": obj["flip"] = [True, False] if obj.get("name", "") == "Arc": obj["rotation"] = 180 - obj.get("rotation", 0) obj["angle"] = -obj["angle"] obj["rotation"] = (obj.get("rotation", 0)) * np.pi / 180 + base_rotation obj["position"] = local_space_to_world_space(rel_pos, base_rotation, base_pos) obj["sensorVisible"] = True k = obj["key"] obj["key"] = f"Tile-{i}-{k}" self.maxZpos = max(self.maxZpos, obj.get("zPos", 0)) t["elements"].append( { "position": local_space_to_world_space(np.array([0, 0]), base_rotation, base_pos), "rotation": base_rotation, "type": "visual", "name": "Rectangle", "width": self.TILE_LENGTH, "height": self.TILE_LENGTH, "fill": None, "stroke_width": 0.1, "stroke": "rescue_outline_color", "zPos": self.maxZpos + 0.1, "key": f"Tile-{i}-outline", "sensorVisible": False, } ) self.tiles[-1]["type"] = t.get("type", "follow") self.tiles[-1]["follows"] = [] self.tiles[-1]["roam_status"] = [] self.tiles[-1]["world_pos"] = base_pos self.tiles[-1]["rotation"] = base_rotation self.tiles[-1]["flip"] = flip if self.tiles[-1]["type"] == "follow": self.tiles[-1]["entries"] = t["entries"] self.tiles[-1]["exits"] = t["exits"] mname, cname = t.get("checker").rsplit(".", 1) import importlib klass = getattr(importlib.import_module(mname), cname) with open(find_abs(t["ui"], allowed_areas=preset_locations()), "r") as f: self.tiles[-1]["ui_elem"] = yaml.safe_load(f) for j, point in enumerate(t["follow_points"]): if isinstance(point[0], (list, tuple)): self.tiles[-1]["follows"].append([]) self.tiles[-1]["roam_status"].append([]) for path in point: self.tiles[-1]["follows"][-1].append([]) self.tiles[-1]["roam_status"][-1].append([]) for point2 in path: if isinstance(point2, str): self.tiles[-1]["roam_status"][-1][-1][-1] = point2 else: if flip: point2[0] = -point2[0] self.tiles[-1]["follows"][-1][-1].append( local_space_to_world_space( np.array(point2), tile.get("rotation", 0) * np.pi / 180, base_pos ) ) self.tiles[-1]["roam_status"][-1][-1].append(None) else: if isinstance(point, str): self.tiles[-1]["roam_status"][-1] = point else: if flip: point[0] = -point[0] self.tiles[-1]["follows"].append( local_space_to_world_space( np.array(point), tile.get("rotation", 0) * np.pi / 180, base_pos ) ) self.tiles[-1]["roam_status"].append(None) self.tiles[-1]["checker"] = klass(self.tiles[-1]["follows"], i, self, **t.get("checker_kwargs", {})) else: self.tiles[-1]["green_conns"] = t.get("green_conns", []) self.tiles[-1]["all_elems"] = ScriptLoader.instance.loadElements(t["elements"]) connecting_objs = [] for tile in self.tiles: # We need to add connections between green tiles if they exist. if tile["type"] == "follow": continue under, right, under_right = self._checkConnectingRescueTiles(tile["world_pos"]) if under_right and under and right: # Draw a big square connecting all 4 tiles. key = "c1-" + str(tile["world_pos"][0]) + "-" + str(tile["world_pos"][1]) connecting_objs.append( { "type": "visual", "name": "Rectangle", "width": 50, "height": 50, "fill": "grass_color", "zPos": 0.3, "key": key, "position": [ tile["world_pos"][0] + self.TILE_LENGTH / 2, tile["world_pos"][1] + self.TILE_LENGTH / 2, ], "sensorVisible": True, } ) else: if under: key = "c2-" + str(tile["world_pos"][0]) + "-" + str(tile["world_pos"][1]) connecting_objs.append( { "type": "visual", "name": "Rectangle", "width": 20, "height": 50, "fill": "grass_color", "zPos": 0.3, "key": key, "position": [tile["world_pos"][0], tile["world_pos"][1] + self.TILE_LENGTH / 2], "sensorVisible": True, } ) if right: key = "c3-" + str(tile["world_pos"][0]) + "-" + str(tile["world_pos"][1]) connecting_objs.append( { "type": "visual", "name": "Rectangle", "width": 50, "height": 20, "fill": "grass_color", "zPos": 0.3, "key": key, "position": [tile["world_pos"][0] + self.TILE_LENGTH / 2, tile["world_pos"][1]], "sensorVisible": True, } ) self.connecting_objs = ScriptLoader.instance.loadElements(connecting_objs)
def playSim(self, preset): abs_path = find_abs(preset, allowed_areas=preset_locations()) with open(abs_path, "r") as f: preset_config = yaml.safe_load(f) sim_path = find_abs(preset_config["sim_location"], allowed_areas=batch_locations()) with open(sim_path, "r") as f: sim_config = yaml.safe_load(f) to_remove = [] for index in range(len(sim_config["bots"])): # Try loading this bot. try: with open( os.path.join( find_abs(sim_config["bots"][index], bot_locations()), "config.bot"), "r") as f: bot_config = yaml.safe_load(f) if not BotValidator.validate_json(bot_config): to_remove.append(index) if bot_config.get("type", "python") == "python": fname = bot_config.get("script", "code.py") else: fname = bot_config.get("script", "program.ev3") if not os.path.exists( os.path.join( find_abs(sim_config["bots"][index], bot_locations()), fname)): def action(): with open( os.path.join( find_abs(sim_config["bots"][index], bot_locations()), fname), "w") as f: f.write("# Put your code here!\n") ScreenObjectManager.instance.forceCloseError( f"Your bot {sim_config['bots'][index]} does not contain the file {fname}. You may have renamed or deleted it by accident. In order to use this bot, you need to add this file back. Click \"Add {fname}\" to create this file, or do it manually.", (f"Add {fname}", action), ) return except: to_remove.append(index) if to_remove: for index in to_remove[::-1]: del sim_config["bots"][index] with open(sim_path, "w") as f: f.write(yaml.dump(sim_config)) if not sim_config["bots"]: # We cannot play, there no are valid bots. return ScreenObjectManager.instance.pushScreen( ScreenObjectManager.SCREEN_BOTS, batch_file=sim_path, next=ScreenObjectManager.instance.SCREEN_SIM, next_kwargs={"batch": sim_path}, ) return ScreenObjectManager.instance.pushScreen( ScreenObjectManager.instance.SCREEN_SIM, batch=sim_path, )
def drawTileDialog(self): class TilePicker(pygame_gui.elements.UIWindow): def kill(self2): self.mode = self.MODE_NORMAL self._all_objs.remove(self2) super().kill() def process_event(self2, event: pygame.event.Event) -> bool: if event.type == pygame.MOUSEWHEEL: self.scroll_container.vert_scroll_bar.scroll_position -= event.y * 10 self.scroll_container.vert_scroll_bar.scroll_position = min( max( self.scroll_container.vert_scroll_bar. scroll_position, self.scroll_container.vert_scroll_bar.top_limit, ), self.scroll_container.vert_scroll_bar.bottom_limit - self.scroll_container.vert_scroll_bar.sliding_button. relative_rect.height, ) x_pos = 0 y_pos = ( self.scroll_container.vert_scroll_bar.scroll_position + self.scroll_container.vert_scroll_bar. arrow_button_height) self.scroll_container.vert_scroll_bar.sliding_button.set_relative_position( (x_pos, y_pos)) self.scroll_container.vert_scroll_bar.start_percentage = ( self.scroll_container.vert_scroll_bar.scroll_position / self.scroll_container.vert_scroll_bar.scrollable_height ) self.scroll_container.vert_scroll_bar.has_moved_recently = True if event.type == pygame.USEREVENT and event.user_type == pygame_gui.UI_BUTTON_PRESSED: if event.ui_object_id.split(".")[-1].startswith("tile-"): index = int( event.ui_object_id.split(".")[-1].split("-")[1]) self.placeTile(index) self2.kill() self.regenerateObjects() return True return super().process_event(event) picker_size = (self._size[0] * 0.7, self._size[1] * 0.7) self.picker = TilePicker( rect=pygame.Rect(self._size[0] * 0.15, self._size[1] * 0.15, *picker_size), manager=self, window_display_title="Pick Tile Type", object_id=pygame_gui.core.ObjectID("tile_dialog"), ) self._all_objs.append(self.picker) button_size = (picker_size[0] - 120) / 3 button_pos = lambda i: ((button_size + 15) * (i % 3), 10 + (button_size + 15) * (i // 3)) self.tile_buttons = [] self.tile_images = [] self.scroll_container = pygame_gui.elements.UIScrollingContainer( relative_rect=pygame.Rect(20, 10, picker_size[0] - 60, picker_size[1] - 80), container=self.picker, manager=self, ) self._all_objs.append(self.scroll_container) for i in range(len(self.tile_locations)): rect = pygame.Rect(*button_pos(i), button_size, button_size) self.tile_buttons.append( pygame_gui.elements.UIButton( relative_rect=rect, text=f"{i}", manager=self, container=self.scroll_container, object_id=pygame_gui.core.ObjectID(f"tile-{i}-button", "invis_button"), )) with open( find_abs(f"tiles/definitions/{self.tile_locations[i]}", preset_locations()), "r") as f: conf = yaml.safe_load(f) self.tile_images.append( pygame_gui.elements.UIImage( relative_rect=rect, image_surface=pygame.image.load( find_abs(conf["preview"], preset_locations())), manager=self, container=self.scroll_container, object_id=pygame_gui.core.ObjectID(f"tile-{i}-image"), )) self._all_objs.extend(self.tile_buttons) self._all_objs.extend(self.tile_images) self.scroll_container.set_scrollable_area_dimensions( (picker_size[0] - 80, (button_size + 15) * ((len(self.tile_locations) + 2) // 3)))
def resetRescueVisual(self): from ev3sim.visual.manager import ScreenObjectManager from ev3sim.simulation.loader import ScriptLoader from ev3sim.presets.rescue import RescueInteractor ScriptLoader.instance.reset() ScriptLoader.instance.startUp() ScreenObjectManager.instance.resetVisualElements() with open(find_abs("rescue.yaml", preset_locations()), "r") as f: conf = yaml.safe_load(f) utils.GLOBAL_COLOURS.update(conf.get("colours", {})) r = RescueInteractor() r.TILE_DEFINITIONS = self.current_tiles r.CAN_SPAWN_POSITION = (self.previous_info.get("settings", {}).get( "rescue", {}).get("CAN_SPAWN_POSITION", [0, 0])) self.customMap = { "SCREEN_WIDTH": self._size[0], "SCREEN_HEIGHT": self._size[1], "MAP_WIDTH": 293.3, "MAP_HEIGHT": 200, } placeableArea = visualFactory( name="Rectangle", width=8 * 30, height=6 * 30, position=(15 + self.tile_offset[0], -15 + self.tile_offset[1]), fill="#6f6f6f", ) placeableArea.customMap = self.customMap placeableArea.calculatePoints() ScreenObjectManager.instance.registerVisual(placeableArea, "placeableArea") remove = [] for key in ScreenObjectManager.instance.objects: if key.startswith("tile-entry"): remove.append(key) for key in remove: ScreenObjectManager.instance.unregisterVisual(key) r.spawnTiles() for index, tile in enumerate(r.tiles): direction, rotation = self.getDirsAndRotations(tile) if tile["type"] == "follow": for i, entry_dir in enumerate(tile["entries"]): startArrow = visualFactory( name="Polygon", verts=[ [1.96, 0], [0.21, 1.75], [0.21, 0.5], [-1.4, 0.5], [-1.4, -0.5], [0.21, -0.5], [0.21, -1.75], [1.96, 0], ], fill="#219ebc", stroke_width=0, zPos=0.1, sensorVisible=False, rotation=rotation[entry_dir], ) startArrow.key = f"tile-{index}-entry-{i}" startArrow.position = [ tile["world_pos"][0] + self.tile_offset[0] + direction[entry_dir][0] * 11, tile["world_pos"][1] + self.tile_offset[1] + direction[entry_dir][1] * 11, ] startArrow.customMap = self.customMap startArrow.calculatePoints() ScreenObjectManager.instance.registerVisual( startArrow, startArrow.key) for obj in tile["all_elems"]: obj.position = [ obj.position[0] + self.tile_offset[0], obj.position[1] + self.tile_offset[1], ] if isinstance(obj, IVisualElement): obj.customMap = self.customMap obj.calculatePoints() elif isinstance(obj, BaseObject): obj.visual.customMap = self.customMap obj.visual.calculatePoints() for obj in r.connecting_objs: obj.customMap = self.customMap obj.position = [ obj.position[0] + self.tile_offset[0], obj.position[1] + self.tile_offset[1], ] r.spawnCan() r.can_obj.visual.customMap = self.customMap r.can_obj.body.position = [ a + b for a, b in zip(r.can_obj.body.position, self.tile_offset) ] r.can_obj.position = np.array(r.can_obj.body.position) r.can_obj.visual.calculatePoints() self.can_obj = r.can_obj self.current_tile_objects = r.tiles self.updateSelectPlacement()