Updatez
This commit is contained in:
71
main.go
71
main.go
@@ -1,65 +1,30 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
"fmt"
|
||||||
"image/color"
|
"log"
|
||||||
"image/png"
|
|
||||||
"os"
|
"test-maze/maze"
|
||||||
"test-maze/mazer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const SIZE_X = 21
|
const (
|
||||||
const SIZE_Y = 21
|
sizeX = 21
|
||||||
const SCALE_FACTOR = 5
|
sizeY = 21
|
||||||
const COLOR_SOLUTION_PATH = true
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
matrix := mazer.GenerateMaze(SIZE_X, SIZE_Y)
|
grid, err := maze.Generate(sizeX, sizeY)
|
||||||
|
|
||||||
var solutionPath [][]bool
|
|
||||||
if COLOR_SOLUTION_PATH {
|
|
||||||
solutionPath = mazer.FindSolutionPath(matrix)
|
|
||||||
}
|
|
||||||
|
|
||||||
img := image.NewRGBA(image.Rect(0, 0, SIZE_X*SCALE_FACTOR, SIZE_Y*SCALE_FACTOR))
|
|
||||||
|
|
||||||
white := color.RGBA{R: 255, G: 255, B: 255, A: 255}
|
|
||||||
black := color.RGBA{R: 0, G: 0, B: 0, A: 255}
|
|
||||||
green := color.RGBA{R: 0, G: 180, B: 0, A: 255}
|
|
||||||
|
|
||||||
for cellY := 0; cellY < SIZE_Y; cellY++ {
|
|
||||||
startY := cellY * SCALE_FACTOR
|
|
||||||
for cellX := 0; cellX < SIZE_X; cellX++ {
|
|
||||||
startX := cellX * SCALE_FACTOR
|
|
||||||
|
|
||||||
p := black
|
|
||||||
if matrix[cellY][cellX] == 1 {
|
|
||||||
p = white
|
|
||||||
if COLOR_SOLUTION_PATH && solutionPath != nil && solutionPath[cellY][cellX] {
|
|
||||||
p = green
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for dy := 0; dy < SCALE_FACTOR; dy++ {
|
|
||||||
row := img.Pix[(startY+dy)*img.Stride:]
|
|
||||||
for dx := 0; dx < SCALE_FACTOR; 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("maze.png")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatalf("generate maze: %v", err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
if err := png.Encode(f, img); err != nil {
|
options := maze.DefaultRenderOptions()
|
||||||
panic(err)
|
options.HighlightPath = true
|
||||||
|
options.Scale = 5
|
||||||
|
|
||||||
|
if err := maze.SavePNG(grid, "maze.png", options); err != nil {
|
||||||
|
log.Fatalf("save maze image: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("Saved maze to maze.png")
|
||||||
}
|
}
|
||||||
|
|||||||
237
maze/maze.go
Normal file
237
maze/maze.go
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
package maze
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/rand/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Grid represents a maze as a matrix of cells.
|
||||||
|
// A value of 0 is a wall and 1 is a walkable path.
|
||||||
|
type Grid [][]int
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvalidDimensions is returned when width or height are not positive.
|
||||||
|
ErrInvalidDimensions = errors.New("maze dimensions must be greater than zero")
|
||||||
|
// ErrInvalidGrid is returned when a maze grid is empty or malformed.
|
||||||
|
ErrInvalidGrid = errors.New("maze grid must be non-empty and rectangular")
|
||||||
|
// ErrNoEntranceExit is returned when the maze has no valid top entrance or bottom exit.
|
||||||
|
ErrNoEntranceExit = errors.New("maze grid must contain a top entrance and bottom exit")
|
||||||
|
// ErrNoPath is returned when no path exists between entrance and exit.
|
||||||
|
ErrNoPath = errors.New("no path exists between maze entrance and exit")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generate creates a maze grid with the given width and height.
|
||||||
|
//
|
||||||
|
// The returned grid uses 0 for walls and 1 for open cells.
|
||||||
|
// The algorithm is randomized depth-first carving with one entrance on the top border
|
||||||
|
// and one exit on the bottom border.
|
||||||
|
func Generate(width, height int) (Grid, error) {
|
||||||
|
if width <= 0 || height <= 0 {
|
||||||
|
return nil, ErrInvalidDimensions
|
||||||
|
}
|
||||||
|
|
||||||
|
cells := make([]int, width*height)
|
||||||
|
grid := make(Grid, height)
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
rowStart := y * width
|
||||||
|
grid[y] = cells[rowStart : rowStart+width]
|
||||||
|
}
|
||||||
|
|
||||||
|
if width < 3 || height < 3 {
|
||||||
|
return grid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
startX, startY := 1, 1
|
||||||
|
grid[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 := directionDelta(d)
|
||||||
|
nx, ny := x+dx, y+dy
|
||||||
|
if nx <= 0 || nx >= width-1 || ny <= 0 || ny >= height-1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if grid[ny][nx] == 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
grid[y+dy/2][x+dx/2] = 1
|
||||||
|
grid[ny][nx] = 1
|
||||||
|
stackX = append(stackX, nx)
|
||||||
|
stackY = append(stackY, ny)
|
||||||
|
carved = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if !carved {
|
||||||
|
stackX = stackX[:last]
|
||||||
|
stackY = stackY[:last]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
topChoices := make([]int, 0, width/2)
|
||||||
|
for x := 1; x < width-1; x++ {
|
||||||
|
if grid[1][x] == 1 {
|
||||||
|
topChoices = append(topChoices, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(topChoices) > 0 {
|
||||||
|
entranceX := topChoices[rand.IntN(len(topChoices))]
|
||||||
|
grid[0][entranceX] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomChoices := make([]int, 0, width/2)
|
||||||
|
for x := 1; x < width-1; x++ {
|
||||||
|
if grid[height-2][x] == 1 {
|
||||||
|
bottomChoices = append(bottomChoices, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(bottomChoices) > 0 {
|
||||||
|
exitX := bottomChoices[rand.IntN(len(bottomChoices))]
|
||||||
|
grid[height-1][exitX] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solve finds a path from the top entrance to the bottom exit in a maze.
|
||||||
|
//
|
||||||
|
// It returns a matrix with the same dimensions as the input grid, where true
|
||||||
|
// marks cells that belong to the computed path.
|
||||||
|
func Solve(grid Grid) ([][]bool, error) {
|
||||||
|
width, height, err := validateGrid(grid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
startX := -1
|
||||||
|
endX := -1
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
if grid[0][x] == 1 {
|
||||||
|
startX = x
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
if grid[height-1][x] == 1 {
|
||||||
|
endX = x
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if startX == -1 || endX == -1 {
|
||||||
|
return nil, ErrNoEntranceExit
|
||||||
|
}
|
||||||
|
|
||||||
|
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 grid[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, ErrNoPath
|
||||||
|
}
|
||||||
|
|
||||||
|
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, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateGrid(grid Grid) (width, height int, err error) {
|
||||||
|
height = len(grid)
|
||||||
|
if height == 0 {
|
||||||
|
return 0, 0, ErrInvalidGrid
|
||||||
|
}
|
||||||
|
width = len(grid[0])
|
||||||
|
if width == 0 {
|
||||||
|
return 0, 0, ErrInvalidGrid
|
||||||
|
}
|
||||||
|
for _, row := range grid {
|
||||||
|
if len(row) != width {
|
||||||
|
return 0, 0, ErrInvalidGrid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return width, height, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func directionDelta(direction uint8) (dx, dy int) {
|
||||||
|
switch direction {
|
||||||
|
case 0:
|
||||||
|
return 0, -2
|
||||||
|
case 1:
|
||||||
|
return 2, 0
|
||||||
|
case 2:
|
||||||
|
return 0, 2
|
||||||
|
default:
|
||||||
|
return -2, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
106
maze/render.go
Normal file
106
maze/render.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package maze
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultWallColor is used for wall cells.
|
||||||
|
DefaultWallColor = color.RGBA{R: 0, G: 0, B: 0, A: 255}
|
||||||
|
// DefaultPathColor is used for open maze cells.
|
||||||
|
DefaultPathColor = color.RGBA{R: 255, G: 255, B: 255, A: 255}
|
||||||
|
// DefaultSolutionColor is used to highlight solution cells.
|
||||||
|
DefaultSolutionColor = color.RGBA{R: 0, G: 180, B: 0, A: 255}
|
||||||
|
)
|
||||||
|
|
||||||
|
// RenderOptions controls how a maze image is rendered.
|
||||||
|
type RenderOptions struct {
|
||||||
|
Scale int
|
||||||
|
WallColor color.RGBA
|
||||||
|
PathColor color.RGBA
|
||||||
|
SolutionColor color.RGBA
|
||||||
|
HighlightPath bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultRenderOptions returns baseline rendering options.
|
||||||
|
func DefaultRenderOptions() RenderOptions {
|
||||||
|
return RenderOptions{
|
||||||
|
Scale: 5,
|
||||||
|
WallColor: DefaultWallColor,
|
||||||
|
PathColor: DefaultPathColor,
|
||||||
|
SolutionColor: DefaultSolutionColor,
|
||||||
|
HighlightPath: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderImage converts a maze grid into an RGBA image.
|
||||||
|
//
|
||||||
|
// If options.HighlightPath is true, Solve is used and solution cells are painted
|
||||||
|
// with options.SolutionColor.
|
||||||
|
func RenderImage(grid Grid, options RenderOptions) (*image.RGBA, error) {
|
||||||
|
width, height, err := validateGrid(grid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if options.Scale <= 0 {
|
||||||
|
return nil, fmt.Errorf("scale must be greater than zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, width*options.Scale, height*options.Scale))
|
||||||
|
|
||||||
|
var solutionPath [][]bool
|
||||||
|
if options.HighlightPath {
|
||||||
|
solutionPath, err = Solve(grid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for cellY := 0; cellY < height; cellY++ {
|
||||||
|
startY := cellY * options.Scale
|
||||||
|
for cellX := 0; cellX < width; cellX++ {
|
||||||
|
startX := cellX * options.Scale
|
||||||
|
|
||||||
|
pixel := options.WallColor
|
||||||
|
if grid[cellY][cellX] == 1 {
|
||||||
|
pixel = options.PathColor
|
||||||
|
if options.HighlightPath && solutionPath[cellY][cellX] {
|
||||||
|
pixel = options.SolutionColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for dy := 0; dy < options.Scale; dy++ {
|
||||||
|
row := img.Pix[(startY+dy)*img.Stride:]
|
||||||
|
for dx := 0; dx < options.Scale; dx++ {
|
||||||
|
offset := (startX + dx) * 4
|
||||||
|
row[offset+0] = pixel.R
|
||||||
|
row[offset+1] = pixel.G
|
||||||
|
row[offset+2] = pixel.B
|
||||||
|
row[offset+3] = pixel.A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePNG renders a maze image and writes it to a PNG file.
|
||||||
|
func SavePNG(grid Grid, outputPath string, options RenderOptions) error {
|
||||||
|
img, err := RenderImage(grid, options)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(outputPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return png.Encode(f, img)
|
||||||
|
}
|
||||||
192
mazer/maze.go
192
mazer/maze.go
@@ -1,192 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user