feat: completed day 10 for advent of code
This commit is contained in:
parent
b64325a60a
commit
b29cef3adc
4 changed files with 218 additions and 9 deletions
|
|
@ -18,18 +18,20 @@ import (
|
|||
"cpatino.com/advent-of-code/2025/day07"
|
||||
"cpatino.com/advent-of-code/2025/day08"
|
||||
"cpatino.com/advent-of-code/2025/day09"
|
||||
"cpatino.com/advent-of-code/2025/day10"
|
||||
)
|
||||
|
||||
var days = map[int]func(string) (any, any){
|
||||
1: day01.Run,
|
||||
2: day02.Run,
|
||||
3: day03.Run,
|
||||
4: day04.Run,
|
||||
5: day05.Run,
|
||||
6: day06.Run,
|
||||
7: day07.Run,
|
||||
8: day08.Run,
|
||||
9: day09.Run,
|
||||
1: day01.Run,
|
||||
2: day02.Run,
|
||||
3: day03.Run,
|
||||
4: day04.Run,
|
||||
5: day05.Run,
|
||||
6: day06.Run,
|
||||
7: day07.Run,
|
||||
8: day08.Run,
|
||||
9: day09.Run,
|
||||
10: day10.Run,
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
|
|
|
|||
170
2025/day10/day10.go
Executable file
170
2025/day10/day10.go
Executable file
|
|
@ -0,0 +1,170 @@
|
|||
package day10
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Combination struct {
|
||||
incr []int
|
||||
presses int
|
||||
}
|
||||
|
||||
func parse(line string) (goalBits []int, buttons [][]int, requirements []int) {
|
||||
fields := strings.Fields(line)
|
||||
pattern := fields[0][1 : len(fields[0])-1]
|
||||
for i := 0; i < len(pattern); i++ {
|
||||
if pattern[i] == '#' {
|
||||
goalBits = append(goalBits, 1)
|
||||
} else {
|
||||
goalBits = append(goalBits, 0)
|
||||
}
|
||||
}
|
||||
for i := 1; i < len(fields)-1; i++ {
|
||||
inside := fields[i][1 : len(fields[i])-1]
|
||||
if inside == "" {
|
||||
buttons = append(buttons, []int{})
|
||||
continue
|
||||
}
|
||||
parts := strings.Split(inside, ",")
|
||||
var idxs []int
|
||||
for _, p := range parts {
|
||||
n, _ := strconv.Atoi(p)
|
||||
idxs = append(idxs, n)
|
||||
}
|
||||
buttons = append(buttons, idxs)
|
||||
}
|
||||
last := fields[len(fields)-1][1 : len(fields[len(fields)-1])-1]
|
||||
if last != "" {
|
||||
for _, p := range strings.Split(last, ",") {
|
||||
n, _ := strconv.Atoi(p)
|
||||
requirements = append(requirements, n)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func buildCombinations(buttons [][]int, counters int) []Combination {
|
||||
n := len(buttons)
|
||||
res := make([]Combination, 0, 1<<uint(n))
|
||||
for mask := 0; mask < 1<<uint(n); mask++ {
|
||||
inc := make([]int, counters)
|
||||
press := 0
|
||||
for j := range n {
|
||||
if (mask>>uint(j))&1 == 1 {
|
||||
press++
|
||||
for _, idx := range buttons[j] {
|
||||
if 0 <= idx && idx < counters {
|
||||
inc[idx]++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
res = append(res, Combination{inc, press})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func sameParity(a, b []int) bool {
|
||||
for i := range a {
|
||||
if (a[i]^b[i])&1 == 1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func notExceed(a, b []int) bool {
|
||||
for i := range a {
|
||||
if a[i] > b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isZero(v []int) bool {
|
||||
for _, x := range v {
|
||||
if x != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Part1(goalBits []int, buttons [][]int) int {
|
||||
counters := len(goalBits)
|
||||
best := math.MaxInt
|
||||
for _, cb := range buildCombinations(buttons, counters) {
|
||||
if sameParity(cb.incr, goalBits) && cb.presses < best {
|
||||
best = cb.presses
|
||||
}
|
||||
}
|
||||
if best == math.MaxInt {
|
||||
return 0
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
func minPressesCounters(state []int, combs []Combination) (int, bool) {
|
||||
if isZero(state) {
|
||||
return 0, true
|
||||
}
|
||||
|
||||
best := math.MaxInt
|
||||
for _, cb := range combs {
|
||||
if !notExceed(cb.incr, state) || !sameParity(cb.incr, state) {
|
||||
continue
|
||||
}
|
||||
next := make([]int, len(state))
|
||||
for i := range state {
|
||||
next[i] = (state[i] - cb.incr[i]) / 2
|
||||
}
|
||||
rec, ok := minPressesCounters(next, combs)
|
||||
if ok {
|
||||
total := 2*rec + cb.presses
|
||||
if total < best {
|
||||
best = total
|
||||
}
|
||||
}
|
||||
}
|
||||
if best < math.MaxInt {
|
||||
return best, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func Part2(requirements []int, buttons [][]int) int {
|
||||
combs := buildCombinations(buttons, len(requirements))
|
||||
ans, _ := minPressesCounters(requirements, combs)
|
||||
return ans
|
||||
}
|
||||
|
||||
func Run(filename string) (any, any) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
a, b := 0, 0
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
line := strings.TrimSpace(s.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
g, btns, req := parse(line)
|
||||
|
||||
a += Part1(g, btns)
|
||||
b += Part2(req, btns)
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return a, b
|
||||
}
|
||||
34
2025/day10/day10_test.go
Executable file
34
2025/day10/day10_test.go
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
package day10
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TestCase struct {
|
||||
FileName string
|
||||
Part1 any
|
||||
Part2 any
|
||||
}
|
||||
|
||||
func TestDay10(t *testing.T) {
|
||||
tests := []TestCase{
|
||||
{"test.txt", 7, 33},
|
||||
{"input.txt", 409, 15489},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if _, err := os.Stat(test.FileName); os.IsNotExist(err) {
|
||||
t.Fatalf("test file does not exist: %v", test.FileName)
|
||||
}
|
||||
|
||||
part1, part2 := Run(test.FileName)
|
||||
if test.Part1 != nil && part1 != test.Part1 {
|
||||
t.Fatalf("%s: unexpected part1:\nwant:\t%v\ngot:\t%v", test.FileName, test.Part1, part1)
|
||||
}
|
||||
|
||||
if test.Part2 != nil && part2 != test.Part2 {
|
||||
t.Fatalf("%s: unexpected part2:\nwant:\t%v\ngot:\t%v", test.FileName, test.Part2, part2)
|
||||
}
|
||||
}
|
||||
}
|
||||
3
2025/day10/test.txt
Executable file
3
2025/day10/test.txt
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
|
||||
[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}
|
||||
[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}
|
||||
Loading…
Add table
Add a link
Reference in a new issue