2023-08-03 21:07:58 +02:00
package main
import (
2023-08-11 12:30:37 +02:00
"errors"
"fmt"
"image/color"
2023-08-03 21:07:58 +02:00
"maze-solver/io/reader"
"maze-solver/io/writer"
2023-08-17 13:37:54 +02:00
"maze-solver/maze"
2023-08-05 12:01:29 +02:00
"maze-solver/maze/parser"
2023-08-03 21:07:58 +02:00
"maze-solver/solver"
"maze-solver/utils"
2023-08-17 13:37:54 +02:00
"maze-solver/visualizer"
2023-08-11 12:30:37 +02:00
"os"
"strings"
2023-08-27 11:21:26 +02:00
"sync"
2023-08-11 12:30:37 +02:00
"github.com/akamensky/argparse"
"github.com/mazznoer/colorgrad"
2023-08-03 21:07:58 +02:00
)
func main ( ) {
2023-08-27 11:21:26 +02:00
readerFactory , writerFactory , solverFactory , visFactory , ok := parse_arguments ( )
2023-08-11 14:09:31 +02:00
if ! ok {
return
}
defer utils . Timer ( "TOTAL" , 1 ) ( )
reader := readerFactory . Get ( )
2023-08-17 13:37:54 +02:00
m , err := parser . Parse ( reader )
2023-08-11 14:09:31 +02:00
utils . Check ( err , "Couldn't read maze" )
2023-08-17 13:37:54 +02:00
var solved * maze . SolvedMaze
2023-08-27 11:21:26 +02:00
if * visFactory . Type != "" {
solved_chan := make ( chan * maze . SolvedMaze , 3 )
solver := solverFactory . Get ( solved_chan )
vis := visFactory . Get ( )
vis . Init ( m )
var wg sync . WaitGroup
wg . Add ( 2 )
lets_go := make ( chan bool , 1 )
2023-08-17 13:37:54 +02:00
go func ( ) {
2023-08-27 11:21:26 +02:00
if <- lets_go {
solved = solver . Solve ( m )
}
close ( solved_chan )
wg . Done ( )
2023-08-17 13:37:54 +02:00
} ( )
2023-08-27 11:21:26 +02:00
go func ( ) {
vis . Visualize ( solved_chan )
wg . Done ( )
} ( )
vis . Run ( lets_go )
wg . Wait ( )
2023-08-17 13:37:54 +02:00
} else {
2023-08-27 11:21:26 +02:00
solver := solverFactory . Get ( nil )
2023-08-17 13:37:54 +02:00
solved = solver . Solve ( m )
}
2023-08-27 11:21:26 +02:00
if solved == nil { // cuz maybe with the window visualization, the user pressed "no"
return
}
2023-08-11 14:09:31 +02:00
writer := writerFactory . Get ( solved )
err = writer . Write ( )
utils . Check ( err , "Couldn't write solved maze" )
}
2023-08-27 11:21:26 +02:00
func parse_arguments ( ) ( * reader . ReaderFactory , * writer . WriterFactory , * solver . SolverFactory , * visualizer . VisualizerFactory , bool ) {
2023-08-11 12:30:37 +02:00
argparser := argparse . NewParser ( "maze-solver" , "Solves the given maze (insane, right? who would've guessed?)" )
var verboseLevel * int = argparser . FlagCounter ( "v" , "verbose" , & argparse . Options {
Help : ` Verbose level of the solver
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 ) ` ,
} )
readerFactory := reader . ReaderFactory { }
writerFactory := writer . WriterFactory { }
solverFactory := solver . SolverFactory { }
2023-08-27 11:21:26 +02:00
visFactory := visualizer . VisualizerFactory { }
2023-08-11 12:30:37 +02:00
readerFactory . Type = reader . TYPES [ ".png" ]
readerFactory . Filename = argparser . String ( "i" , "input" , & argparse . Options {
Help : "Input file" ,
Default : "maze.png" ,
Validate : func ( args [ ] string ) error {
var ok bool
extension := args [ 0 ] [ len ( args [ 0 ] ) - 4 : ]
readerFactory . Type , ok = reader . TYPES [ extension ]
if ok {
return nil
} else {
return errors . New ( fmt . Sprintf ( "Filetype not recognized %q" , extension ) )
}
} ,
} )
writerFactory . Type = writer . TYPES [ ".png" ]
writerFactory . Filename = argparser . String ( "o" , "output" , & argparse . Options {
2023-08-27 13:13:02 +02:00
Help : "Output file" ,
2023-08-27 13:14:35 +02:00
Default : "sol.png" ,
2023-08-11 12:30:37 +02:00
Validate : func ( args [ ] string ) error {
var ok bool
extension := args [ 0 ] [ len ( args [ 0 ] ) - 4 : ]
writerFactory . Type , ok = writer . TYPES [ extension ]
if ok {
return nil
} else {
return errors . New ( fmt . Sprintf ( "Filetype not recognized %q" , extension ) )
}
} ,
} )
readerFactory . PathChar = argparser . String ( "" , "path-char-in" , & argparse . Options {
2023-08-27 13:13:02 +02:00
Help : "Character to represent the path in an input text file" ,
2023-08-11 12:30:37 +02:00
Default : " " ,
Validate : func ( args [ ] string ) error {
if len ( args [ 0 ] ) > 1 {
return errors . New ( "Character must a string of length 1" )
}
return nil
} ,
} )
readerFactory . WallChar = argparser . String ( "" , "wall-char-in" , & argparse . Options {
2023-08-27 13:13:02 +02:00
Help : "Character to represent the wall in an input text file" ,
2023-08-11 12:30:37 +02:00
Default : "#" ,
Validate : func ( args [ ] string ) error {
if len ( args [ 0 ] ) > 1 {
return errors . New ( "Character must a string of length 1" )
}
return nil
} ,
} )
writerFactory . PathChar = argparser . String ( "" , "path-char-out" , & argparse . Options {
2023-08-27 13:13:02 +02:00
Help : "Character to represent the path in an output text file" ,
2023-08-11 12:30:37 +02:00
Default : " " ,
Validate : func ( args [ ] string ) error {
if len ( args [ 0 ] ) > 1 {
return errors . New ( "Character must a string of length 1" )
}
return nil
} ,
} )
writerFactory . WallChar = argparser . String ( "" , "wall-char-out" , & argparse . Options {
2023-08-27 13:13:02 +02:00
Help : "Character to represent the wall in an output text file" ,
2023-08-11 12:30:37 +02:00
Default : "#" ,
Validate : func ( args [ ] string ) error {
if len ( args [ 0 ] ) > 1 {
return errors . New ( "Character must a string of length 1" )
}
return nil
} ,
} )
2023-08-03 21:07:58 +02:00
2023-08-11 12:30:37 +02:00
cellSizeIn := argparser . Int ( "" , "cell-size-in" , & argparse . Options {
Help : "Size of a cell (in pixels) for input file of image type" ,
2023-08-14 19:59:57 +02:00
Default : 3 ,
2023-08-11 12:30:37 +02:00
} )
2023-08-03 21:07:58 +02:00
2023-08-11 12:30:37 +02:00
cellSizeOut := argparser . Int ( "" , "cell-size-out" , & argparse . Options {
Help : "Size of a cell (in pixels) for output file of image type" ,
2023-08-14 19:59:57 +02:00
Default : 3 ,
2023-08-11 12:30:37 +02:00
} )
solverFactory . Type = argparser . Selector ( "a" , "algo" , solver . TYPES , & argparse . Options {
2023-08-27 13:13:02 +02:00
Help : fmt . Sprintf ( "Algorithm to solve the maze, available options: %s" , strings . Join ( solver . TYPES , ", " ) ) ,
2023-08-11 12:30:37 +02:00
Default : solver . TYPES [ 0 ] ,
} )
2023-08-27 11:21:26 +02:00
visFactory . Type = argparser . Selector ( "" , "visualize" , visualizer . VIZ_METHODS , & argparse . Options {
2023-08-27 13:23:34 +02:00
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 , ", " ) ) ,
2023-08-27 11:21:26 +02:00
Default : "" ,
} )
visFactory . Filename = argparser . String ( "" , "video-name" , & argparse . Options {
Help : "Name of the output file if --visualize is set to 'video'" ,
2023-08-27 13:14:35 +02:00
Default : "sol.mp4" ,
2023-08-27 11:21:26 +02:00
} )
visFactory . Framerate = argparser . Float ( "" , "video-framerate" , & argparse . Options {
Help : "Framerate of the video if --visualize is set to 'video'" ,
Default : 60. ,
} )
2023-08-11 12:30:37 +02:00
if err := argparser . Parse ( os . Args ) ; err != nil {
fmt . Println ( argparser . Usage ( err ) )
2023-08-27 11:21:26 +02:00
return nil , nil , nil , nil , false
2023-08-11 12:30:37 +02:00
}
utils . VERBOSE_LEVEL = * verboseLevel
readerFactory . CellHeight , readerFactory . CellWidth = cellSizeIn , cellSizeIn
readerFactory . WallColor = color . RGBA { 0 , 0 , 0 , 255 }
readerFactory . PathColor = color . RGBA { 255 , 255 , 255 , 255 }
writerFactory . CellHeight , writerFactory . CellWidth = cellSizeOut , cellSizeOut
writerFactory . WallColor = color . RGBA { 0 , 0 , 0 , 255 }
writerFactory . PathColor = color . RGBA { 255 , 255 , 255 , 255 }
writerFactory . SolutionGradient = colorgrad . Warm ( )
2023-08-27 11:21:26 +02:00
return & readerFactory , & writerFactory , & solverFactory , & visFactory , true
2023-08-03 21:07:58 +02:00
}