aoc24/day13/main.go

129 lines
3.2 KiB
Go
Raw Normal View History

package main
import (
"fmt"
"regexp"
"strconv"
"strings"
"git.mstar.dev/mstar/aoc24/util"
"git.mstar.dev/mstar/goutils/other"
"git.mstar.dev/mstar/goutils/sliceutils"
)
type Machine struct {
A, B, Price util.Vec2
}
var buttonARegex = regexp.MustCompile(`Button A: X\+(\d+), Y\+(\d+)`)
var buttonBRegex = regexp.MustCompile(`Button B: X\+(\d+), Y\+(\d+)`)
var prizeRegex = regexp.MustCompile(`Prize: X=(\d+), Y=(\d+)`)
func dataToGroups(data []byte) [][]string {
bigGroups := strings.Split(string(data), "\n\n")
return sliceutils.Map(
sliceutils.Filter(bigGroups, func(t string) bool { return len(t) != 0 }),
func(t string) []string {
return sliceutils.Filter(
strings.Split(t, "\n"),
func(t string) bool { return len(t) != 0 },
)
},
)
}
func groupsToMachines(groups [][]string) []Machine {
return sliceutils.Map(groups, func(t []string) Machine {
return groupToMachine(t[0], t[1], t[2])
})
}
func groupToMachine(a, b, p string) Machine {
aMatches := buttonARegex.FindStringSubmatch(a)
vecA := util.Vec2{
X: int64(other.Must(strconv.Atoi(aMatches[1]))),
Y: int64(other.Must(strconv.Atoi(aMatches[2]))),
}
bMatches := buttonBRegex.FindStringSubmatch(b)
vecb := util.Vec2{
X: int64(other.Must(strconv.Atoi(bMatches[1]))),
Y: int64(other.Must(strconv.Atoi(bMatches[2]))),
}
pMatches := prizeRegex.FindStringSubmatch(p)
vecp := util.Vec2{
X: int64(other.Must(strconv.Atoi(pMatches[1]))),
Y: int64(other.Must(strconv.Atoi(pMatches[2]))),
}
return Machine{vecA, vecb, vecp}
}
func totalCost(a, b uint64) uint64 {
return a*3 + b
}
func (m *Machine) CalcLowest() (aCount, bCount int, found bool) {
smallestTotal := totalCost(1000, 1000)
smallestA := 1000
smallestB := 1000
for a := 100; a >= 1; a-- {
for b := 100; b >= 1; b-- {
if m.A.Mult(int64(a)).Add(m.B.Mult(int64(b))).Eq(m.Price) {
if smallestTotal > uint64(a)*3+uint64(b) {
smallestA = a
smallestB = b
smallestTotal = totalCost(uint64(a), uint64(b))
}
}
}
}
return smallestA, smallestB, smallestTotal < totalCost(1000, 1000)
}
func (m *Machine) CalcLowest2() (int64, bool) {
truePrice := m.Price.Add(util.Vec2{X: 10000000000000, Y: 10000000000000})
// Determinant of the two buttons
determinant := m.A.X*m.B.Y - m.A.Y*m.B.X
if determinant == 0 {
return 0, false
}
// Determinant of button B and true price
numA := truePrice.X*m.B.Y - truePrice.Y*m.B.X
// Determinant of button A and true price
numB := truePrice.Y*m.A.X - truePrice.X*m.A.Y
if numA%determinant != 0 || numB%determinant != 0 {
return 0, false
}
a := numA / determinant
b := numB / determinant
if a >= 0 && b >= 0 {
return a*3 + b, true
} else {
return 0, false
}
}
func main() {
groups := dataToGroups(util.LoadFileFromArgs())
machines := groupsToMachines(groups)
var acc uint64 = 0
var maxAcc uint64 = 0
// fmt.Println(machines[0].CalcLowest())
for _, machine := range machines {
if a, b, ok := machine.CalcLowest(); ok {
// fmt.Printf("Machine %d is ok\n", i)
acc += totalCost(uint64(a), uint64(b))
}
// fmt.Println("lowest 1 done")
if a, ok := machine.CalcLowest2(); ok {
maxAcc += uint64(a)
}
// fmt.Println("lowest 2 done")
}
fmt.Printf("Task 1: %d\n", acc)
fmt.Printf("Task 2: %d\n", maxAcc)
}