A-maze-ing project. Check info there
Project description
A-Maze-ing
Algorithms used for maze generation:
Prim's Algorithm:
- Subgraph connecting all vertices
- No cycles
- Always looks for minimum cost
- The MST of N vertices has N - 1 edges, an extra edge will cause a cycle
How it works:
- Start from any vertex
- Select the edge with the smallest weight at each step
- Repeat the second step until all vertices are connected (N - 1 edges)
Required data structures:
- Visited Set: [] Keeps track of visited vertices
- Min Heap A priority queue, to efficiently select the edge with the smalles weight at each step
- MST List A list to store the edges to make up the final minimum spanning tree
How the algorithm works:
-
Loop through all the edges, and for every vertex, store a list of tupils containing the edge's weight and its neighbouring vertex
-
Create a set called nodes, to keep track of all the unique vertices in the graph
-
Initialize four variables:
- visited = set() : A set to track visited vertices
- mst = [] : A list to store the edges included in the minimum spanning tree
- total_weight = 0 : A variable to accumulate the total weight of the spanning tree
- min_heap = [] : A priority queue to select the next edge with the smallest weight
-
Choose any starting point: start = edges [0][0]
-
Mark starting point as visited: visited.add(start)
-
Add all edges eminating from this starting vertex to min_heap: for weight, neighbour in graph[start]: heapq.heappush(min_heap, (weight, start, neighbour))
-
The main loop starts running and the loop continues as long as the min-heap isn't empty and mst of N vertices is smaller than N - 1
-
Each iteration:
- Pop the edge with the smallest weight from the priority queue
- If vertex hasn't been visited, mark is as visited else, continue to the next vertex in min_heap
- Add the edge to the spanning tree, and update the total weight
- Add all new edges to the priority queue provided they connect to unvisited vertices
When N - 1 edges have been reached, the function returns the list with edges in the tree, along with the total weight
import heapq
from collcections import defaultdict
def prim(edges):
graph = defaultdict(list)
nodes = set()
for u, v, w in edges:
graph[u].append((w, v))
graph[v].append((w, u))
nodes.update([u, v])
visited = set()
mst = []
total_weight = 0
min_heap = []
start = edges[0][0]
visited.add(start)
for weight, neighbour in graph[start]:
heapq.heappush(min_heap, (weight, start, neighbour))
while min_heap and len(mst) < len(nodes) - 1:
weight, u, v = heapq.heappop(min_heap)
if v not in visited:
visited.add(v)
mst.append((u, v, weight))
total_weight += weight
for next_weight, neighbour in graph[v]:
if neighbour not in visited:
heapq.heappush(min_heap, (next_weight, v, neighbour))
return mst, total_weight
Kruskal's Algorithm:
- Similar to Prim's algorithm
- A greedy algorithm, that in each step adds to the forest the lowest weight edge that will not form a cycle
- Key steps are sorting, and the use of a disjoint-set data structure to detect cycles
How the algorithm works:
- Create a forest (a set of trees) initially consisting of a seperate single-vertex tree for each vertex in the nput graph
- Sort the graph edges by weight
- Loop through the edges of the graph, in ascending order by their weight
For each edge:
- Test wether adding the edge to the current forest would create a cycle
- If not, add the edge to the forest, combining two trees into as single tree
Kruskal vs Prim
| Feature | Prim's Algorithm | Kruskal's Algorithm |
|---|---|---|
| 'Approach' | Vertex-based, grows the MST one vertex at a time | Edge based, adds edges in increasing order of weight |
| 'Data structure' | Priority queue (min-heap) | Union-Find data structur |
| 'Graph representation' | Adjacency matrix or adjacency list | Edge list |
| 'Initialization' | Starts from an arbitrary vertex | Starts with all vertices as separate trees (forest) |
| 'Edge selection' | Chooses the minimum weight edge from the connected vertices | Chooses the minimum weight edge from all edges |
| 'Cycle management' | Not explicitly managed; grows connected component | Uses Union-Find to avoid cycles |
| 'Complexity' | O(V^2) for adjacency matrix, O((E + V) log V) with a priority queue | O(E log E) or O(E log V), due to edge sorting |
| 'Suitable for' | Dense graphs | Sparse graphs |
| 'Implementation complexity' | Relatively simpler in dense graphs | More complex due to cycle management |
| 'Parallelism' | Difficult to parallelize | Easier to parallelize edge sorting and union operations |
| 'Memory usage' | More memory for priority queue | Less memory of edges can be sorted externally |
| 'Example use cases' | Network design, clustering with dense connections | Road networks, telecommunications with sparse connections |
| 'Starting point' | Requires a starting vertex | No specific starting point, operates on global edges |
| 'Optimal for' | Dense graphs where adjacency list is used | Sparse graphs where edge list is efficient |
Backtracking Algorithm
Backtracking algorithms are strategies that help explore different options to find the solution.
- Work by tring out different paths and if one doen't work, then backtrack and try another until the right one is found. Its like solving a puzzle by testing different pieces until they fit together perfectly
- Useful for problems where you must generate all valid combinations, permutations, or subsets under constraints
How does a backtracking algorithm work? A backtracking algorithm works by recursively exploring all pssible solutions to a problem. It starts by choosing an initial solution, and then it explores all possible extensions of that solution. If an extension leads to a solution, the algorithm returns that solution. If an extension does not lead to a solution, the algorithm backtracks to the previous solution and tries a different extension.
A general outline of how a backtracking algorithm works:
- Choose an initial solution
- Explore all possible extensions of the current solution
- If an extension leads to a solution, return that solution
- If an extension doesn't lead to a solution, backtrack to the previous solution and try a different extension.
- Repeat steps 2-4 until all possible solutions have been explored.
Depth First Search (DFS)
Also known as the backtracking maze.
How it is generated
- Start with a grid where every cell is surrounded by walls
-
- Select a random starting cell
- Mark it as visited
- Look at neighbouring cells
- Select a random unvisited neighbour
-
- Go to the selected cell
- Mark it as visited
- Remove the wall between current and previous cell
- If there is no unvisited cell:
- Backtrack to previous cell
- Repeat until an unvisited neighbour cell is found
- Repeat step 3-5 until all cells are visited
Hunt and Kill Algorithm
How it is generated
- Start with a grid where every cell is surrounded by walls
-
- Select a random starting cell
- Mark it as visited
- Look at neighbouring cells
- Select a random unvisited neighbour
-
- Go to the selected cell
- Mark it as visited
- Remove the wall between current and previous cell
- Repeat steps 3 and 4, until there is no unvisited neighbour cell
- If there is no unvisited neighbour cell:
- Iterate through the visited cell
- Stop when found a cell with an unvisited neighbour
- Select this cell, and continue starting at step 3
- Repeat until all cells are visited
Prim's Algorithm to generate a Maze
How it is generated
- Start with a grid where every cell is surrounded by walls
- Select random starting cell
- Add it walls to the list
- Select a random wall, and if it separates a new cell from a visited one, remove it.
- Repeat until all cells are visited
- Prim's Algorithm is ideal to generate a perfect maze
- It ensures all cells are connected, but only one path from start to finish exists
Seeded maze generation
A seed ensures the same maze is generated everytime!
random.seed() method
The random.seed() method in Python is used to initialize the random number generator so that it produces the same squence of random numbers every time a program is run. By setting a fixed seed value, randomness becomes reproducible, which is essential for debugging, testing and scientific experiments.
- Ensures consistent results across multiple executions
- Helps in debugging and verifying program output
- Supports reproducible experiments in machine learning and simulations
- Useful for controlled randomness in game development and testing
Example:
import random
for i in range(2):
print(random.randint(1, 1000))
for i in range(2):
random.seed(0)
print(random.randint(1, 1000))
Output:
Without seed:
- 21
- 537
With seed:
- 865
- 865
Syntax
random.seed(a=None, version=2)
Parameters:
- a (optional): it's the seed value (int, float, str, bytes or bytearray). If None, the system time is used
- version (optional): default value is 2, using a more advanced seeding algorithm. version=1 uses an older method
Return type: random.seed() method does not return any value
Configuration file
A configuration file is a plain text file used to store application settings and parameters in a readable format.
- Settings are usually written as key-value pairs
- The INI (initialization) format is popular because it is simple and easy to read
- Python provides the configparser module to work with INI files
- The module allows reading from and writing to configuration files efficiently
Creating a configuration file in Python
import configparser
def create_config():
config = configparser.ConfigParser()
config["DEFAULT"] = {
"SEED": None,
"WIDTH": 20,
"HEIGHT": 20,
"ENTRY": (0, 0),
"EXIT": (19, 19),
"OUTPUT_FILE": "maze.txt",
"PERFECT": True
}
with open('config.ini', 'w') as configfile:
config.write(configfile)
if __name__ == "__main__":
create_config()
Makefile
Good source: https://earthly.dev/blog/python-makefile/
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mazegen_luuk_ilias-0.1.0.tar.gz.
File metadata
- Download URL: mazegen_luuk_ilias-0.1.0.tar.gz
- Upload date:
- Size: 19.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
668636bbcbd63d8fc04d270c7f285da1d1d270d695676a0c7556dc31cb1507d9
|
|
| MD5 |
4a1ae30250bbd2e8819255e6f0b25a05
|
|
| BLAKE2b-256 |
21699c169c76dd278b53487428c675beac1bb792241c6354e3e51e8afeb0d2e1
|
File details
Details for the file mazegen_luuk_ilias-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mazegen_luuk_ilias-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa5f067965b6b232779573b444e863eda5d675960ede216c2b53d492d7a67278
|
|
| MD5 |
48f694c67cd669cc3a981a6eff475e91
|
|
| BLAKE2b-256 |
54c6c2329c34ba17fa5d9db4ad10d902aa6e82f12407083b3a5097185760490d
|