From 40408f54ec88059fd6e046f84e462e3c2c5ce398 Mon Sep 17 00:00:00 2001 From: semblanceofsense Date: Fri, 31 Jan 2025 15:50:32 -0700 Subject: [PATCH] Initial maze solver --- .gitignore | 2 + go.mod | 5 ++ go.sum | 2 + internal/getmaze/getmaze.go | 80 +++++++++++++++++++++++++++++++ internal/outputmaze/outputmaze.go | 50 +++++++++++++++++++ internal/solvemaze/solvemaze.go | 51 ++++++++++++++++++++ mazesolver.go | 29 +++++++++++ util/queue/queue.go | 25 ++++++++++ 8 files changed, 244 insertions(+) create mode 100644 .gitignore create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/getmaze/getmaze.go create mode 100644 internal/outputmaze/outputmaze.go create mode 100644 internal/solvemaze/solvemaze.go create mode 100644 mazesolver.go create mode 100644 util/queue/queue.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..805af51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +testing/ +bin/ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a6fdedd --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module mazesolver + +go 1.23.5 + +require golang.org/x/image v0.23.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8ec2c9e --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= +golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= diff --git a/internal/getmaze/getmaze.go b/internal/getmaze/getmaze.go new file mode 100644 index 0000000..4f5622d --- /dev/null +++ b/internal/getmaze/getmaze.go @@ -0,0 +1,80 @@ +package getMaze + +import ( + "errors" + "fmt" + "image/color" + "image/png" + "os" +) + +/* +Pixel types: +-1 - explored +0 - unexplored +5 - wall +9 - end +*/ + +type Point struct { + X int; + Y int; + Value int +} + +type Maze [][]Point + +func GetMaze(imagepath string) (Maze, error) { + returnMaze := *new(Maze) + + imagereader, err := os.Open(imagepath) + if err != nil { + return *new(Maze), err + } + + image, err := png.Decode(imagereader) + if err != nil { + return *new(Maze), err + } + + for y := image.Bounds().Min.Y; y < image.Bounds().Max.Y; y += 10 { + newRow := make([]Point, 0) + for x := image.Bounds().Min.X; x < image.Bounds().Max.X; x+= 10 { + typ := 0 + switch image.At(x, y) { + case color.RGBA{ R: 255, G: 0, B: 0, A: 255 }: + typ = 0 + case color.RGBA{ R: 0, G: 0, B: 255, A: 255 }: + typ = 9 + case color.RGBA{ R: 255, G: 255, B: 255, A: 255 }: + typ = 0 + case color.RGBA{ R: 0, G: 0, B: 0, A: 255 }: + typ = 5 + default: + fmt.Println(image.At(x, y)) + return *new(Maze), errors.New("bad color") + } + newPoint := Point{ + X: x / 10, + Y: y / 10, + Value: typ, + } + newRow = append(newRow, newPoint) + } + returnMaze = append(returnMaze, newRow) + } + + + return returnMaze, err +} + +func PrintMaze(maze Maze) { + for _, v := range maze { + fmt.Print("[") + for _, vv := range v { + fmt.Print(vv.Value) + fmt.Print(", ") + } + fmt.Println("]") + } +} diff --git a/internal/outputmaze/outputmaze.go b/internal/outputmaze/outputmaze.go new file mode 100644 index 0000000..4c64cef --- /dev/null +++ b/internal/outputmaze/outputmaze.go @@ -0,0 +1,50 @@ +package outputmaze + +import ( + "errors" + "image" + "image/color" + "image/png" + getMaze "mazesolver/internal/getmaze" + "os" +) + +func EditMaze(points []getMaze.Point, oldPath, newPath string) (string, error) { + imagereader, err := os.Open(oldPath) + if err != nil { + return "", err + } + + image, err := png.Decode(imagereader) + if err != nil { + return "", err + } + + for _, v := range points { + updateColor(image, v, color.RGBA{0, 255, 0, 255}) + } + + f, err := os.Create(newPath) + if err != nil { + return "", err + } + png.Encode(f, image) + return newPath, nil +} + +type Changeable interface { + Set(x, y int, c color.Color) +} + +func updateColor(image image.Image, p getMaze.Point, color color.Color) error { + for i := p.Y * 10; i < p.Y * 10 + 10; i++ { + for ii := p.X * 10; ii < p.X * 10 + 10; ii++ { + if cimg, ok := image.(Changeable); ok { + cimg.Set(ii, i, color) + } else { + return errors.New("Image not changeable") + } + } + } + return nil +} diff --git a/internal/solvemaze/solvemaze.go b/internal/solvemaze/solvemaze.go new file mode 100644 index 0000000..ecca205 --- /dev/null +++ b/internal/solvemaze/solvemaze.go @@ -0,0 +1,51 @@ +package solvemaze + +import ( + "mazesolver/internal/getmaze" +) + +func FindPath(maze getMaze.Maze) []getMaze.Point { + returnSlice := make([]getMaze.Point, 0) + debugMaze := GetDebugMaze(maze) + findPathRecursive(maze[1][1], maze, &returnSlice, debugMaze) + return returnSlice +} + +func findPathRecursive(next getMaze.Point, maze getMaze.Maze, slice *[]getMaze.Point, debugMaze getMaze.Maze) bool { + maze[next.Y][next.X].Value = -1 + debugMaze[next.Y][next.X].Value = 1 + for _, v := range getAdjacent(next, maze) { + switch v.Value { + case -1: + continue + case 5: + continue + case 0: + if (findPathRecursive(v, maze, slice, debugMaze)) { + *slice = append(*slice, v) + return true + } + case 9: + *slice = append(*slice, v) + return true + } + } + return false +} + +func getAdjacent(point getMaze.Point, maze getMaze.Maze) []getMaze.Point { + returnSlice := make([]getMaze.Point, 0) + returnSlice = append(returnSlice, maze[point.Y + 1][point.X]) + returnSlice = append(returnSlice, maze[point.Y - 1][point.X]) + returnSlice = append(returnSlice, maze[point.Y][point.X + 1]) + returnSlice = append(returnSlice, maze[point.Y][point.X - 1]) + return returnSlice +} + +func GetDebugMaze(maze getMaze.Maze) getMaze.Maze { + a := make(getMaze.Maze, len(maze)) + for i := range a { + a[i] = make([]getMaze.Point, len(maze[0])) + } + return a +} diff --git a/mazesolver.go b/mazesolver.go new file mode 100644 index 0000000..5bcefe6 --- /dev/null +++ b/mazesolver.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "log" + getMaze "mazesolver/internal/getmaze" + "mazesolver/internal/outputmaze" + "mazesolver/internal/solvemaze" +) + +func main() { + path := "/tmp/maze.png" + maze, err := getMaze.GetMaze(path) + if err != nil { + log.Fatal(err) + } + + p := solvemaze.FindPath(maze) + if err != nil { + log.Fatal(err) + } + + newpath := "/tmp/outputmaze.png" + _, err = outputmaze.EditMaze(p, path, newpath) + if err != nil { + log.Fatal(err) + } + fmt.Println(newpath) +} diff --git a/util/queue/queue.go b/util/queue/queue.go new file mode 100644 index 0000000..f90a2a1 --- /dev/null +++ b/util/queue/queue.go @@ -0,0 +1,25 @@ +package queue + +type QueueInterface interface { + enqueue(int) + dequeue() int +} + +type Queue[T any] []T + +func (q Queue[T]) Enqueue(v T) { + q = append(q, v) +} + +func (q Queue[T]) Dequeue() T { + x := q[0] + q = q[1:] + return x +} + +func (q Queue[T]) IsEmpty() bool { + if len(q) == 0 { + return true + } + return false; +}