diff --git a/flatland/envs/env_utils.py b/flatland/envs/env_utils.py index ee2c263711906370900c14a6030d57ada573c2ea..cc4a0015601d0f5820cccb25b6076a0a92f67915 100644 --- a/flatland/envs/env_utils.py +++ b/flatland/envs/env_utils.py @@ -10,7 +10,7 @@ import numpy as np from flatland.core.transitions import Grid4TransitionsEnum -def get_direction(pos1, pos2): +def get_direction(pos1, pos2) -> Grid4TransitionsEnum: """ Assumes pos1 and pos2 are adjacent location on grid. Returns direction (int) that can be used with transitions. @@ -25,7 +25,7 @@ def get_direction(pos1, pos2): return 1 if diff_1 < 0: return 3 - return 0 + raise Exception("Could not determine direction {}->{}".format(pos1, pos2)) def mirror(dir): @@ -71,34 +71,71 @@ def validate_new_transition(rail_trans, rail_array, prev_pos, current_pos, new_p return rail_trans.is_valid(new_trans) -def position_to_coordinate(width, position): - """ +def position_to_coordinate(depth, positions): + """Converts coordinates to positions: + [ (0,0) (0,1) .. (0,w-1) + (1,0) (1,1) (1,w-1) + ... + (d-1,0) (d-1,1) (d-1,w-1) + ] + + --> - :param width: - :param position: + [ 0 d .. (w-1)*d + 1 d+1 + ... + d-1 2d-1 w*d-1 + ] + + :param depth: + :param positions: :return: """ coords = () - for p in position: - coords = coords + ((int(p) % width, int(p) // width),) # changed x_dim to y_dim + for p in positions: + coords = coords + ((int(p) % depth, int(p) // depth),) # changed x_dim to y_dim return coords -def coordinate_to_position(width, coords): +def coordinate_to_position(depth, coords): """ - - :param width: + Converts positions to coordinates: + [ 0 d .. (w-1)*d + 1 d+1 + ... + d-1 2d-1 w*d-1 + ] + --> + [ (0,0) (0,1) .. (0,w-1) + (1,0) (1,1) (1,w-1) + ... + (d-1,0) (d-1,1) (d-1,w-1) + ] + + :param depth: :param coords: :return: """ position = np.empty(len(coords), dtype=int) idx = 0 for t in coords: - position[idx] = int(t[1] * width + t[0]) + position[idx] = int(t[1] * depth + t[0]) idx += 1 return position +def get_new_position(position, movement): + """ Utility function that converts a compass movement over a 2D grid to new positions (r, c). """ + if movement == Grid4TransitionsEnum.NORTH: + return (position[0] - 1, position[1]) + elif movement == Grid4TransitionsEnum.EAST: + return (position[0], position[1] + 1) + elif movement == Grid4TransitionsEnum.SOUTH: + return (position[0] + 1, position[1]) + elif movement == Grid4TransitionsEnum.WEST: + return (position[0], position[1] - 1) + + class AStarNode(): """A node class for A* Pathfinding""" @@ -254,18 +291,6 @@ def distance_on_rail(pos1, pos2): return abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1]) -def get_new_position(position, movement): - """ Utility function that converts a compass movement over a 2D grid to new positions (r, c). """ - if movement == Grid4TransitionsEnum.NORTH: - return (position[0] - 1, position[1]) - elif movement == Grid4TransitionsEnum.EAST: - return (position[0], position[1] + 1) - elif movement == Grid4TransitionsEnum.SOUTH: - return (position[0] + 1, position[1]) - elif movement == Grid4TransitionsEnum.WEST: - return (position[0], position[1] - 1) - - def get_rnd_agents_pos_tgt_dir_on_rail(rail, num_agents): """ Given a `rail' GridTransitionMap, return a random placement of agents (initial position, direction and target). diff --git a/flatland/envs/predictions.py b/flatland/envs/predictions.py index 3fda7378771bf2b5cd20c7d065c9454d3e5629a5..43909669a67c527ae6fb935e22810eb47c9608cd 100644 --- a/flatland/envs/predictions.py +++ b/flatland/envs/predictions.py @@ -112,7 +112,7 @@ class ShortestPathPredictorForRailEnv(PredictionBuilder): agents = self.env.agents if handle: agents = [self.env.agents[handle]] - assert custom_args + assert custom_args is not None distance_map = custom_args.get('distance_map') assert distance_map is not None diff --git a/setup.py b/setup.py index c11b99ffc07632eb7fb7507c8e811fd382ede045..eaabda402f9d802a496042f237abefc08a14fbab 100644 --- a/setup.py +++ b/setup.py @@ -68,6 +68,14 @@ def get_all_svg_files(directory='./svg/'): return ret +def get_all_images_files(directory='./images/'): + ret = [] + for f in os.listdir(directory): + if os.path.isfile(os.path.join(directory, f)): + ret.append(directory + f) + return ret + + # Gather requirements from requirements_dev.txt install_reqs = [] requirements_path = 'requirements_dev.txt' @@ -103,7 +111,7 @@ setup( keywords='flatland', name='flatland-rl', packages=find_packages('.'), - data_files=[('svg', get_all_svg_files())], + data_files=[('svg', get_all_svg_files()), ('images', get_all_images_files())], setup_requires=setup_requirements, test_suite='tests', tests_require=test_requirements, diff --git a/tests/envs/__init__.py b/tests/envs/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/envs/test_env_utils.py b/tests/envs/test_env_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..25952031c1384430421492244e096dbd29fc557e --- /dev/null +++ b/tests/envs/test_env_utils.py @@ -0,0 +1,33 @@ +import numpy as np +import pytest + +from flatland.core.transitions import Grid4TransitionsEnum +from flatland.envs.env_utils import position_to_coordinate, coordinate_to_position, get_direction + +depth_to_test = 5 +positions_to_test = [0, 5, 1, 6, 20, 30] +coordinates_to_test = [[0, 0], [0, 1], [1, 0], [1, 1], [0, 4], [0, 6]] + + +def test_position_to_coordinate(): + actual_coordinates = position_to_coordinate(depth_to_test, positions_to_test) + expected_coordinates = coordinates_to_test + assert np.array_equal(actual_coordinates, expected_coordinates), \ + "converted positions {}, expected {}".format(actual_coordinates, expected_coordinates) + + +def test_coordinate_to_position(): + actual_positions = coordinate_to_position(depth_to_test, coordinates_to_test) + expected_positions = positions_to_test + assert np.array_equal(actual_positions, expected_positions), \ + "converted positions {}, expected {}".format(actual_positions, expected_positions) + + +def test_get_direction(): + assert get_direction((0, 0), (0, 1)) == Grid4TransitionsEnum.EAST + assert get_direction((0, 0), (0, 2)) == Grid4TransitionsEnum.EAST + assert get_direction((0, 0), (1, 0)) == Grid4TransitionsEnum.SOUTH + assert get_direction((1, 0), (0, 0)) == Grid4TransitionsEnum.NORTH + assert get_direction((1, 0), (0, 0)) == Grid4TransitionsEnum.NORTH + with pytest.raises(Exception, match="Could not determine direction"): + get_direction((0, 0), (0, 0)) == Grid4TransitionsEnum.NORTH diff --git a/tox.ini b/tox.ini index f933d793db188a5ccc5997ffde0dd63580cc90f3..43a02f37343c8a09ea7825e525992842944d420f 100644 --- a/tox.ini +++ b/tox.ini @@ -76,10 +76,10 @@ whitelist_externals = sh deps = -r{toxinidir}/requirements_dev.txt commands = - sh -c 'echo DISPLAY=$DISPLAY' - sh -c 'echo XAUTHORITY=$XAUTHORITY' +; run examples from subfolder to ensure that resources are accessed via resources and not via relative paths + sh -c 'mkdir -p {envtmpdir}/c236d3c240d61a0969d4cb59e2180ce5' ; pipe echo into python since some examples expect input to close the window after the example is run - sh -c 'ls examples/*.py | xargs -I{} -n 1 sh -c "echo -e \"\n====== Running {} ========\n\"; echo "q" | python {}"' + sh -c 'cd {envtmpdir}/c236d3c240d61a0969d4cb59e2180ce5 && ls {toxinidir}/examples/*.py | xargs -I{} -n 1 sh -c "echo -e \"\n====== Running {} ========\n\"; echo "q" | python {}"' [testenv:notebooks] basepython = python @@ -96,11 +96,13 @@ deps = -r{toxinidir}/requirements_dev.txt -r{toxinidir}/requirements_continuous_integration.txt commands = +; run tests from subfolder to ensure that resources are accessed via resources and not via relative paths + sh -c 'mkdir -p {envtmpdir}/6f59bc68108c3895b1828abdd04b9a06' sh -c 'jupyter nbextension enable --py --sys-prefix widgetsnbextension' sh -c 'jupyter nbextension enable --py --sys-prefix jpy_canvas' ; https://stackoverflow.com/questions/35545402/how-to-run-an-ipynb-jupyter-notebook-from-terminal/35545463 sh -c 'ls notebooks/*.ipynb | xargs -n 1 jupyter nbconvert --to python' - sh -c 'ls notebooks/*.py | xargs -I{} -n 1 sh -c "echo -e \"\n====== Running {} ========\n\"; ipython {}"' + sh -c 'cd {envtmpdir}/6f59bc68108c3895b1828abdd04b9a06 && ls {toxinidir}/notebooks/*.py | xargs -I{} -n 1 sh -c "echo -e \"\n====== Running {} ========\n\"; ipython {}"' [testenv] whitelist_externals = sh @@ -115,5 +117,6 @@ passenv = deps = -r{toxinidir}/requirements_dev.txt commands = - sh -c 'echo DISPLAY: $DISPLAY' - py.test --basetemp={envtmpdir} +; run tests from subfolder to ensure that resources are accessed via resources and not via relative paths + sh -c 'mkdir -p {envtmpdir}/fefed3ba12bf1ed81dbcc20fb52706ea' + sh -c 'cd {envtmpdir}/fefed3ba12bf1ed81dbcc20fb52706ea && py.test --basetemp={envtmpdir} {toxinidir}'