Compare commits

...

11 Commits

14 changed files with 174 additions and 155 deletions

20
.gitea/workflows/demo.txt Normal file
View File

@ -0,0 +1,20 @@
name: Gitea Actions Demo
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
on: [push]
jobs:
Explore-Gitea-Actions:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Check out repository code
uses: actions/checkout@v4
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ gitea.workspace }}
- run: echo "🍏 This job's status is ${{ job.status }}."

View File

@ -2,9 +2,17 @@
# See https://pre-commit.com/hooks.html for more hooks # See https://pre-commit.com/hooks.html for more hooks
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0 rev: v5.0.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
- id: check-yaml - id: check-yaml
- id: check-added-large-files - id: check-added-large-files
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.1
hooks:
- id: go-fmt
- id: go-imports
- id: no-go-testing
- id: golangci-lint
- id: go-unit-tests

View File

@ -9,10 +9,19 @@ _(This was a learning project to get a better grasp of Golang. But more importan
I wrote [a blog post](https://blog.ligthert.net/posts/exploration-fun-and-process-cycles-of-sudoku/) about this. I wrote [a blog post](https://blog.ligthert.net/posts/exploration-fun-and-process-cycles-of-sudoku/) about this.
## Features
* Worlds least efficient Sudoku solver
* Ability to assign a number of CPU cores to this task
* Split workloads among several computers
## Usage ## Usage
To use the sudoku solver, run the binary with all the parameters available: To use the sudoku solver, run the binary with all the parameters available:
``` ```
Usage of ./sudoku-funpark: Usage of ./sudoku-funpark:
-numcpu int
Number of CPU cores to assign to this task. (default 12)
-part int
Process part x in n parts. Cannot be lower than 1, or higher than specified in split. (default 1)
-row1 string -row1 string
1st row of the sudoku puzzle. (default "000000000") 1st row of the sudoku puzzle. (default "000000000")
-row2 string -row2 string
@ -31,6 +40,8 @@ Usage of ./sudoku-funpark:
8th row of the sudoku puzzle. (default "000000000") 8th row of the sudoku puzzle. (default "000000000")
-row9 string -row9 string
9th row of the sudoku puzzle. (default "000000000") 9th row of the sudoku puzzle. (default "000000000")
-split int
Split the tasks in n parts. This depends on the availability of the first row. (default 1)
``` ```
Instead of using the 3x3 blocks with 3x3 digits, it uses horizontal rows from top to bottom. Instead of using the 3x3 blocks with 3x3 digits, it uses horizontal rows from top to bottom.

View File

@ -21,6 +21,7 @@ tasks:
silent: true silent: true
precommit: precommit:
cmds: cmds:
- pre-commit autoupdate
- pre-commit run --all - pre-commit run --all
silent: true silent: true
lint: lint:
@ -33,3 +34,7 @@ tasks:
- rm {{.BUILD_DIR}}/* || true - rm {{.BUILD_DIR}}/* || true
- go tool dist list | grep -v android | grep -v ios | grep -v wasip1 | awk -F '/' '{printf "echo Compiling %s/%s; env CGO_ENABLED=1 GOOS=%s GOARCH=%s go build -o {{.BUILD_DIR}}/{{.APP}}.%s-%s\n",$1,$2,$1,$2,$1,$2 }' | sh - go tool dist list | grep -v android | grep -v ios | grep -v wasip1 | awk -F '/' '{printf "echo Compiling %s/%s; env CGO_ENABLED=1 GOOS=%s GOARCH=%s go build -o {{.BUILD_DIR}}/{{.APP}}.%s-%s\n",$1,$2,$1,$2,$1,$2 }' | sh
- for i in `ls {{.BUILD_DIR}}/*windows*`; do mv -v $i $i.exe; done - for i in `ls {{.BUILD_DIR}}/*windows*`; do mv -v $i $i.exe; done
gource:
cmds:
- gource --auto-skip-seconds 1 --key -r 60
silent: true

View File

@ -13,7 +13,7 @@ import (
//go:embed blocks.csv //go:embed blocks.csv
var f embed.FS var f embed.FS
func (solver *Solver) load_blocks() { func (solver *Solver) loadBlocks() {
defer solver.timeTrack(time.Now(), "Loaded blocks") defer solver.timeTrack(time.Now(), "Loaded blocks")
log.Println("Loading blocks") log.Println("Loading blocks")

View File

@ -8,7 +8,7 @@ import (
"runtime" "runtime"
) )
func (solver *Solver) parse_flags() { func (solver *Solver) parseFlags() {
// Define variables // Define variables
var row1 string var row1 string
@ -33,7 +33,7 @@ func (solver *Solver) parse_flags() {
flag.StringVar(&row7, "row7", "000000000", "7th 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(&row8, "row8", "000000000", "8th row of the sudoku puzzle.")
flag.StringVar(&row9, "row9", "000000000", "9th 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(&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(&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.") flag.IntVar(&part, "part", 1, "Process part x in n parts. Cannot be lower than 1, or higher than specified in split.")
@ -41,33 +41,33 @@ func (solver *Solver) parse_flags() {
flag.Parse() flag.Parse()
// Process any changes to the CPU usage. // Process any changes to the CPU usage.
if solver.numcpus <= 0 { if solver.numCPUs <= 0 {
log.Printf("ERROR: Number of CPU cores must be 1 or higher.\n\n") log.Printf("ERROR: Number of CPU cores must be 1 or higher.\n\n")
solver.print_Usage() solver.printUsage()
os.Exit(1) os.Exit(1)
} }
if solver.numcpus != runtime.NumCPU() { if solver.numCPUs != runtime.NumCPU() {
runtime.GOMAXPROCS(solver.numcpus) runtime.GOMAXPROCS(solver.numCPUs)
} }
// Process rows // Process rows
if row1 == "000000000" || row2 == "000000000" || row3 == "000000000" || row4 == "000000000" || row5 == "000000000" || row6 == "000000000" || row7 == "000000000" || row8 == "000000000" || row9 == "000000000" { 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") log.Printf("ERROR: All parameters must be entered.\n\n")
solver.print_Usage() solver.printUsage()
os.Exit(1) os.Exit(1)
} }
// Validate the row (never trust user input) // Validate the row (never trust user input)
solver.validate_row("row1", row1) solver.validateRow("row1", row1)
solver.validate_row("row2", row2) solver.validateRow("row2", row2)
solver.validate_row("row3", row3) solver.validateRow("row3", row3)
solver.validate_row("row4", row4) solver.validateRow("row4", row4)
solver.validate_row("row5", row5) solver.validateRow("row5", row5)
solver.validate_row("row6", row6) solver.validateRow("row6", row6)
solver.validate_row("row7", row7) solver.validateRow("row7", row7)
solver.validate_row("row8", row8) solver.validateRow("row8", row8)
solver.validate_row("row9", row9) solver.validateRow("row9", row9)
// Put entries in into the struct // Put entries in into the struct
solver.row1 = row1 solver.row1 = row1
@ -84,14 +84,14 @@ func (solver *Solver) parse_flags() {
// Ensure split and part are 1 or higher // Ensure split and part are 1 or higher
if split <= 0 || part <= 0 { if split <= 0 || part <= 0 {
log.Printf("ERROR: '-split' and '-part' need to be 1 or higher.\n") log.Printf("ERROR: '-split' and '-part' need to be 1 or higher.\n")
solver.print_Usage() solver.printUsage()
os.Exit(1) os.Exit(1)
} }
// Ensure part is between 1 and split // Ensure part is between 1 and split
if part > split { if part > split {
log.Printf("ERROR: '-part' cannot be bigger than `-split`.\n") log.Printf("ERROR: '-part' cannot be bigger than `-split`.\n")
solver.print_Usage() solver.printUsage()
os.Exit(1) os.Exit(1)
} }
@ -100,7 +100,7 @@ func (solver *Solver) parse_flags() {
} }
func (solver *Solver) validate_row(name string, row string) { func (solver *Solver) validateRow(name string, row string) {
var found bool var found bool
var double bool var double bool
@ -109,18 +109,18 @@ func (solver *Solver) validate_row(name string, row string) {
// 1. Make sure the row is 9 in length // 1. Make sure the row is 9 in length
if len(row) != 9 { if len(row) != 9 {
log.Printf("ERROR: Invalid length of %s (%s), must be 9 numbers\n\n", name, row) log.Printf("ERROR: Invalid length of %s (%s), must be 9 numbers\n\n", name, row)
solver.print_Usage() solver.printUsage()
os.Exit(1) os.Exit(1)
} }
// 2. Ensure all digits are numbers // 2. Ensure all digits are numbers
for _, value := range row { for _, value := range row {
found = solver.valid_char(value) found = solver.validChar(value)
} }
if !found { if !found {
log.Printf("ERROR: Invalid character of %s (%s), must be 9 numbers\n\n", name, row) log.Printf("ERROR: Invalid character of %s (%s), must be 9 numbers\n\n", name, row)
solver.print_Usage() solver.printUsage()
os.Exit(1) os.Exit(1)
} }
@ -137,14 +137,13 @@ func (solver *Solver) validate_row(name string, row string) {
if double { if double {
log.Printf("ERROR: Double character of %s (%s), numbers between 1 and 9 may only be entered once\n\n", name, row) log.Printf("ERROR: Double character of %s (%s), numbers between 1 and 9 may only be entered once\n\n", name, row)
solver.print_Usage() solver.printUsage()
os.Exit(1) os.Exit(1)
} }
} }
func (solver *Solver) valid_char(char rune) bool { func (solver *Solver) validChar(char rune) (valid bool) {
var valid bool
decvals := [10]int{48, 49, 50, 51, 52, 53, 54, 55, 56, 57} decvals := [10]int{48, 49, 50, 51, 52, 53, 54, 55, 56, 57}
for _, value := range decvals { for _, value := range decvals {
@ -156,7 +155,7 @@ func (solver *Solver) valid_char(char rune) bool {
return valid return valid
} }
func (solver *Solver) print_Usage() { func (solver *Solver) printUsage() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0]) 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(), "\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]) fmt.Fprintf(flag.CommandLine.Output(), "Example: %s -row1 ... -row2 ... -row3 ... (etc)\n\n", os.Args[0])

View File

@ -5,9 +5,22 @@ import (
"log" "log"
) )
func (solver *Solver) print_solutions() { func (solver *Solver) printSolutions() {
for solution_index, solution := range solver.solutions { for solutionIndex, solution := range solver.solutions {
log.Printf("\nSolution #%d:", solution_index+1) log.Printf("\nSolution #%d:", solutionIndex+1)
fmt.Println(solution) //fmt.Println(solution)
fmt.Println("╔═══════════╗")
fmt.Println("║" + solution[0:3] + "│" + solution[3:6] + "│" + solution[6:9] + "╢")
fmt.Println("║" + solution[10:13] + "│" + solution[13:16] + "│" + solution[16:19] + "╢")
fmt.Println("║" + solution[20:23] + "│" + solution[23:26] + "│" + solution[26:29] + "╢")
fmt.Println("╟───┼───┼───╢")
fmt.Println("║" + solution[30:33] + "│" + solution[33:36] + "│" + solution[36:39] + "╢")
fmt.Println("║" + solution[40:43] + "│" + solution[43:46] + "│" + solution[46:49] + "╢")
fmt.Println("║" + solution[50:53] + "│" + solution[53:56] + "│" + solution[56:59] + "╢")
fmt.Println("╟───┼───┼───╢")
fmt.Println("║" + solution[60:63] + "│" + solution[63:66] + "│" + solution[66:69] + "╢")
fmt.Println("║" + solution[70:73] + "│" + solution[73:76] + "│" + solution[76:79] + "╢")
fmt.Println("║" + solution[80:83] + "│" + solution[83:86] + "│" + solution[86:89] + "╢")
fmt.Println("╚═══════════╝")
} }
} }

View File

@ -6,52 +6,52 @@ import (
"time" "time"
) )
func (solver *Solver) populate_blocks() { func (solver *Solver) populateBlocks() {
defer solver.timeTrack(time.Now(), "Populated blocks") defer solver.timeTrack(time.Now(), "Populated blocks")
log.Println("Populating blocks") log.Println("Populating blocks")
solver.find_blocks(&solver.row1, &solver.row1s) solver.findBlocks(&solver.row1, &solver.row1s)
solver.find_blocks(&solver.row2, &solver.row2s) solver.findBlocks(&solver.row2, &solver.row2s)
solver.find_blocks(&solver.row3, &solver.row3s) solver.findBlocks(&solver.row3, &solver.row3s)
solver.find_blocks(&solver.row4, &solver.row4s) solver.findBlocks(&solver.row4, &solver.row4s)
solver.find_blocks(&solver.row5, &solver.row5s) solver.findBlocks(&solver.row5, &solver.row5s)
solver.find_blocks(&solver.row6, &solver.row6s) solver.findBlocks(&solver.row6, &solver.row6s)
solver.find_blocks(&solver.row7, &solver.row7s) solver.findBlocks(&solver.row7, &solver.row7s)
solver.find_blocks(&solver.row8, &solver.row8s) solver.findBlocks(&solver.row8, &solver.row8s)
solver.find_blocks(&solver.row9, &solver.row9s) solver.findBlocks(&solver.row9, &solver.row9s)
// This calculates and stores the total number of solutions to validate. // 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))
} }
func (solver *Solver) find_blocks(row *string, rows *[]int) { func (solver *Solver) findBlocks(row *string, rows *[]int) {
// Declare selection // Declare selection
var selection []int var selection []int
var curr_blocks []int var currBlocks []int
func_row := *row funcRow := *row
for letter := range func_row { for letter := range funcRow {
if len(selection) == 0 { if len(selection) == 0 {
curr_blocks = solver.blocks currBlocks = solver.blocks
} else { } else {
curr_blocks = selection currBlocks = selection
selection = nil selection = nil
} }
for _, block := range curr_blocks { for _, block := range currBlocks {
curr_row := strconv.Itoa(block) currRow := strconv.Itoa(block)
if func_row[letter] == curr_row[letter] { if funcRow[letter] == currRow[letter] {
found_row, _ := strconv.Atoi(curr_row) foundRow, _ := strconv.Atoi(currRow)
selection = append(selection, found_row) selection = append(selection, foundRow)
} }
if func_row[letter] == '0' { if funcRow[letter] == '0' {
found_row, _ := strconv.Atoi(curr_row) foundRow, _ := strconv.Atoi(currRow)
selection = append(selection, found_row) selection = append(selection, foundRow)
} }
} // End for-loop } // End for-loop
@ -61,17 +61,17 @@ func (solver *Solver) find_blocks(row *string, rows *[]int) {
*rows = selection *rows = selection
} }
func (solver *Solver) check_combinations() { func (solver *Solver) checkCombinations() {
for rows1_index := range solver.row1s { for rows1Index := range solver.row1s {
for rows2_index := range solver.row2s { for rows2Index := range solver.row2s {
for rows3_index := range solver.row3s { for rows3Index := range solver.row3s {
for rows4_index := range solver.row4s { for rows4Index := range solver.row4s {
for rows5_index := range solver.row5s { for rows5Index := range solver.row5s {
for rows6_index := range solver.row6s { for rows6Index := range solver.row6s {
for rows7_index := range solver.row7s { for rows7Index := range solver.row7s {
for rows8_index := range solver.row8s { for rows8Index := range solver.row8s {
for rows9_index := range solver.row9s { for rows9Index := range solver.row9s {
go solver.routine_validator(rows1_index, rows2_index, rows3_index, rows4_index, rows5_index, rows6_index, rows7_index, rows8_index, rows9_index) go solver.routineValidator(rows1Index, rows2Index, rows3Index, rows4Index, rows5Index, rows6Index, rows7Index, rows8Index, rows9Index)
} }
} }
} }
@ -83,13 +83,13 @@ func (solver *Solver) check_combinations() {
} }
} }
func (solver *Solver) routine_validator(rows1_index int, rows2_index int, rows3_index int, rows4_index int, rows5_index int, rows6_index int, rows7_index int, rows8_index int, rows9_index int) { func (solver *Solver) routineValidator(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 = solver.counter + 1
solver.counter.Add(1) solver.counter.Add(1)
if solver.validate_combination(solver.row1s[rows1_index], solver.row2s[rows2_index], solver.row3s[rows3_index], solver.row4s[rows4_index], solver.row5s[rows5_index], solver.row6s[rows6_index], solver.row7s[rows7_index], solver.row8s[rows8_index], solver.row9s[rows9_index]) { 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.render_combination(solver.row1s[rows1_index], solver.row2s[rows2_index], solver.row3s[rows3_index], solver.row4s[rows4_index], solver.row5s[rows5_index], solver.row6s[rows6_index], solver.row7s[rows7_index], solver.row8s[rows8_index], solver.row9s[rows9_index])) 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]))
} }
} }
@ -108,13 +108,13 @@ func (solver *Solver) tracker() {
var track int var track int
// Tracking the rate, starting point // Tracking the rate, starting point
var rate_start int64 var rateStart int64
// Tracking the rate, difference between previous iterations // Tracking the rate, difference between previous iterations
var rate_diff int64 var rateDiff int64
// Tracking duration // Tracking duration
var timer_start = time.Now() var timerStart = time.Now()
// Prevent division-by-zero error when establishing `rate_diff` // Prevent division-by-zero error when establishing `rateDiff`
time.Sleep(time.Second) time.Sleep(time.Second)
// Estimation how long it will take // Estimation how long it will take
@ -127,30 +127,36 @@ func (solver *Solver) tracker() {
percentage = (float32(solver.counter.Load()) / (float32(solver.iter) / 100)) percentage = (float32(solver.counter.Load()) / (float32(solver.iter) / 100))
// Reset the loop // Reset the loop
rate_diff = solver.counter.Load() - rate_start rateDiff = solver.counter.Load() - rateStart
if track <= int(percentage) || rate_diff == 0 { // Start if-statement if track <= int(percentage) || rateDiff == 0 { // Start if-statement
// Make sure something happened, making rate_start the only reliable variable // Make sure something happened, making rateStart the only reliable variable
if rate_diff == 0 { if rateDiff == 0 {
percentage = 100 percentage = 100
solver.counter.Store(solver.iter) solver.counter.Store(solver.iter)
done = true done = true
} }
timer_elapsed := time.Since(timer_start) timer_elapsed := time.Since(timerStart)
solver.rates = append(solver.rates, rate_diff) solver.rates = append(solver.rates, rateDiff)
rate_avg := solver.calc_avg() rate_avg := solver.calcAVG()
// Estimate when this is finished // Estimate when this is finished
if rate_diff == 0 { if rateDiff == 0 {
est_fin = "N/A" est_fin = "N/A"
} else { } else {
est_fin = solver.secondsToHuman((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"
}
est_fin = est.String()
} }
// Printing the progress // Printing the progress
log.Println("Processing: " + strconv.Itoa(int(percentage)) + "% (" + strconv.FormatInt(solver.counter.Load(), 10) + "/" + strconv.Itoa(int(solver.iter)) + "); Rate: " + strconv.FormatInt(rate_diff, 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 // After we are done printing, exit this for-loop
if percentage == 100 { if percentage == 100 {
@ -164,22 +170,22 @@ func (solver *Solver) tracker() {
track = track + 1 track = track + 1
} }
timer_start = time.Now() timerStart = time.Now()
} }
// Resert the rate counter // Resert the rate counter
rate_start = solver.counter.Load() rateStart = solver.counter.Load()
// Sleep for a second // Sleep for a second
time.Sleep(1 * time.Second) if solver.iter != solver.counter.Load() {
time.Sleep(1 * time.Second)
}
} // End for-loop } // End for-loop
} }
func (solver *Solver) validate_combination(row1 int, row2 int, row3 int, row4 int, row5 int, row6 int, row7 int, row8 int, row9 int) bool { func (solver *Solver) validateCombination(row1 int, row2 int, row3 int, row4 int, row5 int, row6 int, row7 int, row8 int, row9 int) (retval bool) {
var retval bool
retval = true retval = true
row1s := strconv.Itoa(row1) row1s := strconv.Itoa(row1)
@ -234,14 +240,14 @@ func (solver *Solver) validate_combination(row1 int, row2 int, row3 int, row4 in
return retval return retval
} }
func (solver *Solver) calc_avg() (avg int64) { func (solver *Solver) calcAVG() (avg int64) {
var avg_sum int64 var avgSum int64
for _, value := range solver.rates { for _, value := range solver.rates {
avg_sum += value avgSum += value
} }
avg = avg_sum / int64(len(solver.rates)) avg = avgSum / int64(len(solver.rates))
return return
} }

View File

@ -6,38 +6,39 @@ import (
"strconv" "strconv"
) )
// The main loop that orchastrates all the logic.
func Run() { func Run() {
// Instantiate the Solver interface // Instantiate the Solver interface
solver := Solver{} solver := Solver{}
// Parse and handle flags // Parse and handle flags
solver.parse_flags() solver.parseFlags()
// Report number of CPUs being used, if set. // Report number of CPUs being used, if set.
if runtime.NumCPU() != solver.numcpus { if runtime.NumCPU() != solver.numCPUs {
log.Println("Using " + strconv.Itoa(solver.numcpus) + " CPUs, (was " + strconv.Itoa(runtime.NumCPU()) + ")") log.Println("Using " + strconv.Itoa(solver.numCPUs) + " CPUs, (was " + strconv.Itoa(runtime.NumCPU()) + ")")
} }
// Load blocks from CSV file // Load blocks from CSV file
solver.load_blocks() solver.loadBlocks()
// Find rows that fit with the entered rows // Find rows that fit with the entered rows
solver.populate_blocks() solver.populateBlocks()
// If needed, split the workload // If needed, split the workload
// May exit and throw an error if the work load isn't viable // May exit and throw an error if the work load isn't viable
if solver.split != 1 { if solver.split != 1 {
solver.select_workload() solver.selectWorkload()
} }
// Print the total number of solutions to validate // Print the total number of solutions to validate
log.Println("Number of (potential) solutions:", solver.iter) log.Println("Number of (potential) solutions:", solver.iter)
// Check the number of solutions // Check the number of solutions
go solver.check_combinations() go solver.checkCombinations()
solver.tracker() solver.tracker()
// Print the valid solutions // Print the valid solutions
solver.print_solutions() solver.printSolutions()
} }

View File

@ -10,7 +10,7 @@ import (
// Perform some checks // Perform some checks
// and // and
// Modify solver.row1s so it limits the workload to what is only desired. // Modify solver.row1s so it limits the workload to what is only desired.
func (solver *Solver) select_workload() { func (solver *Solver) selectWorkload() {
if solver.split > len(solver.row1s) { 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") 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")
os.Exit(1) os.Exit(1)
@ -18,12 +18,12 @@ func (solver *Solver) select_workload() {
defer solver.timeTrack(time.Now(), "Workload set") defer solver.timeTrack(time.Now(), "Workload set")
log.Println("Setting workload") 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.part) + " of " + strconv.Itoa(solver.split))
workloads := solver.split_workload() workloads := solver.splitWorkload()
solver.set_workload(workloads) solver.setWorkload(workloads)
} }
// Determine how workload should be split among the agents // Determine how workload should be split among the agents
func (solver *Solver) split_workload() []int { func (solver *Solver) splitWorkload() []int {
agents := make([]int, solver.split) agents := make([]int, solver.split)
var tracker int var tracker int
var tasks int = len(solver.row1s) var tasks int = len(solver.row1s)
@ -41,7 +41,7 @@ func (solver *Solver) split_workload() []int {
} }
// Set the workload by setting solver.row1s // Set the workload by setting solver.row1s
func (solver *Solver) set_workload(agents []int) { func (solver *Solver) setWorkload(agents []int) {
var start int = 0 var start int = 0
var finish int = 0 var finish int = 0
for key, value := range agents { for key, value := range agents {

View File

@ -4,7 +4,7 @@ import (
"strconv" "strconv"
) )
func (solver *Solver) render_combination(row1 int, row2 int, row3 int, row4 int, row5 int, row6 int, row7 int, row8 int, row9 int) string { func (solver *Solver) renderCombination(row1 int, row2 int, row3 int, row4 int, row5 int, row6 int, row7 int, row8 int, row9 int) string {
row1s := strconv.Itoa(row1) row1s := strconv.Itoa(row1)
row2s := strconv.Itoa(row2) row2s := strconv.Itoa(row2)

View File

@ -2,8 +2,6 @@ package solver
import ( import (
"log" "log"
"math"
"strconv"
"time" "time"
) )
@ -11,46 +9,3 @@ func (solver *Solver) timeTrack(start time.Time, msg string) {
elapsed := time.Since(start) elapsed := time.Since(start)
log.Printf("%s (%s)", msg, elapsed) log.Printf("%s (%s)", msg, elapsed)
} }
// Stolen from https://socketloop.com/tutorials/golang-convert-seconds-to-human-readable-time-format-example
func (solver *Solver) plural(count int, singular string) (result string) {
if (count == 1) || (count == 0) {
result = strconv.Itoa(count) + " " + singular + " "
} else {
result = strconv.Itoa(count) + " " + singular + "s "
}
return
}
func (solver *Solver) secondsToHuman(input int64) (result string) {
years := math.Floor(float64(input) / 60 / 60 / 24 / 7 / 30 / 12)
seconds := input % (60 * 60 * 24 * 7 * 30 * 12)
months := math.Floor(float64(seconds) / 60 / 60 / 24 / 7 / 30)
seconds = input % (60 * 60 * 24 * 7 * 30)
weeks := math.Floor(float64(seconds) / 60 / 60 / 24 / 7)
seconds = input % (60 * 60 * 24 * 7)
days := math.Floor(float64(seconds) / 60 / 60 / 24)
seconds = input % (60 * 60 * 24)
hours := math.Floor(float64(seconds) / 60 / 60)
seconds = input % (60 * 60)
minutes := math.Floor(float64(seconds) / 60)
seconds = input % 60
if years > 0 {
result = solver.plural(int(years), "year") + solver.plural(int(months), "month") + solver.plural(int(weeks), "week") + solver.plural(int(days), "day") + solver.plural(int(hours), "hour") + solver.plural(int(minutes), "minute") + solver.plural(int(seconds), "second")
} else if months > 0 {
result = solver.plural(int(months), "month") + solver.plural(int(weeks), "week") + solver.plural(int(days), "day") + solver.plural(int(hours), "hour") + solver.plural(int(minutes), "minute") + solver.plural(int(seconds), "second")
} else if weeks > 0 {
result = solver.plural(int(weeks), "week") + solver.plural(int(days), "day") + solver.plural(int(hours), "hour") + solver.plural(int(minutes), "minute") + solver.plural(int(seconds), "second")
} else if days > 0 {
result = solver.plural(int(days), "day") + solver.plural(int(hours), "hour") + solver.plural(int(minutes), "minute") + solver.plural(int(seconds), "second")
} else if hours > 0 {
result = solver.plural(int(hours), "hour") + solver.plural(int(minutes), "minute") + solver.plural(int(seconds), "second")
} else if minutes > 0 {
result = solver.plural(int(minutes), "minute") + solver.plural(int(seconds), "second")
} else {
result = solver.plural(int(seconds), "second")
}
return
}

View File

@ -4,6 +4,7 @@ import (
"sync/atomic" "sync/atomic"
) )
// Struct/Interface containing all the important variabes it functions need access to.
type Solver struct { type Solver struct {
blocks []int blocks []int
row1 string row1 string
@ -28,7 +29,7 @@ type Solver struct {
counter atomic.Int64 counter atomic.Int64
solutions []string solutions []string
rates []int64 rates []int64
numcpus int numCPUs int
split int split int
part int part int
} }

View File

@ -48,7 +48,7 @@ func (solver *Solver) routine_row8(index1 int, index2 int, index3 int, index4 in
} }
func (solver *Solver) routine_row9(index1 int, index2 int, index3 int, index4 int, index5 int, index6 int, index7 int, index8 int, index9 int) { 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.routine_validator(index1, index2, index3, index4, index5, index6, index7, index8, index9) go solver.routineValidator(index1, index2, index3, index4, index5, index6, index7, index8, index9)
} }
// blocks.go // blocks.go