Last active
June 9, 2026 16:15
-
-
Save Bigfoot71/5c98c3960b6ec7ae4b5fb250bd762d6e to your computer and use it in GitHub Desktop.
Tiny maze generator in Odin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // This code is distributed under the Unlicense license. | |
| // For more details, please visit https://unlicense.org/ | |
| package maze | |
| import "core:math/rand" | |
| // Maze uses a cell-based representation where walls and passages alternate on a grid. | |
| // Odd coordinates are walkable cells; even coordinates are walls between them. | |
| // Dimensions are always rounded up to the nearest odd number. | |
| // true = wall, false = passage. | |
| Maze :: struct { | |
| cells: [dynamic]bool, | |
| w, h: int, | |
| } | |
| // Creates a maze of size W x H. | |
| // W and H are rounded up to the nearest odd number if even. | |
| create :: proc(W := 33, H := 17) -> Maze { | |
| w := W | 1 | |
| h := H | 1 | |
| m := Maze{ | |
| w = w, | |
| h = h, | |
| cells = make([dynamic]bool, w * h), | |
| } | |
| // Fill all cells with walls | |
| for &c in m.cells do c = true | |
| // Iterative DFS; avoids stack overflow on large mazes | |
| stack := make([dynamic][2]int, 0, w * h / 4) | |
| defer delete(stack) | |
| // Pick a random starting cell on odd coordinates (always a valid walkable cell) | |
| start := [2]int{rand.int_max(w / 2) * 2 + 1, rand.int_max(h / 2) * 2 + 1} | |
| set(&m, start.x, start.y, false) | |
| append(&stack, start) | |
| dirs := [4][2]int{{0, -2}, {2, 0}, {0, 2}, {-2, 0}} | |
| for len(stack) > 0 { | |
| cur := stack[len(stack) - 1] | |
| x, y := cur.x, cur.y | |
| // Collect unvisited neighbours (still walls, at distance 2) | |
| neighbours: [4][2]int | |
| count := 0 | |
| for d in dirs { | |
| nx, ny := x + d[0], y + d[1] | |
| if nx >= 1 && nx < w - 1 && ny >= 1 && ny < h - 1 && get(&m, nx, ny) { | |
| neighbours[count] = {nx, ny} | |
| count += 1 | |
| } | |
| } | |
| if count == 0 { | |
| pop(&stack) | |
| } else { | |
| next := neighbours[rand.int_max(count)] | |
| // Carve passage: remove the wall between current cell and chosen neighbour | |
| set(&m, (x + next.x) / 2, (y + next.y) / 2, false) | |
| set(&m, next.x, next.y, false) | |
| append(&stack, next) | |
| } | |
| } | |
| return m | |
| } | |
| destroy :: proc(m: ^Maze) { | |
| delete(m.cells) | |
| } | |
| get :: proc(m: ^Maze, x, y: int) -> bool { | |
| return m.cells[y * m.w + x] | |
| } | |
| set :: proc(m: ^Maze, x, y: int, value: bool) { | |
| m.cells[y * m.w + x] = value | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment