From b606d0cf5e2fc4fb14b80f90003c1cd4cd322e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20S=C3=A1nchez=20de=20Le=C3=B3n=20Peque?= Date: Tue, 20 Mar 2018 20:32:15 +0100 Subject: [PATCH 1/4] Add some tests and Travis integration --- .gitignore | 2 + .travis.yml | 10 +++ classic/.gitignore | 0 halfsize/.gitignore | 0 test_mazes.py | 144 ++++++++++++++++++++++++++++++++++++++++++++ training/.gitignore | 0 6 files changed, 156 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml delete mode 100644 classic/.gitignore delete mode 100644 halfsize/.gitignore create mode 100644 test_mazes.py delete mode 100644 training/.gitignore 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/classic/.gitignore b/classic/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/halfsize/.gitignore b/halfsize/.gitignore deleted file mode 100644 index e69de29..0000000 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 From 96829da95e835ad4bbadd636c583daae752ec134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20S=C3=A1nchez=20de=20Le=C3=B3n=20Peque?= Date: Tue, 20 Mar 2018 23:50:26 +0100 Subject: [PATCH 2/4] Add Japan 2014 half-size maze file --- halfsize/japan2014ef.txt | 65 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 halfsize/japan2014ef.txt 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 From 571fd72e8879791c81dd14512d664733aa13b632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20S=C3=A1nchez=20de=20Le=C3=B3n=20Peque?= Date: Wed, 21 Mar 2018 00:00:34 +0100 Subject: [PATCH 3/4] Fix maze file names --- classic/{japan2015-EF.txt => japan2015-ef.txt} | 0 classic/{japan2015-EQ.txt => japan2015-eq.txt} | 0 classic/{ukNov2009f.txt => uknov2009f.txt} | 0 classic/{ukNov2010f.txt => uknov2010f.txt} | 0 classic/{ukNov2010q.txt => uknov2010q.txt} | 0 classic/{ukNov2011f.txt => uknov2011f.txt} | 0 classic/{ukNov2011q.txt => uknov2011q.txt} | 0 classic/{ukNov2013f.txt => uknov2013f.txt} | 0 classic/{ukNov2014f.txt => uknov2014f.txt} | 0 classic/{ukNov2015a.txt => uknov2015a.txt} | 0 classic/{ukNov2015b.txt => uknov2015b.txt} | 0 classic/{ukNov2015f.txt => uknov2015f.txt} | 0 classic/{ukNov2016f.txt => uknov2016f.txt} | 0 classic/{ukNov2017.txt => uknov2017.txt} | 0 training/{training-10x5_test2.txt => training-10x5-test2.txt} | 0 training/{training-10x5_test3.txt => training-10x5-test3.txt} | 0 training/{training-10x5_test4.txt => training-10x5-test4.txt} | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename classic/{japan2015-EF.txt => japan2015-ef.txt} (100%) rename classic/{japan2015-EQ.txt => japan2015-eq.txt} (100%) rename classic/{ukNov2009f.txt => uknov2009f.txt} (100%) rename classic/{ukNov2010f.txt => uknov2010f.txt} (100%) rename classic/{ukNov2010q.txt => uknov2010q.txt} (100%) rename classic/{ukNov2011f.txt => uknov2011f.txt} (100%) rename classic/{ukNov2011q.txt => uknov2011q.txt} (100%) rename classic/{ukNov2013f.txt => uknov2013f.txt} (100%) rename classic/{ukNov2014f.txt => uknov2014f.txt} (100%) rename classic/{ukNov2015a.txt => uknov2015a.txt} (100%) rename classic/{ukNov2015b.txt => uknov2015b.txt} (100%) rename classic/{ukNov2015f.txt => uknov2015f.txt} (100%) rename classic/{ukNov2016f.txt => uknov2016f.txt} (100%) rename classic/{ukNov2017.txt => uknov2017.txt} (100%) rename training/{training-10x5_test2.txt => training-10x5-test2.txt} (100%) rename training/{training-10x5_test3.txt => training-10x5-test3.txt} (100%) rename training/{training-10x5_test4.txt => training-10x5-test4.txt} (100%) 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/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 From 2b38f90b086026f76f135c8870c8b02a30139efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20S=C3=A1nchez=20de=20Le=C3=B3n=20Peque?= Date: Wed, 21 Mar 2018 00:29:16 +0100 Subject: [PATCH 4/4] Update README file --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) 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