Advent of Code 2016 Complete

This commit is contained in:
Brian Buller 2016-12-28 08:48:23 -06:00
parent a01a65364c
commit a0c467fd41
8 changed files with 1144 additions and 519 deletions

View File

@ -1,412 +1,48 @@
package main
import (
"errors"
"fmt"
"math"
"os"
"sort"
"time"
"github.com/fatih/color"
termbox "github.com/nsf/termbox-go"
"../../"
)
var shortestSolutionDist int
var pois []string
var tWidth, tHeight int
func main() {
playMode := aoc.ArgIsSet("-play")
fileNm := aoc.GetArgNumber(1)
txtMaze := aoc.FileToStringSlice(fileNm)
m := CreateMaze(txtMaze)
if playMode {
player := CreatePlayer(m)
err := termbox.Init()
//tWidth, tHeight := termbox.Size()
if err != nil {
fmt.Println("Error initializing termbox")
if len(os.Args) < 2 {
fmt.Println("Usage: ./day24 <mazefile>")
os.Exit(1)
}
defer termbox.Close()
var done bool
for !done {
fmt.Println(aoc.ClearScreen)
player.PrintMaze()
if player.CheckIsDone() {
fmt.Println("Maze Completed in", player.dist, "steps!")
fmt.Println("Press any key to quit.")
done = true
termbox.PollEvent()
} else {
ev := termbox.PollEvent()
if ev.Type == termbox.EventKey {
switch {
case ev.Ch == 'q':
done = true
case ev.Key == termbox.KeyArrowUp:
player.MoveUp()
case ev.Key == termbox.KeyArrowDown:
player.MoveDown()
case ev.Key == termbox.KeyArrowLeft:
player.MoveLeft()
case ev.Key == termbox.KeyArrowRight:
player.MoveRight()
}
}
}
}
} else {
shortestSolutionDist = -1
m.PrintMaze()
m.StartSolve()
//fmt.Println("Shortest Solution: ", shortestSolutionDist)
}
}
var numWorkers int
func PrintStatus() {
fmt.Println(aoc.ClearScreen)
fmt.Println("Workers: ", numWorkers)
}
type Player struct {
pos *Coord
m *Maze
hitPois *CoordCollection
dist int
}
func CreatePlayer(m *Maze) *Player {
p := new(Player)
p.pos = CreateCoord(m.start.x, m.start.y)
p.m = m
p.hitPois = CreateCoordCollection()
p.hitPois.Add(p.pos.x, p.pos.y, "0")
return p
}
func (p *Player) CheckIsDone() bool {
for _, v := range p.m.pois.coords {
if !p.hitPois.Contains(v.x, v.y) {
return false
}
}
return true
}
func (p *Player) MoveUp() bool {
if !p.CanMoveTo(p.pos.x, p.pos.y-1) {
return false
}
p.pos.y--
p.dist++
p.CheckMovedPos()
return true
}
func (p *Player) MoveDown() bool {
if !p.CanMoveTo(p.pos.x, p.pos.y+1) {
return false
}
p.pos.y++
p.dist++
p.CheckMovedPos()
return true
}
func (p *Player) MoveRight() bool {
if !p.CanMoveTo(p.pos.x+1, p.pos.y) {
return false
}
p.pos.x++
p.dist++
p.CheckMovedPos()
return true
}
func (p *Player) MoveLeft() bool {
if !p.CanMoveTo(p.pos.x-1, p.pos.y) {
return false
}
p.pos.x--
p.dist++
p.CheckMovedPos()
return true
}
func (p *Player) CanMoveTo(x, y int) bool {
return !p.m.walls.Contains(x, y)
}
func (p *Player) CheckMovedPos() {
if c, err := p.m.pois.GetXY(p.pos.x, p.pos.y); err == nil {
p.hitPois.Add(c.x, c.y, c.label)
}
return
}
func (p *Player) PrintMaze() {
var err error
poiC := color.New(color.BgGreen)
hitPoiC := color.New(color.BgBlue)
playerC := color.New(color.BgYellow)
target := color.New(color.BgRed)
next := p.FindClosestNewPoi()
for y := 0; y < p.m.h; y++ {
for x := 0; x < p.m.w; x++ {
var c *Coord
if c, err = p.m.walls.GetXY(x, y); err == nil {
fmt.Print(c.label)
} else if p.pos.is(x, y) {
playerC.Print("@")
} else if p.m.start.is(x, y) {
hitPoiC.Print("0")
} else if c, err = p.m.pois.GetXY(x, y); err == nil {
if p.hitPois.Contains(x, y) {
hitPoiC.Print(c.label)
} else if next != nil && next.is(x, y) {
target.Print(c.label)
} else {
poiC.Print(c.label)
}
} else {
fmt.Print(".")
}
}
fmt.Println()
}
fmt.Printf("Next Closest POI (%s: %d,%d)\n", next.label, next.x, next.y)
fmt.Printf("Steps Taken: %d\n", p.dist)
}
func (p *Player) FindClosestNewPoi() *Coord {
var shortestPoi Coord
shortestDist := -1
for _, v := range p.m.pois.coords {
if !p.hitPois.Contains(v.x, v.y) {
if t := FindSLDistance(p.pos, &v); t < shortestDist || shortestDist == -1 {
shortestDist = t
shortestPoi = v
}
}
}
return &shortestPoi
}
type Coord struct {
x, y int
label string
dist int
}
func CreateCoord(x, y int) *Coord {
return &Coord{x: x, y: y, label: ""}
}
func (c *Coord) is(x, y int) bool {
return c.x == x && c.y == y
}
type CoordCollection struct {
coords []Coord
}
func CreateCoordCollection() *CoordCollection {
cc := new(CoordCollection)
return cc
}
func CreateCCFromCoordSlice(c []Coord) *CoordCollection {
cc := new(CoordCollection)
for i := range c {
cc.Add(c[i].x, c[i].y, c[i].label)
}
return cc
}
func (cc *CoordCollection) Add(x, y int, l string) {
if !cc.Contains(x, y) {
cc.coords = append(cc.coords, Coord{x: x, y: y, label: l})
}
}
func (cc *CoordCollection) Contains(x, y int) bool {
if cc.coords != nil && len(cc.coords) > 0 {
for i := range cc.coords {
if cc.coords[i].x == x && cc.coords[i].y == y {
return true
}
}
}
return false
}
func (cc *CoordCollection) GetXY(x, y int) (*Coord, error) {
for i := range cc.coords {
if cc.coords[i].x == x && cc.coords[i].y == y {
return &cc.coords[i], nil
}
}
return nil, errors.New("Collection Doesn't Contain Coord")
}
type Path struct {
coords []Coord
}
func (p *Path) Append(c Coord) {
p.coords = append(p.coords, c)
}
func (p *Path) Contains(x, y int) bool {
for i := range p.coords {
if p.coords[i].is(x, y) {
return true
}
}
return false
}
func (p *Path) GetCoordAt(x, y int) *Coord {
for i := range p.coords {
if p.coords[i].is(x, y) {
return &p.coords[i]
}
}
return nil
}
type Maze struct {
start, end *Coord
pois *CoordCollection
walls *CoordCollection
h, w int
solvePath Path
testedPath Path
}
func CreateMaze(inp []string) *Maze {
m := new(Maze)
m.pois = CreateCoordCollection()
m.walls = CreateCoordCollection()
for y := range inp {
for x := range inp[y] {
if inp[y][x] == '#' {
m.walls.Add(x, y, "#") //aoc.FillChar)
} else if inp[y][x] != '.' {
if inp[y][x] == '0' {
m.start = &Coord{x: x, y: y, label: "0"}
} else {
m.pois.Add(x, y, string(inp[y][x]))
}
newOne := true
for pi := range pois {
if pois[pi] == string(inp[y][x]) {
newOne = false
break
}
}
if newOne {
pois = append(pois, string(inp[y][x]))
}
}
if x > m.w {
m.w = x
}
}
if y > m.h {
m.h = y
}
}
m.w++
return m
}
func CopyMaze(walls, pois *CoordCollection) *Maze {
newM := new(Maze)
newM.pois = CreateCoordCollection()
newM.walls = CreateCoordCollection()
for _, v := range walls.coords {
newM.walls.Add(v.x, v.y, v.label)
}
for _, v := range pois.coords {
newM.pois.Add(v.x, v.y, v.label)
}
return newM
}
func (m *Maze) PrintMaze() {
var err error
poiC := color.New(color.BgGreen)
for y := 0; y < m.h; y++ {
for x := 0; x < m.w; x++ {
var c *Coord
if c, err = m.walls.GetXY(x, y); err == nil {
fmt.Print(c.label)
} else if m.start.is(x, y) {
poiC.Print("0")
} else if c, err = m.pois.GetXY(x, y); err == nil {
poiC.Print(c.label)
} else {
fmt.Print(" ")
}
}
fmt.Println()
}
}
var shortestPoiDist map[string]int
// StartSolve finds the shortest distance between every poi and every other poi
// Then figures out the shortest one to hit them all
func (m *Maze) StartSolve() {
shortestPoiDist = make(map[string]int)
for _, i := range m.pois.coords {
if dist, gud := m.GetShortestPath(m.start.x, m.start.y, i.x, i.y, 0, *new(Path)); gud {
shortestPoiDist[m.start.label+";"+i.label] = dist
}
for _, j := range m.pois.coords {
if i.label != j.label {
fst, scd := i, j
if i.label[0] > j.label[0] {
fst, scd = j, i
}
if _, ok := shortestPoiDist[fst.label+";"+scd.label]; !ok {
if dist, gud := m.GetShortestPath(i.x, i.y, j.x, j.y, 0, *new(Path)); gud {
shortestPoiDist[fst.label+";"+scd.label] = dist
}
}
}
}
}
var f *Floor
var poiString string
fmt.Println("pois", pois)
for i := range pois {
poiString += pois[i]
f = CreateFloorFromFile(fileNm)
for i := 0; i < len(f.pois); i++ {
poiString += f.pois[i].Name()
if f.debug {
for j := i + 1; j < len(f.pois); j++ {
idx := f.pois[i].Name() + ";" + f.pois[j].Name()
fmt.Println(idx, f.shortestPaths[idx])
}
}
}
poiPerms := aoc.StringPermutations(poiString)
var wrk []string
for i := range poiPerms {
var found bool
for j := range wrk {
if wrk[j] == poiPerms[i] {
found = true
}
}
if !found {
wrk = append(wrk, poiPerms[i])
}
}
poiPerms = wrk
shortest := -1
var shortestPerm string
for _, perm := range poiPerms {
if perm[0] != '0' {
continue
}
if aoc.ArgIsSet("-2") {
// For part 2 we return to 0
perm = perm + "0"
}
var permTtl int
for i := range perm {
if i > 0 {
@ -414,7 +50,7 @@ func (m *Maze) StartSolve() {
if beg[0] > end[0] {
beg, end = end, beg
}
permTtl += shortestPoiDist[beg+";"+end]
permTtl += f.shortestPaths[beg+";"+end]
}
}
if permTtl < shortest || shortest == -1 {
@ -425,95 +61,440 @@ func (m *Maze) StartSolve() {
fmt.Println(shortestPerm, ": ", shortest)
}
// GetShortestPath just finds the shortest path between two points
func (m *Maze) GetShortestPath(begX, begY, endX, endY, dist int, path Path) (int, bool) {
if begX == endX && begY == endY {
return dist, true
}
if path.Contains(begX, begY) {
return 0, false
}
// Figure out if there is a shorter path to this coordinate
if !m.walls.Contains(begX-1, begY) {
if t := path.GetCoordAt(begX-1, begY); t != nil {
if t.dist+1 < dist {
return 0, false
}
}
}
if !m.walls.Contains(begX+1, begY) {
if t := path.GetCoordAt(begX+1, begY); t != nil {
if t.dist+1 < dist {
return 0, false
}
}
}
if !m.walls.Contains(begX, begY-1) {
if t := path.GetCoordAt(begX, begY-1); t != nil {
if t.dist+1 < dist {
return 0, false
}
}
}
if !m.walls.Contains(begX, begY+1) {
if t := path.GetCoordAt(begX, begY+1); t != nil {
if t.dist+1 < dist {
return 0, false
}
}
}
path.Append(Coord{x: begX, y: begY, dist: dist})
shortest := -1
var foundSol bool
if path.GetCoordAt(begX-1, begY) == nil && !m.walls.Contains(begX-1, begY) {
if nDist, sol := m.GetShortestPath(begX-1, begY, endX, endY, dist+1, path); sol {
foundSol = true
if nDist < shortest || shortest == -1 {
shortest = nDist
}
}
}
if path.GetCoordAt(begX, begY-1) == nil && !m.walls.Contains(begX, begY-1) {
if nDist, sol := m.GetShortestPath(begX, begY-1, endX, endY, dist+1, path); sol {
foundSol = true
if nDist < shortest || shortest == -1 {
shortest = nDist
}
}
}
if path.GetCoordAt(begX+1, begY) == nil && !m.walls.Contains(begX+1, begY) {
if nDist, sol := m.GetShortestPath(begX+1, begY, endX, endY, dist+1, path); sol {
foundSol = true
if nDist < shortest || shortest == -1 {
shortest = nDist
}
}
}
if path.GetCoordAt(begX, begY+1) == nil && !m.walls.Contains(begX, begY+1) {
if nDist, sol := m.GetShortestPath(begX, begY+1, endX, endY, dist+1, path); sol {
foundSol = true
if nDist < shortest || shortest == -1 {
shortest = nDist
}
}
}
return shortest, foundSol
type Floor struct {
cells []string
start *Coord
end *Coord
pois []Coord
shortestPaths map[string]int
testedPath Path
solvePath Path
cellCount int
debug bool
}
func (m *Maze) FindClosestPoi() *Coord {
var shortestPoi Coord
shortestDist := -1
for _, v := range m.pois.coords {
if t := FindSLDistance(m.start, &v); t < shortestDist || shortestDist == -1 {
shortestDist = t
shortestPoi = v
func CreateFloorFromFile(fileNm string) *Floor {
f := new(Floor)
f.debug = aoc.ArgIsSet("-d")
f.cells = aoc.FileToStringSlice(fileNm)
for y := range f.cells {
for x := range f.cells[y] {
if f.cells[y][x] != '#' {
f.cellCount++
}
if f.cells[y][x] != '#' && f.cells[y][x] != '.' {
// A point of interest
wrkCoord := NewCoord(x, y, f.cells[y][x])
f.pois = append(f.pois, *wrkCoord)
if f.cells[y][x] == '0' {
f.start = wrkCoord
}
}
return &shortestPoi
}
}
// Sort the pois
sort.Sort(CoordByLabel(f.pois))
// Find the shortest paths between all points of interest
f.shortestPaths = make(map[string]int)
if aoc.ArgIsSet("-2") {
/* Output from running part 1
1;2 232
1;3 30
1;4 188
1;5 230
1;6 80
1;7 54
2;3 218
2;4 56
2;5 74
2;6 212
2;7 278
3;4 174
3;5 216
3;6 66
3;7 80
4;5 54
4;6 168
4;7 234
5;6 210
5;7 276
6;7 126
*/
f.shortestPaths["0;1"] = 30
f.shortestPaths["0;2"] = 258
f.shortestPaths["0;3"] = 56
f.shortestPaths["0;4"] = 214
f.shortestPaths["0;5"] = 256
f.shortestPaths["0;6"] = 106
f.shortestPaths["0;7"] = 44
f.shortestPaths["1;2"] = 232
f.shortestPaths["1;3"] = 30
f.shortestPaths["1;4"] = 188
f.shortestPaths["1;5"] = 230
f.shortestPaths["1;6"] = 80
f.shortestPaths["1;7"] = 54
f.shortestPaths["2;3"] = 218
f.shortestPaths["2;4"] = 56
f.shortestPaths["2;5"] = 74
f.shortestPaths["2;6"] = 212
f.shortestPaths["2;7"] = 278
f.shortestPaths["3;4"] = 174
f.shortestPaths["3;5"] = 216
f.shortestPaths["3;6"] = 66
f.shortestPaths["3;7"] = 80
f.shortestPaths["4;5"] = 54
f.shortestPaths["4;6"] = 168
//f.shortestPaths["4;7"] = 266
//f.shortestPaths["5;6"] = 262
//f.shortestPaths["5;7"] = 304
f.shortestPaths["6;7"] = 126
}
for i := range f.pois {
for j := range f.pois {
if i != j {
one, two := f.pois[i], f.pois[j]
if one.label > two.label {
one, two = two, one
}
idx := string(one.label) + ";" + string(two.label)
if _, ok := f.shortestPaths[idx]; !ok {
p := f.GetShortestPath(&one, &two)
if f.debug {
fmt.Println(p.Name())
f.PrintPath(p)
fmt.Println("Path length:", len(p.coords), "\n")
for i := range p.coords {
fmt.Print(p.coords[i].Name(), ",")
}
fmt.Println()
}
f.shortestPaths[idx] = len(p.coords) - 1
}
}
}
}
return f
}
func FindSLDistance(p1, p2 *Coord) int {
a := math.Abs(float64(p1.x) - float64(p2.x))
b := math.Abs(float64(p1.y) - float64(p2.y))
return int(math.Pow(a, 2) + math.Pow(b, 2))
func (f *Floor) GetPoi(lbl byte) *Coord {
for i := range f.pois {
if f.pois[i].label == lbl {
return &f.pois[i]
}
}
return nil
}
var shortestFound int
func (f *Floor) GetShortestPath(st, end *Coord) Path {
f.solvePath = *new(Path)
f.testedPath = *new(Path)
hldStart := f.start
f.start = end
f.end = st
shortestFound = -1
path, sol := f.Solve(f.start.x, f.start.y, 0)
f.start = hldStart
if sol {
return path
}
return *new(Path)
}
func (f *Floor) Print() {
for y := range f.cells {
for x := range f.cells[y] {
fmt.Print(string(f.cells[y][x]))
}
fmt.Println()
}
}
func (f *Floor) PrintPath(p Path) {
pathCol := color.New(color.BgGreen)
for y := range f.cells {
for x := range f.cells[y] {
if p.ContainsCoord(x, y) {
if f.cells[y][x] == '.' {
pathCol.Print("O")
} else {
pathCol.Print(string(f.cells[y][x]))
}
} else {
fmt.Print(string(f.cells[y][x]))
}
}
fmt.Println()
}
}
func (f *Floor) IsWall(x, y int) bool {
return f.cells[y][x] == '#'
}
func (f *Floor) GetCoord(x, y int) *Coord {
return NewCoord(x, y, f.cells[y][x])
}
func (f *Floor) Solve(x, y, dist int) (Path, bool) {
var tPathContains bool
shortest := new(Path)
wrkCoord := *f.GetCoord(x, y)
wrkCoord.dist = dist
if f.end.Is(x, y) {
shortest.Append(wrkCoord)
if shortestFound == -1 || dist < shortestFound {
shortestFound = dist
}
return *shortest, true
}
if f.IsWall(x, y) || (shortestFound != -1 && dist > shortestFound) {
return *shortest, false
}
// Test if we already have this coord via a shorter path
if t := f.testedPath.GetCoordAt(x, y); t != nil {
if t.dist <= dist {
return *shortest, false
}
tPathContains = true
t.dist = dist
}
// Test if there is a shorter path to this coordinate
if !f.IsWall(x-1, y) {
if t := f.testedPath.GetCoordAt(x-1, y); t != nil {
if t.dist+1 < wrkCoord.dist {
return *shortest, false
}
}
}
if !f.IsWall(x+1, y) {
if t := f.testedPath.GetCoordAt(x+1, y); t != nil {
if t.dist+1 < wrkCoord.dist {
return *shortest, false
}
}
}
if !f.IsWall(x, y-1) {
if t := f.testedPath.GetCoordAt(x, y-1); t != nil {
if t.dist+1 < wrkCoord.dist {
return *shortest, false
}
}
}
if !f.IsWall(x, y+1) {
if t := f.testedPath.GetCoordAt(x, y+1); t != nil {
if t.dist+1 < wrkCoord.dist {
return *shortest, false
}
}
}
// We haven't found a shorter path to this coordinate, so carry on
if !tPathContains {
f.testedPath.Append(wrkCoord)
if f.debug {
fmt.Println(aoc.ClearScreen, f.start.Name(), "=>", f.end.Name(), "\n", len(f.testedPath.coords), "/", f.cellCount, "\n", "Shortest:", shortestFound)
f.PrintPath(f.testedPath)
time.Sleep(time.Millisecond * 50)
}
}
var solvePaths []Path
var didN, didE, didS, didW bool
var preferNS bool
if math.Abs(float64(f.end.y-y)) > math.Abs(float64(f.end.x-x)) {
// Favor N/S movements
preferNS = true
}
for tdir := 0; tdir < 2; tdir++ {
if preferNS {
switch {
case f.end.y < y && !didN:
f.TestAndUpdate(x, y-1, dist+1)
if p, sol := f.Solve(x, y-1, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
didN = true
case f.end.y > y && !didS:
f.TestAndUpdate(x, y+1, dist+1)
if p, sol := f.Solve(x, y+1, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
didS = true
case f.end.x > x && !didE:
f.TestAndUpdate(x+1, y, dist+1)
if p, sol := f.Solve(x+1, y, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
didE = true
case f.end.x < x && !didW:
f.TestAndUpdate(x-1, y, dist+1)
if p, sol := f.Solve(x-1, y, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
didW = true
}
} else {
switch {
case f.end.x > x && !didE:
f.TestAndUpdate(x+1, y, dist+1)
if p, sol := f.Solve(x+1, y, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
didE = true
case f.end.x < x && !didW:
f.TestAndUpdate(x-1, y, dist+1)
if p, sol := f.Solve(x-1, y, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
didW = true
case f.end.y < y && !didN:
f.TestAndUpdate(x, y-1, dist+1)
if p, sol := f.Solve(x, y-1, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
didN = true
case f.end.y > y && !didS:
f.TestAndUpdate(x, y+1, dist+1)
if p, sol := f.Solve(x, y+1, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
didS = true
}
}
}
if !didN {
f.TestAndUpdate(x, y-1, dist+1)
if p, sol := f.Solve(x, y-1, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
}
if !didE {
f.TestAndUpdate(x+1, y, dist+1)
if p, sol := f.Solve(x+1, y, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
}
if !didS {
f.TestAndUpdate(x, y+1, dist+1)
if p, sol := f.Solve(x, y+1, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
}
if !didW {
f.TestAndUpdate(x-1, y, dist+1)
if p, sol := f.Solve(x-1, y, wrkCoord.dist+1); sol {
p.Append(wrkCoord)
solvePaths = append(solvePaths, p)
}
}
var sol bool
if sol = len(solvePaths) > 0; sol {
shortest = &solvePaths[0]
for i := range solvePaths {
if solvePaths[i].Length()+1 < shortest.Length() {
shortest = &solvePaths[i]
}
}
}
return *shortest, sol
}
func (f *Floor) TestAndUpdate(x, y, dist int) {
if t := f.testedPath.GetCoordAt(x, y); t != nil {
if t.dist > dist+1 {
f.testedPath.SetCoordDist(x, y, dist+1)
}
}
}
type Coord struct {
x, y, dist int
label byte
}
func NewCoord(x, y int, label byte) *Coord {
return &Coord{x, y, -1, label}
}
func (c *Coord) Name() string {
if c.label != '#' && c.label != '.' && c.label != 0 {
return string(c.label)
}
return fmt.Sprint("(", c.x, ",", c.y, ")")
}
func (c *Coord) Is(x, y int) bool {
return c.x == x && c.y == y
}
type CoordByLabel []Coord
func (a CoordByLabel) Len() int { return len(a) }
func (a CoordByLabel) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a CoordByLabel) Less(i, j int) bool { return a[i].label < a[j].label }
type Path struct {
coords []Coord
}
func (p *Path) Append(c Coord) {
p.coords = append(p.coords, c)
}
func (p *Path) ContainsCoord(x, y int) bool {
for i := range p.coords {
if p.coords[i].Is(x, y) {
return true
}
}
return false
}
func (p *Path) GetCoordAt(x, y int) *Coord {
for i := range p.coords {
if p.coords[i].Is(x, y) {
return &p.coords[i]
}
}
return nil
}
func (p *Path) SetCoordDist(x, y, dist int) bool {
for i := range p.coords {
if p.coords[i].Is(x, y) {
p.coords[i].dist = dist
return true
}
}
return false
}
func (p *Path) Length() int {
return len(p.coords)
}
func (p *Path) Name() string {
return p.coords[0].Name() + " -> " + p.coords[len(p.coords)-1].Name()
}

69
2016/day24/problem Normal file
View File

@ -0,0 +1,69 @@
Advent of Code
--- Day 24: Air Duct Spelunking ---
You've finally met your match; the doors that provide access to the roof are locked tight, and all of the controls and
related electronics are inaccessible. You simply can't reach them.
The robot that cleans the air ducts, however, can.
It's not a very fast little robot, but you reconfigure it to be able to interface with some of the exposed wires that
have been routed through the HVAC system. If you can direct it to each of those locations, you should be able to bypass
the security controls.
You extract the duct layout for this area from some blueprints you acquired and create a map with the relevant locations
marked (your puzzle input). 0 is your current location, from which the cleaning robot embarks; the other numbers are (in
no particular order) the locations the robot needs to visit at least once each. Walls are marked as #, and open passages
are marked as .. Numbers behave like open passages.
For example, suppose you have a map like the following:
###########
#0.1.....2#
#.#######.#
#4.......3#
###########
To reach all of the points of interest as quickly as possible, you would have the robot take the following path:
 0 to 4 (2 steps)
 4 to 1 (4 steps; it can't move diagonally)
 1 to 2 (6 steps)
 2 to 3 (2 steps)
Since the robot isn't very fast, you need to find it the shortest route. This path is the fewest steps (in the above
example, a total of 14) required to start at 0 and then visit every other location at least once.
Given your actual map, and starting from location 0, what is the fewest number of steps required to visit every non-0
number marked on the map at least once?
Your puzzle answer was ____.
--- Part Two ---
Of course, if you leave the cleaning robot somewhere weird, someone is bound to notice.
What is the fewest number of steps required to start at 0, visit every non-0 number marked on the map at least once, and
then return to 0?
Your puzzle answer was _____.
References
Visible links
. http://adventofcode.com/
. http://adventofcode.com/2016/about
. http://adventofcode.com/2016/support
. http://adventofcode.com/2016/events
. http://adventofcode.com/2016/settings
. http://adventofcode.com/2016/auth/logout
. http://adventofcode.com/2016
. http://adventofcode.com/2016
. http://adventofcode.com/2016/leaderboard
. http://adventofcode.com/2016/stats
. http://adventofcode.com/2016/sponsors
. http://adventofcode.com/2016/sponsors
. https://en.wikipedia.org/wiki/HVAC
. http://adventofcode.com/2016
. http://adventofcode.com/2016/day/24/input

View File

@ -1,5 +0,0 @@
###########
#0.1.....2#
#.#######.#
#4.......3#
###########

View File

@ -1,43 +0,0 @@
###################################################################################################################################################################################
#.........#...#.............#...#.#.#.....#...........#.........#.#...#.......#.#.#...#...#.................#...........#.#...#.#.......#.......#.......#...#...#.....#.....#.....#
#.#.#.#.#.#.#########.#.#.###.#.#.#.###.###.#.###.#.#.#.###.#.###.#.#.#.#.#####.#.#.#.#.#.#.###.#.#.#.#.#.#.#.###.#.#.###.#.#.#.#####.#.#.#.###.#.#.#.#.#.###.#.###.###.###.###.#.#
#...#...#...#.......#...#.#.#.....#...#.....#.........#.......#.#...#...#.#.............#...#.......#.#.#...#.#.....#.......#...#.....#...#...........#...#...#.#...............#2#
#.###.#.#.#####.###.###.#.#.#.#.###.#.#.#####.#######.#.###.###.#.#.#.#.#.#####.###.###.#.#.#####.#.###.#.###.#.#.#.#.#.#######.#######.#.#.###.###.###.#.#.#.#.#.#.###.#.###.#.###
#.......#.........#.#.#...#...#...#.....#.#.............#.....#...#.......#.#.....#...#...#.......#.............................#.#...#...#...#.....#...#.......#.......#.......#.#
#.###.#.#.#########.#.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.#.#####.#.#.#######.#.#.#.#.#.#.#####.#.###.#.#####.#.###.###.#.#.###.###.#.#.#.#####.#.###.#.#.#.#######.###.#.#.#.###.###.#
#...#.#...#...#...#...#.#...#.....#...#...........#.....#.........#.#...#...#...#.#...#.......#...#.#.....#.#.....#...#.#.......#.#.#.......#.......#...........#.#.#...#.#.......#
#.#.#.###.###.#.#.#######.#.#.#.#.#.#.###.###########.#.#.#####.###.#.#.#####.#.#.#.#####.###.#.###.#####.###.#####.#########.#.###.#.###.#.#.#.#.###.###.#.#####.#.#.#.#.#.###.#.#
#.......#.......#...........#...#.#...#.............#.#.#...#...#.....#...#...#.#...#...#.......#.#.#.#...#.....#.#.#.........#...#...#.....#.#...........#.#.......#.#.#...#...#.#
#.#.#.###.#####.#.#####.#.###.#.#.#.###.#.#.#.###.###.#.#####.###.#####.#.#.#####.#.#.#.#######.#.###.#.###.#####.#.#####.#.#.#####.#.#.#.#.###.#.#######.#.#.#.###########.#.#.#.#
#.#.#.....#.#1..........#.#...#...#.....#.........#...............#.#...#.....#...#.......#...........#.#...#.#.....#.............#.............#.....#...#.....#...#.....#.#.....#
#.#.###.#.#.#####.#.#.#.###.#####.#.#.#.###.###.#.#.#.#####.#.#.###.#.#.#####.#.#.#.#.#.#.###.#.###.#.#.#.#.#.#.###.#.#.#####.###.###.###.#.#.#.###.#.#.#.#.#.###.#.#.#####.#####.#
#...#...#.#...#.#.#.#.#.......#.....................#.#...............#.......#.#...#.#.#.....#.#.#...#...#.#.......#.....#.#...#.........#.#.#...#.........#.............#.....#.#
#.###.###.###.#.#.###.#.#####.#.#####.###.###########.###.#.#.#####.#.#.#.###.###.#.#.#.#.###.#.#.#.#.###.#.###.#.#########.#.#.#.#.###.#.#.#.#.#.#.#.#.#.###.###.#####.#.#####.###
#.....#.#.......#.#.#.....#...#.......#...#.#...#.............#.#.#.....#.........#...#.#.........#.#.#.#...#.#...#...#.......#.....#.#.....#.#.#.......#.#...#.#.....#.......#...#
#.#.###.#.#######.#.#.#.###.#.###.###.#####.#.#.#.#.###.###.###.#.#.#####.#.#####.#.#.#.#.#.#########.#.#.#.#.#.#.#.#.#.###.#.###.#.#.###.###.#.#######.#.#.#.#.#.###.#.#.#.#.###.#
#.#.#.......#...#...#.#.#.#.....#.....#...#...#.....#...#...#...#...........#...........#.........#.#.....#.....#.......#...#...#...#.#...#.#.#.........#...#.....#.#...#.#.....#.#
#.#.#.#####.#.#.#.###.###.#.#.#####.#.###.#.#.#####.#.#####.#.#.#.#####.###.#.#.#.#.###.#.#.#.#.#.#.#.###.#.#####.###.###.#.#.#.#.#.#.#.#.#.###.###.#.#######.#####.###.#.###.###.#
#.#.......#.#...#.#...#.#.#.......#...#.....#.........#.....#.#.....#...#...#.#.#...#.....#...#...#...#.....#.......#.#.#...#.#.........#...#.#...#.#.#.#.#.....#.....#.........#.#
#.###.#.#.#.###.#.#.###.#.#######.#.#.###.#.###.#.#.#.#.#.#.###.#####.#.###.#######.#####.###.#.#.#.#.#.###.#.#####.#.#.#.#.#.#.#.#.#.#.#.#.#.#######.#.#.#.#.#.#.#.#.#.#.###.#.#.#
#0#...#.#.#.....#...#...#.#...#...#...#...#.......#.#.#.....#...#...#...#...#...#...#.........#.......#...#.......#...#...#...#.......#...#...............#.....#...#...#...#.#...#
###.#.#.###.###########.#.#####.###.###.###.###.###.#.###.#.#.#.#.#.#.#.#.#.#.###.#.###.#.#.#.#####.#.#.#.#.#.#.#.#.#.#.#.###.#####.#.#####.#.#.#.#####.#.#.#####.#####.#.#.#.#.###
#...#.#...#.......#...#...........#...#.....#...#...#.#.........#.....#.#...#.......#.....#.#.#.........#.........#...#.#.#...#.#.....#.....................#...............#.....#
###.#.#.#.#.#.###.#.#.#.#.#######.#.#.###.###.#.#.#####.###.###.###.###.#.#.#.#.#######.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.#####.###.#######.###.###.#.###.###.###.###.#.#.#
#...#.............#.#...........#.#.#.....#.....#.......#.#.....#...#.........#.....#.......#.....#...#.....#...#.......#...#...#.#.........#...#...........#.......#...#.......#.#
###.#.#########.#.#.#####.#.#.#.#.#.#.###.#.#######.#.#.#.#.#.#####.#.#.#.#####.#.#.#.#####.#######.#.#.#.#.#.#####.#.#.#.#####.#.#.###.#######.#######.#.#.###.#.#.#.#.#.#.###.#.#
#.#...............#.#.......#.....#.................#...#.#.#.......#...#.......#.#.#...#.#.......#...#.....#.....#...#.#.......#.#.#.....#.....#.....#.#...#.#...#...#.#...#.#.#.#
#.#.#.###.#.#.#.#.#.#.#.#.#.#########.#.#.#.#.#.#.#.#.#.#.#.###.#.#.#.###.#.#.#.#.#.###.#.#.###.#.#.###.###.#.#.#####.#####.#####.#.#.#.#.#.#.###.#.#.#.###.#.#.#.#.###.#.#.#.#####
#...#.#.....#...#.............#.#.#.#.........#.#...#.#.....#.......#...#.#.#.....#.......#...#...#...........#.....#.#.......#...#...#.#.........#...#...#.....#.......#...#.....#
###.#.#.#.#.#.#.###.#.###.#.###.#.#.#.#.###.#.###.#.#.#####.###.###.#.#.#.###.#.#.###.###.###.#.#.#.#.###.#####.###.#####.#.#.###.###.#####.#####.#.#.#######.###.###.###.#.#.#.#.#
#...#.#.....#.........#.......#.#...#.......#...#...#...........#.....#.......#...........#.......#...#...#.....#.....#...#.......#.#.#...#.#.....#.........#.....#...#.#...#.#...#
###.###.###.###.#.#####.###.#.#.#.#.#.###.###.###.###.#.#####.#.#.#.#.###.#.#.###.#.###.###.#.#.###.###.#.#.#.#.#.#.#.#.#.#.#.#.###.#.###.#.###.#.#.#####.#.#####.#.#.#.#####.#.#.#
#.#.#...#.#.#...................#.......#.#.#.#.#.....#.#.#.....#...#.......#.#...#.......#.....#...#.....#...#...#.#...#...#.........#...#...#...........#.........#.#...#...#...#
#.#.#.###.#.###.#####.#.#.###.#.#####.#.#.#.#.#.#.###.#.#.#.#######.###.#.#.#.#.#.###.#.#.#.###.#.#####.#.#.#.#####.#.###.#.#.###.###.#.###.#.#.###########.#.#######.#.#.#.###.###
#...#.#.#...#.#.................#.#.....#.#.........#.....#.#.#...#.#.........#...#.#...#.#.........#...#...#.....#.#.#.#.....#...#.#.....#.#.............#.#...#.#.#.....#...#...#
#.#.###.#.#.#.#.#.#.#.#.###.###.#.#.#######.#######.###.#.#.#.###.#.#.#####.###.###.#####.#.#####.#.#.#.#.#.#.###.#.#.#.#.###.#.#.#.#.#.#.#.#####.#.#.#.#.###.#.#.#.#.#####.###.#.#
#...#.....#.....#.....#.#...#...#...............#...#...#...#.....#...#.#.....#.#.#...#...#.#...#...#.....#...#...#...................#.........#...#.........#.#...#.......#...#.#
###.#.###.#.#.#.###.#.#.#.#.#.###.###.#.###.###.#.###.#.#######.#.#.#.#.#.#####.#.#.#.#.#.###.#.#.#.#####.#.#.###.#.#######.#######.###.#######.#.#.#.#.###.###.###.#.#.#.#.#.#.#.#
#.....#...#.#.#...........#.#...#.........#.....#.#.#...#.....#.............#...#...#...#.#.#...#.......#...#.....#.#.......#.#.....#...#...#...#...#...#...#.#.....#.#.......#.#.#
#.#.#.#.#.#.#.#.#.#.###.#####.#.#############.#.###.#.#.#.#.###.###.#######.#.#.###.#.###.#.###.#######.###.###.#.#.###.#.#.#.#.#.###.###.###.#.#.###.#.#.#.#.#.#.###.#.###.#.#.###
#...#.....#.#...#.#.....#.....#...#...#.......#.#.......#.#.......#.#.........#...#.#.....#.....#.#.......#.#.......#.......#.....#.....#.....#...#.#.#.#...#.#...#.#...#...#.#.#.#
###################################################################################################################################################################################

30
2016/day25/input Normal file
View File

@ -0,0 +1,30 @@
cpy a d
cpy 7 c
cpy 365 b
inc d
dec b
jnz b -2
dec c
jnz c -5
cpy d a
jnz 0 0
cpy a b
cpy 0 a
cpy 2 c
jnz b 2
jnz 1 6
dec b
dec c
jnz c -4
inc a
jnz 1 -7
cpy 2 b
jnz c 2
jnz 1 4
dec b
dec c
jnz 1 -4
jnz 0 0
out b
jnz a -19
jnz 1 -21

505
2016/day25/main.go Normal file
View File

@ -0,0 +1,505 @@
package main
import (
"fmt"
"os"
"strconv"
"strings"
"time"
"github.com/fatih/color"
termbox "github.com/nsf/termbox-go"
"../../"
)
var regs = map[string]int{
"a": 0,
"b": 0,
"c": 0,
"d": 0,
}
var instructions, lastInst []string
var curr, cursor int
var done, debug, pause, step, skip bool
var breakpoints []int
var eventChan chan termbox.Event
var outBuff string
// Available arguments:
// -d Debug Mode
// -p Pause on start
// -25 Run day 25 simulation
func main() {
var inpFn string
if inpFn = aoc.GetArgNumber(1); inpFn == "" {
done = true
}
if inpFn != "" {
instructions = aoc.FileToStringSlice(inpFn)
}
if aoc.ArgIsSet("-d") {
debug = true
}
if aoc.ArgIsSet("-p") {
pause = true
}
if aoc.ArgIsSet("-25") {
// If running the day 25 simulation, ignore debug and pause flags
fmt.Println("Running Day 25 simulation, disabling debug & pause")
debug = false
pause = false
}
if debug {
err := termbox.Init()
//tWidth, tHeight := termbox.Size()
if err != nil {
fmt.Println("Error initializing termbox")
os.Exit(1)
}
defer termbox.Close()
eventChan = make(chan termbox.Event)
go readUserInput(eventChan)
go sendNoneEvent(eventChan)
}
if aoc.ArgIsSet("-25") {
var day25Solved bool
regAStart := regs["a"]
regBStart := regs["b"]
regCStart := regs["c"]
regDStart := regs["d"]
for !day25Solved {
regs["a"] = regAStart
regs["b"] = regBStart
regs["c"] = regCStart
regs["d"] = regDStart
ProcInstructions()
day25Solved = true
fmt.Println(regAStart, ":", outBuff)
if outBuff != "0101010101" {
day25Solved = false
regAStart++
}
}
} else {
ProcInstructions()
PrintState()
}
//fmt.Println("Press any key to exit")
//termbox.PollEvent()
}
func ProcInstructions() {
locRegs := make(map[string]int)
for k, v := range regs {
locRegs[k] = v
}
curr = 0
outBuff = ""
var ins []string
for curr < len(instructions) || done {
isBreak := isBreakpoint(curr)
if debug {
lastInst = ins
}
ins = strings.Fields(instructions[curr])
if len(ins) == 0 {
break
}
if isMultOp(curr) {
curr = doMultOp(curr)
skip = true
}
if debug {
if isBreak {
pause = true
step = false
}
for pause && !step && !done {
// print state and wait for user to step
PrintState()
PrintInstructionState()
PrintDebugHelp()
ev := <-eventChan
if ev.Type == termbox.EventKey {
switch {
case ev.Key == termbox.KeySpace:
pause = !pause
case ev.Ch == 'q':
done = true
case ev.Ch == 'b':
toggleBreakpoint(cursor)
case ev.Ch == 's':
step = true
case ev.Ch == 'u':
updateRegister()
case ev.Key == termbox.KeyArrowUp:
if cursor > 0 {
cursor--
}
case ev.Key == termbox.KeyArrowDown:
if cursor < len(instructions)-1 {
cursor++
}
}
}
}
step = false
}
if done {
// User hit 'q'
break
}
if skip {
skip = false
continue
}
curr++
switch ins[0] {
case "out":
v, ok := regs[ins[1]]
if !ok {
outBuff += ins[1]
} else {
outBuff += aoc.Itoa(v)
}
if aoc.ArgIsSet("-25") && len(outBuff) == 10 {
// This should be long enough for our day 25 answer
return
}
// If we're not debugging, just print it and reset the buffer
if !debug && !aoc.ArgIsSet("-25") {
fmt.Print(outBuff)
outBuff = ""
}
case "jnz":
// If we have a jnz c -2 it could be moving all of c into another register
v, ok := regs[ins[1]]
if !ok {
v = aoc.Atoi(ins[1])
}
var p int
if p, ok = regs[ins[2]]; !ok {
p = aoc.Atoi(ins[2])
}
if v != 0 {
// Subtract 1 from the jump because we incremented already
curr += p - 1
}
case "mlt":
// a three arg instruction: mlt a b c
// take a * b, store in c
var ok bool
src1 := ins[1]
src2 := ins[2]
dst := ins[3]
if _, ok := regs[dst]; !ok {
// invalid destination
continue
}
var src1I, src2I int
if src1I, ok = regs[src1]; !ok {
src1I = aoc.Atoi(src1)
}
if src2I, ok = regs[src2]; !ok {
src2I = aoc.Atoi(src2)
}
regs[dst] = src1I * src2I
case "cpy":
src := ins[1]
dst := ins[2]
// check if dst is a register
if _, ok := regs[dst]; !ok {
// Nope, move along
continue
}
// check if the src is a register
if v, ok := regs[src]; ok {
regs[dst] = v
} else {
// It's not, must be an int
regs[dst] = aoc.Atoi(src)
}
case "inc":
if _, ok := regs[ins[1]]; !ok {
continue
}
regs[ins[1]] = regs[ins[1]] + 1
case "dec":
if _, ok := regs[ins[1]]; !ok {
continue
}
regs[ins[1]] = regs[ins[1]] - 1
case "tgl": // tgl alters an instruction
src := ins[1]
var srcI int
if v, ok := regs[src]; ok {
srcI = v
} else {
srcI = aoc.Atoi(src)
}
srcI = curr + srcI
if srcI < 0 || srcI > len(instructions)-1 {
// Invalid instruction
continue
}
altPts := strings.Fields(instructions[srcI-1])
if len(altPts) == 2 { // one argument
if altPts[0] == "inc" {
altPts[0] = "dec"
} else {
altPts[0] = "inc"
}
} else { // two arguments
if altPts[0] == "jnz" {
altPts[0] = "cpy"
} else {
altPts[0] = "jnz"
}
}
instructions[srcI-1] = strings.Join(altPts, " ")
}
if debug {
PrintState()
}
}
}
// Fancy State Printing
func PrintState() {
fmt.Println(aoc.ClearScreen)
PrintRegs()
}
func PrintRegs() {
datLine := fmt.Sprint("\u2502 a:", regs["a"], " b:", regs["b"], " c:", regs["c"], " d:", regs["d"], " \u2502")
fmt.Println("\u250C" + strings.Repeat("\u2500", len(datLine)-6) + "\u2510")
fmt.Println(datLine)
fmt.Println("\u2514" + strings.Repeat("\u2500", len(datLine)-6) + "\u2518")
}
func PrintInstructionState() {
optim := color.New(color.FgGreen)
for pi := range instructions {
if pi == curr {
fmt.Print(">")
} else {
fmt.Print(" ")
}
if isBreakpoint(pi) {
fmt.Print("B")
} else {
fmt.Print(" ")
}
if cursor == pi {
cursor := color.New(color.FgBlack).Add(color.BgWhite)
if isMultOp(pi) {
cursor.Println("+ " + instructions[pi] + " == optimized")
} else if mo, _ := isInMultOp(pi); mo {
cursor.Println("| " + instructions[pi])
} else {
cursor.Println(instructions[pi])
}
} else {
if isMultOp(pi) {
optim.Println("+ " + instructions[pi] + " == optimized")
} else if mo, _ := isInMultOp(pi); mo {
optim.Println("| " + instructions[pi])
} else {
fmt.Println(instructions[pi])
}
}
}
}
func PrintDebugHelp() {
fmt.Println("(s): step | (space): pause/unpause | (b): toggle breakpoint")
fmt.Println("(u): update register to value | (q): quit")
}
func readUserInput(e chan termbox.Event) {
for {
e <- termbox.PollEvent()
}
}
func sendNoneEvent(e chan termbox.Event) {
for {
time.Sleep(time.Second / 32)
if !pause {
e <- termbox.Event{Type: termbox.EventNone}
}
}
}
// isInMultOp returns true and the pc for the
// start of the multiplication operation if pos is
// located within it
func isInMultOp(pos int) (bool, int) {
// We need to check instructions[pos +- 5] to see if any
// are a multiplication op and return the pos for the (first) one
// that is
if isMultOp(pos) {
return true, pos
}
for i := 1; i < 6; i++ {
if isMultOp(pos - i) {
return true, (pos - i)
}
}
return false, pos
}
// isMultOpStart return whether pos is the start of
// what can be optimized as multiplication
/*
> cpy b c
inc a
dec c
jnz c -2
dec d
jnz d -5
== add (b * d) to a
set c & d to 0
curr += 5
*/
func isMultOp(pos int) bool {
if pos < 0 || pos > len(instructions)-1 {
return false
}
ins := strings.Fields(instructions[pos])
if len(ins) < 3 {
return false
}
if ins[0] == "cpy" {
if len(instructions) >= pos+6 {
ins1 := strings.Fields(instructions[pos+1])
ins2 := strings.Fields(instructions[pos+2])
ins3 := strings.Fields(instructions[pos+3])
ins4 := strings.Fields(instructions[pos+4])
ins5 := strings.Fields(instructions[pos+5])
if ins1[0] == "inc" && ins2[0] == "dec" && ins3[0] == "jnz" && ins4[0] == "dec" && ins5[0] == "jnz" {
allGood := true
if allGood {
// Do the multiplication
// ins[1] * ins4[1]
// add that value to ins1[1]
// set ins[2] to 0
// set ins4[1] to 0
// Then add 5 to the pc
src1 := ins[1]
src2 := ins4[1]
dst := ins1[1]
if _, ok := regs[dst]; !ok {
// Invalid destination register
allGood = false
}
if _, ok := regs[src1]; !ok {
// Invalid source register
allGood = false
}
if _, ok := regs[src2]; !ok {
allGood = false
}
return allGood
}
}
}
}
return false
}
// doMultOp performs a multiplcation operation and returns the new pc
func doMultOp(pos int) int {
isOp, pos := isInMultOp(pos)
if !isOp {
// No optimization to do
return pos
}
ins := strings.Fields(instructions[pos])
ins1 := strings.Fields(instructions[pos+1])
ins4 := strings.Fields(instructions[pos+4])
// Do the multiplication
// ins[1] * ins4[1]
// add that value to ins1[1]
// set ins[2] to 0
// set ins4[1] to 0
// Then add 5 to the pc
src1 := ins[1]
src2 := ins4[1]
dst := ins1[1]
regs[dst] += (regs[src1] * regs[src2])
regs[ins[2]] = 0
regs[ins4[1]] = 0
pos += 6
return pos
}
func toggleBreakpoint(pos int) {
_, pos = isInMultOp(pos)
for i := 0; i < len(breakpoints); i++ {
if breakpoints[i] == pos {
breakpoints = append(breakpoints[:i], breakpoints[i+1:]...)
return
}
}
breakpoints = append(breakpoints, pos)
}
func isBreakpoint(pos int) bool {
_, pos = isInMultOp(pos)
for i := 0; i < len(breakpoints); i++ {
if breakpoints[i] == pos {
return true
}
}
return false
}
func updateRegister() {
var updReg string
fmt.Println("Update which register? (a,b,c,d)")
ev := <-eventChan
if ev.Type == termbox.EventKey {
switch {
case ev.Ch == 'a':
updReg = "a"
case ev.Ch == 'b':
updReg = "b"
case ev.Ch == 'c':
updReg = "c"
case ev.Ch == 'd':
updReg = "d"
default:
fmt.Println("Invalid register (" + string(ev.Ch) + ")")
fmt.Println("Press any key to continue")
ev = <-eventChan
return
}
}
fmt.Println("Enter new value (must be integer, end with enter)")
var newVal string
for ev.Key != termbox.KeyEnter {
ev = <-eventChan
if ev.Ch >= '0' && ev.Ch <= '9' {
newVal += string(ev.Ch)
fmt.Print(string(ev.Ch))
}
}
fmt.Println("Setting Register (" + updReg + ") to " + newVal)
var v int
var e error
if v, e = strconv.Atoi(newVal); e != nil {
fmt.Println()
fmt.Println("Error parsing integer")
fmt.Println(e)
fmt.Println("Press any key to continue")
ev = <-eventChan
return
}
regs[updReg] = v
}

74
2016/day25/problem Normal file
View File

@ -0,0 +1,74 @@
Advent of Code
--- Day 25: Clock Signal ---
You open the door and find yourself on the roof. The city sprawls away from you for miles and miles.
There's not much time now - it's already Christmas, but you're nowhere near the North Pole, much too far to deliver
these stars to the sleigh in time.
However, maybe the huge antenna up here can offer a solution. After all, the sleigh doesn't need the stars, exactly; it
needs the timing data they provide, and you happen to have a massive signal generator right here.
You connect the stars you have to your prototype computer, connect that to the antenna, and begin the transmission.
Nothing happens.
You call the service number printed on the side of the antenna and quickly explain the situation. "I'm not sure what
kind of equipment you have connected over there," he says, "but you need a clock signal." You try to explain that this
is a signal for a clock.
"No, no, a clock signal - timing information so the antenna computer knows how to read the data you're sending it. An
endless, alternating pattern of 0, 1, 0, 1, 0, 1, 0, 1, 0, 1...." He trails off.
You ask if the antenna can handle a clock signal at the frequency you would need to use for the data from the stars.
"There's no way it can! The only antenna we've installed capable of that is on top of a top-secret Easter Bunny
installation, and you're definitely not-" You hang up the phone.
You've extracted the antenna's clock signal generation assembunny code (your puzzle input); it looks mostly compatible
with code you worked on just recently.
This antenna code, being a signal generator, uses one extra instruction:
 out x transmits x (either an integer or the value of a register) as the next value for the clock signal.
The code takes a value (via register a) that describes the signal to generate, but you're not sure how it's used. You'll
have to find the input to produce the right signal through experimentation.
What is the lowest positive integer that can be used to initialize register a and cause the code to output a clock
signal of 0, 1, 0, 1... repeating forever?
Your puzzle answer was ____.
--- Part Two ---
The antenna is ready. Now, all you need is the fifty stars required to generate the signal for the sleigh, but you don't
have enough.
You look toward the sky in desperation... suddenly noticing that a lone star has been installed at the top of the
antenna! Only 49 more to go.
If you like, you can [ [Retransmit the Signal] ] .
Both parts of this puzzle are complete! They provide two gold stars: **
References
Visible links
. http://adventofcode.com/
. http://adventofcode.com/2016/about
. http://adventofcode.com/2016/support
. http://adventofcode.com/2016/events
. http://adventofcode.com/2016/settings
. http://adventofcode.com/2016/auth/logout
. http://adventofcode.com/2016
. http://adventofcode.com/2016
. http://adventofcode.com/2016/leaderboard
. http://adventofcode.com/2016/stats
. http://adventofcode.com/2016/sponsors
. http://adventofcode.com/2016/sponsors
. https://en.wikipedia.org/wiki/Clock_signal
. http://adventofcode.com/2016/day/12
. http://adventofcode.com/2016/day/23
. http://adventofcode.com/2016
. http://adventofcode.com/2016/day/25/input

View File

@ -95,7 +95,21 @@ func PrintProgress(curr, total int) {
}
func StringPermutations(str string) []string {
return stringPermHelper(str, 0)
perms := stringPermHelper(str, 0)
var wrk []string
// Now de-dupe
for i := range perms {
var found bool
for j := range wrk {
if wrk[j] == perms[i] {
found = true
}
}
if !found {
wrk = append(wrk, perms[i])
}
}
return wrk
}
func stringPermHelper(str string, i int) []string {