diff --git a/maze/maze.go b/maze/maze.go index 825024b..81017c7 100644 --- a/maze/maze.go +++ b/maze/maze.go @@ -9,6 +9,8 @@ import ( // A value of 0 is a wall and 1 is a walkable path. type Grid [][]int +const GOLDEN_RATION_BIT_MIXER = 0x9e3779b97f4a7c15 + var ( // ErrInvalidDimensions is returned when width or height are not positive. ErrInvalidDimensions = errors.New("maze dimensions must be greater than zero") @@ -33,7 +35,7 @@ func Generate(width, height int) (Grid, error) { // // The same width, height, and seed always produce the same maze. func GenerateWithSeed(width, height int, seed uint64) (Grid, error) { - rng := rand.New(rand.NewPCG(seed, seed^0x9e3779b97f4a7c15)) + rng := rand.New(rand.NewPCG(seed, seed^GOLDEN_RATION_BIT_MIXER)) return generate(width, height, rng.IntN) } @@ -60,6 +62,9 @@ func generate(width, height int, intN func(int) int) (Grid, error) { stackY := make([]int, 1, width*height/2) stackX[0], stackY[0] = startX, startY + // Depth-first backtracking carve: + // grow the maze from the current cell into a random unvisited neighbor, + // and backtrack when no further expansion is possible. for len(stackX) > 0 { last := len(stackX) - 1 x, y := stackX[last], stackY[last] @@ -90,6 +95,7 @@ func generate(width, height int, intN func(int) int) (Grid, error) { } } + // Choose a top-border entrance that connects to an already carved cell. topChoices := make([]int, 0, width/2) for x := 1; x < width-1; x++ { if grid[1][x] == 1 { @@ -101,6 +107,7 @@ func generate(width, height int, intN func(int) int) (Grid, error) { grid[0][entranceX] = 1 } + // Choose a bottom-border exit that connects to an already carved cell. bottomChoices := make([]int, 0, width/2) for x := 1; x < width-1; x++ { if grid[height-2][x] == 1 { @@ -158,6 +165,8 @@ func Solve(grid Grid) ([][]bool, error) { dx := [4]int{0, 1, 0, -1} dy := [4]int{-1, 0, 1, 0} + // Breadth-first search from entrance to exit while recording parent links + // so the shortest path can be reconstructed afterward. found := false for head := 0; head < len(queue); head++ { idx := queue[head] @@ -192,6 +201,7 @@ func Solve(grid Grid) ([][]bool, error) { return nil, ErrNoPath } + // Reconstruct the path by following parent pointers backward from exit to entrance. pathCells := make([]bool, width*height) for idx := end; idx != -1; idx = parent[idx] { pathCells[idx] = true @@ -200,6 +210,7 @@ func Solve(grid Grid) ([][]bool, error) { } } + // Expose the flat path buffer as a 2D matrix aligned with the maze grid. path := make([][]bool, height) for y := 0; y < height; y++ { rowStart := y * width