package main import ( "bufio" "fmt" "log" "math" "os" "strconv" "strings" ) var particles map[int]*Particle var confidenceThreshold int func main() { inp := StdinToStrings() particles = make(map[int]*Particle) for i := range inp { particles[i] = NewParticle(inp[i], i) } confidenceThreshold = 1000 doPart := 2 if len(os.Args) > 1 { if os.Args[1] == "-1" { doPart = 1 } } switch doPart { case 1: part1(inp) case 2: part2(inp) } } func part1(inp []string) { var ticks, confidence, last int cPart := -1 for { cDist := 10000000000 ticks++ for i := range particles { particles[i].Tick() if particles[i].GetDistance() < cDist { cPart = i cDist = particles[i].GetDistance() } } if cPart == last { confidence++ } else { confidence = 0 last = cPart } if confidence >= confidenceThreshold { break } } fmt.Println("After", ticks, "the closest is", cPart) } func part2(inp []string) { var ticks, lastNum, confidence int for { ticks++ for i := range particles { particles[i].Tick() } var collisions []int for k1, v1 := range particles { for k2, v2 := range particles { if k1 == k2 { continue } if v1.Collides(v2) { collisions = AddToSlice(collisions, k1) collisions = AddToSlice(collisions, k2) break } } } for i := len(collisions) - 1; i >= 0; i-- { delete(particles, collisions[i]) } if len(particles) == lastNum { confidence++ } else { confidence = 0 lastNum = len(particles) } if confidence >= confidenceThreshold { break } } fmt.Println("After", ticks, "there are", len(particles), "particles left") } func AddToSlice(sl []int, v int) []int { for i := range sl { if sl[i] == v { return sl } } sl = append(sl, v) return sl } type Coordinate struct { X, Y, Z int } func NewCoordinate(inp string) Coordinate { inp = inp[1 : len(inp)-1] pts := strings.Split(inp, ",") px, py, pz := Atoi(pts[0]), Atoi(pts[1]), Atoi(pts[2]) return Coordinate{X: px, Y: py, Z: pz} } func (c *Coordinate) toString() string { return fmt.Sprintf("<%d,%d,%d>", c.X, c.Y, c.Z) } type Particle struct { Index int Position Coordinate Velocity Coordinate Acceleration Coordinate LastPosition Coordinate } func NewParticle(inp string, idx int) *Particle { ret := new(Particle) ret.Index = idx pts := strings.Split(inp, " ") for i := range pts { keyVal := strings.Split(pts[i], "=") if keyVal[1][len(keyVal[1])-1] == ',' { keyVal[1] = keyVal[1][:len(keyVal[1])-1] } switch keyVal[0] { case "p": ret.Position = NewCoordinate(keyVal[1]) case "v": ret.Velocity = NewCoordinate(keyVal[1]) case "a": ret.Acceleration = NewCoordinate(keyVal[1]) } } ret.LastPosition = NewCoordinate(ret.Position.toString()) return ret } func (p *Particle) Collides(tst *Particle) bool { if p.Position.X == tst.Position.X && p.Position.Y == tst.Position.Y && p.Position.Z == tst.Position.Z { return true } return false } func (p *Particle) String() string { return fmt.Sprintf( "p=<%d,%d,%d>, v=<%d,%d,%d>, a=<%d,%d,%d>", p.Position.X, p.Position.Y, p.Position.Z, p.Velocity.X, p.Velocity.Y, p.Velocity.Z, p.Acceleration.X, p.Acceleration.Y, p.Acceleration.Z, ) } func (p *Particle) Tick() { p.LastPosition = NewCoordinate(p.Position.toString()) p.Velocity.X += p.Acceleration.X p.Velocity.Y += p.Acceleration.Y p.Velocity.Z += p.Acceleration.Z p.Position.X += p.Velocity.X p.Position.Y += p.Velocity.Y p.Position.Z += p.Velocity.Z } func (p *Particle) IsDone() bool { return p.GetDistance() == p.GetLastDistance() } func (p *Particle) GetDistance() int { return GetAbs(p.Position.X) + GetAbs(p.Position.Y) + GetAbs(p.Position.Z) } func (p *Particle) GetLastDistance() int { return int(math.Abs(float64(p.LastPosition.X + p.LastPosition.Y + p.LastPosition.Z))) } func StdinToStrings() []string { var input []string scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { input = append(input, scanner.Text()) } return input } func GetAbs(v int) int { return int(math.Abs(float64(v))) } func Atoi(i string) int { var ret int var err error if ret, err = strconv.Atoi(i); err != nil { log.Fatal("Invalid Atoi") } return ret }