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

Merged
sacha merged 8 commits from #19_the-great-refactor into trunk 2025-01-28 00:07:07 +01:00
16 changed files with 312 additions and 370 deletions

View File

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

View File

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

View File

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

18
controller/types.go Normal file
View File

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

View File

@ -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
export/types.go Normal file
View File

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

136
flags/parse.go Normal file
View File

@ -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
flags/types.go Normal file
View File

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

44
main.go
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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])
}