the-maze-py 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- the_maze_py-0.1.0/LICENSE +7 -0
- the_maze_py-0.1.0/PKG-INFO +93 -0
- the_maze_py-0.1.0/README.md +76 -0
- the_maze_py-0.1.0/pyproject.toml +29 -0
- the_maze_py-0.1.0/setup.cfg +4 -0
- the_maze_py-0.1.0/src/mazepy/__init__.py +14 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/__init__.py +87 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/aldous_broder.py +32 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/binary_tree.py +65 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/ellers.py +143 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/fractal_tesselation.py +74 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/growing_tree.py +40 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/hunt_and_kill.py +45 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/kruskals.py +105 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/origin_shift.py +163 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/prims.py +126 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/recursive_backtrack.py +38 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/recursive_division.py +78 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/sidewinder.py +93 -0
- the_maze_py-0.1.0/src/mazepy/algorithms/wilsons.py +51 -0
- the_maze_py-0.1.0/src/mazepy/cells/__init__.py +53 -0
- the_maze_py-0.1.0/src/mazepy/cells/cell.py +72 -0
- the_maze_py-0.1.0/src/mazepy/cells/hex_cell.py +28 -0
- the_maze_py-0.1.0/src/mazepy/cells/polar_cell.py +27 -0
- the_maze_py-0.1.0/src/mazepy/cells/triangle_cell.py +21 -0
- the_maze_py-0.1.0/src/mazepy/cells/weave_cells.py +80 -0
- the_maze_py-0.1.0/src/mazepy/cells/weighted_cell.py +24 -0
- the_maze_py-0.1.0/src/mazepy/create_animation_2d.py +838 -0
- the_maze_py-0.1.0/src/mazepy/deadends.py +72 -0
- the_maze_py-0.1.0/src/mazepy/distances.py +48 -0
- the_maze_py-0.1.0/src/mazepy/fill_animation_2d.py +88 -0
- the_maze_py-0.1.0/src/mazepy/grid_not_supported_exception.py +5 -0
- the_maze_py-0.1.0/src/mazepy/grids/__init__.py +138 -0
- the_maze_py-0.1.0/src/mazepy/grids/colored_grid.py +75 -0
- the_maze_py-0.1.0/src/mazepy/grids/colored_hex_grid.py +28 -0
- the_maze_py-0.1.0/src/mazepy/grids/colored_polar_grid.py +31 -0
- the_maze_py-0.1.0/src/mazepy/grids/colored_triangle_grid.py +28 -0
- the_maze_py-0.1.0/src/mazepy/grids/colored_weave_grid.py +54 -0
- the_maze_py-0.1.0/src/mazepy/grids/cube_grid.py +267 -0
- the_maze_py-0.1.0/src/mazepy/grids/cylinder_grid.py +86 -0
- the_maze_py-0.1.0/src/mazepy/grids/distance_grid.py +35 -0
- the_maze_py-0.1.0/src/mazepy/grids/grid.py +354 -0
- the_maze_py-0.1.0/src/mazepy/grids/grid_3D.py +187 -0
- the_maze_py-0.1.0/src/mazepy/grids/hex_grid.py +98 -0
- the_maze_py-0.1.0/src/mazepy/grids/masked_grid.py +18 -0
- the_maze_py-0.1.0/src/mazepy/grids/mobius_grid.py +137 -0
- the_maze_py-0.1.0/src/mazepy/grids/polar_grid.py +145 -0
- the_maze_py-0.1.0/src/mazepy/grids/sphere_grid.py +170 -0
- the_maze_py-0.1.0/src/mazepy/grids/toroidal_grid.py +77 -0
- the_maze_py-0.1.0/src/mazepy/grids/triangle_grid.py +87 -0
- the_maze_py-0.1.0/src/mazepy/grids/weave_grid.py +87 -0
- the_maze_py-0.1.0/src/mazepy/grids/weighted_grid.py +36 -0
- the_maze_py-0.1.0/src/mazepy/longest_path.py +14 -0
- the_maze_py-0.1.0/src/mazepy/mask.py +98 -0
- the_maze_py-0.1.0/src/mazepy/preconfigure_grid_example.py +19 -0
- the_maze_py-0.1.0/src/mazepy/weighted_shortest_path_example.py +27 -0
- the_maze_py-0.1.0/src/the_maze_py.egg-info/PKG-INFO +93 -0
- the_maze_py-0.1.0/src/the_maze_py.egg-info/SOURCES.txt +60 -0
- the_maze_py-0.1.0/src/the_maze_py.egg-info/dependency_links.txt +1 -0
- the_maze_py-0.1.0/src/the_maze_py.egg-info/requires.txt +4 -0
- the_maze_py-0.1.0/src/the_maze_py.egg-info/top_level.txt +1 -0
- the_maze_py-0.1.0/tests/TestScript.py +160 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright © 2026 CaptaininjaGuy
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: the-maze-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A package that generates mazes
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/Captaininja-Guy/mazepy
|
|
7
|
+
Keywords: maze
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Natural Language :: English
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: numpy>=2.4.6
|
|
13
|
+
Requires-Dist: pyvista>=0.48.4
|
|
14
|
+
Requires-Dist: matplotlib>=3.10.9
|
|
15
|
+
Requires-Dist: pillow>=12.2.0
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
mazepy
|
|
19
|
+
=======
|
|
20
|
+
|
|
21
|
+
A maze library based off of the book [Mazes for Programmers](https://pragprog.com/titles/jbmaze/mazes-for-programmers/) by Jamis Buck but with more stuff.
|
|
22
|
+
|
|
23
|
+
Installation
|
|
24
|
+
```bash
|
|
25
|
+
pip install maze-py
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Maze Shapes Supported
|
|
29
|
+
---------------------
|
|
30
|
+
#### 2D
|
|
31
|
+
* Rectangular
|
|
32
|
+
* Polar (Circular)
|
|
33
|
+
* Hexagonal
|
|
34
|
+
* Triangular
|
|
35
|
+
* Arbitrary shapes with masking
|
|
36
|
+
#### 3D
|
|
37
|
+
* Layers (Grid 3D)
|
|
38
|
+
* Cylindrical
|
|
39
|
+
* Spherical
|
|
40
|
+
* Cube
|
|
41
|
+
* Toroidal
|
|
42
|
+
* Möbius Strip
|
|
43
|
+
|
|
44
|
+
Generation Algorithms Supported
|
|
45
|
+
-------------------------------
|
|
46
|
+
* Aldous Broder
|
|
47
|
+
* Binary Tree (not triangular)
|
|
48
|
+
* Ellers (not triangular)
|
|
49
|
+
* Fractal Tesselation (only rectangualar)
|
|
50
|
+
* Growing Tree
|
|
51
|
+
* Hunt and Kill
|
|
52
|
+
* Kruskals (not polar, haxagonal, or triangular)
|
|
53
|
+
* Origin Shift
|
|
54
|
+
* Prims (Simplified, True, Modified)
|
|
55
|
+
* Recursive Backtrack (DFS)
|
|
56
|
+
* Recursive Division (only Rectangular)
|
|
57
|
+
* Sidewinder
|
|
58
|
+
* Wilsons
|
|
59
|
+
|
|
60
|
+
Other Features
|
|
61
|
+
--------------
|
|
62
|
+
* Printing rectangular mazes
|
|
63
|
+
* Generating pngs of mazes
|
|
64
|
+
* Color grids based on distance
|
|
65
|
+
* Viewing 3d grids in 3d
|
|
66
|
+
* Animations of maze creation and filling with color gradient
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
Examples
|
|
70
|
+
========
|
|
71
|
+
Showing a colored maze
|
|
72
|
+
----------------------
|
|
73
|
+
```python
|
|
74
|
+
import mazepy as mp
|
|
75
|
+
|
|
76
|
+
grid = mp.grids.ColoredGrid(10,10, base='red', end='blue')
|
|
77
|
+
mp.algorithms.Ellers(grid)
|
|
78
|
+
grid.distances = grid[5, 5].distances() # Set starting cell (this is required)
|
|
79
|
+
grid.show()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
<img src="https://raw.githubusercontent.com/Captaininja-Guy/mazepy/refs/heads/main/pictures/colored.png" width="400"/>
|
|
83
|
+
|
|
84
|
+
Mazes of different shapes
|
|
85
|
+
-------------------------
|
|
86
|
+
```python
|
|
87
|
+
import mazepy as mp
|
|
88
|
+
|
|
89
|
+
grid = mp.grids.PolarGrid(10)
|
|
90
|
+
mp.algorithms.RecursiveBacktrack(grid)
|
|
91
|
+
grid.show()
|
|
92
|
+
```
|
|
93
|
+
<img src="https://raw.githubusercontent.com/Captaininja-Guy/mazepy/refs/heads/main/pictures/polar.png" width="400"/>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
mazepy
|
|
2
|
+
=======
|
|
3
|
+
|
|
4
|
+
A maze library based off of the book [Mazes for Programmers](https://pragprog.com/titles/jbmaze/mazes-for-programmers/) by Jamis Buck but with more stuff.
|
|
5
|
+
|
|
6
|
+
Installation
|
|
7
|
+
```bash
|
|
8
|
+
pip install maze-py
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Maze Shapes Supported
|
|
12
|
+
---------------------
|
|
13
|
+
#### 2D
|
|
14
|
+
* Rectangular
|
|
15
|
+
* Polar (Circular)
|
|
16
|
+
* Hexagonal
|
|
17
|
+
* Triangular
|
|
18
|
+
* Arbitrary shapes with masking
|
|
19
|
+
#### 3D
|
|
20
|
+
* Layers (Grid 3D)
|
|
21
|
+
* Cylindrical
|
|
22
|
+
* Spherical
|
|
23
|
+
* Cube
|
|
24
|
+
* Toroidal
|
|
25
|
+
* Möbius Strip
|
|
26
|
+
|
|
27
|
+
Generation Algorithms Supported
|
|
28
|
+
-------------------------------
|
|
29
|
+
* Aldous Broder
|
|
30
|
+
* Binary Tree (not triangular)
|
|
31
|
+
* Ellers (not triangular)
|
|
32
|
+
* Fractal Tesselation (only rectangualar)
|
|
33
|
+
* Growing Tree
|
|
34
|
+
* Hunt and Kill
|
|
35
|
+
* Kruskals (not polar, haxagonal, or triangular)
|
|
36
|
+
* Origin Shift
|
|
37
|
+
* Prims (Simplified, True, Modified)
|
|
38
|
+
* Recursive Backtrack (DFS)
|
|
39
|
+
* Recursive Division (only Rectangular)
|
|
40
|
+
* Sidewinder
|
|
41
|
+
* Wilsons
|
|
42
|
+
|
|
43
|
+
Other Features
|
|
44
|
+
--------------
|
|
45
|
+
* Printing rectangular mazes
|
|
46
|
+
* Generating pngs of mazes
|
|
47
|
+
* Color grids based on distance
|
|
48
|
+
* Viewing 3d grids in 3d
|
|
49
|
+
* Animations of maze creation and filling with color gradient
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Examples
|
|
53
|
+
========
|
|
54
|
+
Showing a colored maze
|
|
55
|
+
----------------------
|
|
56
|
+
```python
|
|
57
|
+
import mazepy as mp
|
|
58
|
+
|
|
59
|
+
grid = mp.grids.ColoredGrid(10,10, base='red', end='blue')
|
|
60
|
+
mp.algorithms.Ellers(grid)
|
|
61
|
+
grid.distances = grid[5, 5].distances() # Set starting cell (this is required)
|
|
62
|
+
grid.show()
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
<img src="https://raw.githubusercontent.com/Captaininja-Guy/mazepy/refs/heads/main/pictures/colored.png" width="400"/>
|
|
66
|
+
|
|
67
|
+
Mazes of different shapes
|
|
68
|
+
-------------------------
|
|
69
|
+
```python
|
|
70
|
+
import mazepy as mp
|
|
71
|
+
|
|
72
|
+
grid = mp.grids.PolarGrid(10)
|
|
73
|
+
mp.algorithms.RecursiveBacktrack(grid)
|
|
74
|
+
grid.show()
|
|
75
|
+
```
|
|
76
|
+
<img src="https://raw.githubusercontent.com/Captaininja-Guy/mazepy/refs/heads/main/pictures/polar.png" width="400"/>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "the-maze-py"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A package that generates mazes"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
dependencies = [
|
|
13
|
+
"numpy>=2.4.6",
|
|
14
|
+
"pyvista>=0.48.4",
|
|
15
|
+
"matplotlib>=3.10.9",
|
|
16
|
+
"pillow>=12.2.0",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Natural Language :: English",
|
|
21
|
+
]
|
|
22
|
+
keywords = ["maze"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/Captaininja-Guy/mazepy"
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages.find]
|
|
29
|
+
where = ["src"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from . import grids
|
|
2
|
+
from . import algorithms
|
|
3
|
+
from . import cells
|
|
4
|
+
from .create_animation_2d import play_create_2d
|
|
5
|
+
from .deadends import Deadends
|
|
6
|
+
from .fill_animation_2d import play_fill_2d
|
|
7
|
+
from .longest_path import longest_path
|
|
8
|
+
from .mask import Mask
|
|
9
|
+
from .preconfigure_grid_example import precon_grid_ex
|
|
10
|
+
from .weighted_shortest_path_example import weight_grid_ex
|
|
11
|
+
|
|
12
|
+
__all__ = ["grids", "algorithms", "cells", "play_create_2d",
|
|
13
|
+
"Deadends", "play_fill_2d", "longest_path",
|
|
14
|
+
"Mask", "precon_grid_ex", "weight_grid_ex"]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from .aldous_broder import AldousBroder
|
|
5
|
+
from .binary_tree import BinaryTree
|
|
6
|
+
from .ellers import Ellers
|
|
7
|
+
from .fractal_tesselation import FractalTessalation
|
|
8
|
+
from .growing_tree import GrowingTree
|
|
9
|
+
from .hunt_and_kill import HuntandKill
|
|
10
|
+
from .kruskals import Kruskals
|
|
11
|
+
from .origin_shift import OriginShift
|
|
12
|
+
from .prims import Prims, TruePrims, ModifiedPrims, SimplifiedPrims
|
|
13
|
+
from .recursive_backtrack import RecursiveBacktrack
|
|
14
|
+
from .recursive_division import RecursiveDivision
|
|
15
|
+
from .sidewinder import Sidewinder
|
|
16
|
+
from .wilsons import Wilsons
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def __getattr__(name):
|
|
20
|
+
if name == "AldousBroder":
|
|
21
|
+
from .aldous_broder import AldousBroder
|
|
22
|
+
return AldousBroder
|
|
23
|
+
if name == "BinaryTree":
|
|
24
|
+
from .binary_tree import BinaryTree
|
|
25
|
+
return BinaryTree
|
|
26
|
+
if name == "Ellers":
|
|
27
|
+
from .ellers import Ellers
|
|
28
|
+
return Ellers
|
|
29
|
+
if name == "FractalTessalation":
|
|
30
|
+
from .fractal_tesselation import FractalTessalation
|
|
31
|
+
return FractalTessalation
|
|
32
|
+
if name == "GrowingTree":
|
|
33
|
+
from .growing_tree import GrowingTree
|
|
34
|
+
return GrowingTree
|
|
35
|
+
if name == "HuntandKill":
|
|
36
|
+
from .hunt_and_kill import HuntandKill
|
|
37
|
+
return HuntandKill
|
|
38
|
+
if name == "Kruskals":
|
|
39
|
+
from .kruskals import Kruskals
|
|
40
|
+
return Kruskals
|
|
41
|
+
if name == "OriginShift":
|
|
42
|
+
from .origin_shift import OriginShift
|
|
43
|
+
return OriginShift
|
|
44
|
+
if name == "Prims":
|
|
45
|
+
from .prims import Prims
|
|
46
|
+
return Prims
|
|
47
|
+
if name == "TruePrims":
|
|
48
|
+
from .prims import TruePrims
|
|
49
|
+
return TruePrims
|
|
50
|
+
if name == "ModifiedPrims":
|
|
51
|
+
from .prims import ModifiedPrims
|
|
52
|
+
return ModifiedPrims
|
|
53
|
+
if name == "SimplifiedPrims":
|
|
54
|
+
from .prims import SimplifiedPrims
|
|
55
|
+
return SimplifiedPrims
|
|
56
|
+
if name == "RecursiveBacktrack":
|
|
57
|
+
from .recursive_backtrack import RecursiveBacktrack
|
|
58
|
+
return RecursiveBacktrack
|
|
59
|
+
if name == "RecursiveDivision":
|
|
60
|
+
from .recursive_division import RecursiveDivision
|
|
61
|
+
return RecursiveDivision
|
|
62
|
+
if name == "Sidewinder":
|
|
63
|
+
from .sidewinder import Sidewinder
|
|
64
|
+
return Sidewinder
|
|
65
|
+
if name == "Wilsons":
|
|
66
|
+
from .wilsons import Wilsons
|
|
67
|
+
return Wilsons
|
|
68
|
+
|
|
69
|
+
raise AttributeError(name)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
__all__ = ["AldousBroder",
|
|
73
|
+
"BinaryTree",
|
|
74
|
+
"Ellers",
|
|
75
|
+
"FractalTessalation",
|
|
76
|
+
"GrowingTree",
|
|
77
|
+
"HuntandKill",
|
|
78
|
+
"Kruskals",
|
|
79
|
+
"OriginShift",
|
|
80
|
+
"Prims",
|
|
81
|
+
"TruePrims",
|
|
82
|
+
"ModifiedPrims",
|
|
83
|
+
"SimplifiedPrims",
|
|
84
|
+
"RecursiveBacktrack",
|
|
85
|
+
"RecursiveDivision",
|
|
86
|
+
"Sidewinder",
|
|
87
|
+
"Wilsons"]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import random as rand
|
|
2
|
+
from ..grids.grid import Grid
|
|
3
|
+
|
|
4
|
+
def AldousBroder(grid: Grid) -> None:
|
|
5
|
+
'''
|
|
6
|
+
Performs the AldousBroder algorithm on a given Grid in place.
|
|
7
|
+
Any type of grid can be used
|
|
8
|
+
|
|
9
|
+
Parameters:
|
|
10
|
+
grid (Grid)
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
None, the grid is modified in place
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
import mazepy as mp
|
|
17
|
+
|
|
18
|
+
grid = mp.grids.Grid(20,20)
|
|
19
|
+
mp.algorithms.AldousBroder(grid)
|
|
20
|
+
grid.show()
|
|
21
|
+
'''
|
|
22
|
+
cell = grid.random_cell()
|
|
23
|
+
unvisited = grid.size - 1
|
|
24
|
+
|
|
25
|
+
while unvisited > 0:
|
|
26
|
+
neighbor = rand.choice(cell.neighbors())
|
|
27
|
+
|
|
28
|
+
if not neighbor.links():
|
|
29
|
+
cell.link(neighbor)
|
|
30
|
+
unvisited -= 1
|
|
31
|
+
|
|
32
|
+
cell = neighbor
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import random as rand
|
|
2
|
+
from ..grids.grid import Grid
|
|
3
|
+
from ..grid_not_supported_exception import GridNotSupportedException
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def BinaryTree(grid: Grid) -> None:
|
|
9
|
+
'''
|
|
10
|
+
Performs Binary Tree algorithm on a given Grid in place
|
|
11
|
+
Most non-triangle Grids can be used with satisfactory results
|
|
12
|
+
|
|
13
|
+
Parameters:
|
|
14
|
+
grid (Grid)
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
None the grid is modified in place
|
|
18
|
+
|
|
19
|
+
Warnings:
|
|
20
|
+
RuntimeWarning Binary tree cannot produce weaving on weaved grids
|
|
21
|
+
|
|
22
|
+
Errors:
|
|
23
|
+
Throws GridNotSupportedException when given a TriangleGrid
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
import mazepy as mp
|
|
27
|
+
|
|
28
|
+
grid = mp.grids.Grid(20,20)
|
|
29
|
+
mp.algorithms.BinaryTree(grid)
|
|
30
|
+
grid.show()
|
|
31
|
+
'''
|
|
32
|
+
|
|
33
|
+
if grid.type_of_grid == 'triangle':
|
|
34
|
+
raise GridNotSupportedException('TriangleGrid not compatible with Binary Tree')
|
|
35
|
+
if grid.type_of_grid == 'weave': # Warning for Weaved Grids as weaving will not occur
|
|
36
|
+
warnings.warn('Binary Tree will NOT produce weaved grid')
|
|
37
|
+
|
|
38
|
+
_hex, polar, three_d = grid.type_of_grid == 'hex', grid.type_of_grid == 'polar', grid.type_of_grid == '3d'
|
|
39
|
+
|
|
40
|
+
for cell in grid.each_cell():
|
|
41
|
+
neighbors = []
|
|
42
|
+
if cell.north:
|
|
43
|
+
neighbors.append(cell.north)
|
|
44
|
+
if cell.east:
|
|
45
|
+
neighbors.append(cell.east)
|
|
46
|
+
if _hex: # HexGrid support
|
|
47
|
+
if cell.northeast:
|
|
48
|
+
neighbors.append(cell.northeast)
|
|
49
|
+
elif cell.southeast:
|
|
50
|
+
neighbors.append(cell.southeast)
|
|
51
|
+
elif polar: # Polar support
|
|
52
|
+
if cell is grid[0,0]:
|
|
53
|
+
neighbors = cell.outward[0:2]
|
|
54
|
+
else:
|
|
55
|
+
if cell.column != 0:
|
|
56
|
+
neighbors.append(cell.ccw)
|
|
57
|
+
if cell.outward:
|
|
58
|
+
neighbors.append(cell.outward[0])
|
|
59
|
+
|
|
60
|
+
if three_d and cell.up:
|
|
61
|
+
neighbors.append(cell.up)
|
|
62
|
+
|
|
63
|
+
if len(neighbors) > 0:
|
|
64
|
+
neighbor = rand.choice(neighbors)
|
|
65
|
+
cell.link(neighbor)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from ..grids.grid import Grid
|
|
2
|
+
from ..cells.cell import Cell
|
|
3
|
+
import random as rand
|
|
4
|
+
from ..grid_not_supported_exception import GridNotSupportedException
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
class _RowState:
|
|
8
|
+
def __init__(self, starting_set: int = 0):
|
|
9
|
+
self.cells_in_set = {}
|
|
10
|
+
self.set_for_cell = []
|
|
11
|
+
self.next_set = starting_set
|
|
12
|
+
|
|
13
|
+
def record(self, _set: int, cell: Cell) -> None:
|
|
14
|
+
while cell.column >= len(self.set_for_cell):
|
|
15
|
+
self.set_for_cell.append(None)
|
|
16
|
+
|
|
17
|
+
self.set_for_cell[cell.column] = _set
|
|
18
|
+
self.cells_in_set.setdefault(_set, [])
|
|
19
|
+
self.cells_in_set[_set].append(cell)
|
|
20
|
+
|
|
21
|
+
def set_for(self, cell: Cell) -> int:
|
|
22
|
+
if cell.column >= len(self.set_for_cell) or self.set_for_cell[cell.column] is None:
|
|
23
|
+
self.record(self.next_set, cell)
|
|
24
|
+
self.next_set += 1
|
|
25
|
+
|
|
26
|
+
return self.set_for_cell[cell.column]
|
|
27
|
+
|
|
28
|
+
def merge(self, winner: int, loser: int) -> None:
|
|
29
|
+
for cell in self.cells_in_set[loser]:
|
|
30
|
+
self.set_for_cell[cell.column] = winner
|
|
31
|
+
self.cells_in_set[winner].append(cell)
|
|
32
|
+
|
|
33
|
+
del self.cells_in_set[loser]
|
|
34
|
+
|
|
35
|
+
def _next(self):
|
|
36
|
+
return _RowState(self.next_set)
|
|
37
|
+
|
|
38
|
+
def each_set(self):
|
|
39
|
+
for set_, cells in self.cells_in_set.items():
|
|
40
|
+
yield set_, cells
|
|
41
|
+
|
|
42
|
+
def Ellers(grid: Grid) -> None:
|
|
43
|
+
'''
|
|
44
|
+
Performs Ellers algorithm on a given Grid in place.
|
|
45
|
+
Triangle grids not supported 3d and cube Grids are not perfect
|
|
46
|
+
|
|
47
|
+
Parameters:
|
|
48
|
+
grid (Grid)
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
None, the grid is modified in place
|
|
52
|
+
|
|
53
|
+
Errors:
|
|
54
|
+
GridNotSupportedException if Grid is triangular
|
|
55
|
+
|
|
56
|
+
Warnings:
|
|
57
|
+
RuntimeWarning Ellers produced non-perfect mazes on 3d and cube grids
|
|
58
|
+
|
|
59
|
+
Example:
|
|
60
|
+
import mazepy as mp
|
|
61
|
+
|
|
62
|
+
grid = mp.grids.Grid(20,20)
|
|
63
|
+
mp.algorithms.Ellers(grid)
|
|
64
|
+
grid.show()
|
|
65
|
+
'''
|
|
66
|
+
|
|
67
|
+
if grid.type_of_grid == 'triangle':
|
|
68
|
+
raise GridNotSupportedException('Triangle Grids not compatible with Ellers')
|
|
69
|
+
_hex = grid.type_of_grid == 'hex'
|
|
70
|
+
polar = grid.type_of_grid == 'polar'
|
|
71
|
+
if threed := grid.type_of_grid == '3d':
|
|
72
|
+
warnings.warn('Ellers on 3d grids produces seperate 2d grids with no up/down connections', RuntimeWarning)
|
|
73
|
+
if grid.type_of_grid == 'cube':
|
|
74
|
+
warnings.warn('Ellers on a cube grid may not produce a perfect maze', RuntimeWarning)
|
|
75
|
+
|
|
76
|
+
if threed:
|
|
77
|
+
each_level = grid.each_level()
|
|
78
|
+
else:
|
|
79
|
+
each_level = [grid]
|
|
80
|
+
|
|
81
|
+
for level in each_level:
|
|
82
|
+
row_state = _RowState()
|
|
83
|
+
if threed:
|
|
84
|
+
rows = level
|
|
85
|
+
else:
|
|
86
|
+
rows = level.each_row()
|
|
87
|
+
|
|
88
|
+
for row in rows:
|
|
89
|
+
for cell in row:
|
|
90
|
+
if not _hex and not polar and not cell.west: continue
|
|
91
|
+
elif _hex and cell.column%2 == 0 and not cell.northwest: continue
|
|
92
|
+
elif polar and len(row) == 1: continue
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
_set = row_state.set_for(cell)
|
|
96
|
+
if not _hex and not polar:
|
|
97
|
+
prior_set = row_state.set_for(cell.west)
|
|
98
|
+
elif _hex:
|
|
99
|
+
prior_set = row_state.set_for(cell.southwest) if cell.column%2 == 1 else row_state.set_for(cell.northwest)
|
|
100
|
+
elif polar:
|
|
101
|
+
prior_set = row_state.set_for(cell.ccw)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if not polar and _set != prior_set and (cell.south is None or rand.randrange(2) == 0):
|
|
105
|
+
if not _hex and not polar:
|
|
106
|
+
cell.link(cell.west)
|
|
107
|
+
else:
|
|
108
|
+
if cell.column % 2 == 1:
|
|
109
|
+
cell.link(cell.southwest)
|
|
110
|
+
else:
|
|
111
|
+
cell.link(cell.northwest)
|
|
112
|
+
row_state.merge(prior_set, _set)
|
|
113
|
+
|
|
114
|
+
elif polar and _set != prior_set and (not cell.outward or rand.randrange(2) == 0):
|
|
115
|
+
cell.link(cell.ccw)
|
|
116
|
+
row_state.merge(prior_set, _set)
|
|
117
|
+
|
|
118
|
+
if row[0].south:
|
|
119
|
+
next_row = row_state._next()
|
|
120
|
+
|
|
121
|
+
for _, _list in row_state.each_set():
|
|
122
|
+
list2 = _list.copy()
|
|
123
|
+
rand.shuffle(list2)
|
|
124
|
+
|
|
125
|
+
for index, cell in enumerate(list2):
|
|
126
|
+
if index == 0 or rand.randrange(3) == 0:
|
|
127
|
+
cell.link(cell.south)
|
|
128
|
+
next_row.record(row_state.set_for(cell), cell.south)
|
|
129
|
+
|
|
130
|
+
elif polar and row[0].outward:
|
|
131
|
+
next_row = row_state._next()
|
|
132
|
+
|
|
133
|
+
for _, _list in row_state.each_set():
|
|
134
|
+
list2 = _list.copy()
|
|
135
|
+
rand.shuffle(list2)
|
|
136
|
+
for index, cell in enumerate(list2):
|
|
137
|
+
if index == 0 or rand.randrange(3) == 0:
|
|
138
|
+
cell2 = cell.outward[rand.randrange(len(cell.outward))]
|
|
139
|
+
cell.link(cell2)
|
|
140
|
+
next_row.record(row_state.set_for(cell), cell2)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
row_state = next_row
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from ..grids.grid import Grid
|
|
2
|
+
from ..grids.colored_grid import ColoredGrid
|
|
3
|
+
import random as rand
|
|
4
|
+
from itertools import product
|
|
5
|
+
from ..grid_not_supported_exception import GridNotSupportedException
|
|
6
|
+
|
|
7
|
+
def FractalTessalation(grid: Grid|None = None, times: int = 4, base: tuple[int]|str|None = None, end: tuple[int]|str|None = None) -> ColoredGrid|None:
|
|
8
|
+
'''
|
|
9
|
+
Performs fractal tesselation on an existing Grid or generates a Grid
|
|
10
|
+
|
|
11
|
+
Parameters:
|
|
12
|
+
grid (optional Grid)
|
|
13
|
+
times (optional int) = 4, how many iterations to perform
|
|
14
|
+
base (optional tuple[int]|str) PIL/pillow valid color for return grid
|
|
15
|
+
end (optional tuple[int]|str) PIL/pillow valid color for return grid
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
ColoredGrid
|
|
19
|
+
|
|
20
|
+
Errors:
|
|
21
|
+
GridNotSupportedException if grid is not rectangular
|
|
22
|
+
'''
|
|
23
|
+
if grid is None:
|
|
24
|
+
grid = Grid(1, 1)
|
|
25
|
+
elif hasattr(grid, 'base'):
|
|
26
|
+
base = grid.base
|
|
27
|
+
end = grid.end
|
|
28
|
+
|
|
29
|
+
if grid.type_of_grid != 'regular':
|
|
30
|
+
raise GridNotSupportedException('Non-rectangular grids not compatible with Fractal Tessalation')
|
|
31
|
+
|
|
32
|
+
for i in range(times):
|
|
33
|
+
if base is not None and i == times - 1:
|
|
34
|
+
grid_new = ColoredGrid(grid.rows*2, grid.columns*2, base=base, end=end)
|
|
35
|
+
else:
|
|
36
|
+
grid_new = Grid(grid.rows*2, grid.columns*2)
|
|
37
|
+
for cell in grid.each_cell():
|
|
38
|
+
new_row, new_column = cell.row+grid.rows, cell.column+grid.columns
|
|
39
|
+
for row, col in product((cell.row, new_row), (cell.column, new_column)):
|
|
40
|
+
links = cell.links()
|
|
41
|
+
cell2 = grid_new[row, col]
|
|
42
|
+
if cell.north in links:
|
|
43
|
+
cell2.link(cell2.north)
|
|
44
|
+
if cell.east in links:
|
|
45
|
+
cell2.link(cell2.east)
|
|
46
|
+
if cell.south in links:
|
|
47
|
+
cell2.link(cell2.south)
|
|
48
|
+
if cell.west in links:
|
|
49
|
+
cell2.link(cell2.west)
|
|
50
|
+
|
|
51
|
+
exc_path = rand.randrange(2)
|
|
52
|
+
if exc_path == 0:
|
|
53
|
+
num1, num2, num3 = rand.randrange(grid.columns), rand.randrange(grid.columns), rand.randrange(grid.rows)
|
|
54
|
+
cell1 = grid_new[grid.rows, num1]
|
|
55
|
+
cell2 = grid_new[grid.rows, grid.columns+num2]
|
|
56
|
+
cell3 = grid_new[grid.rows*rand.randrange(2) + num3, grid.columns]
|
|
57
|
+
|
|
58
|
+
cell1.link(cell1.north)
|
|
59
|
+
cell2.link(cell2.north)
|
|
60
|
+
cell3.link(cell3.west)
|
|
61
|
+
else:
|
|
62
|
+
num1, num2, num3 = rand.randrange(grid.columns), rand.randrange(grid.rows), rand.randrange(grid.rows)
|
|
63
|
+
cell1 = grid_new[grid.rows, num1 + grid.columns*rand.randrange(2)]
|
|
64
|
+
cell2 = grid_new[num2, grid.columns]
|
|
65
|
+
cell3 = grid_new[grid.rows + num3, grid.columns]
|
|
66
|
+
|
|
67
|
+
cell1.link(cell1.north)
|
|
68
|
+
cell2.link(cell2.west)
|
|
69
|
+
cell3.link(cell3.west)
|
|
70
|
+
|
|
71
|
+
grid = grid_new
|
|
72
|
+
|
|
73
|
+
return grid_new
|
|
74
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from ..grids.grid import Grid
|
|
2
|
+
from ..cells.cell import Cell
|
|
3
|
+
import random as rand
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
def GrowingTree(grid: Grid, func: Callable = lambda x: rand.choice(x), start_at: Cell|None = None) -> None:
|
|
7
|
+
'''
|
|
8
|
+
Performs the GrowingTree algorithm on a given Grid in place.
|
|
9
|
+
Any type of grid can be used
|
|
10
|
+
|
|
11
|
+
Parameters:
|
|
12
|
+
grid (Grid)
|
|
13
|
+
func (optional Callable) = lambda x: rand.choice(x), function to select next cell from visited list (list -> element from it)
|
|
14
|
+
start_at (optional Cell) = grid.random_cell(), which cell to start algorithm at
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
None, the grid is modified in place
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
import mazepy as mp
|
|
21
|
+
|
|
22
|
+
grid = mp.grids.Grid(20,20)
|
|
23
|
+
mp.algorithms.GrowingGree(grid)
|
|
24
|
+
grid.show()
|
|
25
|
+
'''
|
|
26
|
+
if start_at is None:
|
|
27
|
+
start_at = grid.random_cell()
|
|
28
|
+
|
|
29
|
+
active = [start_at]
|
|
30
|
+
|
|
31
|
+
while active:
|
|
32
|
+
cell = func(active) # func takes a list and returns an element of it
|
|
33
|
+
availible_neighbors = [c for c in cell.neighbors() if not c.links()]
|
|
34
|
+
|
|
35
|
+
if availible_neighbors:
|
|
36
|
+
neighbor = rand.choice(availible_neighbors)
|
|
37
|
+
cell.link(neighbor)
|
|
38
|
+
active.append(neighbor)
|
|
39
|
+
else:
|
|
40
|
+
active.remove(cell)
|