diff --git a/.gitea/workflows/demo.txt b/.gitea/workflows/demo.txt index 49db389..c537cc6 100644 --- a/.gitea/workflows/demo.txt +++ b/.gitea/workflows/demo.txt @@ -17,4 +17,3 @@ jobs: run: | ls ${{ gitea.workspace }} - run: echo "🍏 This job's status is ${{ job.status }}." - diff --git a/README.md b/README.md index 4f02ae4..d0f47c1 100644 --- a/README.md +++ b/README.md @@ -134,4 +134,4 @@ func (solver *Solver) generate_blocks() []int { return blocks } -``` \ No newline at end of file +``` diff --git a/controller/types.go b/controller/types.go new file mode 100644 index 0000000..340c32c --- /dev/null +++ b/controller/types.go @@ -0,0 +1,34 @@ +package controller + +import ( + "sync/atomic" +) + +type Controller struct { + Blocks []int + Row1 string + Row2 string + Row3 string + Row4 string + Row5 string + Row6 string + Row7 string + Row8 string + Row9 string + Row1s []int + Row2s []int + Row3s []int + Row4s []int + Row5s []int + Row6s []int + Row7s []int + Row8s []int + Row9s []int + Iter int64 + Counter atomic.Int64 + Solutions []string + Rates []int64 + NumCPUs int + Split int + Part int +} diff --git a/flags/parse.go b/flags/parse.go index 991da21..d029d90 100644 --- a/flags/parse.go +++ b/flags/parse.go @@ -11,61 +11,61 @@ import ( func (flags *Flags) ParseFlags() { // Define parameters - flag.StringVar(&flags.Row1, "row1", "000000000", "1st row of the sudoku puzzle.") - flag.StringVar(&flags.Row2, "row2", "000000000", "2nd row of the sudoku puzzle.") - flag.StringVar(&flags.Row3, "row3", "000000000", "4rd row of the sudoku puzzle.") - flag.StringVar(&flags.Row4, "row4", "000000000", "4th row of the sudoku puzzle.") - flag.StringVar(&flags.Row5, "row5", "000000000", "5th row of the sudoku puzzle.") - flag.StringVar(&flags.Row6, "row6", "000000000", "6th row of the sudoku puzzle.") - flag.StringVar(&flags.Row7, "row7", "000000000", "7th row of the sudoku puzzle.") - flag.StringVar(&flags.Row8, "row8", "000000000", "8th row of the sudoku puzzle.") - flag.StringVar(&flags.Row9, "row9", "000000000", "9th row of the sudoku puzzle.") - flag.IntVar(&flags.NumCPUs, "numcpu", runtime.NumCPU(), "Number of CPU cores to assign to this task.") - flag.IntVar(&flags.Split, "split", 1, "Split the tasks in n parts. This depends on the availability of the first row.") - flag.IntVar(&flags.Part, "part", 1, "Process part x in n parts. Cannot be lower than 1, or higher than specified in split.") + flag.StringVar(&flags.Controller.Row1, "row1", "000000000", "1st row of the sudoku puzzle.") + flag.StringVar(&flags.Controller.Row2, "row2", "000000000", "2nd row of the sudoku puzzle.") + flag.StringVar(&flags.Controller.Row3, "row3", "000000000", "4rd row of the sudoku puzzle.") + flag.StringVar(&flags.Controller.Row4, "row4", "000000000", "4th row of the sudoku puzzle.") + flag.StringVar(&flags.Controller.Row5, "row5", "000000000", "5th row of the sudoku puzzle.") + flag.StringVar(&flags.Controller.Row6, "row6", "000000000", "6th row of the sudoku puzzle.") + flag.StringVar(&flags.Controller.Row7, "row7", "000000000", "7th row of the sudoku puzzle.") + flag.StringVar(&flags.Controller.Row8, "row8", "000000000", "8th row of the sudoku puzzle.") + flag.StringVar(&flags.Controller.Row9, "row9", "000000000", "9th row of the sudoku puzzle.") + flag.IntVar(&flags.Controller.NumCPUs, "numcpu", runtime.NumCPU(), "Number of CPU cores to assign to this task.") + flag.IntVar(&flags.Controller.Split, "split", 1, "Split the tasks in n parts. This depends on the availability of the first row.") + flag.IntVar(&flags.Controller.Part, "part", 1, "Process part x in n parts. Cannot be lower than 1, or higher than specified in split.") // Parse the flags flag.Parse() // Process any changes to the CPU usage. - if flags.NumCPUs <= 0 { + if flags.Controller.NumCPUs <= 0 { log.Printf("ERROR: Number of CPU cores must be 1 or higher.\n\n") flags.printUsage() os.Exit(1) } - if flags.NumCPUs != runtime.NumCPU() { - runtime.GOMAXPROCS(flags.NumCPUs) + if flags.Controller.NumCPUs != runtime.NumCPU() { + runtime.GOMAXPROCS(flags.Controller.NumCPUs) } // Process rows - if flags.Row1 == "000000000" || flags.Row2 == "000000000" || flags.Row3 == "000000000" || flags.Row4 == "000000000" || flags.Row5 == "000000000" || flags.Row6 == "000000000" || flags.Row7 == "000000000" || flags.Row8 == "000000000" || flags.Row9 == "000000000" { + if flags.Controller.Row1 == "000000000" || flags.Controller.Row2 == "000000000" || flags.Controller.Row3 == "000000000" || flags.Controller.Row4 == "000000000" || flags.Controller.Row5 == "000000000" || flags.Controller.Row6 == "000000000" || flags.Controller.Row7 == "000000000" || flags.Controller.Row8 == "000000000" || flags.Controller.Row9 == "000000000" { log.Printf("ERROR: All parameters must be entered.\n\n") flags.printUsage() os.Exit(1) } // Validate the row (never trust user input) - flags.validateRow("row1", flags.Row1) - flags.validateRow("row2", flags.Row2) - flags.validateRow("row3", flags.Row3) - flags.validateRow("row4", flags.Row4) - flags.validateRow("row5", flags.Row5) - flags.validateRow("row6", flags.Row6) - flags.validateRow("row7", flags.Row7) - flags.validateRow("row8", flags.Row8) - flags.validateRow("row9", flags.Row9) + flags.validateRow("row1", flags.Controller.Row1) + flags.validateRow("row2", flags.Controller.Row2) + flags.validateRow("row3", flags.Controller.Row3) + flags.validateRow("row4", flags.Controller.Row4) + flags.validateRow("row5", flags.Controller.Row5) + flags.validateRow("row6", flags.Controller.Row6) + flags.validateRow("row7", flags.Controller.Row7) + flags.validateRow("row8", flags.Controller.Row8) + flags.validateRow("row9", flags.Controller.Row9) // Process workload splitting // Ensure split and part are 1 or higher - if flags.Split <= 0 || flags.Part <= 0 { + if flags.Controller.Split <= 0 || flags.Controller.Part <= 0 { log.Printf("ERROR: '-split' and '-part' need to be 1 or higher.\n") flags.printUsage() os.Exit(1) } // Ensure part is between 1 and split - if flags.Part > flags.Split { + if flags.Controller.Part > flags.Controller.Split { log.Printf("ERROR: '-part' cannot be bigger than `-split`.\n") flags.printUsage() os.Exit(1) diff --git a/flags/transfer.go b/flags/transfer.go deleted file mode 100644 index d77f8a9..0000000 --- a/flags/transfer.go +++ /dev/null @@ -1,19 +0,0 @@ -package flags - -import "gitea.ligthert.net/golang/sudoku-funpark/solver" - -func (flags *Flags) TransferConfig(solver *solver.Solver) { - // Parse variables parsed from the flags to solver - solver.NumCPUs = flags.NumCPUs - solver.Split = flags.Split - solver.Part = flags.Part - solver.Row1 = flags.Row1 - solver.Row2 = flags.Row2 - solver.Row3 = flags.Row3 - solver.Row4 = flags.Row4 - solver.Row5 = flags.Row5 - solver.Row6 = flags.Row6 - solver.Row7 = flags.Row7 - solver.Row8 = flags.Row8 - solver.Row9 = flags.Row9 -} diff --git a/flags/types.go b/flags/types.go index 8d03d81..439a4dc 100644 --- a/flags/types.go +++ b/flags/types.go @@ -1,16 +1,7 @@ package flags +import "gitea.ligthert.net/golang/sudoku-funpark/controller" + type Flags struct { - Row1 string - Row2 string - Row3 string - Row4 string - Row5 string - Row6 string - Row7 string - Row8 string - Row9 string - NumCPUs int - Split int - Part int + Controller *controller.Controller } diff --git a/main.go b/main.go index 311ecd4..8c11848 100644 --- a/main.go +++ b/main.go @@ -5,22 +5,23 @@ import ( "runtime" "strconv" + "gitea.ligthert.net/golang/sudoku-funpark/controller" "gitea.ligthert.net/golang/sudoku-funpark/flags" "gitea.ligthert.net/golang/sudoku-funpark/solver" ) func main() { // Instantiate the interfaces - solver := solver.Solver{} - flags := flags.Flags{} + controller := controller.Controller{} + solver := solver.Solver{Controller: &controller} + flags := flags.Flags{Controller: &controller} // Parse and handle flags flags.ParseFlags() - flags.TransferConfig(&solver) // Report number of CPUs being used, if set. - if runtime.NumCPU() != solver.NumCPUs { - log.Println("Using " + strconv.Itoa(solver.NumCPUs) + " CPUs, (was " + strconv.Itoa(runtime.NumCPU()) + ")") + if runtime.NumCPU() != controller.NumCPUs { + log.Println("Using " + strconv.Itoa(controller.NumCPUs) + " CPUs, (was " + strconv.Itoa(runtime.NumCPU()) + ")") } // Load blocks from CSV file @@ -31,12 +32,12 @@ func main() { // If needed, split the workload // May exit and throw an error if the work load isn't viable - if solver.Split != 1 { + if controller.Split != 1 { solver.SelectWorkload() } // Print the total number of solutions to validate - log.Println("Number of (potential) solutions:", solver.Iter) + log.Println("Number of (potential) solutions:", controller.Iter) // Check the number of solutions go solver.CheckCombinations() diff --git a/solver/blocks.go b/solver/blocks.go index 0ac1c83..2bfd2ae 100644 --- a/solver/blocks.go +++ b/solver/blocks.go @@ -35,6 +35,6 @@ func (solver *Solver) LoadBlocks() { blocks = append(blocks, block) } - solver.blocks = blocks + solver.Controller.Blocks = blocks } diff --git a/solver/printers.go b/solver/printers.go index 3764a11..b9b4f00 100644 --- a/solver/printers.go +++ b/solver/printers.go @@ -6,7 +6,7 @@ import ( ) func (solver *Solver) PrintSolutions() { - for solutionIndex, solution := range solver.solutions { + for solutionIndex, solution := range solver.Controller.Solutions { log.Printf("\nSolution #%d:", solutionIndex+1) //fmt.Println(solution) fmt.Println("╔═══════════╗") diff --git a/solver/processing.go b/solver/processing.go index 23b771d..b55094e 100644 --- a/solver/processing.go +++ b/solver/processing.go @@ -11,18 +11,18 @@ func (solver *Solver) PopulateBlocks() { defer solver.timeTrack(time.Now(), "Populated blocks") log.Println("Populating blocks") - solver.findBlocks(&solver.Row1, &solver.row1s) - solver.findBlocks(&solver.Row2, &solver.row2s) - solver.findBlocks(&solver.Row3, &solver.row3s) - solver.findBlocks(&solver.Row4, &solver.row4s) - solver.findBlocks(&solver.Row5, &solver.row5s) - solver.findBlocks(&solver.Row6, &solver.row6s) - solver.findBlocks(&solver.Row7, &solver.row7s) - solver.findBlocks(&solver.Row8, &solver.row8s) - solver.findBlocks(&solver.Row9, &solver.row9s) + solver.findBlocks(&solver.Controller.Row1, &solver.Controller.Row1s) + solver.findBlocks(&solver.Controller.Row2, &solver.Controller.Row2s) + solver.findBlocks(&solver.Controller.Row3, &solver.Controller.Row3s) + solver.findBlocks(&solver.Controller.Row4, &solver.Controller.Row4s) + solver.findBlocks(&solver.Controller.Row5, &solver.Controller.Row5s) + solver.findBlocks(&solver.Controller.Row6, &solver.Controller.Row6s) + solver.findBlocks(&solver.Controller.Row7, &solver.Controller.Row7s) + solver.findBlocks(&solver.Controller.Row8, &solver.Controller.Row8s) + solver.findBlocks(&solver.Controller.Row9, &solver.Controller.Row9s) // This calculates and stores the total number of solutions to validate. - solver.Iter = int64(len(solver.row1s)) * int64(len(solver.row2s)) * int64(len(solver.row3s)) * int64(len(solver.row4s)) * int64(len(solver.row5s)) * int64(len(solver.row6s)) * int64(len(solver.row7s)) * int64(len(solver.row8s)) * int64(len(solver.row9s)) + solver.Controller.Iter = int64(len(solver.Controller.Row1s)) * int64(len(solver.Controller.Row2s)) * int64(len(solver.Controller.Row3s)) * int64(len(solver.Controller.Row4s)) * int64(len(solver.Controller.Row5s)) * int64(len(solver.Controller.Row6s)) * int64(len(solver.Controller.Row7s)) * int64(len(solver.Controller.Row8s)) * int64(len(solver.Controller.Row9s)) } @@ -35,7 +35,7 @@ func (solver *Solver) findBlocks(row *string, rows *[]int) { for letter := range funcRow { if len(selection) == 0 { - currBlocks = solver.blocks + currBlocks = solver.Controller.Blocks } else { currBlocks = selection selection = nil @@ -62,15 +62,15 @@ func (solver *Solver) findBlocks(row *string, rows *[]int) { } func (solver *Solver) CheckCombinations() { - for rows1Index := range solver.row1s { - for rows2Index := range solver.row2s { - for rows3Index := range solver.row3s { - for rows4Index := range solver.row4s { - for rows5Index := range solver.row5s { - for rows6Index := range solver.row6s { - for rows7Index := range solver.row7s { - for rows8Index := range solver.row8s { - for rows9Index := range solver.row9s { + for rows1Index := range solver.Controller.Row1s { + for rows2Index := range solver.Controller.Row2s { + for rows3Index := range solver.Controller.Row3s { + for rows4Index := range solver.Controller.Row4s { + for rows5Index := range solver.Controller.Row5s { + for rows6Index := range solver.Controller.Row6s { + for rows7Index := range solver.Controller.Row7s { + for rows8Index := range solver.Controller.Row8s { + for rows9Index := range solver.Controller.Row9s { go solver.validator(rows1Index, rows2Index, rows3Index, rows4Index, rows5Index, rows6Index, rows7Index, rows8Index, rows9Index) } } @@ -85,10 +85,10 @@ func (solver *Solver) CheckCombinations() { func (solver *Solver) validator(rows1Index int, rows2Index int, rows3Index int, rows4Index int, rows5Index int, rows6Index int, rows7Index int, rows8Index int, rows9Index int) { - solver.counter.Add(1) + solver.Controller.Counter.Add(1) - if solver.validateCombination(solver.row1s[rows1Index], solver.row2s[rows2Index], solver.row3s[rows3Index], solver.row4s[rows4Index], solver.row5s[rows5Index], solver.row6s[rows6Index], solver.row7s[rows7Index], solver.row8s[rows8Index], solver.row9s[rows9Index]) { - solver.solutions = append(solver.solutions, solver.renderCombination(solver.row1s[rows1Index], solver.row2s[rows2Index], solver.row3s[rows3Index], solver.row4s[rows4Index], solver.row5s[rows5Index], solver.row6s[rows6Index], solver.row7s[rows7Index], solver.row8s[rows8Index], solver.row9s[rows9Index])) + if solver.validateCombination(solver.Controller.Row1s[rows1Index], solver.Controller.Row2s[rows2Index], solver.Controller.Row3s[rows3Index], solver.Controller.Row4s[rows4Index], solver.Controller.Row5s[rows5Index], solver.Controller.Row6s[rows6Index], solver.Controller.Row7s[rows7Index], solver.Controller.Row8s[rows8Index], solver.Controller.Row9s[rows9Index]) { + solver.Controller.Solutions = append(solver.Controller.Solutions, solver.renderCombination(solver.Controller.Row1s[rows1Index], solver.Controller.Row2s[rows2Index], solver.Controller.Row3s[rows3Index], solver.Controller.Row4s[rows4Index], solver.Controller.Row5s[rows5Index], solver.Controller.Row6s[rows6Index], solver.Controller.Row7s[rows7Index], solver.Controller.Row8s[rows8Index], solver.Controller.Row9s[rows9Index])) } } @@ -121,33 +121,33 @@ func (solver *Solver) Tracker() { // While not needed for rateDiff anymore, it makes estimation calculations more accurate. ☚ī¸ time.Sleep(time.Second) - // for solver.Iter != solver.counter { // Start for-loop + // for solver.Controller.Iter != solver.Controller.Counter { // Start for-loop for !done { // Determine how far we are. - percentage = (float32(solver.counter.Load()) / (float32(solver.Iter) / 100)) + percentage = (float32(solver.Controller.Counter.Load()) / (float32(solver.Controller.Iter) / 100)) // Reset the loop - rateDiff = solver.counter.Load() - rateStart + rateDiff = solver.Controller.Counter.Load() - rateStart if track <= int(percentage) || rateDiff == 0 { // Start if-statement // Make sure something happened, making rateStart the only reliable variable - if solver.Iter == solver.counter.Load() { + if solver.Controller.Iter == solver.Controller.Counter.Load() { percentage = 100 - solver.counter.Store(solver.Iter) + solver.Controller.Counter.Store(solver.Controller.Iter) done = true } timer_elapsed := time.Since(timerStart) - solver.rates = append(solver.rates, rateDiff) + solver.Controller.Rates = append(solver.Controller.Rates, rateDiff) rate_avg := solver.calcAVG() // Estimate when this is finished if rateDiff == 0 { est_fin = "N/A" } else { - duration_int := (solver.Iter - solver.counter.Load()) / rate_avg + duration_int := (solver.Controller.Iter - solver.Controller.Counter.Load()) / rate_avg duration_string := strconv.Itoa(int(duration_int)) + "s" est, err := time.ParseDuration(duration_string) if err != nil { @@ -158,7 +158,7 @@ func (solver *Solver) Tracker() { } // Printing the progress - log.Println("Processing: " + strconv.Itoa(int(percentage)) + "% (" + strconv.FormatInt(solver.counter.Load(), 10) + "/" + strconv.Itoa(int(solver.Iter)) + "); Rate: " + strconv.FormatInt(rateDiff, 10) + "/sec for " + timer_elapsed.String() + "; Time left (est.): " + est_fin) + log.Println("Processing: " + strconv.Itoa(int(percentage)) + "% (" + strconv.FormatInt(solver.Controller.Counter.Load(), 10) + "/" + strconv.Itoa(int(solver.Controller.Iter)) + "); Rate: " + strconv.FormatInt(rateDiff, 10) + "/sec for " + timer_elapsed.String() + "; Time left (est.): " + est_fin) // After we are done printing, exit this for-loop if percentage == 100 { @@ -177,10 +177,10 @@ func (solver *Solver) Tracker() { } // Resert the rate counter - rateStart = solver.counter.Load() + rateStart = solver.Controller.Counter.Load() // Sleep for a second - if solver.Iter != solver.counter.Load() { + if solver.Controller.Iter != solver.Controller.Counter.Load() { time.Sleep(1 * time.Second) } } // End for-loop @@ -245,11 +245,11 @@ func (solver *Solver) validateCombination(row1 int, row2 int, row3 int, row4 int func (solver *Solver) calcAVG() (avg int64) { var avgSum int64 - for _, value := range solver.rates { + for _, value := range solver.Controller.Rates { avgSum += value } - avg = avgSum / int64(len(solver.rates)) + avg = avgSum / int64(len(solver.Controller.Rates)) return } diff --git a/solver/split.go b/solver/split.go index 6189118..373f0ac 100644 --- a/solver/split.go +++ b/solver/split.go @@ -11,28 +11,28 @@ import ( // and // Modify solver.row1s so it limits the workload to what is only desired. func (solver *Solver) SelectWorkload() { - if solver.Split > len(solver.row1s) { - log.Println("ERROR: Unable to divide the workload in " + strconv.Itoa(solver.Split) + " parts, when only " + strconv.Itoa(len(solver.row1s)) + " are available.\n\n") + if solver.Controller.Split > len(solver.Controller.Row1s) { + log.Println("ERROR: Unable to divide the workload in " + strconv.Itoa(solver.Controller.Split) + " parts, when only " + strconv.Itoa(len(solver.Controller.Row1s)) + " are available.\n\n") os.Exit(1) } defer solver.timeTrack(time.Now(), "Workload set") log.Println("Setting workload") - log.Println("We are agent " + strconv.Itoa(solver.Part) + " of " + strconv.Itoa(solver.Split)) + log.Println("We are agent " + strconv.Itoa(solver.Controller.Part) + " of " + strconv.Itoa(solver.Controller.Split)) workloads := solver.splitWorkload() solver.setWorkload(workloads) } // Determine how workload should be split among the agents func (solver *Solver) splitWorkload() []int { - agents := make([]int, solver.Split) + agents := make([]int, solver.Controller.Split) var tracker int - var tasks int = len(solver.row1s) + var tasks int = len(solver.Controller.Row1s) for tasks != 0 { agents[tracker] += 1 tasks -= 1 tracker += 1 - if tracker == solver.Split { + if tracker == solver.Controller.Split { tracker = 0 } } @@ -45,7 +45,7 @@ func (solver *Solver) setWorkload(agents []int) { var start int = 0 var finish int = 0 for key, value := range agents { - if key == solver.Part-1 { + if key == solver.Controller.Part-1 { finish = start + value break } else { @@ -54,8 +54,8 @@ func (solver *Solver) setWorkload(agents []int) { } // Set the shortened set of instructions - solver.row1s = solver.row1s[start:finish] + solver.Controller.Row1s = solver.Controller.Row1s[start:finish] // Recalculate how much we need to grind through - solver.Iter = int64(len(solver.row1s)) * int64(len(solver.row2s)) * int64(len(solver.row3s)) * int64(len(solver.row4s)) * int64(len(solver.row5s)) * int64(len(solver.row6s)) * int64(len(solver.row7s)) * int64(len(solver.row8s)) * int64(len(solver.row9s)) + solver.Controller.Iter = int64(len(solver.Controller.Row1s)) * int64(len(solver.Controller.Row2s)) * int64(len(solver.Controller.Row3s)) * int64(len(solver.Controller.Row4s)) * int64(len(solver.Controller.Row5s)) * int64(len(solver.Controller.Row6s)) * int64(len(solver.Controller.Row7s)) * int64(len(solver.Controller.Row8s)) * int64(len(solver.Controller.Row9s)) } diff --git a/solver/types.go b/solver/types.go index 11af96b..a793d36 100644 --- a/solver/types.go +++ b/solver/types.go @@ -1,35 +1,39 @@ package solver import ( - "sync/atomic" + "gitea.ligthert.net/golang/sudoku-funpark/controller" ) // Struct/Interface containing all the important variabes it functions need access to. type Solver struct { - blocks []int - Row1 string - Row2 string - Row3 string - Row4 string - Row5 string - Row6 string - Row7 string - Row8 string - Row9 string - row1s []int - row2s []int - row3s []int - row4s []int - row5s []int - row6s []int - row7s []int - row8s []int - row9s []int - Iter int64 - counter atomic.Int64 - solutions []string - rates []int64 - NumCPUs int - Split int - Part int + Controller *controller.Controller } + +// type Solver struct { +// blocks []int +// Row1 string +// Row2 string +// Row3 string +// Row4 string +// Row5 string +// Row6 string +// Row7 string +// Row8 string +// Row9 string +// row1s []int +// row2s []int +// row3s []int +// row4s []int +// row5s []int +// row6s []int +// row7s []int +// row8s []int +// row9s []int +// Iter int64 +// counter atomic.Int64 +// solutions []string +// rates []int64 +// NumCPUs int +// Split int +// Part int +// }