180 lines
4.1 KiB
Go
180 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,
|
||
|
)
|
||
|
}
|