package main import ( "fmt" "os" "math" "errors" "strings" "regexp" "strconv" ) // data types type Vec struct { x uint32 y uint32 } func (v Vec) Sub(other Vec) Vec { return Vec{x: v.x - other.x, y: v.y - other.y} } func (v Vec) Mul(skalar uint32) Vec { return Vec{x: v.x * skalar, y: v.y *skalar} } func (v Vec) VecDiv(other Vec) (float64, error) { /* Returns an error if v and other do not lie on a staight line. * It holds: v.VecDiv(other).Abs() = v.Abs() / other.Abs() */ cx := float64(v.x) / float64(other.x) cy := float64(v.y) / float64(other.y) if abs(cx - cy) > 10e-5 { return 0, errors.New("vectors do not lie in a straight line") } return cx, nil } func (v Vec) Abs() float64 { return math.Sqrt(float64(v.x * v.x + v.y * v.y)) } type ClawMachine struct { buttonA Vec buttonB Vec price Vec } func (cm ClawMachine) FindPath(max_presses_per_button uint32) ([2]uint32, error) { /* Find all combinations of cm.buttonA and cm.buttonB that result in cm.price . * => x1 * cm.buttonA + x2 * cm.buttonB = cm.price * where x1, x2 are of type uint and x1,x2 <= max_presses_per_button * * Returns a list of all the [x1, x2] where the equation is solvable with the * restrictions above. */ for i := range max_presses_per_button { x1, err := cm.price.Sub( cm.buttonB.Mul(i + 1) ).VecDiv( cm.buttonA ) if err != nil || uint32(x1 - 10e-5) > max_presses_per_button { continue } x2, err := cm.price.Sub( cm.buttonA.Mul(uint32(x1)) ).VecDiv( cm.buttonB ) if err == nil && i + 1 == uint32(x2) { return [2]uint32{uint32(x1), uint32(x2)}, nil } } return [2]uint32{0,0}, errors.New("no path found") } // general util func check(e error) { if e != nil { panic(e) } } func abs(n float64) float64 { if n < 0 { return -n } return n } // main loop func main() { // read in file dat, err := os.ReadFile("data.txt") check(err) dat_str := strings.TrimSpace(string(dat)) cms := make([]ClawMachine, 0) pattern := regexp.MustCompile(`\d+`) numbers := [6]uint32{} for _, machine_str := range strings.Split(dat_str, "\n\n") { // collect numbers as ints for i, match := range pattern.FindAllString(machine_str, -1) { numInt, err := strconv.Atoi(match) check(err) numbers[i] = uint32(numInt) } // construct ClawMachines cms = append(cms, ClawMachine{ Vec{numbers[0],numbers[1]}, Vec{numbers[2], numbers[3]}, Vec{numbers[4],numbers[5]}, }) } // calculate cost cost := 0 for _, cm := range cms { path, err := cm.FindPath(100) if err == nil { cost += int(path[0]) * 3 + int(path[1]) } } fmt.Println(cost) }