aoc24/day14/main.go

179 lines
4.1 KiB
Go

package main
import (
"flag"
"fmt"
"regexp"
"strconv"
"strings"
"git.mstar.dev/mstar/aoc24/util"
"git.mstar.dev/mstar/goutils/other"
"git.mstar.dev/mstar/goutils/sliceutils"
)
type Bot struct {
Pos, Vel util.Vec2
}
var inputRegex = regexp.MustCompile(`p=(?P<px>\d+),(?P<py>\d+) v=(?P<vx>-?\d+),(?P<vy>-?\d+)`)
var areaSizeX = flag.Int("sizex", 101, "set area x size")
var areaSizeY = flag.Int("sizey", 103, "set area y size")
func parseLine(line string) (pos, vel util.Vec2) {
match := inputRegex.FindStringSubmatch(line)
pxs := match[1]
pys := match[2]
vxs := match[3]
vys := match[4]
return util.Vec2{
X: other.Must(strconv.ParseInt(pxs, 10, 64)),
Y: other.Must(strconv.ParseInt(pys, 10, 64)),
}, util.Vec2{
X: other.Must(strconv.ParseInt(vxs, 10, 64)),
Y: other.Must(strconv.ParseInt(vys, 10, 64)),
}
}
func linesToBots(lines []string) []*Bot {
return sliceutils.Map(lines, func(t string) *Bot {
p, v := parseLine(t)
return &Bot{p, v}
})
}
func wrap(x, a int64) int64 {
if x < 0 {
return x + a + 1
}
if x > a {
return x - a - 1
}
return x
}
func (b *Bot) Step() {
// fmt.Printf("Pos %v, add %v\n", b.Pos, b.Vel)
intendedPos := b.Pos.Add(b.Vel)
// fmt.Println("Intended", intendedPos)
actualPos := util.Vec2{
X: wrap(intendedPos.X, int64(*areaSizeX)-1),
Y: wrap(intendedPos.Y, int64(*areaSizeY)-1),
}
// fmt.Println("Moduloed", actualPos)
b.Pos = actualPos
}
func stepAll(bots []*Bot) {
for _, b := range bots {
b.Step()
}
}
// Corner 1 inclusive bottom top left, corner 2 exclusive bottom right
func filterForArea(bots []*Bot, corner1, corner2 util.Vec2) []*Bot {
if corner1.X > corner2.X || corner1.Y > corner2.Y {
panic("Bad corner sizes")
}
return sliceutils.Filter(bots, func(t *Bot) bool {
return t.Pos.X >= corner1.X && t.Pos.X < corner2.X && t.Pos.Y >= corner1.Y &&
t.Pos.Y < corner2.Y
})
}
func visualise(bots []*Bot) string {
builder := strings.Builder{}
field := [][]int{}
for range *areaSizeY {
line := []int{}
for range *areaSizeX {
line = append(line, 0)
}
field = append(field, line)
}
for _, b := range bots {
field[b.Pos.Y][b.Pos.X]++
}
for _, line := range field {
for _, char := range line {
if char == 0 {
builder.WriteRune('.')
} else {
builder.WriteString(fmt.Sprint(char))
}
}
builder.WriteRune('\n')
}
return builder.String()
}
func safetyFactor(bots []*Bot) int {
topLeft := filterForArea(
bots,
util.Vec2{X: 0, Y: 0},
util.Vec2{X: int64(*areaSizeX) / 2, Y: int64(*areaSizeY) / 2},
)
topRight := filterForArea(
bots,
util.Vec2{X: int64(*areaSizeX)/2 + 1, Y: 0},
util.Vec2{X: int64(*areaSizeX), Y: int64(*areaSizeY) / 2},
)
bottomLeft := filterForArea(
bots,
util.Vec2{X: 0, Y: int64(*areaSizeY)/2 + 1},
util.Vec2{X: int64(*areaSizeX) / 2, Y: int64(*areaSizeY)},
)
bottomRight := filterForArea(
bots,
util.Vec2{X: int64(*areaSizeX)/2 + 1, Y: int64(*areaSizeY)/2 + 1},
util.Vec2{X: int64(*areaSizeX), Y: int64(*areaSizeY)},
)
return len(topLeft) * len(topRight) * len(bottomLeft) * len(bottomRight)
}
func main() {
flag.Parse()
inputLines := util.FileContentToNonEmptyLines(util.LoadFileFromArgs())
fmt.Println(*areaSizeX, *areaSizeY)
bots := linesToBots(inputLines)
// bots := []*Bot{&Bot{Pos: util.Vec2{X: 2, Y: 4}, Vel: util.Vec2{X: 2, Y: -3}}}
// fmt.Println(visualise(bots))
// fmt.Println(bots[11])
for range 100 {
// bots[11].Step()
// fmt.Println(i)
// fmt.Println(visualise(bots))
stepAll(bots)
// fmt.Println(bots[0])
}
// fmt.Println(visualise(bots))
highestFactor := safetyFactor(bots)
highestFactorCounter := 100
lowestFactor := highestFactor
lowestFactorCounter := 100
fmt.Printf("Task 1: %d\n", highestFactor)
counter := 100
for range 10000 {
counter++
stepAll(bots)
tmp := safetyFactor(bots)
if highestFactor < tmp {
highestFactor = tmp
highestFactorCounter = counter
}
if tmp < lowestFactor {
lowestFactor = tmp
lowestFactorCounter = counter
}
}
fmt.Printf(
"Highest factor of %d at %d, lowest of %d at %d\n",
highestFactor,
highestFactorCounter,
lowestFactor,
lowestFactorCounter,
)
}