Add project structure with maze solver, simulator, and tests
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Failing after 3s
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Failing after 3s
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
"""!
|
||||
@file test_maze_solver.py
|
||||
@brief Runs the solver on every provided world from all four start headings.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from reeborg_maze import SimulatedRobot, load_world, solve_maze
|
||||
from reeborg_maze.constants import BLOCKING_TILES, HEADING_COUNT
|
||||
|
||||
WORLDS = sorted((Path(__file__).parent / "worlds").glob("*.json"))
|
||||
|
||||
|
||||
def free_starts() -> list[tuple[str, int, int, int]]:
|
||||
"""!
|
||||
@brief Every (world, x, y, heading) where the robot can start.
|
||||
"""
|
||||
starts = []
|
||||
for path in WORLDS:
|
||||
world = load_world(path)
|
||||
for x in range(1, world["cols"] + 1):
|
||||
for y in range(1, world["rows"] + 1):
|
||||
if BLOCKING_TILES.intersection(world["tiles"].get(f"{x},{y}", [])):
|
||||
continue
|
||||
for heading in range(HEADING_COUNT):
|
||||
starts.append((path.name, x, y, heading))
|
||||
return starts
|
||||
|
||||
|
||||
def test_worlds_exist() -> None:
|
||||
assert WORLDS
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", WORLDS, ids=lambda p: p.name)
|
||||
def test_reaches_goal_from_file_start(path: Path) -> None:
|
||||
robot = SimulatedRobot(load_world(path))
|
||||
solve_maze(robot)
|
||||
assert robot.at_goal()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("name", "x", "y", "heading"), free_starts())
|
||||
def test_reaches_goal_from_every_free_cell_and_heading(
|
||||
name: str, x: int, y: int, heading: int
|
||||
) -> None:
|
||||
robot = SimulatedRobot(load_world(Path(__file__).parent / "worlds" / name), x, y, heading)
|
||||
solve_maze(robot)
|
||||
assert robot.at_goal()
|
||||
@@ -0,0 +1,248 @@
|
||||
{
|
||||
"walls": {
|
||||
"1,1": [
|
||||
"east"
|
||||
],
|
||||
"1,2": [
|
||||
"east"
|
||||
],
|
||||
"1,3": [
|
||||
"north"
|
||||
],
|
||||
"2,5": [
|
||||
"north"
|
||||
],
|
||||
"1,5": [
|
||||
"north"
|
||||
],
|
||||
"3,5": [
|
||||
"north"
|
||||
],
|
||||
"4,5": [
|
||||
"north"
|
||||
],
|
||||
"5,5": [
|
||||
"north",
|
||||
"east"
|
||||
],
|
||||
"2,3": [
|
||||
"north",
|
||||
"east"
|
||||
],
|
||||
"2,2": [
|
||||
"east"
|
||||
],
|
||||
"3,2": [
|
||||
"east"
|
||||
],
|
||||
"5,1": [
|
||||
"east"
|
||||
],
|
||||
"5,3": [
|
||||
"east",
|
||||
"north"
|
||||
],
|
||||
"4,4": [
|
||||
"east",
|
||||
"north"
|
||||
],
|
||||
"4,2": [
|
||||
"east"
|
||||
],
|
||||
"1,4": [
|
||||
"east"
|
||||
],
|
||||
"5,2": [
|
||||
"east",
|
||||
"north"
|
||||
],
|
||||
"3,1": [
|
||||
"north"
|
||||
]
|
||||
},
|
||||
"small_tiles": false,
|
||||
"rows": 6,
|
||||
"cols": 6,
|
||||
"tiles": {
|
||||
"6,1": [
|
||||
"mud"
|
||||
],
|
||||
"6,2": [
|
||||
"mud"
|
||||
],
|
||||
"6,3": [
|
||||
"mud"
|
||||
],
|
||||
"6,5": [
|
||||
"mud"
|
||||
],
|
||||
"6,6": [
|
||||
"mud"
|
||||
],
|
||||
"5,6": [
|
||||
"mud"
|
||||
],
|
||||
"4,6": [
|
||||
"mud"
|
||||
],
|
||||
"3,6": [
|
||||
"mud"
|
||||
],
|
||||
"2,6": [
|
||||
"mud"
|
||||
],
|
||||
"1,6": [
|
||||
"mud"
|
||||
],
|
||||
"6,4": [
|
||||
"grass"
|
||||
],
|
||||
"5,4": [
|
||||
"#000000"
|
||||
],
|
||||
"5,5": [
|
||||
"#000000"
|
||||
],
|
||||
"4,5": [
|
||||
"#000000"
|
||||
],
|
||||
"3,5": [
|
||||
"#000000"
|
||||
],
|
||||
"3,4": [
|
||||
"#000000"
|
||||
],
|
||||
"4,4": [
|
||||
"#000000"
|
||||
],
|
||||
"4,3": [
|
||||
"#000000"
|
||||
],
|
||||
"3,3": [
|
||||
"#000000"
|
||||
],
|
||||
"3,2": [
|
||||
"#000000"
|
||||
],
|
||||
"4,2": [
|
||||
"#000000"
|
||||
],
|
||||
"5,3": [
|
||||
"#000000"
|
||||
],
|
||||
"5,2": [
|
||||
"#000000"
|
||||
],
|
||||
"5,1": [
|
||||
"#000000"
|
||||
],
|
||||
"4,1": [
|
||||
"#000000"
|
||||
],
|
||||
"3,1": [
|
||||
"#000000"
|
||||
],
|
||||
"2,1": [
|
||||
"#000000"
|
||||
],
|
||||
"2,2": [
|
||||
"#000000"
|
||||
],
|
||||
"2,3": [
|
||||
"#000000"
|
||||
],
|
||||
"2,4": [
|
||||
"#000000"
|
||||
],
|
||||
"2,5": [
|
||||
"#000000"
|
||||
],
|
||||
"1,5": [
|
||||
"#000000"
|
||||
],
|
||||
"1,4": [
|
||||
"#000000"
|
||||
],
|
||||
"1,3": [
|
||||
"#000000"
|
||||
],
|
||||
"1,2": [
|
||||
"#000000"
|
||||
],
|
||||
"1,1": [
|
||||
"#000000"
|
||||
]
|
||||
},
|
||||
"goal": {
|
||||
"possible_final_positions": [
|
||||
[
|
||||
6,
|
||||
4
|
||||
]
|
||||
],
|
||||
"position": {
|
||||
"image": "racing_flag",
|
||||
"x": 6,
|
||||
"y": 4
|
||||
}
|
||||
},
|
||||
"robots": [
|
||||
{
|
||||
"x": 3,
|
||||
"y": 4,
|
||||
"objects": {},
|
||||
"model": "classic",
|
||||
"_orientation": 3,
|
||||
"_is_leaky": true,
|
||||
"_prev_x": 3,
|
||||
"_prev_y": 3,
|
||||
"_prev_orientation": 2,
|
||||
"_trace_history": [
|
||||
{
|
||||
"color": "seagreen",
|
||||
"thickness": 2,
|
||||
"prev_x": 140,
|
||||
"x": 140,
|
||||
"prev_y": 170,
|
||||
"y": 210,
|
||||
"grid_x": 3,
|
||||
"grid_y": 3
|
||||
},
|
||||
{
|
||||
"color": "seagreen",
|
||||
"thickness": 2,
|
||||
"prev_x": 140,
|
||||
"x": 140,
|
||||
"prev_y": 200,
|
||||
"y": 210,
|
||||
"grid_x": 3,
|
||||
"grid_y": 3
|
||||
}
|
||||
],
|
||||
"_trace_style": "default",
|
||||
"_trace_color": "seagreen",
|
||||
"__id": 80,
|
||||
"initial_position": [
|
||||
3,
|
||||
3
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
"",
|
||||
"<h1>Lost in a maze</h1>",
|
||||
"<p>Reeborg was exploring a dark maze and the battery in its flashlight ran out.</p>",
|
||||
"<p>Write a program using an <code>if/elif/else</code> statement so Reeborg can find the exit. ",
|
||||
"The secret is to have Reeborg follow along the right edge of the maze, ",
|
||||
"turning right if it can, going straight ahead if it ",
|
||||
"can’t turn right, or turning left as a last resort.</p>",
|
||||
"<h3>What you need to know</h3>",
|
||||
"<ul><li>The functions <code>move()</code> and <code>turn_left()</code>.</li>",
|
||||
"<li>Either the test <code>front_is_clear()</code> or <code>wall_in_front()</code>,",
|
||||
"<code>right_is_clear()</code> or <code>wall_on_right()</code>, and <code>at_goal()</code>.</li>",
|
||||
"<li>How to use a <code>while</code> loop and <code>if/elif/else</code> statements.</li>",
|
||||
"<li>It might be useful to know how to use the negation of a test (<code>not</code> in Python).</li></ul>",
|
||||
"DIFFICULTY4",
|
||||
""
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
{
|
||||
"walls": {
|
||||
"1,1": [
|
||||
"east"
|
||||
],
|
||||
"1,2": [
|
||||
"east"
|
||||
],
|
||||
"1,3": [
|
||||
"north"
|
||||
],
|
||||
"2,5": [
|
||||
"north"
|
||||
],
|
||||
"1,5": [
|
||||
"north"
|
||||
],
|
||||
"3,5": [
|
||||
"north"
|
||||
],
|
||||
"4,5": [
|
||||
"north"
|
||||
],
|
||||
"5,5": [
|
||||
"north",
|
||||
"east"
|
||||
],
|
||||
"2,3": [
|
||||
"north",
|
||||
"east"
|
||||
],
|
||||
"2,2": [
|
||||
"east"
|
||||
],
|
||||
"3,2": [
|
||||
"east"
|
||||
],
|
||||
"5,1": [
|
||||
"east"
|
||||
],
|
||||
"5,3": [
|
||||
"east",
|
||||
"north"
|
||||
],
|
||||
"4,4": [
|
||||
"east",
|
||||
"north"
|
||||
],
|
||||
"4,2": [
|
||||
"east"
|
||||
],
|
||||
"1,4": [
|
||||
"east"
|
||||
],
|
||||
"5,2": [
|
||||
"east",
|
||||
"north"
|
||||
],
|
||||
"3,1": [
|
||||
"north"
|
||||
]
|
||||
},
|
||||
"small_tiles": false,
|
||||
"rows": 6,
|
||||
"cols": 6,
|
||||
"tiles": {
|
||||
"6,1": [
|
||||
"mud"
|
||||
],
|
||||
"6,2": [
|
||||
"mud"
|
||||
],
|
||||
"6,3": [
|
||||
"mud"
|
||||
],
|
||||
"6,5": [
|
||||
"mud"
|
||||
],
|
||||
"6,6": [
|
||||
"mud"
|
||||
],
|
||||
"5,6": [
|
||||
"mud"
|
||||
],
|
||||
"4,6": [
|
||||
"mud"
|
||||
],
|
||||
"3,6": [
|
||||
"mud"
|
||||
],
|
||||
"2,6": [
|
||||
"mud"
|
||||
],
|
||||
"1,6": [
|
||||
"mud"
|
||||
],
|
||||
"6,4": [
|
||||
"grass"
|
||||
],
|
||||
"5,4": [
|
||||
"#000000"
|
||||
],
|
||||
"5,5": [
|
||||
"#000000"
|
||||
],
|
||||
"4,5": [
|
||||
"#000000"
|
||||
],
|
||||
"3,5": [
|
||||
"#000000"
|
||||
],
|
||||
"3,4": [
|
||||
"#000000"
|
||||
],
|
||||
"4,4": [
|
||||
"#000000"
|
||||
],
|
||||
"4,3": [
|
||||
"#000000"
|
||||
],
|
||||
"3,3": [
|
||||
"#000000"
|
||||
],
|
||||
"3,2": [
|
||||
"#000000"
|
||||
],
|
||||
"4,2": [
|
||||
"#000000"
|
||||
],
|
||||
"5,3": [
|
||||
"#000000"
|
||||
],
|
||||
"5,2": [
|
||||
"#000000"
|
||||
],
|
||||
"5,1": [
|
||||
"#000000"
|
||||
],
|
||||
"4,1": [
|
||||
"#000000"
|
||||
],
|
||||
"3,1": [
|
||||
"#000000"
|
||||
],
|
||||
"2,1": [
|
||||
"#000000"
|
||||
],
|
||||
"2,2": [
|
||||
"#000000"
|
||||
],
|
||||
"2,3": [
|
||||
"#000000"
|
||||
],
|
||||
"2,4": [
|
||||
"#000000"
|
||||
],
|
||||
"2,5": [
|
||||
"#000000"
|
||||
],
|
||||
"1,5": [
|
||||
"#000000"
|
||||
],
|
||||
"1,4": [
|
||||
"#000000"
|
||||
],
|
||||
"1,3": [
|
||||
"#000000"
|
||||
],
|
||||
"1,2": [
|
||||
"#000000"
|
||||
],
|
||||
"1,1": [
|
||||
"#000000"
|
||||
]
|
||||
},
|
||||
"goal": {
|
||||
"possible_final_positions": [
|
||||
[
|
||||
6,
|
||||
4
|
||||
]
|
||||
],
|
||||
"position": {
|
||||
"image": "racing_flag",
|
||||
"x": 6,
|
||||
"y": 4
|
||||
}
|
||||
},
|
||||
"robots": [
|
||||
{
|
||||
"x": 3,
|
||||
"y": 4,
|
||||
"objects": {},
|
||||
"model": "classic",
|
||||
"_orientation": 1,
|
||||
"_is_leaky": true,
|
||||
"_prev_x": 3,
|
||||
"_prev_y": 3,
|
||||
"_prev_orientation": 2,
|
||||
"_trace_history": [
|
||||
{
|
||||
"color": "seagreen",
|
||||
"thickness": 2,
|
||||
"prev_x": 140,
|
||||
"x": 140,
|
||||
"prev_y": 170,
|
||||
"y": 210,
|
||||
"grid_x": 3,
|
||||
"grid_y": 3
|
||||
},
|
||||
{
|
||||
"color": "seagreen",
|
||||
"thickness": 2,
|
||||
"prev_x": 140,
|
||||
"x": 140,
|
||||
"prev_y": 200,
|
||||
"y": 210,
|
||||
"grid_x": 3,
|
||||
"grid_y": 3
|
||||
}
|
||||
],
|
||||
"_trace_style": "default",
|
||||
"_trace_color": "seagreen",
|
||||
"__id": 80,
|
||||
"initial_position": [
|
||||
3,
|
||||
3
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
"",
|
||||
"<h1>Lost in a maze</h1>",
|
||||
"<p>Reeborg was exploring a dark maze and the battery in its flashlight ran out.</p>",
|
||||
"<p>Write a program using an <code>if/elif/else</code> statement so Reeborg can find the exit. ",
|
||||
"The secret is to have Reeborg follow along the right edge of the maze, ",
|
||||
"turning right if it can, going straight ahead if it ",
|
||||
"can’t turn right, or turning left as a last resort.</p>",
|
||||
"<h3>What you need to know</h3>",
|
||||
"<ul><li>The functions <code>move()</code> and <code>turn_left()</code>.</li>",
|
||||
"<li>Either the test <code>front_is_clear()</code> or <code>wall_in_front()</code>,",
|
||||
"<code>right_is_clear()</code> or <code>wall_on_right()</code>, and <code>at_goal()</code>.</li>",
|
||||
"<li>How to use a <code>while</code> loop and <code>if/elif/else</code> statements.</li>",
|
||||
"<li>It might be useful to know how to use the negation of a test (<code>not</code> in Python).</li></ul>",
|
||||
"DIFFICULTY4",
|
||||
""
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
{
|
||||
"walls": {
|
||||
"1,1": [
|
||||
"east"
|
||||
],
|
||||
"1,2": [
|
||||
"east"
|
||||
],
|
||||
"1,3": [
|
||||
"north"
|
||||
],
|
||||
"2,5": [
|
||||
"north"
|
||||
],
|
||||
"1,5": [
|
||||
"north"
|
||||
],
|
||||
"3,5": [
|
||||
"north"
|
||||
],
|
||||
"4,5": [
|
||||
"north"
|
||||
],
|
||||
"5,5": [
|
||||
"north",
|
||||
"east"
|
||||
],
|
||||
"2,3": [
|
||||
"north",
|
||||
"east"
|
||||
],
|
||||
"2,2": [
|
||||
"east"
|
||||
],
|
||||
"3,2": [
|
||||
"east"
|
||||
],
|
||||
"5,1": [
|
||||
"east"
|
||||
],
|
||||
"5,3": [
|
||||
"east",
|
||||
"north"
|
||||
],
|
||||
"4,4": [
|
||||
"east",
|
||||
"north"
|
||||
],
|
||||
"4,2": [
|
||||
"east"
|
||||
],
|
||||
"1,4": [
|
||||
"east"
|
||||
],
|
||||
"5,2": [
|
||||
"east",
|
||||
"north"
|
||||
],
|
||||
"3,1": [
|
||||
"north"
|
||||
]
|
||||
},
|
||||
"small_tiles": false,
|
||||
"rows": 6,
|
||||
"cols": 6,
|
||||
"tiles": {
|
||||
"6,1": [
|
||||
"mud"
|
||||
],
|
||||
"6,2": [
|
||||
"mud"
|
||||
],
|
||||
"6,3": [
|
||||
"mud"
|
||||
],
|
||||
"6,5": [
|
||||
"mud"
|
||||
],
|
||||
"6,6": [
|
||||
"mud"
|
||||
],
|
||||
"5,6": [
|
||||
"mud"
|
||||
],
|
||||
"4,6": [
|
||||
"mud"
|
||||
],
|
||||
"3,6": [
|
||||
"mud"
|
||||
],
|
||||
"2,6": [
|
||||
"mud"
|
||||
],
|
||||
"1,6": [
|
||||
"mud"
|
||||
],
|
||||
"6,4": [
|
||||
"grass"
|
||||
],
|
||||
"5,4": [
|
||||
"#000000"
|
||||
],
|
||||
"5,5": [
|
||||
"#000000"
|
||||
],
|
||||
"4,5": [
|
||||
"#000000"
|
||||
],
|
||||
"3,5": [
|
||||
"#000000"
|
||||
],
|
||||
"3,4": [
|
||||
"#000000"
|
||||
],
|
||||
"4,4": [
|
||||
"#000000"
|
||||
],
|
||||
"4,3": [
|
||||
"#000000"
|
||||
],
|
||||
"3,3": [
|
||||
"#000000"
|
||||
],
|
||||
"3,2": [
|
||||
"#000000"
|
||||
],
|
||||
"4,2": [
|
||||
"#000000"
|
||||
],
|
||||
"5,3": [
|
||||
"#000000"
|
||||
],
|
||||
"5,2": [
|
||||
"#000000"
|
||||
],
|
||||
"5,1": [
|
||||
"#000000"
|
||||
],
|
||||
"4,1": [
|
||||
"#000000"
|
||||
],
|
||||
"3,1": [
|
||||
"#000000"
|
||||
],
|
||||
"2,1": [
|
||||
"#000000"
|
||||
],
|
||||
"2,2": [
|
||||
"#000000"
|
||||
],
|
||||
"2,3": [
|
||||
"#000000"
|
||||
],
|
||||
"2,4": [
|
||||
"#000000"
|
||||
],
|
||||
"2,5": [
|
||||
"#000000"
|
||||
],
|
||||
"1,5": [
|
||||
"#000000"
|
||||
],
|
||||
"1,4": [
|
||||
"#000000"
|
||||
],
|
||||
"1,3": [
|
||||
"#000000"
|
||||
],
|
||||
"1,2": [
|
||||
"#000000"
|
||||
],
|
||||
"1,1": [
|
||||
"#000000"
|
||||
]
|
||||
},
|
||||
"goal": {
|
||||
"possible_final_positions": [
|
||||
[
|
||||
6,
|
||||
4
|
||||
]
|
||||
],
|
||||
"position": {
|
||||
"image": "racing_flag",
|
||||
"x": 6,
|
||||
"y": 4
|
||||
}
|
||||
},
|
||||
"robots": [
|
||||
{
|
||||
"x": 3,
|
||||
"y": 4,
|
||||
"objects": {},
|
||||
"model": "classic",
|
||||
"_orientation": 2,
|
||||
"_is_leaky": true,
|
||||
"_prev_x": 3,
|
||||
"_prev_y": 3,
|
||||
"_prev_orientation": 2,
|
||||
"_trace_history": [
|
||||
{
|
||||
"color": "seagreen",
|
||||
"thickness": 2,
|
||||
"prev_x": 140,
|
||||
"x": 140,
|
||||
"prev_y": 170,
|
||||
"y": 210,
|
||||
"grid_x": 3,
|
||||
"grid_y": 3
|
||||
},
|
||||
{
|
||||
"color": "seagreen",
|
||||
"thickness": 2,
|
||||
"prev_x": 140,
|
||||
"x": 140,
|
||||
"prev_y": 200,
|
||||
"y": 210,
|
||||
"grid_x": 3,
|
||||
"grid_y": 3
|
||||
}
|
||||
],
|
||||
"_trace_style": "default",
|
||||
"_trace_color": "seagreen",
|
||||
"__id": 80,
|
||||
"initial_position": [
|
||||
3,
|
||||
3
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
"",
|
||||
"<h1>Lost in a maze</h1>",
|
||||
"<p>Reeborg was exploring a dark maze and the battery in its flashlight ran out.</p>",
|
||||
"<p>Write a program using an <code>if/elif/else</code> statement so Reeborg can find the exit. ",
|
||||
"The secret is to have Reeborg follow along the right edge of the maze, ",
|
||||
"turning right if it can, going straight ahead if it ",
|
||||
"can’t turn right, or turning left as a last resort.</p>",
|
||||
"<h3>What you need to know</h3>",
|
||||
"<ul><li>The functions <code>move()</code> and <code>turn_left()</code>.</li>",
|
||||
"<li>Either the test <code>front_is_clear()</code> or <code>wall_in_front()</code>,",
|
||||
"<code>right_is_clear()</code> or <code>wall_on_right()</code>, and <code>at_goal()</code>.</li>",
|
||||
"<li>How to use a <code>while</code> loop and <code>if/elif/else</code> statements.</li>",
|
||||
"<li>It might be useful to know how to use the negation of a test (<code>not</code> in Python).</li></ul>",
|
||||
"DIFFICULTY4",
|
||||
""
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user