adventofcode/2017/day20/day20.go

216 lines
4.2 KiB
Go

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
}