Compare commits

...

26 Commits
v1.5.1 ... main

Author SHA1 Message Date
Karma Riuk
d1108be94b updated readme 2023-08-28 11:58:47 +02:00
Karma Riuk
2e8ccce703 fix: video visualizer 2023-08-28 11:57:40 +02:00
Karma Riuk
77843db6de fixed assets 2023-08-27 19:58:22 +02:00
Karma Riuk
b8546a4d57 fix: video visualizer 2023-08-27 19:35:40 +02:00
Karma Riuk
741db457fb updated gitignore 2023-08-27 19:35:11 +02:00
Karma Riuk
7bac753507 made examples bigger 2023-08-27 19:33:59 +02:00
Karma Riuk
dea1586280 updated readme 2023-08-27 18:59:23 +02:00
Karma Riuk
64864bb47c updated readme 2023-08-27 18:58:13 +02:00
Karma Riuk
81d682cbed updated readme 2023-08-27 18:55:24 +02:00
Karma Riuk
2b74c459f4 updated readme 2023-08-27 18:54:13 +02:00
Karma Riuk
e757402406 updated readme 2023-08-27 18:50:05 +02:00
Karma Riuk
c0c2f81fd2 updated readme 2023-08-27 18:49:41 +02:00
Karma Riuk
c7dfaf1ca7 updated readme 2023-08-27 18:48:51 +02:00
Karma Riuk
0181914013 updated readme 2023-08-27 18:46:56 +02:00
Karma Riuk
5caf21634e updated readme 2023-08-27 18:45:37 +02:00
Karma Riuk
015a656abf updated readme 2023-08-27 18:44:15 +02:00
Karma Riuk
aac0e4628a testing something with the readme 2023-08-27 18:36:38 +02:00
Karma Riuk
fc02af0f17 updated readme 2023-08-27 13:35:41 +02:00
Karma Riuk
dedc997c9d updated readme 2023-08-27 13:33:22 +02:00
Karma Riuk
06306fdf55 fix: readme and arg help messages 2023-08-27 13:23:34 +02:00
Karma Riuk
f08c406ca2 changed name of default solution files 2023-08-27 13:14:35 +02:00
Karma Riuk
d1b50ba372 fix: some typos :) 2023-08-27 13:13:02 +02:00
Karma Riuk
86d3496bca first version of readme 2023-08-27 13:09:07 +02:00
Karma Riuk
37db489838 github action should be working now 2023-08-27 12:42:03 +02:00
Karma Riuk
28fef3736b still testing github actions 2023-08-27 12:36:09 +02:00
Karma Riuk
4ff4b6b2c1 testing something with the github action 2023-08-27 12:30:34 +02:00
10 changed files with 118 additions and 31 deletions

View File

@ -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

View File

@ -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
View File

@ -21,4 +21,4 @@
go.work
/Session.vim
/maze.png
/maze_sol.png
/sol.png

89
README.md Normal file
View 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 |
| :----------------------------------------: | :-----------------------------------: |
| ![bfs](./assets/videos/bfs.gif) | ![dfs](./assets/videos/dfs.gif) |
| ![disjkstra](./assets/videos/dijkstra.gif) | ![a-star](./assets/videos/a-star.gif) |
| **Dijkstra** | **A\*** |

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

BIN
assets/videos/dfs.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

BIN
assets/videos/dijkstra.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 MiB

18
main.go
View File

@ -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{

View File

@ -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()