This commit is contained in:
2026-02-25 21:47:27 +02:00
parent 7d1f2f8934
commit 42029cdfec
4 changed files with 103 additions and 108 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
maze.png

37
main.go
View File

@@ -5,32 +5,45 @@ import (
"image/color" "image/color"
"image/png" "image/png"
"os" "os"
"test-maze/mazer"
) )
const SIZE_X = 128 const SIZE_X = 48
const SIZE_Y = 128 const SIZE_Y = 48
const CELL_SIZE = 16 const CELL_SCALE = 10
const CELL_SCALE = 8
func main() { func main() {
matrix := mazer.GenerateMaze(SIZE_X, SIZE_Y)
img := image.NewRGBA(image.Rect(0, 0, SIZE_X*CELL_SCALE, SIZE_Y*CELL_SCALE)) img := image.NewRGBA(image.Rect(0, 0, SIZE_X*CELL_SCALE, SIZE_Y*CELL_SCALE))
white := color.RGBA{R: 255, G: 255, B: 255, A: 255} white := color.RGBA{R: 255, G: 255, B: 255, A: 255}
black := color.RGBA{R: 0, G: 0, B: 0, A: 255} black := color.RGBA{R: 0, G: 0, B: 0, A: 255}
for y := 0; y < SIZE_Y*CELL_SCALE; y++ { for cellY := 0; cellY < SIZE_Y; cellY++ {
for x := 0; x < SIZE_X*CELL_SCALE; x++ { startY := cellY * CELL_SCALE
stripeIndex := (x / CELL_SIZE) % 2 for cellX := 0; cellX < SIZE_X; cellX++ {
if stripeIndex == 0 { startX := cellX * CELL_SCALE
img.SetRGBA(x, y, white)
} else { p := black
img.SetRGBA(x, y, black) if matrix[cellY][cellX] == 1 {
p = white
}
for dy := 0; dy < CELL_SCALE; dy++ {
row := img.Pix[(startY+dy)*img.Stride:]
for dx := 0; dx < CELL_SCALE; dx++ {
offset := (startX + dx) * 4
row[offset+0] = p.R
row[offset+1] = p.G
row[offset+2] = p.B
row[offset+3] = p.A
}
} }
} }
} }
f, err := os.Create("stripes.png") f, err := os.Create("maze.png")
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@@ -1,124 +1,105 @@
package mazer package mazer
import ( import "math/rand/v2"
"math/rand/v2"
)
func GenerateMaze(width int, height int) { func GenerateMaze(width int, height int) [][]int {
if width <= 0 || height <= 0 {
return [][]int{}
}
// Init array cells := make([]int, width*height)
matrix := make([][]int, height) matrix := make([][]int, height)
// 2. Allocate each inner slice (width) for y := 0; y < height; y++ {
for i := range matrix { rowStart := y * width
matrix[i] = make([]int, width) matrix[y] = cells[rowStart : rowStart+width]
} }
startPoint := rand.IntN(width + 1) // Keep a full black border: path only exists inside [1..width-2] x [1..height-2].
matrix[0][startPoint] = 1 if width < 3 || height < 3 {
return matrix
// Start moving until out of moves
}
func pickRandomDirection(matrix [][]int, posX int, posY int) []int {
height := len(matrix)
if posY == height-1 {
return nil
} }
validDirs := getValidDirections(matrix, posX, posY) startX, startY := 1, 1
if len(validDirs) == 0 { endX, endY := width-2, height-2
return []int{0, 1}
parent := make([]int, width*height)
for i := range parent {
parent[i] = -1
}
visited := make([]bool, width*height)
dx := [4]int{0, -1, 1, 0}
dy := [4]int{1, 0, 0, -1}
type node struct {
x int
y int
dirs [4]uint8
next int
} }
return validDirs[rand.IntN(len(validDirs))] stack := make([]node, 1, (width-2)*(height-2))
} stack[0] = node{x: startX, y: startY, dirs: shuffledDirections()}
startIdx := startY*width + startX
endIdx := endY*width + endX
visited[startIdx] = true
func getValidDirections(matrix [][]int, posX int, posY int) [][]int { found := false
height := len(matrix) for len(stack) > 0 {
width := len(matrix[0]) last := len(stack) - 1
validDirs := make([][]int, 0, 4) top := &stack[last]
possibleDirs := [][]int{ if top.x == endX && top.y == endY {
{0, 1}, // Down found = true
{-1, 0}, // Left break
{1, 0}, // Right
{0, -1}, // Up
}
prevX, prevY, hasPrev := getPreviousPathCell(matrix, posX, posY)
if hasPrev && prevX == posX && prevY == posY+1 {
// If we just moved up, force the next step to be side/down (no repeated up).
possibleDirs = [][]int{
{-1, 0}, // Left
{1, 0}, // Right
{0, 1}, // Down
} }
}
neighborDirs := [][]int{ if top.next == 4 {
{0, -1}, stack = stack[:last]
{0, 1},
{-1, 0},
{1, 0},
}
for _, dir := range possibleDirs {
newX := posX + dir[0]
newY := posY + dir[1]
if newX < 0 || newX >= width || newY < 0 || newY >= height {
continue
}
if matrix[newY][newX] == 1 {
continue continue
} }
tooClose := false d := top.dirs[top.next]
for _, neighborDir := range neighborDirs { top.next++
neighborX := newX + neighborDir[0]
neighborY := newY + neighborDir[1]
if neighborX < 0 || neighborX >= width || neighborY < 0 || neighborY >= height { nx := top.x + dx[d]
continue ny := top.y + dy[d]
} if nx <= 0 || nx >= width-1 || ny <= 0 || ny >= height-1 {
if neighborX == posX && neighborY == posY {
continue
}
if matrix[neighborY][neighborX] == 1 {
tooClose = true
break
}
}
if tooClose {
continue continue
} }
validDirs = append(validDirs, dir) nIdx := ny*width + nx
} if visited[nIdx] {
return validDirs
}
func getPreviousPathCell(matrix [][]int, posX int, posY int) (int, int, bool) {
height := len(matrix)
width := len(matrix[0])
dirs := [][]int{
{0, -1},
{0, 1},
{-1, 0},
{1, 0},
}
for _, dir := range dirs {
x := posX + dir[0]
y := posY + dir[1]
if x < 0 || x >= width || y < 0 || y >= height {
continue continue
} }
if matrix[y][x] == 1 {
return x, y, true visited[nIdx] = true
parent[nIdx] = top.y*width + top.x
stack = append(stack, node{x: nx, y: ny, dirs: shuffledDirections()})
}
if !found {
matrix[startY][startX] = 1
return matrix
}
for idx := endIdx; idx != -1; idx = parent[idx] {
x := idx % width
y := idx / width
matrix[y][x] = 1
if idx == startIdx {
break
} }
} }
return 0, 0, false return matrix
}
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
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB