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\d+),(?P\d+) v=(?P-?\d+),(?P-?\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, ) }