Advent of Code 2016 Complete
This commit is contained in:
parent
a01a65364c
commit
a0c467fd41
@ -1,412 +1,48 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
termbox "github.com/nsf/termbox-go"
|
|
||||||
|
|
||||||
"../../"
|
"../../"
|
||||||
)
|
)
|
||||||
|
|
||||||
var shortestSolutionDist int
|
var tWidth, tHeight int
|
||||||
var pois []string
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
playMode := aoc.ArgIsSet("-play")
|
|
||||||
fileNm := aoc.GetArgNumber(1)
|
fileNm := aoc.GetArgNumber(1)
|
||||||
txtMaze := aoc.FileToStringSlice(fileNm)
|
if len(os.Args) < 2 {
|
||||||
m := CreateMaze(txtMaze)
|
fmt.Println("Usage: ./day24 <mazefile>")
|
||||||
if playMode {
|
|
||||||
player := CreatePlayer(m)
|
|
||||||
err := termbox.Init()
|
|
||||||
//tWidth, tHeight := termbox.Size()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error initializing termbox")
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
defer termbox.Close()
|
var f *Floor
|
||||||
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 poiString string
|
var poiString string
|
||||||
fmt.Println("pois", pois)
|
f = CreateFloorFromFile(fileNm)
|
||||||
for i := range pois {
|
for i := 0; i < len(f.pois); i++ {
|
||||||
poiString += 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)
|
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
|
shortest := -1
|
||||||
var shortestPerm string
|
var shortestPerm string
|
||||||
for _, perm := range poiPerms {
|
for _, perm := range poiPerms {
|
||||||
if perm[0] != '0' {
|
if perm[0] != '0' {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if aoc.ArgIsSet("-2") {
|
||||||
|
// For part 2 we return to 0
|
||||||
|
perm = perm + "0"
|
||||||
|
}
|
||||||
var permTtl int
|
var permTtl int
|
||||||
for i := range perm {
|
for i := range perm {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
@ -414,7 +50,7 @@ func (m *Maze) StartSolve() {
|
|||||||
if beg[0] > end[0] {
|
if beg[0] > end[0] {
|
||||||
beg, end = end, beg
|
beg, end = end, beg
|
||||||
}
|
}
|
||||||
permTtl += shortestPoiDist[beg+";"+end]
|
permTtl += f.shortestPaths[beg+";"+end]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if permTtl < shortest || shortest == -1 {
|
if permTtl < shortest || shortest == -1 {
|
||||||
@ -425,95 +61,440 @@ func (m *Maze) StartSolve() {
|
|||||||
fmt.Println(shortestPerm, ": ", shortest)
|
fmt.Println(shortestPerm, ": ", shortest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetShortestPath just finds the shortest path between two points
|
type Floor struct {
|
||||||
func (m *Maze) GetShortestPath(begX, begY, endX, endY, dist int, path Path) (int, bool) {
|
cells []string
|
||||||
if begX == endX && begY == endY {
|
start *Coord
|
||||||
return dist, true
|
end *Coord
|
||||||
}
|
pois []Coord
|
||||||
if path.Contains(begX, begY) {
|
|
||||||
return 0, false
|
shortestPaths map[string]int
|
||||||
}
|
|
||||||
// Figure out if there is a shorter path to this coordinate
|
testedPath Path
|
||||||
if !m.walls.Contains(begX-1, begY) {
|
solvePath Path
|
||||||
if t := path.GetCoordAt(begX-1, begY); t != nil {
|
|
||||||
if t.dist+1 < dist {
|
cellCount int
|
||||||
return 0, false
|
|
||||||
}
|
debug bool
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Maze) FindClosestPoi() *Coord {
|
func CreateFloorFromFile(fileNm string) *Floor {
|
||||||
var shortestPoi Coord
|
f := new(Floor)
|
||||||
shortestDist := -1
|
f.debug = aoc.ArgIsSet("-d")
|
||||||
for _, v := range m.pois.coords {
|
f.cells = aoc.FileToStringSlice(fileNm)
|
||||||
if t := FindSLDistance(m.start, &v); t < shortestDist || shortestDist == -1 {
|
for y := range f.cells {
|
||||||
shortestDist = t
|
for x := range f.cells[y] {
|
||||||
shortestPoi = v
|
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 {
|
func (f *Floor) GetPoi(lbl byte) *Coord {
|
||||||
a := math.Abs(float64(p1.x) - float64(p2.x))
|
for i := range f.pois {
|
||||||
b := math.Abs(float64(p1.y) - float64(p2.y))
|
if f.pois[i].label == lbl {
|
||||||
return int(math.Pow(a, 2) + math.Pow(b, 2))
|
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
69
2016/day24/problem
Normal 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
|
@ -1,5 +0,0 @@
|
|||||||
###########
|
|
||||||
#0.1.....2#
|
|
||||||
#.#######.#
|
|
||||||
#4.......3#
|
|
||||||
###########
|
|
@ -1,43 +0,0 @@
|
|||||||
###################################################################################################################################################################################
|
|
||||||
#.........#...#.............#...#.#.#.....#...........#.........#.#...#.......#.#.#...#...#.................#...........#.#...#.#.......#.......#.......#...#...#.....#.....#.....#
|
|
||||||
#.#.#.#.#.#.#########.#.#.###.#.#.#.###.###.#.###.#.#.#.###.#.###.#.#.#.#.#####.#.#.#.#.#.#.###.#.#.#.#.#.#.#.###.#.#.###.#.#.#.#####.#.#.#.###.#.#.#.#.#.###.#.###.###.###.###.#.#
|
|
||||||
#...#...#...#.......#...#.#.#.....#...#.....#.........#.......#.#...#...#.#.............#...#.......#.#.#...#.#.....#.......#...#.....#...#...........#...#...#.#...............#2#
|
|
||||||
#.###.#.#.#####.###.###.#.#.#.#.###.#.#.#####.#######.#.###.###.#.#.#.#.#.#####.###.###.#.#.#####.#.###.#.###.#.#.#.#.#.#######.#######.#.#.###.###.###.#.#.#.#.#.#.###.#.###.#.###
|
|
||||||
#.......#.........#.#.#...#...#...#.....#.#.............#.....#...#.......#.#.....#...#...#.......#.............................#.#...#...#...#.....#...#.......#.......#.......#.#
|
|
||||||
#.###.#.#.#########.#.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.#.#####.#.#.#######.#.#.#.#.#.#.#####.#.###.#.#####.#.###.###.#.#.###.###.#.#.#.#####.#.###.#.#.#.#######.###.#.#.#.###.###.#
|
|
||||||
#...#.#...#...#...#...#.#...#.....#...#...........#.....#.........#.#...#...#...#.#...#.......#...#.#.....#.#.....#...#.#.......#.#.#.......#.......#...........#.#.#...#.#.......#
|
|
||||||
#.#.#.###.###.#.#.#######.#.#.#.#.#.#.###.###########.#.#.#####.###.#.#.#####.#.#.#.#####.###.#.###.#####.###.#####.#########.#.###.#.###.#.#.#.#.###.###.#.#####.#.#.#.#.#.###.#.#
|
|
||||||
#.......#.......#...........#...#.#...#.............#.#.#...#...#.....#...#...#.#...#...#.......#.#.#.#...#.....#.#.#.........#...#...#.....#.#...........#.#.......#.#.#...#...#.#
|
|
||||||
#.#.#.###.#####.#.#####.#.###.#.#.#.###.#.#.#.###.###.#.#####.###.#####.#.#.#####.#.#.#.#######.#.###.#.###.#####.#.#####.#.#.#####.#.#.#.#.###.#.#######.#.#.#.###########.#.#.#.#
|
|
||||||
#.#.#.....#.#1..........#.#...#...#.....#.........#...............#.#...#.....#...#.......#...........#.#...#.#.....#.............#.............#.....#...#.....#...#.....#.#.....#
|
|
||||||
#.#.###.#.#.#####.#.#.#.###.#####.#.#.#.###.###.#.#.#.#####.#.#.###.#.#.#####.#.#.#.#.#.#.###.#.###.#.#.#.#.#.#.###.#.#.#####.###.###.###.#.#.#.###.#.#.#.#.#.###.#.#.#####.#####.#
|
|
||||||
#...#...#.#...#.#.#.#.#.......#.....................#.#...............#.......#.#...#.#.#.....#.#.#...#...#.#.......#.....#.#...#.........#.#.#...#.........#.............#.....#.#
|
|
||||||
#.###.###.###.#.#.###.#.#####.#.#####.###.###########.###.#.#.#####.#.#.#.###.###.#.#.#.#.###.#.#.#.#.###.#.###.#.#########.#.#.#.#.###.#.#.#.#.#.#.#.#.#.###.###.#####.#.#####.###
|
|
||||||
#.....#.#.......#.#.#.....#...#.......#...#.#...#.............#.#.#.....#.........#...#.#.........#.#.#.#...#.#...#...#.......#.....#.#.....#.#.#.......#.#...#.#.....#.......#...#
|
|
||||||
#.#.###.#.#######.#.#.#.###.#.###.###.#####.#.#.#.#.###.###.###.#.#.#####.#.#####.#.#.#.#.#.#########.#.#.#.#.#.#.#.#.#.###.#.###.#.#.###.###.#.#######.#.#.#.#.#.###.#.#.#.#.###.#
|
|
||||||
#.#.#.......#...#...#.#.#.#.....#.....#...#...#.....#...#...#...#...........#...........#.........#.#.....#.....#.......#...#...#...#.#...#.#.#.........#...#.....#.#...#.#.....#.#
|
|
||||||
#.#.#.#####.#.#.#.###.###.#.#.#####.#.###.#.#.#####.#.#####.#.#.#.#####.###.#.#.#.#.###.#.#.#.#.#.#.#.###.#.#####.###.###.#.#.#.#.#.#.#.#.#.###.###.#.#######.#####.###.#.###.###.#
|
|
||||||
#.#.......#.#...#.#...#.#.#.......#...#.....#.........#.....#.#.....#...#...#.#.#...#.....#...#...#...#.....#.......#.#.#...#.#.........#...#.#...#.#.#.#.#.....#.....#.........#.#
|
|
||||||
#.###.#.#.#.###.#.#.###.#.#######.#.#.###.#.###.#.#.#.#.#.#.###.#####.#.###.#######.#####.###.#.#.#.#.#.###.#.#####.#.#.#.#.#.#.#.#.#.#.#.#.#.#######.#.#.#.#.#.#.#.#.#.#.###.#.#.#
|
|
||||||
#0#...#.#.#.....#...#...#.#...#...#...#...#.......#.#.#.....#...#...#...#...#...#...#.........#.......#...#.......#...#...#...#.......#...#...............#.....#...#...#...#.#...#
|
|
||||||
###.#.#.###.###########.#.#####.###.###.###.###.###.#.###.#.#.#.#.#.#.#.#.#.#.###.#.###.#.#.#.#####.#.#.#.#.#.#.#.#.#.#.#.###.#####.#.#####.#.#.#.#####.#.#.#####.#####.#.#.#.#.###
|
|
||||||
#...#.#...#.......#...#...........#...#.....#...#...#.#.........#.....#.#...#.......#.....#.#.#.........#.........#...#.#.#...#.#.....#.....................#...............#.....#
|
|
||||||
###.#.#.#.#.#.###.#.#.#.#.#######.#.#.###.###.#.#.#####.###.###.###.###.#.#.#.#.#######.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.#####.###.#######.###.###.#.###.###.###.###.#.#.#
|
|
||||||
#...#.............#.#...........#.#.#.....#.....#.......#.#.....#...#.........#.....#.......#.....#...#.....#...#.......#...#...#.#.........#...#...........#.......#...#.......#.#
|
|
||||||
###.#.#########.#.#.#####.#.#.#.#.#.#.###.#.#######.#.#.#.#.#.#####.#.#.#.#####.#.#.#.#####.#######.#.#.#.#.#.#####.#.#.#.#####.#.#.###.#######.#######.#.#.###.#.#.#.#.#.#.###.#.#
|
|
||||||
#.#...............#.#.......#.....#.................#...#.#.#.......#...#.......#.#.#...#.#.......#...#.....#.....#...#.#.......#.#.#.....#.....#.....#.#...#.#...#...#.#...#.#.#.#
|
|
||||||
#.#.#.###.#.#.#.#.#.#.#.#.#.#########.#.#.#.#.#.#.#.#.#.#.#.###.#.#.#.###.#.#.#.#.#.###.#.#.###.#.#.###.###.#.#.#####.#####.#####.#.#.#.#.#.#.###.#.#.#.###.#.#.#.#.###.#.#.#.#####
|
|
||||||
#...#.#.....#...#.............#.#.#.#.........#.#...#.#.....#.......#...#.#.#.....#.......#...#...#...........#.....#.#.......#...#...#.#.........#...#...#.....#.......#...#.....#
|
|
||||||
###.#.#.#.#.#.#.###.#.###.#.###.#.#.#.#.###.#.###.#.#.#####.###.###.#.#.#.###.#.#.###.###.###.#.#.#.#.###.#####.###.#####.#.#.###.###.#####.#####.#.#.#######.###.###.###.#.#.#.#.#
|
|
||||||
#...#.#.....#.........#.......#.#...#.......#...#...#...........#.....#.......#...........#.......#...#...#.....#.....#...#.......#.#.#...#.#.....#.........#.....#...#.#...#.#...#
|
|
||||||
###.###.###.###.#.#####.###.#.#.#.#.#.###.###.###.###.#.#####.#.#.#.#.###.#.#.###.#.###.###.#.#.###.###.#.#.#.#.#.#.#.#.#.#.#.#.###.#.###.#.###.#.#.#####.#.#####.#.#.#.#####.#.#.#
|
|
||||||
#.#.#...#.#.#...................#.......#.#.#.#.#.....#.#.#.....#...#.......#.#...#.......#.....#...#.....#...#...#.#...#...#.........#...#...#...........#.........#.#...#...#...#
|
|
||||||
#.#.#.###.#.###.#####.#.#.###.#.#####.#.#.#.#.#.#.###.#.#.#.#######.###.#.#.#.#.#.###.#.#.#.###.#.#####.#.#.#.#####.#.###.#.#.###.###.#.###.#.#.###########.#.#######.#.#.#.###.###
|
|
||||||
#...#.#.#...#.#.................#.#.....#.#.........#.....#.#.#...#.#.........#...#.#...#.#.........#...#...#.....#.#.#.#.....#...#.#.....#.#.............#.#...#.#.#.....#...#...#
|
|
||||||
#.#.###.#.#.#.#.#.#.#.#.###.###.#.#.#######.#######.###.#.#.#.###.#.#.#####.###.###.#####.#.#####.#.#.#.#.#.#.###.#.#.#.#.###.#.#.#.#.#.#.#.#####.#.#.#.#.###.#.#.#.#.#####.###.#.#
|
|
||||||
#...#.....#.....#.....#.#...#...#...............#...#...#...#.....#...#.#.....#.#.#...#...#.#...#...#.....#...#...#...................#.........#...#.........#.#...#.......#...#.#
|
|
||||||
###.#.###.#.#.#.###.#.#.#.#.#.###.###.#.###.###.#.###.#.#######.#.#.#.#.#.#####.#.#.#.#.#.###.#.#.#.#####.#.#.###.#.#######.#######.###.#######.#.#.#.#.###.###.###.#.#.#.#.#.#.#.#
|
|
||||||
#.....#...#.#.#...........#.#...#.........#.....#.#.#...#.....#.............#...#...#...#.#.#...#.......#...#.....#.#.......#.#.....#...#...#...#...#...#...#.#.....#.#.......#.#.#
|
|
||||||
#.#.#.#.#.#.#.#.#.#.###.#####.#.#############.#.###.#.#.#.#.###.###.#######.#.#.###.#.###.#.###.#######.###.###.#.#.###.#.#.#.#.#.###.###.###.#.#.###.#.#.#.#.#.#.###.#.###.#.#.###
|
|
||||||
#...#.....#.#...#.#.....#.....#...#...#.......#.#.......#.#.......#.#.........#...#.#.....#.....#.#.......#.#.......#.......#.....#.....#.....#...#.#.#.#...#.#...#.#...#...#.#.#.#
|
|
||||||
###################################################################################################################################################################################
|
|
30
2016/day25/input
Normal file
30
2016/day25/input
Normal 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
505
2016/day25/main.go
Normal 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
74
2016/day25/problem
Normal 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
|
16
helpers.go
16
helpers.go
@ -95,7 +95,21 @@ func PrintProgress(curr, total int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StringPermutations(str string) []string {
|
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 {
|
func stringPermHelper(str string, i int) []string {
|
||||||
|
Loading…
Reference in New Issue
Block a user