193 lines
3.4 KiB
Go
193 lines
3.4 KiB
Go
package mazer
|
|
|
|
import "math/rand/v2"
|
|
|
|
func GenerateMaze(width int, height int) [][]int {
|
|
if width <= 0 || height <= 0 {
|
|
return [][]int{}
|
|
}
|
|
|
|
cells := make([]int, width*height)
|
|
matrix := make([][]int, height)
|
|
for y := 0; y < height; y++ {
|
|
rowStart := y * width
|
|
matrix[y] = cells[rowStart : rowStart+width]
|
|
}
|
|
|
|
if width < 3 || height < 3 {
|
|
return matrix
|
|
}
|
|
|
|
startX, startY := 1, 1
|
|
matrix[startY][startX] = 1
|
|
|
|
stackX := make([]int, 1, width*height/2)
|
|
stackY := make([]int, 1, width*height/2)
|
|
stackX[0], stackY[0] = startX, startY
|
|
|
|
for len(stackX) > 0 {
|
|
last := len(stackX) - 1
|
|
x, y := stackX[last], stackY[last]
|
|
dirs := shuffledDirections()
|
|
|
|
carved := false
|
|
for _, d := range dirs {
|
|
dx, dy := 0, 0
|
|
switch d {
|
|
case 0:
|
|
dy = -2
|
|
case 1:
|
|
dx = 2
|
|
case 2:
|
|
dy = 2
|
|
default:
|
|
dx = -2
|
|
}
|
|
|
|
nx, ny := x+dx, y+dy
|
|
if nx <= 0 || nx >= width-1 || ny <= 0 || ny >= height-1 {
|
|
continue
|
|
}
|
|
if matrix[ny][nx] == 1 {
|
|
continue
|
|
}
|
|
|
|
matrix[y+dy/2][x+dx/2] = 1
|
|
matrix[ny][nx] = 1
|
|
stackX = append(stackX, nx)
|
|
stackY = append(stackY, ny)
|
|
carved = true
|
|
break
|
|
}
|
|
|
|
if !carved {
|
|
stackX = stackX[:last]
|
|
stackY = stackY[:last]
|
|
}
|
|
}
|
|
|
|
// Entrance on top border at a random connected X.
|
|
topChoices := make([]int, 0, width/2)
|
|
for x := 1; x < width-1; x++ {
|
|
if matrix[1][x] == 1 {
|
|
topChoices = append(topChoices, x)
|
|
}
|
|
}
|
|
entranceX := topChoices[rand.IntN(len(topChoices))]
|
|
matrix[0][entranceX] = 1
|
|
|
|
// Exit on bottom border at a random connected X.
|
|
bottomChoices := make([]int, 0, width/2)
|
|
for x := 1; x < width-1; x++ {
|
|
if matrix[height-2][x] == 1 {
|
|
bottomChoices = append(bottomChoices, x)
|
|
}
|
|
}
|
|
exitX := bottomChoices[rand.IntN(len(bottomChoices))]
|
|
matrix[height-1][exitX] = 1
|
|
|
|
return matrix
|
|
}
|
|
|
|
func FindSolutionPath(matrix [][]int) [][]bool {
|
|
height := len(matrix)
|
|
if height == 0 {
|
|
return nil
|
|
}
|
|
width := len(matrix[0])
|
|
if width == 0 {
|
|
return nil
|
|
}
|
|
|
|
startX := -1
|
|
endX := -1
|
|
for x := 0; x < width; x++ {
|
|
if matrix[0][x] == 1 {
|
|
startX = x
|
|
break
|
|
}
|
|
}
|
|
for x := 0; x < width; x++ {
|
|
if matrix[height-1][x] == 1 {
|
|
endX = x
|
|
break
|
|
}
|
|
}
|
|
if startX == -1 || endX == -1 {
|
|
return nil
|
|
}
|
|
|
|
start := startX
|
|
end := (height-1)*width + endX
|
|
parent := make([]int, width*height)
|
|
for i := range parent {
|
|
parent[i] = -1
|
|
}
|
|
visited := make([]bool, width*height)
|
|
queue := make([]int, 1, width*height)
|
|
queue[0] = start
|
|
visited[start] = true
|
|
|
|
dx := [4]int{0, 1, 0, -1}
|
|
dy := [4]int{-1, 0, 1, 0}
|
|
|
|
found := false
|
|
for head := 0; head < len(queue); head++ {
|
|
idx := queue[head]
|
|
if idx == end {
|
|
found = true
|
|
break
|
|
}
|
|
|
|
x := idx % width
|
|
y := idx / width
|
|
for i := 0; i < 4; i++ {
|
|
nx := x + dx[i]
|
|
ny := y + dy[i]
|
|
if nx < 0 || nx >= width || ny < 0 || ny >= height {
|
|
continue
|
|
}
|
|
if matrix[ny][nx] == 0 {
|
|
continue
|
|
}
|
|
|
|
nIdx := ny*width + nx
|
|
if visited[nIdx] {
|
|
continue
|
|
}
|
|
visited[nIdx] = true
|
|
parent[nIdx] = idx
|
|
queue = append(queue, nIdx)
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return nil
|
|
}
|
|
|
|
pathCells := make([]bool, width*height)
|
|
for idx := end; idx != -1; idx = parent[idx] {
|
|
pathCells[idx] = true
|
|
if idx == start {
|
|
break
|
|
}
|
|
}
|
|
|
|
path := make([][]bool, height)
|
|
for y := 0; y < height; y++ {
|
|
rowStart := y * width
|
|
path[y] = pathCells[rowStart : rowStart+width]
|
|
}
|
|
|
|
return path
|
|
}
|
|
|
|
func shuffledDirections() [4]uint8 {
|
|
dirs := [4]uint8{0, 1, 2, 3}
|
|
for i := 3; i > 0; i-- {
|
|
j := rand.IntN(i + 1)
|
|
dirs[i], dirs[j] = dirs[j], dirs[i]
|
|
}
|
|
return dirs
|
|
}
|