-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Fred
committed
May 13, 2023
1 parent
a4d5ec6
commit b8d310f
Showing
26 changed files
with
1,187 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# If you prefer the allow list template instead of the deny list, see community template: | ||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore | ||
# | ||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test binary, built with `go test -c` | ||
*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Dependency directories (remove the comment below to include it) | ||
# vendor/ | ||
|
||
# Go workspace file | ||
go.work | ||
.vscode | ||
amaze |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# :door: amaze :door: | ||
|
||
A program to create, solve and draw mazes in your terminal or in 2D/3D | ||
|
||
## Gallery | ||
|
||
2D Terminal view | ||
|
||
<p align="center"> | ||
<img style="float: right;" src="https://github.com/fred1268/amaze/blob/development/readme/terminal_15x15_recursivebacktracker_solved.png" alt="Amaze in Action"/> | ||
</p> | ||
2D Graphical view | ||
<p align="center"> | ||
<img style="float: right;" src="https://github.com/fred1268/amaze/blob/development/readme/2D_20x20_sidewinder_solved.png" alt="Amaze in Action"/> | ||
</p> | ||
3D Graphical view | ||
<p align="center"> | ||
<img style="float: right;" src="https://github.com/fred1268/amaze/blob/development/readme/3D_50x50_growingtree_solved2.png" alt="Amaze in Action"/> | ||
</p> | ||
|
||
--- | ||
|
||
## Setup | ||
|
||
This program uses G3n, so it has [the same prerequisites](https://github.com/g3n/engine#dependencies). | ||
It is pretty straightforward, since most of the computers nowadays, have an OpenGL driver and a C | ||
compiler. I tested both on Linux (manjaro) and mac M1. | ||
|
||
> Also, note that Amaze uses [**clap :clap:, the Command Line Argument Parser**](https://github.com/fred1268/go-clap). | ||
> clap is a non intrusive, lightweight command line parsing library you may want to try out in your | ||
> own projects. Feel free to give it a try! | ||
--- | ||
|
||
## Installation | ||
|
||
``` | ||
git clone github.com/fred1268/amaze | ||
cd amaze | ||
go install | ||
``` | ||
|
||
--- | ||
|
||
## Running the program | ||
|
||
You can run the program with several flags, although it has decent defaults. Here are the available flags: | ||
|
||
``` | ||
amaze: creates, solves and displays mazes | ||
--width, -w <width>: set maze's width | ||
--length, -l <length>: set maze's length | ||
--algo, -a <name>: choose the algorithm used to create the maze: | ||
binarytree | ||
sidewinder | ||
aldousbroder | ||
wilson | ||
huntandkill | ||
recursivebacktracker | ||
growingtree | ||
use --choice, -c to chose sub algorithm: | ||
random, last, or mix | ||
--seed, -d <seed>: use seed to generate identical maze | ||
--braid, -b <percent>: braid to remove deadends | ||
--solve, -s: solve the maze | ||
--print, -p: print maze in the terminal | ||
``` | ||
|
||
Description of the command line parameters | ||
|
||
- width and length allow you to chose the size of your map (defaults to 20). | ||
|
||
- Amaze uses various algorithms to generate mazes, so you can chose the one you prefer (defaults to binarytree). | ||
|
||
> Please note that **Growing Tree** requires the choice of a sub-algorithm. | ||
> You can chose between `random`, `last` and `mix` (defaults to `random`) | ||
- seed allows you to replay a maze you liked in the past. The current maze's seed is displayed in the console | ||
when the program starts. | ||
|
||
- braid allows you to remove none (0%), some (1-99%) or all (100%) of the deadends in the maze | ||
(defaults to 0, keep deadends). Do **not** include the % sign like in `--braid 50`. | ||
|
||
- solve allows you to solve the maze, and will display the maze with a color gradient. | ||
The entrance (red dot) will be in lighter colors whereas the exit (green dot) will be in darker colors. | ||
By default, mazes are unresolved, just in case you want to solve them :wink:. | ||
|
||
> Please note: in the terminal, only the exit path is highlighted in a gradient of color. | ||
- print allows you to display the maze in the terminal only (no 3D). | ||
|
||
## Feedback | ||
|
||
Feel free to send feedback, star, etc. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package algo | ||
|
||
import ( | ||
"github.com/fred1268/maze/maze" | ||
) | ||
|
||
func AldousBroder(m *maze.Maze) { | ||
cell := m.GetCell(rnd.Int()%m.Width, rnd.Int()%m.Length) | ||
m.Visit(cell) | ||
for { | ||
next := cell.Neighbors()[rnd.Int()%len(cell.Neighbors())] | ||
if !m.Visited(next) { | ||
cell.Link(next) | ||
m.Visit(next) | ||
} | ||
cell = next | ||
if m.IsFullyVisited() { | ||
break | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package algo | ||
|
||
import ( | ||
"github.com/fred1268/maze/maze" | ||
) | ||
|
||
func BinaryTree(m *maze.Maze) { | ||
for y := m.Length - 1; y >= 0; y-- { | ||
for x := 0; x < m.Width; x++ { | ||
cell := m.GetCell(x, y) | ||
if rnd.Int()%2 == 0 { | ||
if !cell.LinkEast() { | ||
cell.LinkNorth() | ||
} | ||
} else { | ||
if !cell.LinkNorth() { | ||
cell.LinkEast() | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package algo | ||
|
||
import ( | ||
"github.com/fred1268/maze/maze" | ||
"golang.org/x/exp/slices" | ||
) | ||
|
||
type choiceFunc func([]*maze.Cell) *maze.Cell | ||
|
||
func GTRandom(list []*maze.Cell) *maze.Cell { | ||
return list[rnd.Int()%len(list)] | ||
} | ||
|
||
func GTLast(list []*maze.Cell) *maze.Cell { | ||
return list[len(list)-1] | ||
} | ||
|
||
func GTMix(list []*maze.Cell) *maze.Cell { | ||
if rnd.Int()%2 == 0 { | ||
return GTRandom(list) | ||
} | ||
return GTLast(list) | ||
} | ||
|
||
func GrowingTree(m *maze.Maze, fn choiceFunc) { | ||
var active []*maze.Cell | ||
cell := m.GetCell(rnd.Int()%m.Width, rnd.Int()%m.Length) | ||
active = append(active, cell) | ||
m.Visit(cell) | ||
for { | ||
cell := fn(active) | ||
neighbors := cell.UnvisitedNeighbors() | ||
if len(neighbors) != 0 { | ||
target := neighbors[rnd.Int()%len(neighbors)] | ||
cell.Link(target) | ||
active = append(active, target) | ||
m.Visit(target) | ||
} else { | ||
n := slices.Index(active, cell) | ||
newActive := active[0:n] | ||
newActive = append(newActive, active[n+1:]...) | ||
active = newActive | ||
if len(active) == 0 { | ||
break | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package algo | ||
|
||
import ( | ||
"github.com/fred1268/maze/maze" | ||
) | ||
|
||
func huntAndKill_NextCell(m *maze.Maze) *maze.Cell { | ||
var cell *maze.Cell | ||
for y := m.Length - 1; y >= 0; y-- { | ||
for x := 0; x < m.Width; x++ { | ||
cell = m.GetCell(x, y) | ||
if !m.Visited(cell) { | ||
visitedNeighbors := cell.VisitedNeighbors() | ||
if len(visitedNeighbors) != 0 { | ||
c := visitedNeighbors[rnd.Int()%len(visitedNeighbors)] | ||
cell.Link(c) | ||
m.Visit(cell) | ||
return cell | ||
} | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func HuntAndKill(m *maze.Maze) { | ||
cell := m.GetCell(rnd.Int()%m.Width, rnd.Int()%m.Length) | ||
m.Visit(cell) | ||
for { | ||
unvisitedNeighbors := cell.UnvisitedNeighbors() | ||
if len(unvisitedNeighbors) != 0 { | ||
next := unvisitedNeighbors[rnd.Int()%len(unvisitedNeighbors)] | ||
cell.Link(next) | ||
m.Visit(next) | ||
cell = next | ||
} else { | ||
cell = huntAndKill_NextCell(m) | ||
} | ||
if m.IsFullyVisited() { | ||
break | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package algo | ||
|
||
import ( | ||
"github.com/fred1268/maze/maze" | ||
) | ||
|
||
type path struct { | ||
list []*maze.Cell | ||
all map[*maze.Cell]struct{} | ||
} | ||
|
||
func newPath() *path { | ||
return &path{all: make(map[*maze.Cell]struct{})} | ||
} | ||
|
||
func (p *path) visit(cell *maze.Cell) { | ||
p.list = append(p.list, cell) | ||
p.all[cell] = struct{}{} | ||
} | ||
|
||
func (p *path) backtrackTo(cell *maze.Cell) { | ||
for i := len(p.list) - 1; i >= 0; i-- { | ||
current := p.list[i] | ||
if current == cell { | ||
p.list = p.list[0 : i+1] | ||
break | ||
} | ||
delete(p.all, current) | ||
} | ||
} | ||
|
||
func (p *path) backtrack() *maze.Cell { | ||
if len(p.list) == 0 { | ||
return nil | ||
} | ||
cell := p.list[len(p.list)-1] | ||
p.list = p.list[0 : len(p.list)-1] | ||
delete(p.all, cell) | ||
return cell | ||
} | ||
|
||
func (p *path) visited(cell *maze.Cell) bool { | ||
_, ok := p.all[cell] | ||
return ok | ||
} | ||
|
||
func (p *path) getPath() []*maze.Cell { | ||
return p.list | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package algo | ||
|
||
import "math/rand" | ||
|
||
var rnd *rand.Rand | ||
|
||
func SetSeed(seed int64) { | ||
rnd = rand.New(rand.NewSource(seed)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package algo | ||
|
||
import ( | ||
"github.com/fred1268/maze/maze" | ||
) | ||
|
||
func recursiveBacktracker_NextCell(m *maze.Maze, path *path) *maze.Cell { | ||
var prev *maze.Cell | ||
var unvisitedNeighbors []*maze.Cell | ||
for { | ||
prev = path.backtrack() | ||
if prev == nil { | ||
break | ||
} | ||
unvisitedNeighbors = prev.UnvisitedNeighbors() | ||
if len(unvisitedNeighbors) != 0 { | ||
break | ||
} | ||
} | ||
if len(unvisitedNeighbors) != 0 { | ||
next := unvisitedNeighbors[rnd.Int()%len(unvisitedNeighbors)] | ||
prev.Link(next) | ||
return next | ||
} | ||
return nil | ||
} | ||
|
||
func RecursiveBacktracker(m *maze.Maze) { | ||
path := newPath() | ||
cell := m.GetCell(rnd.Int()%m.Width, rnd.Int()%m.Length) | ||
m.Visit(cell) | ||
path.visit(cell) | ||
for { | ||
var next *maze.Cell | ||
unvisitedNeighbors := cell.UnvisitedNeighbors() | ||
if len(unvisitedNeighbors) != 0 { | ||
next = unvisitedNeighbors[rnd.Int()%len(unvisitedNeighbors)] | ||
cell.Link(next) | ||
} else { | ||
next = recursiveBacktracker_NextCell(m, path) | ||
} | ||
m.Visit(next) | ||
path.visit(next) | ||
cell = next | ||
if m.IsFullyVisited() { | ||
break | ||
} | ||
} | ||
} |
Oops, something went wrong.