diff --git a/env-data/tests/test-10x10.mpk b/env-data/tests/test-10x10.mpk new file mode 100644 index 0000000000000000000000000000000000000000..e7e9ad5a5fd875eacced553d816ee407fa8215d1 Binary files /dev/null and b/env-data/tests/test-10x10.mpk differ diff --git a/flatland/envs/generators.py b/flatland/envs/generators.py index c1578a816e2e30127fb77dda6e72ab51b2f41cb2..f396164615baad167adac5860201af3359d8efbc 100644 --- a/flatland/envs/generators.py +++ b/flatland/envs/generators.py @@ -9,6 +9,21 @@ from flatland.envs.env_utils import distance_on_rail, connect_rail, get_directio from flatland.envs.env_utils import get_rnd_agents_pos_tgt_dir_on_rail +def empty_rail_generator(): + """ + Returns a generator which returns an empty rail mail with no agents. + Primarily used by the editor + """ + def generator(width, height, num_agents=0, num_resets=0): + rail_trans = RailEnvTransitions() + grid_map = GridTransitionMap(width=width, height=height, transitions=rail_trans) + rail_array = grid_map.grid + rail_array.fill(0) + + return grid_map, [], [], [] + return generator + + def complex_rail_generator(nr_start_goal=1, nr_extra=100, min_dist=20, max_dist=99999, seed=0): """ Parameters diff --git a/flatland/envs/observations.py b/flatland/envs/observations.py index fa77b58a53ef63767c2690edc782b6699f6c8459..ba159cb7fb6df9fd171a3b509f352d3ba8d2d30c 100644 --- a/flatland/envs/observations.py +++ b/flatland/envs/observations.py @@ -195,7 +195,8 @@ class TreeObsForRailEnv(ObservationBuilder): # for loc in self.env.agents_position: # self.location_has_agent[(loc[0], loc[1])] = 1 self.location_has_agent = {tuple(agent.position): 1 for agent in self.env.agents} - + if handle > len(self.env.agents): + print("ERROR: obs _get - handle ", handle, " len(agents)", len(self.env.agents)) agent = self.env.agents[handle] # TODO: handle being treated as index # position = self.env.agents_position[handle] # orientation = self.env.agents_direction[handle] diff --git a/flatland/utils/editor.py b/flatland/utils/editor.py index a33a0116a93b9c1aa25c9da8103925f7c5ed51ae..7e813d763d7f26a96a1e1ca1f4c3d1bceef68ee3 100644 --- a/flatland/utils/editor.py +++ b/flatland/utils/editor.py @@ -15,7 +15,7 @@ import os # from ipywidgets import IntSlider, link, VBox from flatland.envs.rail_env import RailEnv, random_rail_generator -from flatland.envs.generators import complex_rail_generator +from flatland.envs.generators import complex_rail_generator, empty_rail_generator # from flatland.core.transitions import RailEnvTransitions from flatland.envs.observations import TreeObsForRailEnv import flatland.utils.rendertools as rt @@ -60,7 +60,7 @@ class View(object): self.new_env() self.oRT.renderEnv(spacing=False, arrows=False, sRailColor="gray", show=False) img = self.oRT.getImage() - plt.clf() + plt.clf() # TODO: remove this plt.clf() call self.wImage = jpy_canvas.Canvas(img) self.yxSize = self.wImage.data.shape[:2] self.writableData = np.copy(self.wImage.data) # writable copy of image - wid_img.data is somehow readonly @@ -86,6 +86,9 @@ class View(object): self.wDebug_move = Checkbox(description="Debug mouse move") self.wDebug_move.observe(self.controller.setDebugMove, names="value") + # Checkbox for rendering observations + self.wShowObs = Checkbox(description="Show Agent Observations") + # This is like a cell widget where loggin goes self.wOutput = Output() @@ -95,13 +98,15 @@ class View(object): self.wFilename.observe(self.controller.setFilename, names="value") # Size of environment when regenerating - self.wSize = IntSlider(value=10, min=5, max=30, step=5, description="Regen Size") - self.wSize.observe(self.controller.setRegenSize, names="value") + self.wRegenSize = IntSlider(value=10, min=5, max=100, step=5, description="Regen Size", + tip="Click Regenerate after changing this") + self.wRegenSize.observe(self.controller.setRegenSize, names="value") # Number of Agents when regenerating - self.wNAgents = IntSlider(value=1, min=0, max=20, step=1, description="# Agents") + self.wRegenNAgents = IntSlider(value=1, min=0, max=20, step=1, description="# Agents", + tip="Click regenerate or reset after changing this") - self.wRegenMethod = RadioButtons(description="Regen\nMethod", options=["Random Cell", "Path-based"]) + self.wRegenMethod = RadioButtons(description="Regen\nMethod", options=["Empty", "Random Cell", "Path-based"]) self.wReplaceAgents = Checkbox(value=True, description="Replace Agents") self.wTab = Tab() @@ -109,8 +114,8 @@ class View(object): for i, title in enumerate(tab_contents): self.wTab.set_title(i, title) self.wTab.children = [ - VBox([self.wDebug, self.wDebug_move]), - VBox([self.wRegenMethod, self.wReplaceAgents])] + VBox([self.wDebug, self.wDebug_move, self.wShowObs]), + VBox([self.wRegenSize, self.wRegenNAgents, self.wRegenMethod, self.wReplaceAgents])] # Progress bar intended for stepping in the background (not yet working) self.wProg_steps = ipywidgets.IntProgress(value=0, min=0, max=20, step=1, description="Step") @@ -140,8 +145,8 @@ class View(object): self.wVbox_controls = VBox([ self.wFilename, # self.wDrawMode, *self.lwButtons, - self.wSize, - self.wNAgents, + # self.wRegenSize, + # self.wRegenNAgents, self.wProg_steps, self.wTab]) @@ -161,13 +166,17 @@ class View(object): with self.wOutput: # plt.figure(figsize=(10, 10)) self.oRT.renderEnv(spacing=False, arrows=False, sRailColor="gray", - show=False, iSelectedAgent=self.model.iSelectedAgent) + show=False, iSelectedAgent=self.model.iSelectedAgent, + show_observations=self.show_observations()) img = self.oRT.getImage() # plt.clf() # plt.close() self.wImage.data = img self.writableData = np.copy(self.wImage.data) + + # the size should only be updated on regenerate at most + self.yxSize = self.wImage.data.shape[:2] return img def redisplayImage(self): @@ -191,6 +200,13 @@ class View(object): else: print(*args, **kwargs) + def show_observations(self): + ''' returns whether to show observations - boolean ''' + if self.wShowObs.value: + return True + else: + return False + class Controller(object): """ @@ -297,17 +313,17 @@ class Controller(object): self.model.clear() def reset(self, event): - self.log("Reset - nAgents:", self.view.wNAgents.value) + self.log("Reset - nAgents:", self.view.wRegenNAgents.value) self.model.reset(replace_agents=self.view.wReplaceAgents.value, - nAgents=self.view.wNAgents.value) + nAgents=self.view.wRegenNAgents.value) def restartAgents(self, event): - self.log("Restart Agents - nAgents:", self.view.wNAgents.value) + self.log("Restart Agents - nAgents:", self.view.wRegenNAgents.value) self.model.restartAgents() def regenerate(self, event): method = self.view.wRegenMethod.value - nAgents = self.view.wNAgents.value + nAgents = self.view.wRegenNAgents.value self.model.regenerate(method, nAgents) def setRegenSize(self, event): @@ -375,6 +391,43 @@ class EditorModel(object): def setDrawMode(self, sDrawMode): self.drawMode = sDrawMode + def interpolate_path(self, rcLast, rcCell): + if np.array_equal(rcLast, rcCell): + return [] + rcLast = array(rcLast) + rcCell = array(rcCell) + rcDelta = rcCell - rcLast + + lrcInterp = [] # extra row,col points + + if np.any(np.abs(rcDelta) >= 1): + iDim0 = np.argmax(np.abs(rcDelta)) # the dimension with the bigger move + iDim1 = 1 - iDim0 # the dim with the smaller move + rcRatio = rcDelta[iDim1] / rcDelta[iDim0] + delta0 = rcDelta[iDim0] + sgn0 = np.sign(delta0) + + iDelta1 = 0 + + # count integers along the larger dimension + for iDelta0 in range(sgn0, delta0 + sgn0, sgn0): + rDelta1 = iDelta0 * rcRatio + + if np.abs(rDelta1 - iDelta1) >= 1: + rcInterp = (iDelta0, iDelta1) # fill in the "corner" for "Manhattan interpolation" + lrcInterp.append(rcInterp) + iDelta1 = int(rDelta1) + + rcInterp = (iDelta0, int(rDelta1)) + lrcInterp.append(rcInterp) + g2Interp = array(lrcInterp) + if iDim0 == 1: # if necessary, swap c,r to make r,c + g2Interp = g2Interp[:, [1, 0]] + g2Interp += rcLast + # Convert the array to a list of tuples + lrcInterp = list(map(tuple, g2Interp)) + return lrcInterp + def drag_path_element(self, rcCell): """Mouse motion event handler for drawing. """ @@ -384,8 +437,9 @@ class EditorModel(object): if len(lrcStroke) > 0: rcLast = lrcStroke[-1] if not np.array_equal(rcLast, rcCell): # only save at transition - lrcStroke.append(rcCell) - self.debug("lrcStroke ", len(lrcStroke), rcCell) + lrcInterp = self.interpolate_path(rcLast, rcCell) + lrcStroke.extend(lrcInterp) + self.debug("lrcStroke ", len(lrcStroke), rcCell, "interp:", lrcInterp) else: # This is the first cell in a mouse stroke @@ -545,7 +599,7 @@ class EditorModel(object): self.redraw() def setFilename(self, filename): - self.log("filename = ", filename, type(filename)) + # self.log("filename = ", filename, type(filename)) self.env_filename = filename def load(self): @@ -567,7 +621,9 @@ class EditorModel(object): def regenerate(self, method=None, nAgents=0): self.log("Regenerate size", self.regen_size) - if method is None or method == "Random Cell": + if method is None or method == "Empty": + fnMethod = empty_rail_generator() + elif method == "Random Cell": fnMethod = random_rail_generator(cell_type_relative_proportion=[1] * 11) else: fnMethod = complex_rail_generator(nr_start_goal=5, nr_extra=20, min_dist=12) @@ -583,6 +639,7 @@ class EditorModel(object): self.set_env(self.env) self.player = Player(self.env) self.view.new_env() + # self.view.init_canvas() # Can't do init_canvas - need to keep the same canvas widget! self.redraw() def setRegenSize(self, size): diff --git a/flatland/utils/graphics_layer.py b/flatland/utils/graphics_layer.py index 40bf319da737c8bb49e6c228c98b70604734ddc4..4cfcc64bffb82f91a0f36822188db297bc1ed37e 100644 --- a/flatland/utils/graphics_layer.py +++ b/flatland/utils/graphics_layer.py @@ -51,7 +51,7 @@ class GraphicsLayer(object): elif type(color) is tuple: if type(color[0]) is not int: gcolor = array(color) - color = tuple((gcolor[:4] * 255).astype(int)) + color = tuple((gcolor[:3] * 255).astype(int)) else: color = self.tColGrid diff --git a/flatland/utils/rendertools.py b/flatland/utils/rendertools.py index 26ec39d1f426691e7d4fe5a8b4b6aec3bcc7b1fd..30dccfaca0f0d8506c9fed7bf34601275148bd7b 100644 --- a/flatland/utils/rendertools.py +++ b/flatland/utils/rendertools.py @@ -606,7 +606,7 @@ class RenderTool(object): def renderEnv( self, show=False, curves=True, spacing=False, - arrows=False, agents=True, obsrender=True, sRailColor="gray", frames=False, + arrows=False, agents=True, show_observations=True, sRailColor="gray", frames=False, iEpisode=None, iStep=None, iSelectedAgent=None, action_dict=None): """ @@ -643,7 +643,7 @@ class RenderTool(object): # Draw each agent + its orientation + its target if agents: self.plotAgents(targets=True, iSelectedAgent=iSelectedAgent) - if obsrender: + if show_observations: self.renderObs(range(env.get_num_agents()), env.dev_obs_dict) # Draw some textual information like fps yText = [-0.3, -0.6, -0.9] diff --git a/notebooks/Editor2.ipynb b/notebooks/Editor2.ipynb index f2481d086d67376ec6018b758e1a88edd4222183..7a55510f486280d7910dadfcbfae14b79ca9b043 100644 --- a/notebooks/Editor2.ipynb +++ b/notebooks/Editor2.ipynb @@ -56,15 +56,7 @@ "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "cpu\n" - ] - } - ], + "outputs": [], "source": [ "from flatland.utils.editor import EditorMVC, EditorModel, View, Controller" ] @@ -93,14 +85,14 @@ "metadata": {}, "source": [ "## Instructions\n", - "- Drag to draw\n", - " - improved dead-ends\n", - "- Shift-Drag to erase rails\n", - " - erasing dead ends not yet automated - drag right across them\n", + "- Drag to draw (improved dead-ends)\n", + "- Shift-Drag to erase rails (erasing dead ends not yet automated - drag right across them)\n", "- ctrl-click to add agent\n", " - direction chosen randomly to fit rail\n", "- ctrl-shift-click to add target for last agent\n", - " - target can be moved by repeating " + " - target can be moved by repeating\n", + "- to Resize the env (cannot preserve work):\n", + " - select \"Regen\" tab, set regen size slider, click regenerate." ] }, { @@ -113,7 +105,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "6b1f996bbb834fcc962c80041465ac2d", + "model_id": "4bff3defb8dc40fb8c2ba74bdbd3c231", "version_major": 2, "version_minor": 0 }, @@ -123,6 +115,13 @@ }, "metadata": {}, "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "len: 190 <class 'bytes'> temp.pkl /home/jeremy/projects/aicrowd/rl-trains/notebooks\n" + ] } ], "source": [ @@ -139,7 +138,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "bf8e0dd6aa564b42a5ec3aa19c31a679", + "model_id": "961d8340f5d444c3b7a31f6684249233", "version_major": 2, "version_minor": 0 }, @@ -155,14 +154,34 @@ "mvc.view.wOutput.clear_output()\n", "mvc.view.wOutput" ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0, 0)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(mvc.editor.env.agents), len(mvc.editor.env.agents_static)" + ] } ], "metadata": { "hide_input": false, "kernelspec": { - "display_name": "Python 3", + "display_name": "ve367", "language": "python", - "name": "python3" + "name": "ve367" }, "language_info": { "codemirror_mode": { @@ -174,7 +193,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.6.7" }, "latex_envs": { "LaTeX_envs_menu_present": true,