264 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			264 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package solver
 | |
| 
 | |
| import (
 | |
| 	"log"
 | |
| 	"strconv"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // Find all possible blocks that can be used to find a solution.
 | |
| func (solver *Solver) PopulateBlocks() {
 | |
| 
 | |
| 	defer solver.timeTrack(time.Now(), "Populated blocks")
 | |
| 	log.Println("Populating blocks")
 | |
| 
 | |
| 	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))
 | |
| 
 | |
| }
 | |
| 
 | |
| // The actual function that finds the blocks matching the partial blocks.
 | |
| func (solver *Solver) findBlocks(row *string, rows *[]int) {
 | |
| 	// Declare selection
 | |
| 	var selection []int
 | |
| 	var currBlocks []int
 | |
| 	funcRow := *row
 | |
| 
 | |
| 	for letter := range funcRow {
 | |
| 
 | |
| 		if len(selection) == 0 {
 | |
| 			currBlocks = solver.Controller.Blocks
 | |
| 		} else {
 | |
| 			currBlocks = selection
 | |
| 			selection = nil
 | |
| 		}
 | |
| 
 | |
| 		for _, block := range currBlocks {
 | |
| 
 | |
| 			currRow := strconv.Itoa(block)
 | |
| 
 | |
| 			if funcRow[letter] == currRow[letter] {
 | |
| 				foundRow, _ := strconv.Atoi(currRow)
 | |
| 				selection = append(selection, foundRow)
 | |
| 			}
 | |
| 			if funcRow[letter] == '0' {
 | |
| 				foundRow, _ := strconv.Atoi(currRow)
 | |
| 				selection = append(selection, foundRow)
 | |
| 			}
 | |
| 
 | |
| 		} // End for-loop
 | |
| 
 | |
| 	} // End for-loop
 | |
| 
 | |
| 	*rows = selection
 | |
| }
 | |
| 
 | |
| // Iterate through all combination of blocks and validate them.
 | |
| 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 {
 | |
| 										go solver.validator(rows1Index, rows2Index, rows3Index, rows4Index, rows5Index, rows6Index, rows7Index, rows8Index, rows9Index)
 | |
| 									}
 | |
| 								}
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Validate the provided rows and verify it is a valid solution.
 | |
| 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)
 | |
| 
 | |
| 	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.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]))
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| // Keep track and output progress.
 | |
| // Calculate rates, display percentages, estimate the ETA till completion.
 | |
| func (solver *Solver) Tracker() {
 | |
| 
 | |
| 	// Add time tracking
 | |
| 	defer solver.timeTrack(time.Now(), "Validated solutions")
 | |
| 	log.Println("Validating solutions")
 | |
| 
 | |
| 	// Determine if the main-loop is done
 | |
| 	var done bool
 | |
| 
 | |
| 	// Tracking progress in percentages
 | |
| 	var percentage float32
 | |
| 	// Tracking progress in validated solutions
 | |
| 	var track int
 | |
| 
 | |
| 	// Tracking the rate, starting point
 | |
| 	var rateStart int64
 | |
| 	// Tracking the rate, difference between previous iterations
 | |
| 	var rateDiff int64
 | |
| 
 | |
| 	// Tracking duration
 | |
| 	var timerStart = time.Now()
 | |
| 
 | |
| 	// Estimation how long it will take
 | |
| 	var est_fin string
 | |
| 
 | |
| 	// 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))
 | |
| 
 | |
| 		// Reset the loop
 | |
| 		rateDiff = solver.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() {
 | |
| 				percentage = 100
 | |
| 				solver.counter.Store(solver.Iter)
 | |
| 				done = true
 | |
| 			}
 | |
| 
 | |
| 			timer_elapsed := time.Since(timerStart)
 | |
| 			solver.rates = append(solver.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_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()
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// 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)
 | |
| 
 | |
| 			// After we are done printing, exit this for-loop
 | |
| 			if percentage == 100 {
 | |
| 				break
 | |
| 			}
 | |
| 
 | |
| 			// Wrap up the loop or break
 | |
| 			if int(percentage) > track {
 | |
| 				track = int(percentage)
 | |
| 			} else {
 | |
| 				track = track + 1
 | |
| 			}
 | |
| 
 | |
| 			timerStart = time.Now()
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		// Resert the rate counter
 | |
| 		rateStart = solver.counter.Load()
 | |
| 
 | |
| 		// Sleep for a second
 | |
| 		if solver.Iter != solver.counter.Load() {
 | |
| 			time.Sleep(1 * time.Second)
 | |
| 		}
 | |
| 	} // End for-loop
 | |
| 
 | |
| }
 | |
| 
 | |
