Advent of Code 2016 Complete
This commit is contained in:
parent
a01a65364c
commit
a0c467fd41
@ -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
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 {
|
||||
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user