From a2024af1b41663bf04214fa9f54d496426727803 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Sat, 20 Dec 2025 13:53:21 -0600 Subject: [PATCH 1/3] feat: wip on day 10 part 2 --- cmd/10/machine/machine.go | 50 +++++++++++++++++++++++++++++++++++++++ cmd/10/main.go | 12 ++++++++++ cmd/10/main_test.go | 10 ++++++++ 3 files changed, 72 insertions(+) diff --git a/cmd/10/machine/machine.go b/cmd/10/machine/machine.go index f2d21fa..e303b4e 100644 --- a/cmd/10/machine/machine.go +++ b/cmd/10/machine/machine.go @@ -2,6 +2,7 @@ package machine import ( + "fmt" "math" "regexp" "slices" @@ -13,6 +14,7 @@ import ( type Machine interface { ConfigureLights() int + ConfigureJoltages() int } type machine struct { @@ -115,3 +117,51 @@ func (m machine) ConfigureLights() int { return minPresses } + +func (m machine) ConfigureJoltages() int { + minPresses := 0 + // counters := make([]int, len(m.joltageSettings)) + + // given the target joltage of a counter + // how many times can I press a particular button + // before making one of the counters that the button + // affects invalid + // 3,5,4,7 + // 0 (3) + // 1 (1,3) + // 2 (2) + // 3 (2,3) + // 4 (0,2) + // 5 (0,1) + + // (0n * 1) + (1n * 0) + (1n * 1) + (3n * 1) = 7 + // 2n + 3n + 4n = 4 + + // TODO: I need to use Gausian elimination + // to solve this + // TODO: Or matrix method maybe? + + rows := len(m.desiredJoltages) + cols := len(m.buttons) + grid := make([][]int, rows) + + for r := range rows { + grid[r] = make([]int, cols+1) + + for i, b := range m.buttons { + for _, sw := range b.Switches() { + if sw == r { + grid[r][i] = 1 + } + } + } + + grid[r][cols] = m.desiredJoltages[r] + + fmt.Println(grid[r]) + } + + fmt.Println() + + return minPresses +} diff --git a/cmd/10/main.go b/cmd/10/main.go index 8f45ea2..1ef9b74 100644 --- a/cmd/10/main.go +++ b/cmd/10/main.go @@ -16,3 +16,15 @@ func SolvePartOne(filePath string) int { return total } + +func SolvePartTwo(filePath string) int { + total := 0 + + for line := range file.ReadLines(filePath) { + machine := machine.From(line) + buttonsPressed := machine.ConfigureJoltages() + total += buttonsPressed + } + + return total +} diff --git a/cmd/10/main_test.go b/cmd/10/main_test.go index 63a1fa5..25a06a8 100644 --- a/cmd/10/main_test.go +++ b/cmd/10/main_test.go @@ -25,3 +25,13 @@ func TestSolvePartOneWithInput(t *testing.T) { t.Errorf("got %d but wanted %d", result, expected) } } + +func TestSolvePartTwoWithExampleInput(t *testing.T) { + expected := 7 + + result := solution.SolvePartTwo("EXAMPLE.txt") + + if result != expected { + t.Errorf("got %d but wanted %d", result, expected) + } +} From 838eb99ed9910a40cf40486325dce5b085feba8a Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Sat, 20 Dec 2025 22:28:28 -0600 Subject: [PATCH 2/3] feat: wip on day 10 part 2 --- cmd/10/machine/machine.go | 215 +++++++++++++++++++++++++++++++++----- cmd/10/main.go | 7 ++ cmd/10/main_test.go | 12 ++- cmd/10/solver.go | 143 +++++++++++++++++++++++++ 4 files changed, 349 insertions(+), 28 deletions(-) create mode 100644 cmd/10/solver.go diff --git a/cmd/10/machine/machine.go b/cmd/10/machine/machine.go index e303b4e..7ab0cb4 100644 --- a/cmd/10/machine/machine.go +++ b/cmd/10/machine/machine.go @@ -2,7 +2,6 @@ package machine import ( - "fmt" "math" "regexp" "slices" @@ -119,49 +118,211 @@ func (m machine) ConfigureLights() int { } func (m machine) ConfigureJoltages() int { - minPresses := 0 - // counters := make([]int, len(m.joltageSettings)) + matrix := m.createMatrix() - // given the target joltage of a counter - // how many times can I press a particular button - // before making one of the counters that the button - // affects invalid - // 3,5,4,7 - // 0 (3) - // 1 (1,3) - // 2 (2) - // 3 (2,3) - // 4 (0,2) - // 5 (0,1) + eliminated := performGaussianElimination(matrix) + pivots, freeVars := analyzeMatrix(eliminated) - // (0n * 1) + (1n * 0) + (1n * 1) + (3n * 1) = 7 - // 2n + 3n + 4n = 4 + numVars := len(matrix[0]) - 1 + values := make([]int, numVars) + bestSolution := Solution{sum: math.MaxInt} - // TODO: I need to use Gausian elimination - // to solve this - // TODO: Or matrix method maybe? + iterativeSearch(freeVars, pivots, eliminated, values, &bestSolution) + return bestSolution.sum +} + +type Solution struct { + values []int + sum int + found bool +} + +func (m machine) createMatrix() [][]float64 { rows := len(m.desiredJoltages) cols := len(m.buttons) - grid := make([][]int, rows) + matrix := make([][]float64, rows) for r := range rows { - grid[r] = make([]int, cols+1) + matrix[r] = make([]float64, cols+1) for i, b := range m.buttons { for _, sw := range b.Switches() { if sw == r { - grid[r][i] = 1 + matrix[r][i] = 1 } } } - grid[r][cols] = m.desiredJoltages[r] - - fmt.Println(grid[r]) + matrix[r][cols] = float64(m.desiredJoltages[r]) } - fmt.Println() + return matrix +} - return minPresses +func performGaussianElimination(m [][]float64) [][]float64 { + rows := len(m) + cols := len(m[0]) + pivotColumn := 0 + + mCopy := make([][]float64, rows) + + for i := range rows { + mCopy[i] = make([]float64, cols) + copy(mCopy[i], m[i]) + } + + for r1 := range rows { + if cols <= pivotColumn { + return mCopy + } + + currentRow := r1 + + for mCopy[currentRow][pivotColumn] == 0 { + currentRow++ + + if rows == currentRow { + currentRow = r1 + pivotColumn++ + + if cols == pivotColumn { + return mCopy + } + } + } + + mCopy[currentRow], mCopy[r1] = mCopy[r1], mCopy[currentRow] + + pivotValue := mCopy[r1][pivotColumn] + + if pivotValue != 0 { + for j := range cols { + mCopy[r1][j] /= pivotValue + } + } + + for r2 := range rows { + if r2 != r1 { + factor := mCopy[r2][pivotColumn] + + for col := range cols { + mCopy[r2][col] -= factor * mCopy[r1][col] + } + } + } + + pivotColumn++ + } + + return mCopy +} + +func analyzeMatrix(m [][]float64) (map[int]int, []int) { + pivots := make(map[int]int) + + cols := len(m[0]) + numVars := cols - 1 + + isFree := make([]bool, numVars) + + for i := range isFree { + isFree[i] = true + } + + rows := len(m) + + for r := range rows { + for c := 0; c < cols-1; c++ { + if math.Abs(m[r][c]-1.0) < 1e-9 { + pivots[c] = r + isFree[c] = false + break + } + } + } + + freeVars := []int{} + + for i, free := range isFree { + if free { + freeVars = append(freeVars, i) + } + } + + return pivots, freeVars +} + +func iterativeSearch(freeVars []int, pivots map[int]int, matrix [][]float64, values []int, best *Solution) { + if len(freeVars) == 0 { + evaluateSolution(pivots, matrix, values, best) + return + } + + counters := make([]int, len(freeVars)) + limit := 250 + + for { + for i, counterVal := range counters { + values[freeVars[i]] = counterVal + } + + evaluateSolution(pivots, matrix, values, best) + + idx := len(counters) - 1 + + for idx >= 0 { + counters[idx]++ + + if counters[idx] > limit { + counters[idx] = 0 + idx-- + } else { + break + } + } + + if idx < 0 { + break + } + } +} + +func evaluateSolution(pivots map[int]int, m [][]float64, values []int, best *Solution) { + isValid := true + currentSum := 0 + cols := len(m[0]) + + for col, row := range pivots { + sum := m[row][cols-1] + + for c := 0; c < cols-1; c++ { + if c != col { + coeff := m[row][c] + sum -= coeff * float64(values[c]) + } + } + + values[col] = int(math.Round(sum)) + } + + for _, v := range values { + if v < 0 { + isValid = false + break + } + + currentSum += v + } + + if isValid { + if currentSum < best.sum { + best.sum = currentSum + + best.values = make([]int, len(values)) + copy(best.values, values) + + best.found = true + } + } } diff --git a/cmd/10/main.go b/cmd/10/main.go index 1ef9b74..37c1a5b 100644 --- a/cmd/10/main.go +++ b/cmd/10/main.go @@ -1,6 +1,8 @@ package main import ( + "fmt" + "github.com/StevanFreeborn/advent-of-code-2025/cmd/10/machine" "github.com/StevanFreeborn/advent-of-code-2025/internal/file" ) @@ -20,9 +22,14 @@ func SolvePartOne(filePath string) int { func SolvePartTwo(filePath string) int { total := 0 + linesRead := 0 for line := range file.ReadLines(filePath) { + linesRead++ machine := machine.From(line) buttonsPressed := machine.ConfigureJoltages() + if buttonsPressed == 0 { + fmt.Printf("No solution found for line %d: %s\n", linesRead, line) + } total += buttonsPressed } diff --git a/cmd/10/main_test.go b/cmd/10/main_test.go index 25a06a8..58cced7 100644 --- a/cmd/10/main_test.go +++ b/cmd/10/main_test.go @@ -27,7 +27,7 @@ func TestSolvePartOneWithInput(t *testing.T) { } func TestSolvePartTwoWithExampleInput(t *testing.T) { - expected := 7 + expected := 33 result := solution.SolvePartTwo("EXAMPLE.txt") @@ -35,3 +35,13 @@ func TestSolvePartTwoWithExampleInput(t *testing.T) { t.Errorf("got %d but wanted %d", result, expected) } } + +func TestSolvePartTwoWithInput(t *testing.T) { + expected := -1 + + result := solution.SolvePartTwo("INPUT.txt") + + if result != expected { + t.Errorf("got %d but wanted %d", result, expected) + } +} diff --git a/cmd/10/solver.go b/cmd/10/solver.go new file mode 100644 index 0000000..dc37819 --- /dev/null +++ b/cmd/10/solver.go @@ -0,0 +1,143 @@ +package main + +import ( + "fmt" + "strings" + "time" +) + +const ( + Reset = "\033[0m" + Red = "\033[31m" + Green = "\033[32m" + Yellow = "\033[33m" + Blue = "\033[34m" + White = "\033[37m" + Gray = "\033[90m" + Clear = "\033[H\033[2J" +) + +type Solution struct { + a, b, c, d, e, f int + sum int + found bool +} + +func main() { + best := Solution{sum: 99999} + delay := 150 * time.Millisecond + + for f := 0; f <= 5; f++ { + for d := 0; d <= 7; d++ { + + a := 2 - d + f + b := 5 - f + c := 4 - d - f + e := 3 - f + + currentSum := a + b + c + d + e + f + + validA := a >= 0 + validB := b >= 0 + validC := c >= 0 + validE := e >= 0 + + isSolution := validA && validB && validC && validE + + isNewBest := false + + if isSolution { + if currentSum < best.sum { + best = Solution{a, b, c, d, e, f, currentSum, true} + isNewBest = true + } + } + + printDashboard(d, f, a, b, c, e, currentSum, best, isSolution) + + if isNewBest { + time.Sleep(1 * time.Second) + } else { + time.Sleep(delay) + } + } + } + + printFinalResult(best) +} + +func printDashboard(d, f, a, b, c, e, sum int, best Solution, valid bool) { + var sb strings.Builder + + sb.WriteString(Clear) + + sb.WriteString(fmt.Sprintf(" %sFREE VARIABLES%s\n", Yellow, Reset)) + sb.WriteString(" ────────────────────────────────────────\n") + sb.WriteString(fmt.Sprintf(" INPUT d: %s%-3d%s %s\n", White, d, Reset, bar(d, 7))) + sb.WriteString(fmt.Sprintf(" INPUT f: %s%-3d%s %s\n\n", White, f, Reset, bar(f, 7))) + + sb.WriteString(fmt.Sprintf(" %sDEPENDENT VARIABLES%s\n", Yellow, Reset)) + sb.WriteString(" ────────────────────────────────────────\n") + sb.WriteString(formatVar("a", "2 - d + f", a)) + sb.WriteString(formatVar("b", "5 - f", b)) + sb.WriteString(formatVar("c", "4 - d - f", c)) + sb.WriteString(formatVar("e", "3 - f", e)) + + sb.WriteString("\n") + + sb.WriteString(fmt.Sprintf(" %sSTATUS%s\n", Yellow, Reset)) + sb.WriteString(" ────────────────────────────────────────\n") + + statusColor := Red + statusText := "INVALID (Constraints Failed)" + + if valid { + statusColor = Green + statusText = "VALID SOLUTION" + } + + sb.WriteString(fmt.Sprintf(" Current Sum: %d\n", sum)) + sb.WriteString(fmt.Sprintf(" Constraint Check: %s%s%s\n\n", statusColor, statusText, Reset)) + + sb.WriteString(fmt.Sprintf(" %sBEST MINIMUM FOUND SO FAR%s\n", Yellow, Reset)) + sb.WriteString(" ────────────────────────────────────────\n") + + if best.found { + sb.WriteString(fmt.Sprintf(" Total Sum: %s%d%s\n", Green, best.sum, Reset)) + sb.WriteString(fmt.Sprintf(" Values: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d\n", best.a, best.b, best.c, best.d, best.e, best.f)) + } else { + sb.WriteString(" Searching...\n") + } + + fmt.Print(sb.String()) +} + +func formatVar(name, eq string, val int) string { + color := Green + check := "OK" + + if val < 0 { + color = Red + check = "FAIL (< 0)" + } + + displayVal := max(val, 0) + visual := bar(displayVal, 10) + + return fmt.Sprintf(" %s = %-10s = %s%-3d%s [%-10s] %s\n", name, eq, color, val, Reset, check, visual) +} + +func bar(val, max int) string { + if val < 0 { + val = 0 + } + if val > max { + val = max + } + return "[" + strings.Repeat("█", val) + strings.Repeat("░", max-val) + "]" +} + +func printFinalResult(s Solution) { + fmt.Printf("Smallest Total Possible: %s%d%s\n", Green, s.sum, Reset) + fmt.Printf("Configuration: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d\n", s.a, s.b, s.c, s.d, s.e, s.f) +} From 67df8a1ae70e0a6de177a3e437b8aad6bc827340 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Wed, 24 Dec 2025 18:13:00 -0600 Subject: [PATCH 3/3] feat: solve day 10 part 2 --- cmd/10/button/button.go | 2 +- cmd/10/machine/machine.go | 384 ++++++++++++++++---------------------- cmd/10/main_test.go | 2 +- cmd/10/solver.go | 143 -------------- 4 files changed, 168 insertions(+), 363 deletions(-) delete mode 100644 cmd/10/solver.go diff --git a/cmd/10/button/button.go b/cmd/10/button/button.go index 29f17c6..416c05f 100644 --- a/cmd/10/button/button.go +++ b/cmd/10/button/button.go @@ -19,7 +19,7 @@ func (b button) String() string { return fmt.Sprintf("%v", b.switches) } -func From(line string) button { +func From(line string) Button { buttonRegex := regexp.MustCompile(`\d+`) matches := buttonRegex.FindAllString(line, -1) diff --git a/cmd/10/machine/machine.go b/cmd/10/machine/machine.go index 7ab0cb4..a0ecced 100644 --- a/cmd/10/machine/machine.go +++ b/cmd/10/machine/machine.go @@ -4,11 +4,12 @@ package machine import ( "math" "regexp" - "slices" + "sort" "strconv" "strings" "github.com/StevanFreeborn/advent-of-code-2025/cmd/10/button" + "github.com/StevanFreeborn/advent-of-code-2025/internal/stack" ) type Machine interface { @@ -66,263 +67,210 @@ func From(line string) Machine { } func (m machine) ConfigureLights() int { - combinations := [][]bool{} + combs := m.generateCombinations(len(m.desiredLightState)) + minPresses := math.MaxInt - numberOfButtons := len(m.buttons) - numberOfCombinations := int(math.Pow(2, float64(numberOfButtons))) - currentCombination := make([]bool, numberOfButtons) + found := false - for range numberOfCombinations { - temp := make([]bool, numberOfButtons) - copy(temp, currentCombination) + for _, comb := range combs { + matches := true - combinations = append(combinations, temp) + for i, count := range comb.deltas { + isLightOn := count%2 != 0 - for j := range numberOfButtons { - if currentCombination[j] == false { - currentCombination[j] = true + if isLightOn != m.desiredLightState[i] { + matches = false break - } else { - currentCombination[j] = false + } + } + + if matches { + if comb.numPresses < minPresses { + minPresses = comb.numPresses + found = true } } } - for _, currentCombination := range combinations { - currentPresses := 0 - initialLightState := make([]bool, len(m.desiredLightState)) - - for bi, bs := range currentCombination { - if bs == false { - continue - } - - currentPresses++ - switchesToToggle := m.buttons[bi].Switches() - - for _, switchToToggle := range switchesToToggle { - initialLightState[switchToToggle] = !initialLightState[switchToToggle] - } - } - - if slices.Equal(initialLightState, m.desiredLightState) == false { - continue - } - - if currentPresses < minPresses { - minPresses = currentPresses - } + if found == false { + return 0 } return minPresses } +type combination struct { + deltas []int + numPresses int +} + +type searchState struct { + goal []int + currentCost int + weight int +} + +// If a target is odd I must press a combination of +// buttons that contributes an odd value to the target. +// This means I can pre-compute what all combinations +// of buttons do when pressed exactly once. +// I then can look for a combination that matches the +// odd/even pattern of the target +// When I find a match I can subtract it from the target +// and then divide the target by 2 to get a new target +// I repeat this until I reach a target of all zeros + +// i.e. Goal: [13, 7] +// Button A: [1, 0] +// Button B: [1, 1] +// +// Combinations: +// 0 presses: [0, 0] +// 1 press: [1, 0] (A) +// 1 press: [1, 1] (B) +// 1 press: [2, 1] (A, B) +// +// 1st iteration: +// Target: [13, 7] (odd, odd) +// Match: [1, 1] (B) +// New Target: [(13-1)/2, (7-1)/2] = [6, 3] +// Presses: 1 * weight 1 = 1 +// +// Second iteration: +// Target: [6, 3] (even, odd) +// Match: [2, 1] (A, B) +// New Target: [(6-2)/2, (3-1)/2] = [2, 1] +// Presses: 2 * weight 2 = 4 +// +// Third iteration: +// Target: [2, 1] (even, odd) +// Match: [2, 1] (A, B) +// New Target: [(2-1)/2, (1-0)/2] = [0, 0] +// Presses: 2 * weight 4 = 8 +// +// Total presses: 1 + 4 + 8 = 13 func (m machine) ConfigureJoltages() int { - matrix := m.createMatrix() + combinations := m.generateCombinations(len(m.desiredJoltages)) - eliminated := performGaussianElimination(matrix) - pivots, freeVars := analyzeMatrix(eliminated) + sort.Slice(combinations, func(i, j int) bool { + return combinations[i].numPresses < combinations[j].numPresses + }) - numVars := len(matrix[0]) - 1 - values := make([]int, numVars) - bestSolution := Solution{sum: math.MaxInt} + stack := stack.New[searchState]() + stack.Push(searchState{ + goal: m.desiredJoltages, + currentCost: 0, + weight: 1, + }) - iterativeSearch(freeVars, pivots, eliminated, values, &bestSolution) + minTotalCost := math.MaxInt + foundSolution := false - return bestSolution.sum + for stack.IsEmpty() == false { + curr, _ := stack.Pop() + + if curr.currentCost >= minTotalCost { + continue + } + + if isZero(curr.goal) { + if curr.currentCost < minTotalCost { + minTotalCost = curr.currentCost + foundSolution = true + } + + continue + } + + for _, combination := range combinations { + if smallerOrEqual(combination.deltas, curr.goal) == false { + continue + } + + if hasSameParity(combination.deltas, curr.goal) == false { + continue + } + + nextGoal := make([]int, len(curr.goal)) + + for i := 0; i < len(curr.goal); i++ { + nextGoal[i] = (curr.goal[i] - combination.deltas[i]) / 2 + } + + stepCost := combination.numPresses * curr.weight + + stack.Push(searchState{ + goal: nextGoal, + currentCost: curr.currentCost + stepCost, + weight: curr.weight * 2, + }) + } + } + + if foundSolution == false { + return 0 + } + + return minTotalCost } -type Solution struct { - values []int - sum int - found bool -} +func (m machine) generateCombinations(size int) []combination { + res := []combination{{ + deltas: make([]int, size), + numPresses: 0, + }} -func (m machine) createMatrix() [][]float64 { - rows := len(m.desiredJoltages) - cols := len(m.buttons) - matrix := make([][]float64, rows) + for _, btn := range m.buttons { + currentCount := len(res) - for r := range rows { - matrix[r] = make([]float64, cols+1) + for i := range currentCount { + existing := res[i] - for i, b := range m.buttons { - for _, sw := range b.Switches() { - if sw == r { - matrix[r][i] = 1 + newDeltas := make([]int, size) + copy(newDeltas, existing.deltas) + + for _, switchIdx := range btn.Switches() { + if switchIdx < size { + newDeltas[switchIdx]++ } } - } - matrix[r][cols] = float64(m.desiredJoltages[r]) + res = append(res, combination{ + deltas: newDeltas, + numPresses: existing.numPresses + 1, + }) + } } - return matrix + return res } -func performGaussianElimination(m [][]float64) [][]float64 { - rows := len(m) - cols := len(m[0]) - pivotColumn := 0 - - mCopy := make([][]float64, rows) - - for i := range rows { - mCopy[i] = make([]float64, cols) - copy(mCopy[i], m[i]) +func isZero(arr []int) bool { + for _, v := range arr { + if v != 0 { + return false + } } - for r1 := range rows { - if cols <= pivotColumn { - return mCopy - } - - currentRow := r1 - - for mCopy[currentRow][pivotColumn] == 0 { - currentRow++ - - if rows == currentRow { - currentRow = r1 - pivotColumn++ - - if cols == pivotColumn { - return mCopy - } - } - } - - mCopy[currentRow], mCopy[r1] = mCopy[r1], mCopy[currentRow] - - pivotValue := mCopy[r1][pivotColumn] - - if pivotValue != 0 { - for j := range cols { - mCopy[r1][j] /= pivotValue - } - } - - for r2 := range rows { - if r2 != r1 { - factor := mCopy[r2][pivotColumn] - - for col := range cols { - mCopy[r2][col] -= factor * mCopy[r1][col] - } - } - } - - pivotColumn++ - } - - return mCopy + return true } -func analyzeMatrix(m [][]float64) (map[int]int, []int) { - pivots := make(map[int]int) - - cols := len(m[0]) - numVars := cols - 1 - - isFree := make([]bool, numVars) - - for i := range isFree { - isFree[i] = true - } - - rows := len(m) - - for r := range rows { - for c := 0; c < cols-1; c++ { - if math.Abs(m[r][c]-1.0) < 1e-9 { - pivots[c] = r - isFree[c] = false - break - } +func smallerOrEqual(a []int, b []int) bool { + for i := range a { + if a[i] > b[i] { + return false } } - freeVars := []int{} - - for i, free := range isFree { - if free { - freeVars = append(freeVars, i) - } - } - - return pivots, freeVars + return true } -func iterativeSearch(freeVars []int, pivots map[int]int, matrix [][]float64, values []int, best *Solution) { - if len(freeVars) == 0 { - evaluateSolution(pivots, matrix, values, best) - return - } - - counters := make([]int, len(freeVars)) - limit := 250 - - for { - for i, counterVal := range counters { - values[freeVars[i]] = counterVal - } - - evaluateSolution(pivots, matrix, values, best) - - idx := len(counters) - 1 - - for idx >= 0 { - counters[idx]++ - - if counters[idx] > limit { - counters[idx] = 0 - idx-- - } else { - break - } - } - - if idx < 0 { - break - } - } -} - -func evaluateSolution(pivots map[int]int, m [][]float64, values []int, best *Solution) { - isValid := true - currentSum := 0 - cols := len(m[0]) - - for col, row := range pivots { - sum := m[row][cols-1] - - for c := 0; c < cols-1; c++ { - if c != col { - coeff := m[row][c] - sum -= coeff * float64(values[c]) - } - } - - values[col] = int(math.Round(sum)) - } - - for _, v := range values { - if v < 0 { - isValid = false - break - } - - currentSum += v - } - - if isValid { - if currentSum < best.sum { - best.sum = currentSum - - best.values = make([]int, len(values)) - copy(best.values, values) - - best.found = true +func hasSameParity(a []int, b []int) bool { + for i := range a { + if a[i]%2 != b[i]%2 { + return false } } + + return true } diff --git a/cmd/10/main_test.go b/cmd/10/main_test.go index 58cced7..d92fcfb 100644 --- a/cmd/10/main_test.go +++ b/cmd/10/main_test.go @@ -37,7 +37,7 @@ func TestSolvePartTwoWithExampleInput(t *testing.T) { } func TestSolvePartTwoWithInput(t *testing.T) { - expected := -1 + expected := 19810 result := solution.SolvePartTwo("INPUT.txt") diff --git a/cmd/10/solver.go b/cmd/10/solver.go deleted file mode 100644 index dc37819..0000000 --- a/cmd/10/solver.go +++ /dev/null @@ -1,143 +0,0 @@ -package main - -import ( - "fmt" - "strings" - "time" -) - -const ( - Reset = "\033[0m" - Red = "\033[31m" - Green = "\033[32m" - Yellow = "\033[33m" - Blue = "\033[34m" - White = "\033[37m" - Gray = "\033[90m" - Clear = "\033[H\033[2J" -) - -type Solution struct { - a, b, c, d, e, f int - sum int - found bool -} - -func main() { - best := Solution{sum: 99999} - delay := 150 * time.Millisecond - - for f := 0; f <= 5; f++ { - for d := 0; d <= 7; d++ { - - a := 2 - d + f - b := 5 - f - c := 4 - d - f - e := 3 - f - - currentSum := a + b + c + d + e + f - - validA := a >= 0 - validB := b >= 0 - validC := c >= 0 - validE := e >= 0 - - isSolution := validA && validB && validC && validE - - isNewBest := false - - if isSolution { - if currentSum < best.sum { - best = Solution{a, b, c, d, e, f, currentSum, true} - isNewBest = true - } - } - - printDashboard(d, f, a, b, c, e, currentSum, best, isSolution) - - if isNewBest { - time.Sleep(1 * time.Second) - } else { - time.Sleep(delay) - } - } - } - - printFinalResult(best) -} - -func printDashboard(d, f, a, b, c, e, sum int, best Solution, valid bool) { - var sb strings.Builder - - sb.WriteString(Clear) - - sb.WriteString(fmt.Sprintf(" %sFREE VARIABLES%s\n", Yellow, Reset)) - sb.WriteString(" ────────────────────────────────────────\n") - sb.WriteString(fmt.Sprintf(" INPUT d: %s%-3d%s %s\n", White, d, Reset, bar(d, 7))) - sb.WriteString(fmt.Sprintf(" INPUT f: %s%-3d%s %s\n\n", White, f, Reset, bar(f, 7))) - - sb.WriteString(fmt.Sprintf(" %sDEPENDENT VARIABLES%s\n", Yellow, Reset)) - sb.WriteString(" ────────────────────────────────────────\n") - sb.WriteString(formatVar("a", "2 - d + f", a)) - sb.WriteString(formatVar("b", "5 - f", b)) - sb.WriteString(formatVar("c", "4 - d - f", c)) - sb.WriteString(formatVar("e", "3 - f", e)) - - sb.WriteString("\n") - - sb.WriteString(fmt.Sprintf(" %sSTATUS%s\n", Yellow, Reset)) - sb.WriteString(" ────────────────────────────────────────\n") - - statusColor := Red - statusText := "INVALID (Constraints Failed)" - - if valid { - statusColor = Green - statusText = "VALID SOLUTION" - } - - sb.WriteString(fmt.Sprintf(" Current Sum: %d\n", sum)) - sb.WriteString(fmt.Sprintf(" Constraint Check: %s%s%s\n\n", statusColor, statusText, Reset)) - - sb.WriteString(fmt.Sprintf(" %sBEST MINIMUM FOUND SO FAR%s\n", Yellow, Reset)) - sb.WriteString(" ────────────────────────────────────────\n") - - if best.found { - sb.WriteString(fmt.Sprintf(" Total Sum: %s%d%s\n", Green, best.sum, Reset)) - sb.WriteString(fmt.Sprintf(" Values: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d\n", best.a, best.b, best.c, best.d, best.e, best.f)) - } else { - sb.WriteString(" Searching...\n") - } - - fmt.Print(sb.String()) -} - -func formatVar(name, eq string, val int) string { - color := Green - check := "OK" - - if val < 0 { - color = Red - check = "FAIL (< 0)" - } - - displayVal := max(val, 0) - visual := bar(displayVal, 10) - - return fmt.Sprintf(" %s = %-10s = %s%-3d%s [%-10s] %s\n", name, eq, color, val, Reset, check, visual) -} - -func bar(val, max int) string { - if val < 0 { - val = 0 - } - if val > max { - val = max - } - return "[" + strings.Repeat("█", val) + strings.Repeat("░", max-val) + "]" -} - -func printFinalResult(s Solution) { - fmt.Printf("Smallest Total Possible: %s%d%s\n", Green, s.sum, Reset) - fmt.Printf("Configuration: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d\n", s.a, s.b, s.c, s.d, s.e, s.f) -}