Files

123 lines
2.8 KiB
Go
Raw Permalink Normal View History

2024-12-14 10:39:50 +01:00
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)
}