diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6af855 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.pytest_cache/ +__pycache__/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..188b8c7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: python + +python: + - 3.6 + +install: + - pip install pytest pytest-pep8 + +script: + - pytest --pep8 diff --git a/README.md b/README.md index 6c4886f..42644b9 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,61 @@ A set of micromouse maze files in text format. They have been collected over som These files are a companion to the maze tool https://github.com/micromouseonline/micromouse_maze_tool. -The format used here has three characters per horizontal wall to make them easier to read when displayed on the screen or printed out. +The format used here is as follows: + +- All posts are represented with the character `o`. +- All posts must be present in the grid even if there is no wall attached to + them. +- Horizontal walls are represented with three `---`. +- Vertical walls are represented with a single `|`. +- The goal cell in half-size mazes has a `G` in the certer of the cell. +- Starting cells might have an `S` in the certer of the cell. + +Here is an example of a 4x4 maze: + +``` +o---o---o---o---o +| G | | +o o o o---o +| | | +o---o---o---o o +| | +o o---o---o o +| | | +o---o---o---o---o +``` **classic** mazes are all 16x16. Some of these have smaller active areaswhere the event in which they were used only had a smaller maze available. That may have been 7x8, 8x8, 11x11 or some other size. All these mazes have a legal goal cell in cell 7,7. -**halfsize** mazes are all 32x32. The goal is not fixed in the same way as it might be in the classic contest. Goal cells are marked with an asterisk in the centre of the cell. The goal may be a single cell or a rectangular block of cells. +**halfsize** mazes are all 32x32. The goal is not fixed in the same way as it might be in the classic contest. The goal for halfsize mazes is a single cell, marked with a "G" in the center of the cell, which may be part of a larger area. **training** mazes are all 16x16 but may not have legal goal areas as the useable area might be only 5x5, 8x8, 10x5 or some other size representing small mazes used for testing a mouse where there is not room for a full sized maze. The filename should indicate the active area. The files may, or may not, represent actual contest mazes. If there are errors in contest mazes, please let me know. Preferably with a photo of the original maze so that I can make changes. +## FAQ +### How can I contribute to the repository? +Thanks for your interest! Start by reading GitHub's guide on [how to propose +changes to someone else's project][0], then feel free to create a pull request +with your changes or new maze files. + +### Why text files? + +- They are easy to understand by humans. +- They get along with version control systems (Git). +- They are easy to read/write with any programming language. +- They are easy to visualize (just use your browser or favorite text editor). +- No need for special tools if you want to edit them or create your own. Just + a bit of patience is required. + +### Are there any tools to help me create maze files? + +- If you happen to have an image of a micromouse maze setup you might want to + try the [Optical Micromouse Maze Recognition software][1]. + + +[0]: https://help.github.com/articles/fork-a-repo/ +[1]: https://github.com/Theseus/ommr diff --git a/classic/.gitignore b/classic/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/classic/japan2015-EF.txt b/classic/japan2015-ef.txt similarity index 100% rename from classic/japan2015-EF.txt rename to classic/japan2015-ef.txt diff --git a/classic/japan2015-EQ.txt b/classic/japan2015-eq.txt similarity index 100% rename from classic/japan2015-EQ.txt rename to classic/japan2015-eq.txt diff --git a/classic/ukNov2009f.txt b/classic/uknov2009f.txt similarity index 100% rename from classic/ukNov2009f.txt rename to classic/uknov2009f.txt diff --git a/classic/ukNov2010f.txt b/classic/uknov2010f.txt similarity index 100% rename from classic/ukNov2010f.txt rename to classic/uknov2010f.txt diff --git a/classic/ukNov2010q.txt b/classic/uknov2010q.txt similarity index 100% rename from classic/ukNov2010q.txt rename to classic/uknov2010q.txt diff --git a/classic/ukNov2011f.txt b/classic/uknov2011f.txt similarity index 100% rename from classic/ukNov2011f.txt rename to classic/uknov2011f.txt diff --git a/classic/ukNov2011q.txt b/classic/uknov2011q.txt similarity index 100% rename from classic/ukNov2011q.txt rename to classic/uknov2011q.txt diff --git a/classic/ukNov2013f.txt b/classic/uknov2013f.txt similarity index 100% rename from classic/ukNov2013f.txt rename to classic/uknov2013f.txt diff --git a/classic/ukNov2014f.txt b/classic/uknov2014f.txt similarity index 100% rename from classic/ukNov2014f.txt rename to classic/uknov2014f.txt diff --git a/classic/ukNov2015a.txt b/classic/uknov2015a.txt similarity index 100% rename from classic/ukNov2015a.txt rename to classic/uknov2015a.txt diff --git a/classic/ukNov2015b.txt b/classic/uknov2015b.txt similarity index 100% rename from classic/ukNov2015b.txt rename to classic/uknov2015b.txt diff --git a/classic/ukNov2015f.txt b/classic/uknov2015f.txt similarity index 100% rename from classic/ukNov2015f.txt rename to classic/uknov2015f.txt diff --git a/classic/ukNov2016f.txt b/classic/uknov2016f.txt similarity index 100% rename from classic/ukNov2016f.txt rename to classic/uknov2016f.txt diff --git a/classic/ukNov2017.txt b/classic/uknov2017.txt similarity index 100% rename from classic/ukNov2017.txt rename to classic/uknov2017.txt diff --git a/halfsize/.gitignore b/halfsize/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/halfsize/japan2014ef.txt b/halfsize/japan2014ef.txt new file mode 100644 index 0000000..706bd9d --- /dev/null +++ b/halfsize/japan2014ef.txt @@ -0,0 +1,65 @@ +o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o +| | | | | | | | +o o o---o---o---o---o---o---o---o---o---o---o---o---o o o o o o o o o o o o o o---o o o o o +| | | | | | | | | | | | | | | +o o---o---o---o---o---o---o---o---o---o---o---o---o o o---o o---o o o o o o o o o---o o---o o o o +| | | | | | | | | | | | | +o o o---o---o---o---o---o---o---o---o---o---o o o---o---o---o o o o o o o o o---o o---o o---o o o +| | | | | | | | | | | | | | | | | +o o o o o o o o o o o o o o o o---o o o---o---o---o---o o o---o o---o o---o---o o o +| | | | | | | | | | | | | | | | +o o o o o o o o o o o o o o o o o---o---o---o---o---o o---o---o o---o o o---o---o o o +| | | | | | | | | | | | | | | | | | | +o o o o o o o o o o o o o o o o o o o o o o o---o o---o---o o o o---o---o o +| | | | | | | | | | | | | | | | | | | +o o o o o o o o o o o o o o o o o o---o---o---o o---o o---o o o o o o o o o +| | | | | | | | | | | | | | | | | | +o o o o o---o---o---o---o o o o o o o o---o---o---o o o---o o---o---o o o o o o o o o +| | | | | | | | | | | | | | | | | +o o o o---o---o---o---o o o o o o o o---o---o---o o o---o o---o o o o o o o o o o o +| | | | | | | | | | | | | | | | | | +o o o---o---o---o---o o o---o---o---o---o o o o---o---o---o---o o---o o---o o o o o o---o---o---o o +| | | | | | | | | | | | | +o o o---o---o---o o o---o---o---o---o---o---o o---o---o o---o o---o o---o o o o o o---o---o---o o o +| | | | | | | | | | | | +o o o o---o---o---o---o---o---o---o---o---o---o---o o---o---o o---o---o o---o---o o---o---o---o---o---o o---o o +| | | | | | | | | | +o o o---o---o---o---o---o---o---o---o---o---o---o---o---o---o o---o o o o o o---o---o---o---o---o---o o---o o +| | | | | | | | | | | | | | +o o o---o---o o---o---o o o o o o o o o o---o o---o o---o---o---o---o---o---o---o o---o o---o o +| | | | | | | | | | | +o o o---o o---o---o o---o---o---o---o---o---o---o o---o---o---o---o---o---o o o---o---o---o---o o---o o---o o +| | | | | | | | | | | | | +o o---o o---o---o o o o o o o o o o---o---o---o---o---o---o o o o o o o---o---o---o---o o o +| | | | | | | | | | +o o o o---o o---o o o o o o o o o o---o---o---o---o---o---o o---o---o---o---o o---o---o---o---o o +| | | | | | | | | | +o o o---o o---o o---o o o o o o o o o o---o---o---o---o---o---o---o---o---o o---o---o---o o o o +| | | | | | | | | | | | | +o o o o---o o---o o---o---o---o---o---o o o o---o o---o o---o o o o o o o o---o---o---o o o +| | | | | | | | | | | | | | | +o o o o o---o o---o o---o---o---o o o o o o---o o---o o---o o o o o o o o---o o o o +| | | | | | | | | | | | | | | | | | +o o o o o o---o o---o o---o o o o o o---o---o---o---o---o o o o o o o o o o---o o o +| | | | | | | | | | | | | | | | +o o o o o o o---o o---o o---o o---o---o o o---o---o---o---o o o o o o o---o---o---o---o o o +| | | | | | | | | | | | | +o o---o---o---o---o---o---o---o o---o o---o o---o---o o o---o---o---o---o o o o o o---o---o---o---o---o o +| | | | | | | | | | | +o---o---o---o---o---o---o---o o---o o---o o---o o---o o---o---o---o---o o o o o---o---o---o---o---o---o o o +| | | | | | | | | | | | | +o o---o---o---o---o---o o o---o---o o---o o---o---o o o---o---o o o---o---o---o o o o o---o o---o o +| | | | | | | | | | | | | | | | | +o o o---o---o---o o o o o o---o o---o---o o---o o o o o o o o o o o o---o o---o o o +| | | | | | | | | | | | | | | | | G | | | +o o o o---o o o o o o---o o---o---o o---o---o---o o o o o o o o---o---o o o---o o---o o +| | | | | | | | | | | | | | | | +o o o o o o---o o o o o---o o o o---o---o---o---o---o o o o o o o---o o---o o---o o o +| | | | | | | | | | | | | | | +o o o o o---o---o---o o o---o---o---o---o---o o---o---o---o o---o---o---o---o---o---o---o---o---o---o---o---o o +| | | | | | | | | +o o o o---o---o---o---o---o---o---o---o---o---o---o---o o---o o---o---o---o---o---o---o---o---o---o---o---o---o o o +| | | | | | | +o o o---o---o---o---o---o---o---o---o---o---o---o---o---o---o o---o---o---o---o---o---o---o---o---o---o---o---o---o---o o +| S | | +o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o---o diff --git a/test_mazes.py b/test_mazes.py new file mode 100644 index 0000000..81420dc --- /dev/null +++ b/test_mazes.py @@ -0,0 +1,144 @@ +""" +Basic tests to ensure maze files are properly formatted. +""" +from collections import Counter +from pathlib import Path +import re + +import pytest + + +def parametrize_maze_files(maze_files): + return pytest.mark.parametrize('maze_file', maze_files, + ids=[str(x) for x in maze_files]) + + +CLASSIC_SIZE = 16 +HALFSIZE_SIZE = 32 + +classic_mazes = list(Path('classic').glob('*')) +halfsize_mazes = list(Path('halfsize').glob('*')) +training_mazes = list(Path('training').glob('*')) +competition_mazes = classic_mazes + halfsize_mazes +all_mazes = classic_mazes + halfsize_mazes + training_mazes + +classic_mazes = parametrize_maze_files(classic_mazes) +halfsize_mazes = parametrize_maze_files(halfsize_mazes) +training_mazes = parametrize_maze_files(training_mazes) +competition_mazes = parametrize_maze_files(competition_mazes) +all_mazes = parametrize_maze_files(all_mazes) + + +def read_maze(maze_file): + rows = maze_file.read_text().split('\n') + for i, row in enumerate(rows): + if not row.startswith(('o', '|')): + break + return rows[:i] + + +@all_mazes +def test_file_name(maze_file): + """ + - All maze files must be text files with `.txt` extension. + - Only "a-z", "0-9" and "-" characters are allowed. + """ + assert maze_file.suffix == '.txt' + assert re.match('^[a-z0-9\-]*$', maze_file.stem) + + +@classic_mazes +def test_classic_size(maze_file): + """ + Classic maze size is always 16x16. + """ + rows = read_maze(maze_file) + assert len(rows) == (CLASSIC_SIZE * 2 + 1) + assert all(len(row) == (CLASSIC_SIZE * 4 + 1) for row in rows) + + +@halfsize_mazes +def test_halfsize_size(maze_file): + """ + Half-size maze size is always 32x32. + """ + rows = read_maze(maze_file) + assert len(rows) == (HALFSIZE_SIZE * 2 + 1) + assert all(len(row) == (HALFSIZE_SIZE * 4 + 1) for row in rows) + + +@training_mazes +def test_training_size(maze_file): + """ + Training maze size is the same as classic size, even if not fully used. + """ + rows = read_maze(maze_file) + assert len(rows) == (CLASSIC_SIZE * 2 + 1) + assert all(len(row) == (CLASSIC_SIZE * 4 + 1) for row in rows) + + +@all_mazes +def test_format(maze_file): + """ + Maze file format must be homogeneous. Vertical walls are represented with + a `|`, horizontal walls with `---` and posts with `o`. + + Even rows must: + + - Have a post each 4 characters. + - Have a wall `---` or nothing ` ` between posts. + + Odd rows must: + + - Have a wall `|` or nothing ` ` each 4 characters. + """ + rows = read_maze(maze_file) + even_rows = rows[::2] + assert all(all(x == 'o' for x in row[::4]) for row in even_rows) + assert all(all(x in ('---', ' ') for x in row.strip('o').split('o')) + for row in even_rows) + odd_rows = rows[1::2] + assert all(all(x in ('|', ' ') for x in row[::4]) for row in odd_rows) + + +@all_mazes +def test_boundaries(maze_file): + """ + Maze boundaries are expected to be closed. + """ + rows = read_maze(maze_file) + east_boundary = [row[-1] for row in rows[1::2]] + west_boundary = [row[0] for row in rows[1::2]] + assert all(v == '|' for v in east_boundary + west_boundary) + north_boundary = rows[0].strip('o').split('o') + south_boundary = rows[-1].strip('o').split('o') + assert all(h == '---' for h in north_boundary + south_boundary) + + +@all_mazes +def test_starting_cell_unique(maze_file): + """ + The starting cell, if marked, must be unique. + """ + rows = read_maze(maze_file) + assert Counter(''.join(rows))['S'] in (0, 1) + + +@competition_mazes +def test_starting_cell(maze_file): + """ + The starting cell is expected to be at the South-West and facing North. + """ + rows = read_maze(maze_file) + assert rows[-3][:5] == 'o o' + assert rows[-2][:5] in ('| |', '| S |') + assert rows[-1][:5] == 'o---o' + + +@halfsize_mazes +def test_goal_cell_is_marked(maze_file): + """ + Goal cell is expected to be marked with a "G". + """ + rows = read_maze(maze_file) + assert ' G ' in ''.join(rows) diff --git a/training/.gitignore b/training/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/training/training-10x5_test2.txt b/training/training-10x5-test2.txt similarity index 100% rename from training/training-10x5_test2.txt rename to training/training-10x5-test2.txt diff --git a/training/training-10x5_test3.txt b/training/training-10x5-test3.txt similarity index 100% rename from training/training-10x5_test3.txt rename to training/training-10x5-test3.txt diff --git a/training/training-10x5_test4.txt b/training/training-10x5-test4.txt similarity index 100% rename from training/training-10x5_test4.txt rename to training/training-10x5-test4.txt