| // Validate combination
 | |
| func (solver *Solver) validateCombination(row1 int, row2 int, row3 int, row4 int, row5 int, row6 int, row7 int, row8 int, row9 int) (retval bool) {
 | |
| 	retval = true
 | |
| 
 | |
| 	row1s := strconv.Itoa(row1)
 | |
| 	row2s := strconv.Itoa(row2)
 | |
| 	row3s := strconv.Itoa(row3)
 | |
| 	row4s := strconv.Itoa(row4)
 | |
| 	row5s := strconv.Itoa(row5)
 | |
| 	row6s := strconv.Itoa(row6)
 | |
| 	row7s := strconv.Itoa(row7)
 | |
| 	row8s := strconv.Itoa(row8)
 | |
| 	row9s := strconv.Itoa(row9)
 | |
| 
 | |
| 	for index := range 9 {
 | |
| 		if row1s[index] == row2s[index] || row1s[index] == row3s[index] || row1s[index] == row4s[index] || row1s[index] == row5s[index] || row1s[index] == row6s[index] || row1s[index] == row7s[index] || row1s[index] == row8s[index] || row1s[index] == row9s[index] {
 | |
| 			retval = false
 | |
| 		}
 | |
| 
 | |
| 		if row2s[index] == row1s[index] || row2s[index] == row3s[index] || row2s[index] == row4s[index] || row2s[index] == row5s[index] || row2s[index] == row6s[index] || row2s[index] == row7s[index] || row2s[index] == row8s[index] || row2s[index] == row9s[index] {
 | |
| 			retval = false
 | |
| 		}
 | |
| 
 | |
| 		if row3s[index] == row1s[index] || row3s[index] == row2s[index] || row3s[index] == row4s[index] || row3s[index] == row5s[index] || row3s[index] == row6s[index] || row3s[index] == row7s[index] || row3s[index] == row8s[index] || row3s[index] == row9s[index] {
 | |
| 			retval = false
 | |
| 		}
 | |
| 
 | |
| 		if row4s[index] == row1s[index] || row4s[index] == row2s[index] || row4s[index] == row3s[index] || row4s[index] == row5s[index] || row4s[index] == row6s[index] || row4s[index] == row7s[index] || row4s[index] == row8s[index] || row4s[index] == row9s[index] {
 | |
| 			retval = false
 | |
| 		}
 | |
| 
 | |
| 		if row5s[index] == row1s[index] || row5s[index] == row2s[index] || row5s[index] == row3s[index] || row5s[index] == row4s[index] || row5s[index] == row6s[index] || row5s[index] == row7s[index] || row5s[index] == row8s[index] || row5s[index] == row9s[index] {
 | |
| 			retval = false
 | |
| 		}
 | |
| 
 | |
| 		if row6s[index] == row1s[index] || row6s[index] == row2s[index] || row6s[index] == row3s[index] || row6s[index] == row4s[index] || row6s[index] == row5s[index] || row6s[index] == row7s[index] || row6s[index] == row8s[index] || row6s[index] == row9s[index] {
 | |
| 			retval = false
 | |
| 		}
 | |
| 
 | |
| 		if row7s[index] == row1s[index] || row7s[index] == row2s[index] || row7s[index] == row3s[index] || row7s[index] == row4s[index] || row5s[index] == row6s[index] || row7s[index] == row6s[index] || row7s[index] == row8s[index] || row7s[index] == row9s[index] {
 | |
| 			retval = false
 | |
| 		}
 | |
| 
 | |
| 		if row8s[index] == row1s[index] || row8s[index] == row2s[index] || row8s[index] == row3s[index] || row8s[index] == row4s[index] || row8s[index] == row5s[index] || row8s[index] == row6s[index] || row8s[index] == row7s[index] || row8s[index] == row9s[index] {
 | |
| 			retval = false
 | |
| 		}
 | |
| 
 | |
| 		if row9s[index] == row1s[index] || row9s[index] == row2s[index] || row9s[index] == row3s[index] || row9s[index] == row4s[index] || row9s[index] == row5s[index] || row9s[index] == row6s[index] || row9s[index] == row7s[index] || row9s[index] == row8s[index] {
 | |
| 			retval = false
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	return retval
 | |
| }
 | |
| 
 | |
| // Calculate the average rate in a stored slice of rates.
 | |
| func (solver *Solver) calcAVG() (avg int64) {
 | |
| 	var avgSum int64
 | |
| 
 | |
| 	for _, value := range solver.rates {
 | |
| 		avgSum += value
 | |
| 	}
 | |
| 
 | |
| 	avg = avgSum / int64(len(solver.rates))
 | |
| 
 | |
| 	return
 | |
| }
 |