Move all variables to the controller interface.

This commit is contained in:
Sacha Ligthert 2025-01-27 23:46:26 +01:00
parent 3bcb5b95e7
commit d8636d07f4
12 changed files with 150 additions and 140 deletions

View File

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

View File

@ -134,4 +134,4 @@ func (solver *Solver) generate_blocks() []int {
return blocks
}
```
```

34
controller/types.go Normal file
View File

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

View File

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

View File

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

View File

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

15
main.go
View File

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

View File

@ -35,6 +35,6 @@ func (solver *Solver) LoadBlocks() {
blocks = append(blocks, block)
}
solver.blocks = blocks
solver.Controller.Blocks = blocks
}

View File

@ -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("╔═══════════╗")

View File

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

View File

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

View File

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