Przeglądaj źródła

Move parts of solver into its own package. Closes #19

Reviewed-on: https://gitea.ligthert.net/golang/sudoku-funpark/pulls/22
Sacha Ligthert 1 rok temu
rodzic
commit
719dd43307
16 zmienionych plików z 312 dodań i 370 usunięć
  1. 0 1
      .gitea/workflows/demo.txt
  2. 40 1
      README.md
  3. 4 0
      Taskfile.yml
  4. 18 0
      controller/types.go
  5. 3 3
      export/human-readable.go
  6. 7 0
      export/types.go
  7. 136 0
      flags/parse.go
  8. 7 0
      flags/types.go
  9. 42 2
      main.go
  10. 2 2
      solver/blocks.go
  11. 0 163
      solver/flags.go
  12. 30 26
      solver/processing.go
  13. 0 44
      solver/solver.go
  14. 8 8
      solver/split.go
  15. 15 26
      solver/types.go
  16. 0 94
      unused.txt

+ 0 - 1
.gitea/workflows/demo.txt

@@ -17,4 +17,3 @@ jobs:
         run: |
           ls ${{ gitea.workspace }}
       - run: echo "🍏 This job's status is ${{ job.status }}."
-

+ 40 - 1
README.md

@@ -94,5 +94,44 @@ Solution #1:
 283597416
 ```
 
-# Caveats
+## Caveats
 While this may very well solve all possible Sudoku puzzles (including the one [designed against brute force algorithms](https://en.wikipedia.org/wiki/Sudoku_solving_algorithms)), the blanks in the puzzle, the harder it is, the more possible solutions there are, the more solutions it needs to parse, the longer it takes. As this is a computational heavy program, the more CPU you throw against it the faster it will solve issues.
+
+## Generating your own blocks
+To generate your own blocks, you could use the following code:
+```go
+func (solver *Solver) generate_blocks() []int {
+
+	var blocks []int
+	decvals := [9]int{49, 50, 51, 52, 53, 54, 55, 56, 57}
+
+	for counter := 123456789; counter <= 987654321; counter++ {
+
+		// Convert number to string ([]byte)
+		digits := strconv.Itoa(counter)
+
+		// Check if every number is only represented only once
+		var valid bool
+		valid = true
+		for decval := range decvals {
+			var count int
+			for digit := range digits {
+				if digits[digit] == byte(decvals[decval]) {
+					count = count + 1
+				}
+			}
+
+			if count != 1 {
+				valid = false
+			}
+		}
+
+		if valid {
+			blocks = append(blocks, counter)
+		}
+	}
+
+	return blocks
+
+}
+```

+ 4 - 0
Taskfile.yml

@@ -38,3 +38,7 @@ tasks:
     cmds:
       - gource --auto-skip-seconds 1 --key -r 60
     silent: true
+  godoc:
+    cmds:
+      - godoc -http=:6060
+    silent: true

+ 18 - 0
controller/types.go

@@ -0,0 +1,18 @@
+package controller
+
+type Controller struct {
+	Blocks    []int
+	Row1      string
+	Row2      string
+	Row3      string
+	Row4      string
+	Row5      string
+	Row6      string
+	Row7      string
+	Row8      string
+	Row9      string
+	Solutions []string
+	NumCPUs   int
+	Split     int
+	Part      int
+}

+ 3 - 3
solver/printers.go → export/human-readable.go

@@ -1,12 +1,12 @@
-package solver
+package export
 
 import (
 	"fmt"
 	"log"
 )
 
-func (solver *Solver) printSolutions() {
-	for solutionIndex, solution := range solver.solutions {
+func (export *Export) PrintSolutions() {
+	for solutionIndex, solution := range export.Controller.Solutions {
 		log.Printf("\nSolution #%d:", solutionIndex+1)
 		//fmt.Println(solution)
 		fmt.Println("╔═══════════╗")

+ 7 - 0
export/types.go

@@ -0,0 +1,7 @@
+package export
+
+import "gitea.ligthert.net/golang/sudoku-funpark/controller"
+
+type Export struct {
+	Controller *controller.Controller
+}

+ 136 - 0
flags/parse.go

@@ -0,0 +1,136 @@
+package flags
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"runtime"
+)
+
+func (flags *Flags) ParseFlags() {
+
+	// Define parameters
+	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.Controller.NumCPUs <= 0 {
+		log.Printf("ERROR: Number of CPU cores must be 1 or higher.\n\n")
+		flags.printUsage()
+		os.Exit(1)
+	}
+
+	if flags.Controller.NumCPUs != runtime.NumCPU() {
+		runtime.GOMAXPROCS(flags.Controller.NumCPUs)
+	}
+
+	// Process rows
+	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.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.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.Controller.Part > flags.Controller.Split {
+		log.Printf("ERROR: '-part' cannot be bigger than `-split`.\n")
+		flags.printUsage()
+		os.Exit(1)
+	}
+
+}
+
+func (flags *Flags) validateRow(name string, row string) {
+
+	var found bool
+	var double bool
+	count := make(map[rune]int)
+
+	// 1. Make sure the row is 9 in length
+	if len(row) != 9 {
+		log.Printf("ERROR: Invalid length of %s (%s), must be 9 numbers\n\n", name, row)
+		flags.printUsage()
+		os.Exit(1)
+	}
+
+	// 2. Ensure all digits are numbers
+	for _, value := range row {
+		found = flags.validChar(value)
+	}
+
+	if !found {
+		log.Printf("ERROR: Invalid character of %s (%s), must be 9 numbers\n\n", name, row)
+		flags.printUsage()
+		os.Exit(1)
+	}
+
+	// 3. Ensure all digits (except zero) are there only once
+	for _, digits := range row {
+		count[digits] = count[digits] + 1
+	}
+
+	for key, value := range count {
+		if value > 1 && key != 48 {
+			double = true
+		}
+	}
+
+	if double {
+		log.Printf("ERROR: Double character of %s (%s), numbers between 1 and 9 may only be entered once\n\n", name, row)
+		flags.printUsage()
+		os.Exit(1)
+	}
+
+}
+
+func (flags *Flags) validChar(char rune) (valid bool) {
+	decvals := [10]int{48, 49, 50, 51, 52, 53, 54, 55, 56, 57}
+
+	for _, value := range decvals {
+		if char == rune(value) {
+			valid = true
+		}
+	}
+
+	return
+}
+
+func (flags *Flags) printUsage() {
+	fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0])
+	fmt.Fprintf(flag.CommandLine.Output(), "\nPut every row of a Sudoku puzzle as paramters.\nUse '0' for what is currently blank in the puzzle you wish to solve.\n\n")
+	fmt.Fprintf(flag.CommandLine.Output(), "Example: %s -row1 ... -row2 ... -row3 ... (etc)\n\n", os.Args[0])
+	flag.PrintDefaults()
+}

+ 7 - 0
flags/types.go

@@ -0,0 +1,7 @@
+package flags
+
+import "gitea.ligthert.net/golang/sudoku-funpark/controller"
+
+type Flags struct {
+	Controller *controller.Controller
+}

+ 42 - 2
main.go

@@ -1,10 +1,50 @@
 package main
 
 import (
+	"log"
+	"runtime"
+	"strconv"
+
+	"gitea.ligthert.net/golang/sudoku-funpark/controller"
+	"gitea.ligthert.net/golang/sudoku-funpark/export"
+	"gitea.ligthert.net/golang/sudoku-funpark/flags"
 	"gitea.ligthert.net/golang/sudoku-funpark/solver"
 )
 
 func main() {
-	// Run the meat of the program
-	solver.Run()
+	// Instantiate the interfaces
+	controller := controller.Controller{}
+	solver := solver.Solver{Controller: &controller}
+	flags := flags.Flags{Controller: &controller}
+	export := export.Export{Controller: &controller}
+
+	// Parse and handle flags
+	flags.ParseFlags()
+
+	// Report number of CPUs being used, if set.
+	if runtime.NumCPU() != controller.NumCPUs {
+		log.Println("Using " + strconv.Itoa(controller.NumCPUs) + " CPUs, (was " + strconv.Itoa(runtime.NumCPU()) + ")")
+	}
+
+	// Load blocks from CSV file
+	solver.LoadBlocks()
+
+	// Find rows that fit with the entered rows
+	solver.PopulateBlocks()
+
+	// If needed, split the workload
+	// May exit and throw an error if the work load isn't viable
+	if controller.Split != 1 {
+		solver.SelectWorkload()
+	}
+
+	// Print the total number of solutions to validate
+	log.Println("Number of (potential) solutions:", solver.Iter)
+
+	// Check the number of solutions
+	go solver.CheckCombinations()
+	solver.Tracker()
+
+	// Print the valid solutions
+	export.PrintSolutions()
 }

+ 2 - 2
solver/blocks.go

@@ -13,7 +13,7 @@ import (
 //go:embed blocks.csv
 var f embed.FS
 
-func (solver *Solver) loadBlocks() {
+func (solver *Solver) LoadBlocks() {
 
 	defer solver.timeTrack(time.Now(), "Loaded blocks")
 	log.Println("Loading blocks")
@@ -35,6 +35,6 @@ func (solver *Solver) loadBlocks() {
 		blocks = append(blocks, block)
 	}
 
-	solver.blocks = blocks
+	solver.Controller.Blocks = blocks
 
 }

+ 0 - 163
solver/flags.go

@@ -1,163 +0,0 @@
-package solver
-
-import (
-	"flag"
-	"fmt"
-	"log"
-	"os"
-	"runtime"
-)
-
-func (solver *Solver) parseFlags() {
-
-	// Define variables
-	var row1 string
-	var row2 string
-	var row3 string
-	var row4 string
-	var row5 string
-	var row6 string
-	var row7 string
-	var row8 string
-	var row9 string
-	var split int
-	var part int
-
-	// Define parameters
-	flag.StringVar(&row1, "row1", "000000000", "1st row of the sudoku puzzle.")
-	flag.StringVar(&row2, "row2", "000000000", "2nd row of the sudoku puzzle.")
-	flag.StringVar(&row3, "row3", "000000000", "4rd row of the sudoku puzzle.")
-	flag.StringVar(&row4, "row4", "000000000", "4th row of the sudoku puzzle.")
-	flag.StringVar(&row5, "row5", "000000000", "5th row of the sudoku puzzle.")
-	flag.StringVar(&row6, "row6", "000000000", "6th row of the sudoku puzzle.")
-	flag.StringVar(&row7, "row7", "000000000", "7th row of the sudoku puzzle.")
-	flag.StringVar(&row8, "row8", "000000000", "8th row of the sudoku puzzle.")
-	flag.StringVar(&row9, "row9", "000000000", "9th row of the sudoku puzzle.")
-	flag.IntVar(&solver.numCPUs, "numcpu", runtime.NumCPU(), "Number of CPU cores to assign to this task.")
-	flag.IntVar(&split, "split", 1, "Split the tasks in n parts. This depends on the availability of the first row.")
-	flag.IntVar(&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 solver.numCPUs <= 0 {
-		log.Printf("ERROR: Number of CPU cores must be 1 or higher.\n\n")
-		solver.printUsage()
-		os.Exit(1)
-	}
-
-	if solver.numCPUs != runtime.NumCPU() {
-		runtime.GOMAXPROCS(solver.numCPUs)
-	}
-
-	// Process rows
-	if row1 == "000000000" || row2 == "000000000" || row3 == "000000000" || row4 == "000000000" || row5 == "000000000" || row6 == "000000000" || row7 == "000000000" || row8 == "000000000" || row9 == "000000000" {
-		log.Printf("ERROR: All parameters must be entered.\n\n")
-		solver.printUsage()
-		os.Exit(1)
-	}
-
-	// Validate the row (never trust user input)
-	solver.validateRow("row1", row1)
-	solver.validateRow("row2", row2)
-	solver.validateRow("row3", row3)
-	solver.validateRow("row4", row4)
-	solver.validateRow("row5", row5)
-	solver.validateRow("row6", row6)
-	solver.validateRow("row7", row7)
-	solver.validateRow("row8", row8)
-	solver.validateRow("row9", row9)
-
-	// Put entries in into the struct
-	solver.row1 = row1
-	solver.row2 = row2
-	solver.row3 = row3
-	solver.row4 = row4
-	solver.row5 = row5
-	solver.row6 = row6
-	solver.row7 = row7
-	solver.row8 = row8
-	solver.row9 = row9
-
-	// Process workload splitting
-	// Ensure split and part are 1 or higher
-	if split <= 0 || part <= 0 {
-		log.Printf("ERROR: '-split' and '-part' need to be 1 or higher.\n")
-		solver.printUsage()
-		os.Exit(1)
-	}
-
-	// Ensure part is between 1 and split
-	if part > split {
-		log.Printf("ERROR: '-part' cannot be bigger than `-split`.\n")
-		solver.printUsage()
-		os.Exit(1)
-	}
-
-	solver.split = split
-	solver.part = part
-
-}
-
-func (solver *Solver) validateRow(name string, row string) {
-
-	var found bool
-	var double bool
-	count := make(map[rune]int)
-
-	// 1. Make sure the row is 9 in length
-	if len(row) != 9 {
-		log.Printf("ERROR: Invalid length of %s (%s), must be 9 numbers\n\n", name, row)
-		solver.printUsage()
-		os.Exit(1)
-	}
-
-	// 2. Ensure all digits are numbers
-	for _, value := range row {
-		found = solver.validChar(value)
-	}
-
-	if !found {
-		log.Printf("ERROR: Invalid character of %s (%s), must be 9 numbers\n\n", name, row)
-		solver.printUsage()
-		os.Exit(1)
-	}
-
-	// 3. Ensure all digits (except zero) are there only once
-	for _, digits := range row {
-		count[digits] = count[digits] + 1
-	}
-
-	for key, value := range count {
-		if value > 1 && key != 48 {
-			double = true
-		}
-	}
-
-	if double {
-		log.Printf("ERROR: Double character of %s (%s), numbers between 1 and 9 may only be entered once\n\n", name, row)
-		solver.printUsage()
-		os.Exit(1)
-	}
-
-}
-
-func (solver *Solver) validChar(char rune) (valid bool) {
-	decvals := [10]int{48, 49, 50, 51, 52, 53, 54, 55, 56, 57}
-
-	for _, value := range decvals {
-		if char == rune(value) {
-			valid = true
-		}
-	}
-
-	return valid
-}
-
-func (solver *Solver) printUsage() {
-	fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0])
-	fmt.Fprintf(flag.CommandLine.Output(), "\nPut every row of a Sudoku puzzle as paramters.\nUse '0' for what is currently blank in the puzzle you wish to solve.\n\n")
-	fmt.Fprintf(flag.CommandLine.Output(), "Example: %s -row1 ... -row2 ... -row3 ... (etc)\n\n", os.Args[0])
-	flag.PrintDefaults()
-}

+ 30 - 26
solver/processing.go

@@ -6,23 +6,23 @@ import (
 	"time"
 )
 
-func (solver *Solver) populateBlocks() {
+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.row1s)
+	solver.findBlocks(&solver.Controller.Row2, &solver.row2s)
+	solver.findBlocks(&solver.Controller.Row3, &solver.row3s)
+	solver.findBlocks(&solver.Controller.Row4, &solver.row4s)
+	solver.findBlocks(&solver.Controller.Row5, &solver.row5s)
+	solver.findBlocks(&solver.Controller.Row6, &solver.row6s)
+	solver.findBlocks(&solver.Controller.Row7, &solver.row7s)
+	solver.findBlocks(&solver.Controller.Row8, &solver.row8s)
+	solver.findBlocks(&solver.Controller.Row9, &solver.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.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))
 
 }
 
@@ -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
@@ -61,7 +61,7 @@ func (solver *Solver) findBlocks(row *string, rows *[]int) {
 	*rows = selection
 }
 
-func (solver *Solver) checkCombinations() {
+func (solver *Solver) CheckCombinations() {
 	for rows1Index := range solver.row1s {
 		for rows2Index := range solver.row2s {
 			for rows3Index := range solver.row3s {
@@ -71,7 +71,7 @@ func (solver *Solver) checkCombinations() {
 							for rows7Index := range solver.row7s {
 								for rows8Index := range solver.row8s {
 									for rows9Index := range solver.row9s {
-										go solver.routineValidator(rows1Index, rows2Index, rows3Index, rows4Index, rows5Index, rows6Index, rows7Index, rows8Index, rows9Index)
+										go solver.validator(rows1Index, rows2Index, rows3Index, rows4Index, rows5Index, rows6Index, rows7Index, rows8Index, rows9Index)
 									}
 								}
 							}
@@ -83,19 +83,19 @@ func (solver *Solver) checkCombinations() {
 	}
 }
 
-func (solver *Solver) routineValidator(rows1Index int, rows2Index int, rows3Index int, rows4Index int, rows5Index int, rows6Index int, rows7Index int, rows8Index int, rows9Index int) {
+func (solver *Solver) validator(rows1Index int, rows2Index int, rows3Index int, rows4Index int, rows5Index int, rows6Index int, rows7Index int, rows8Index int, rows9Index int) {
 
-	// solver.counter = solver.counter + 1
 	solver.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]))
+		solver.Controller.Solutions = append(solver.Controller.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]))
 	}
 
 }
 
-func (solver *Solver) tracker() {
+func (solver *Solver) Tracker() {
 
+	// Add time tracking
 	defer solver.timeTrack(time.Now(), "Validated solutions")
 	log.Println("Validating solutions")
 
@@ -118,11 +118,14 @@ func (solver *Solver) tracker() {
 	// Estimation how long it will take
 	var est_fin string
 
-	// for solver.iter != solver.counter { // Start for-loop
+	// 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 !done {
 
 		// Determine how far we are.
-		percentage = (float32(solver.counter.Load()) / (float32(solver.iter) / 100))
+		percentage = (float32(solver.counter.Load()) / (float32(solver.Iter) / 100))
 
 		// Reset the loop
 		rateDiff = solver.counter.Load() - rateStart
@@ -130,9 +133,9 @@ func (solver *Solver) tracker() {
 		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.Iter == solver.counter.Load() {
 				percentage = 100
-				solver.counter.Store(solver.iter)
+				solver.counter.Store(solver.Iter)
 				done = true
 			}
 
@@ -144,17 +147,18 @@ func (solver *Solver) tracker() {
 			if rateDiff == 0 {
 				est_fin = "N/A"
 			} else {
-				duration_int := (solver.iter - solver.counter.Load()) / rate_avg
+				duration_int := (solver.Iter - solver.counter.Load()) / rate_avg
 				duration_string := strconv.Itoa(int(duration_int)) + "s"
 				est, err := time.ParseDuration(duration_string)
 				if err != nil {
 					est_fin = "parse error"
+				} else {
+					est_fin = est.String()
 				}
-				est_fin = est.String()
 			}
 
 			// 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.counter.Load(), 10) + "/" + strconv.Itoa(int(solver.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 {
@@ -176,7 +180,7 @@ func (solver *Solver) tracker() {
 		rateStart = solver.counter.Load()
 
 		// Sleep for a second
-		if solver.iter != solver.counter.Load() {
+		if solver.Iter != solver.counter.Load() {
 			time.Sleep(1 * time.Second)
 		}
 	} // End for-loop

+ 0 - 44
solver/solver.go

@@ -1,44 +0,0 @@
-package solver
-
-import (
-	"log"
-	"runtime"
-	"strconv"
-)
-
-// The main loop that orchastrates all the logic.
-func Run() {
-	// Instantiate the Solver interface
-	solver := Solver{}
-
-	// Parse and handle flags
-	solver.parseFlags()
-
-	// 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()) + ")")
-	}
-
-	// Load blocks from CSV file
-	solver.loadBlocks()
-
-	// Find rows that fit with the entered rows
-	solver.populateBlocks()
-
-	// If needed, split the workload
-	// May exit and throw an error if the work load isn't viable
-	if solver.split != 1 {
-		solver.selectWorkload()
-	}
-
-	// Print the total number of solutions to validate
-	log.Println("Number of (potential) solutions:", solver.iter)
-
-	// Check the number of solutions
-	go solver.checkCombinations()
-	solver.tracker()
-
-	// Print the valid solutions
-	solver.printSolutions()
-
-}

+ 8 - 8
solver/split.go

@@ -10,21 +10,21 @@ import (
 // Perform some checks
 // 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")
+func (solver *Solver) SelectWorkload() {
+	if solver.Controller.Split > len(solver.row1s) {
+		log.Println("ERROR: Unable to divide the workload in " + strconv.Itoa(solver.Controller.Split) + " parts, when only " + strconv.Itoa(len(solver.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)
 
@@ -32,7 +32,7 @@ func (solver *Solver) splitWorkload() []int {
 		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 {
@@ -57,5 +57,5 @@ func (solver *Solver) setWorkload(agents []int) {
 	solver.row1s = solver.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.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))
 }

+ 15 - 26
solver/types.go

@@ -2,34 +2,23 @@ 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
+	row1s      []int
+	row2s      []int
+	row3s      []int
+	row4s      []int
+	row5s      []int
+	row6s      []int
+	row7s      []int
+	row8s      []int
+	row9s      []int
+	Iter       int64
+	counter    atomic.Int64
+	rates      []int64
 }

+ 0 - 94
unused.txt

@@ -1,94 +0,0 @@
-// Processing
-func (solver *Solver) routine_row1(index1 int) {
-	for index2 := range solver.row2s {
-		go solver.routine_row2(index1, index2)
-	}
-}
-
-func (solver *Solver) routine_row2(index1 int, index2 int) {
-	for index3 := range solver.row3s {
-		go solver.routine_row3(index1, index2, index3)
-	}
-}
-
-func (solver *Solver) routine_row3(index1 int, index2 int, index3 int) {
-	for index4 := range solver.row4s {
-		go solver.routine_row4(index1, index2, index3, index4)
-	}
-}
-
-func (solver *Solver) routine_row4(index1 int, index2 int, index3 int, index4 int) {
-	for index5 := range solver.row5s {
-		go solver.routine_row5(index1, index2, index3, index4, index5)
-	}
-}
-
-func (solver *Solver) routine_row5(index1 int, index2 int, index3 int, index4 int, index5 int) {
-	for index6 := range solver.row6s {
-		go solver.routine_row6(index1, index2, index3, index4, index5, index6)
-	}
-}
-
-func (solver *Solver) routine_row6(index1 int, index2 int, index3 int, index4 int, index5 int, index6 int) {
-	for index7 := range solver.row7s {
-		go solver.routine_row7(index1, index2, index3, index4, index5, index6, index7)
-	}
-}
-
-func (solver *Solver) routine_row7(index1 int, index2 int, index3 int, index4 int, index5 int, index6 int, index7 int) {
-	for index8 := range solver.row8s {
-		go solver.routine_row8(index1, index2, index3, index4, index5, index6, index7, index8)
-	}
-}
-
-func (solver *Solver) routine_row8(index1 int, index2 int, index3 int, index4 int, index5 int, index6 int, index7 int, index8 int) {
-	for index9 := range solver.row9s {
-		go solver.routine_row9(index1, index2, index3, index4, index5, index6, index7, index8, index9)
-	}
-}
-
-func (solver *Solver) routine_row9(index1 int, index2 int, index3 int, index4 int, index5 int, index6 int, index7 int, index8 int, index9 int) {
-	go solver.routineValidator(index1, index2, index3, index4, index5, index6, index7, index8, index9)
-}
-
-// blocks.go
-func (solver *Solver) generate_blocks() []int {
-
-	var blocks []int
-	decvals := [9]int{49, 50, 51, 52, 53, 54, 55, 56, 57}
-
-	for counter := 123456789; counter <= 987654321; counter++ {
-
-		// Convert number to string ([]byte)
-		digits := strconv.Itoa(counter)
-
-		// Check if every number is only represented only once
-		var valid bool
-		valid = true
-		for decval := range decvals {
-			var count int
-			for digit := range digits {
-				if digits[digit] == byte(decvals[decval]) {
-					count = count + 1
-				}
-			}
-
-			if count != 1 {
-				valid = false
-			}
-		}
-
-		if valid {
-			blocks = append(blocks, counter)
-		}
-	}
-
-	return blocks
-
-}
-
-// stash.go
-func (solver *Solver) print_block(block int) {
-	digits := strconv.Itoa(block)
-	fmt.Printf("%c %c %c\n%c %c %c\n%c %c %c\n\n", digits[0], digits[1], digits[2], digits[3], digits[4], digits[5], digits[6], digits[7], digits[8])
-}