Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d1108be94b | ||
|
2e8ccce703 | ||
|
77843db6de | ||
|
b8546a4d57 | ||
|
741db457fb | ||
|
7bac753507 | ||
|
dea1586280 | ||
|
64864bb47c | ||
|
81d682cbed | ||
|
2b74c459f4 | ||
|
e757402406 | ||
|
c0c2f81fd2 | ||
|
c7dfaf1ca7 | ||
|
0181914013 | ||
|
5caf21634e | ||
|
015a656abf | ||
|
aac0e4628a | ||
|
fc02af0f17 | ||
|
dedc997c9d | ||
|
06306fdf55 | ||
|
f08c406ca2 | ||
|
d1b50ba372 | ||
|
86d3496bca | ||
|
37db489838 | ||
|
28fef3736b | ||
|
4ff4b6b2c1 |
5
.github/workflows/automatic-prerelease.yml
vendored
5
.github/workflows/automatic-prerelease.yml
vendored
@ -23,7 +23,9 @@ jobs:
|
||||
run: sudo apt install libxcursor-dev libxinerama-dev libxrandr-dev libxi-dev libgl-dev libxxf86vm-dev
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
run: |
|
||||
go build -v ./...
|
||||
go build maze-solver
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
@ -34,3 +36,4 @@ jobs:
|
||||
automatic_release_tag: "latest"
|
||||
prerelease: true
|
||||
title: "Development Build"
|
||||
files: maze-solver
|
||||
|
5
.github/workflows/tagged-release.yml
vendored
5
.github/workflows/tagged-release.yml
vendored
@ -23,7 +23,9 @@ jobs:
|
||||
run: sudo apt install libxcursor-dev libxinerama-dev libxrandr-dev libxi-dev libgl-dev libxxf86vm-dev
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
run: |
|
||||
go build -v ./...
|
||||
go build maze-solver
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
@ -32,3 +34,4 @@ jobs:
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
prerelease: false
|
||||
files: maze-solver
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,4 +21,4 @@
|
||||
go.work
|
||||
/Session.vim
|
||||
/maze.png
|
||||
/maze_sol.png
|
||||
/sol.png
|
||||
|
89
README.md
Normal file
89
README.md
Normal file
@ -0,0 +1,89 @@
|
||||
# maze-solver-go
|
||||
|
||||
## Introduction
|
||||
|
||||
This is a simple little maze solver made for fun and practise writing code in
|
||||
golang. This project is a complete re-write of another maze solver I've written
|
||||
in Java back in 2018 after the first semester of uni at EPFL. Needless to say,
|
||||
this version is _way_ better as I have written it now that I have my Bachelor's
|
||||
degree in Computer Science.
|
||||
|
||||
### Goal of the project
|
||||
|
||||
The goal of this side project was to deepen my understanding of path-finding
|
||||
algorithms, together with trying to create a good design, focusing on
|
||||
dependency injection and unit testing. It was very instructive.
|
||||
|
||||
Not to brag or anything, but the design was quite good, because after
|
||||
completing the following steps of the project:
|
||||
|
||||
- reading a maze;
|
||||
- solving it;
|
||||
- writing the solution to the file-system;
|
||||
|
||||
an idea came to my mind: add the feature of visualizing the progress of the
|
||||
solving algorithm by opening a window that displays the progress of the solver.
|
||||
Well, each module (readers, writers and solvers) were design to be as decoupled
|
||||
as possible, and it allowed me to implement the `visualizer` feature in only a
|
||||
couple of hours (which I was honestly not expecting).
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `go` >= 1.21
|
||||
- `ffmpeg` (optional, for video visualization, see [visualization methods](#visulazation-methods))
|
||||
|
||||
## Usage
|
||||
|
||||
After downloading `maze-solver` from the
|
||||
[assets of the latest release](releases/latest "Latest release"), you can use
|
||||
it with the following arguments
|
||||
|
||||
| Short | Long | Default | Description |
|
||||
| ----- | ----------------- | ---------- | ----------------------------------------------------------------------------------------- |
|
||||
| -h | --help | | Print help information |
|
||||
| -v | --verbose | 0 | Verbose level of the solver, see [verbose levels](#verbose-levels) |
|
||||
| -i | --input | `maze.png` | Input file, can be a `.txt` or `.png` file |
|
||||
| -o | --output | `sol.png` | Output file, can be a `.txt` or `.png` file |
|
||||
| | --path-char-in | `' '` | Character to represent the path in an input text file. |
|
||||
| | --wall-char-in | `'#'` | Character to represent the wall in an input text file. |
|
||||
| | --path-char-out | `' '` | Character to represent the path in an output text file. |
|
||||
| | --wall-char-out | `'#'` | Character to represent the wall in an output text file. |
|
||||
| | --cell-size-in | 3 | Size of a cell (in pixels) for input file of image type. |
|
||||
| | --cell-size-out | 3 | Size of a cell (in pixels) for output file of image type. |
|
||||
| -a | --algo | a-star | Algorithm to solve the maze, see [solving algorithms](#solving-algorithms) |
|
||||
| | --visualize | | Visualizer the progress of the solver, see [visualization methods](#visulazation-methods) |
|
||||
| | --video-name | `sol.mp4` | Name of the output file if --visualize is set to 'video'. |
|
||||
| | --video-framerate | 60 | Framerate of the video if --visualize is set to 'video'. |
|
||||
|
||||
### Verbose levels
|
||||
|
||||
| Level | Description |
|
||||
| ----- | ------------------------------------------------------------------------- |
|
||||
| 0 | nothing printed to stdout |
|
||||
| 1 | print the total time taken by the solver (time of the main() function) |
|
||||
| 2 | prints the time the solving algorithm took to run |
|
||||
| 3 | prints the time taken by each section (reader, solving algorithm, writer) |
|
||||
|
||||
### Visualization methods
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ---------------------------------------------------------------------- |
|
||||
| window | will give a live feed of the solver |
|
||||
| video | creates a video where each frame is a step the solving algorithm takes |
|
||||
|
||||
### Solving algorithms
|
||||
|
||||
| Option | Description |
|
||||
| -------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| dfs | [Depth-first search](https://en.wikipedia.org/wiki/Depth-first_search "Wikipedia: Depth-first search") |
|
||||
| bfs | [Breadth-first search](https://en.wikipedia.org/wiki/Breadth-first_search "Wikipedia: Breadth-first search") |
|
||||
| dijkstra | [Dijkstra's algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm "Wikipedia: Dijkstra's algorithm") |
|
||||
| a-star | [A\*](https://en.wikipedia.org/wiki/A*_search_algorithm "Wikipedia: A* search algorithm") |
|
||||
|
||||
## Examples
|
||||
|
||||
| BFS | DFS |
|
||||
| :----------------------------------------: | :-----------------------------------: |
|
||||
|  |  |
|
||||
|  |  |
|
||||
| **Dijkstra** | **A\*** |
|
BIN
assets/videos/a-star.gif
Normal file
BIN
assets/videos/a-star.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.3 MiB |
BIN
assets/videos/bfs.gif
Normal file
BIN
assets/videos/bfs.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 MiB |
BIN
assets/videos/dfs.gif
Normal file
BIN
assets/videos/dfs.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 MiB |
BIN
assets/videos/dijkstra.gif
Normal file
BIN
assets/videos/dijkstra.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.7 MiB |
18
main.go
18
main.go
@ -105,8 +105,8 @@ func parse_arguments() (*reader.ReaderFactory, *writer.WriterFactory, *solver.So
|
||||
|
||||
writerFactory.Type = writer.TYPES[".png"]
|
||||
writerFactory.Filename = argparser.String("o", "output", &argparse.Options{
|
||||
Help: "Input file",
|
||||
Default: "maze_sol.png",
|
||||
Help: "Output file",
|
||||
Default: "sol.png",
|
||||
Validate: func(args []string) error {
|
||||
var ok bool
|
||||
extension := args[0][len(args[0])-4:]
|
||||
@ -120,7 +120,7 @@ func parse_arguments() (*reader.ReaderFactory, *writer.WriterFactory, *solver.So
|
||||
})
|
||||
|
||||
readerFactory.PathChar = argparser.String("", "path-char-in", &argparse.Options{
|
||||
Help: "Character to represent the path in a input text file",
|
||||
Help: "Character to represent the path in an input text file",
|
||||
Default: " ",
|
||||
Validate: func(args []string) error {
|
||||
if len(args[0]) > 1 {
|
||||
@ -131,7 +131,7 @@ func parse_arguments() (*reader.ReaderFactory, *writer.WriterFactory, *solver.So
|
||||
})
|
||||
|
||||
readerFactory.WallChar = argparser.String("", "wall-char-in", &argparse.Options{
|
||||
Help: "Character to represent the wall in a input text file",
|
||||
Help: "Character to represent the wall in an input text file",
|
||||
Default: "#",
|
||||
Validate: func(args []string) error {
|
||||
if len(args[0]) > 1 {
|
||||
@ -142,7 +142,7 @@ func parse_arguments() (*reader.ReaderFactory, *writer.WriterFactory, *solver.So
|
||||
})
|
||||
|
||||
writerFactory.PathChar = argparser.String("", "path-char-out", &argparse.Options{
|
||||
Help: "Character to represent the path in a output text file",
|
||||
Help: "Character to represent the path in an output text file",
|
||||
Default: " ",
|
||||
Validate: func(args []string) error {
|
||||
if len(args[0]) > 1 {
|
||||
@ -153,7 +153,7 @@ func parse_arguments() (*reader.ReaderFactory, *writer.WriterFactory, *solver.So
|
||||
})
|
||||
|
||||
writerFactory.WallChar = argparser.String("", "wall-char-out", &argparse.Options{
|
||||
Help: "Character to represent the wall in a output text file",
|
||||
Help: "Character to represent the wall in an output text file",
|
||||
Default: "#",
|
||||
Validate: func(args []string) error {
|
||||
if len(args[0]) > 1 {
|
||||
@ -174,18 +174,18 @@ func parse_arguments() (*reader.ReaderFactory, *writer.WriterFactory, *solver.So
|
||||
})
|
||||
|
||||
solverFactory.Type = argparser.Selector("a", "algo", solver.TYPES, &argparse.Options{
|
||||
Help: fmt.Sprintf("Algorithm to solve the maze, avaiable options: %s", strings.Join(solver.TYPES, ", ")),
|
||||
Help: fmt.Sprintf("Algorithm to solve the maze, available options: %s", strings.Join(solver.TYPES, ", ")),
|
||||
Default: solver.TYPES[0],
|
||||
})
|
||||
|
||||
visFactory.Type = argparser.Selector("", "visualize", visualizer.VIZ_METHODS, &argparse.Options{
|
||||
Help: fmt.Sprintf("Visualizer the progress of the solver, avaiable options: %s. Window will give a live feed of the solver, whereas video creates a video --output with mp4 extension", strings.Join(visualizer.VIZ_METHODS, ", ")),
|
||||
Help: fmt.Sprintf("Visualizer the progress of the solver, available options: %s. Window will give a live feed of the solver, whereas video creates a video creates a video where each frame is a step the solving algorithm takes", strings.Join(visualizer.VIZ_METHODS, ", ")),
|
||||
Default: "",
|
||||
})
|
||||
|
||||
visFactory.Filename = argparser.String("", "video-name", &argparse.Options{
|
||||
Help: "Name of the output file if --visualize is set to 'video'",
|
||||
Default: "maze_sol.mp4",
|
||||
Default: "sol.mp4",
|
||||
})
|
||||
|
||||
visFactory.Framerate = argparser.Float("", "video-framerate", &argparse.Options{
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/mazznoer/colorgrad"
|
||||
)
|
||||
@ -25,7 +24,6 @@ func (v *VideoVisualizer) Init(*maze.Maze) {
|
||||
panic(err)
|
||||
}
|
||||
v.ffmpeg_cmd = path
|
||||
println(path)
|
||||
}
|
||||
func (v *VideoVisualizer) Run(lets_go chan<- bool) { lets_go <- true }
|
||||
|
||||
@ -36,32 +34,26 @@ func (v *VideoVisualizer) Visualize(solved_chan <-chan *maze.SolvedMaze) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
i := 0
|
||||
for solved := range solved_chan {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
img_writer := writer.ImageWriter{
|
||||
Filename: path.Join(tmp_dir, fmt.Sprintf("%07v.png", i)),
|
||||
Maze: solved,
|
||||
CellWidth: 2,
|
||||
CellHeight: 2,
|
||||
WallColor: color.Black,
|
||||
PathColor: color.White,
|
||||
SolutionGradient: colorgrad.Warm(),
|
||||
}
|
||||
img_writer.Write()
|
||||
wg.Done()
|
||||
}()
|
||||
img_writer := writer.ImageWriter{
|
||||
Filename: path.Join(tmp_dir, fmt.Sprintf("%07v.png", i)),
|
||||
Maze: solved,
|
||||
CellWidth: 5,
|
||||
CellHeight: 5,
|
||||
WallColor: color.Black,
|
||||
PathColor: color.White,
|
||||
SolutionGradient: colorgrad.Warm(),
|
||||
}
|
||||
img_writer.Write()
|
||||
i++
|
||||
}
|
||||
wg.Wait()
|
||||
cmd := exec.Command(
|
||||
v.ffmpeg_cmd,
|
||||
"-y",
|
||||
"-pattern_type", "glob",
|
||||
"-i", path.Join(tmp_dir, "*.png"),
|
||||
"-framerate", fmt.Sprint(v.Framerate),
|
||||
"-r", fmt.Sprint(int(v.Framerate)),
|
||||
v.Filename,
|
||||
)
|
||||
err = cmd.Run()
|
||||
|
Loading…
x
Reference in New Issue
Block a user