diff --git a/day13/day13 b/day13/day13 index 435e75d..187b430 100755 Binary files a/day13/day13 and b/day13/day13 differ diff --git a/day13/main.go b/day13/main.go index 63cf167..dce9f47 100644 --- a/day13/main.go +++ b/day13/main.go @@ -15,33 +15,35 @@ import ( // Puzzle 1 Test Input: 10 7 4 // Puzzle 2: 101 < x < 128 ?? 103 func main() { + mode := "solve" if len(os.Args) < 4 { fmt.Println("Usage: day13 ") os.Exit(1) } seed := atoi(os.Args[1]) destX, destY := atoi(os.Args[2]), atoi(os.Args[3]) + if len(os.Args) >= 5 { + mode = os.Args[4] + } f := CreateFloor(1, 1, destX, destY, seed) - //f.Print() - //SolveMaze(f) - fmt.Println("WalkFloor(50):", f.player.WalkFloor(50)) - f.Print() -} - -// SolveMaze finds a solution to the maze -func SolveMaze(f *Floor) { - for !f.player.FoundExit() { - time.Sleep(time.Millisecond * 100) + switch mode { + case "solve": + if f.Solve(f.start.x, f.start.y, 0, true) { + f.dispCoord = f.end + } ClearScreen() - f.player.MoveToExit() f.Print() + fmt.Println("Shortest Path:", len(f.solvePath.coords)) + case "walk": + dist := 50 + f.Walk(f.start.x, f.start.y, 0, dist, true) + fmt.Println("Within", dist, "steps: ", len(f.testedPath.coords)) } - fmt.Println("Found the exit in", f.player.NumSteps(), "steps") } type Coord struct { - x, y int + x, y, dist int } func (c *Coord) Is(x, y int) bool { @@ -53,169 +55,7 @@ func (c *Coord) Equals(t *Coord) bool { } func NewCoord(x, y int) *Coord { - return &Coord{x, y} -} - -type Player struct { - pos *Coord - currentFloor *Floor - visited []Coord - path Path -} - -// WalkFloor determines how many places you can get to by taking -// steps -func (p *Player) WalkFloor(dist int) int { - if dist <= 0 { - return 0 - } - fmt.Println("WalkFloor(", dist, ")") - var ret int - tryX, tryY := p.pos.x+1, p.pos.y - if !p.HasBeenAt(tryX, tryY) && !p.currentFloor.IsWall(tryX, tryY) { - ret++ - p.WalkEast() - fmt.Println(" Walking East") - ret += p.WalkFloor(dist - 1) - p.WalkWest() - } - tryX, tryY = p.pos.x-1, p.pos.y - if !p.HasBeenAt(tryX, tryY) && !p.currentFloor.IsWall(tryX, tryY) { - ret++ - p.WalkWest() - fmt.Println(" Walking West") - ret += p.WalkFloor(dist - 1) - p.WalkEast() - } - tryX, tryY = p.pos.x, p.pos.y+1 - if !p.HasBeenAt(tryX, tryY) && !p.currentFloor.IsWall(tryX, tryY) { - ret++ - p.WalkSouth() - fmt.Println(" Walking South") - ret += p.WalkFloor(dist - 1) - p.WalkNorth() - } - tryX, tryY = p.pos.x, p.pos.y-1 - if !p.HasBeenAt(tryX, tryY) && !p.currentFloor.IsWall(tryX, tryY) { - ret++ - p.WalkNorth() - fmt.Println(" Walking North") - ret += p.WalkFloor(dist - 1) - p.WalkSouth() - } - return ret -} - -func (p *Player) NumSteps() int { - return len(p.path.coords) -} - -func (p *Player) MoveToExit() bool { - // First try to move closer to the goal - // Horizontally, then Vertically - if p.pos.x < p.currentFloor.end.x { - if !p.HasBeenAt(p.pos.x+1, p.pos.y) && p.WalkEast() { - return true - } - } - if p.pos.x > p.currentFloor.end.x { - if !p.HasBeenAt(p.pos.x-1, p.pos.y) && p.WalkWest() { - return true - } - } - if p.pos.y < p.currentFloor.end.y { - if !p.HasBeenAt(p.pos.x, p.pos.y+1) && p.WalkSouth() { - return true - } - } - if p.pos.y > p.currentFloor.end.y { - if !p.HasBeenAt(p.pos.x, p.pos.y-1) && p.WalkNorth() { - return true - } - } - // Couldn't move towards it, can we move anywhere? - if !p.HasBeenAt(p.pos.x+1, p.pos.y) && p.WalkEast() { - return true - } - if !p.HasBeenAt(p.pos.x-1, p.pos.y) && p.WalkWest() { - return true - } - if !p.HasBeenAt(p.pos.x, p.pos.y+1) && p.WalkSouth() { - return true - } - if !p.HasBeenAt(p.pos.x, p.pos.y-1) && p.WalkNorth() { - return true - } - - // Couldn't do any of those. Pop the path - if !p.Backtrack() { - // Couldn't backtrack! Game over man! - fmt.Println("Error! Couldn't find valid path!") - os.Exit(1) - } - return false -} - -func (p *Player) Backtrack() bool { - if len(p.path.coords) == 0 { - return false - } - p.path.Pop() - p.pos = &p.path.coords[len(p.path.coords)-1] - return true -} - -func (p *Player) WalkEast() bool { - if !p.currentFloor.IsWall(p.pos.x+1, p.pos.y) { - p.pos.x++ - p.path.Append(*p.pos) - p.visited = append(p.visited, *p.pos) - return true - } - return false -} - -func (p *Player) WalkWest() bool { - if !p.currentFloor.IsWall(p.pos.x-1, p.pos.y) && p.pos.x > 0 { - p.pos.x-- - p.path.Append(*p.pos) - p.visited = append(p.visited, *p.pos) - return true - } - return false -} - -func (p *Player) WalkNorth() bool { - if !p.currentFloor.IsWall(p.pos.x, p.pos.y-1) && p.pos.y > 0 { - p.pos.y-- - p.path.Append(*p.pos) - p.visited = append(p.visited, *p.pos) - return true - } - return false -} - -func (p *Player) WalkSouth() bool { - if !p.currentFloor.IsWall(p.pos.x, p.pos.y+1) { - p.pos.y++ - p.path.Append(*p.pos) - p.visited = append(p.visited, *p.pos) - return true - } - return false -} - -func (p *Player) HasBeenAt(x, y int) bool { - for i := range p.visited { - if p.visited[i].Is(x, y) { - return true - } - } - return false -} - -func (p *Player) FoundExit() bool { - return p.pos.Equals(p.currentFloor.end) + return &Coord{x, y, -1} } type Path struct { @@ -228,7 +68,12 @@ func (p *Path) Append(c Coord) { func (p *Path) Pop() Coord { var ret Coord + prePop := len(p.coords) ret, p.coords = p.coords[len(p.coords)-1], p.coords[:len(p.coords)-1] + if len(p.coords) != prePop-1 { + fmt.Println("Error, popping didn't result in one less size") + os.Exit(1) + } return ret } @@ -240,12 +85,31 @@ func (p *Path) ContainsCoord(c *Coord) bool { } return false } +func (p *Path) ContainsCoordXY(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 Floor struct { - player *Player - start *Coord - end *Coord - seed int + start *Coord + end *Coord + seed int + testedPath Path + solvePath Path + dispCoord *Coord } func CreateFloor(stX, stY, endX, endY, seed int) *Floor { @@ -254,11 +118,169 @@ func CreateFloor(stX, stY, endX, endY, seed int) *Floor { end: NewCoord(endX, endY), seed: seed, } - f.player = &Player{pos: f.start} - f.player.currentFloor = &f return &f } +func (f *Floor) Walk(x, y, dist, maxDist int, print bool) { + wrkCoord := Coord{x, y, dist} + if f.IsWall(x, y) || f.testedPath.ContainsCoordXY(x, y) { + return + } + if dist == maxDist { + f.testedPath.Append(wrkCoord) + return + } + if print { + f.dispCoord = &wrkCoord + ClearScreen() + f.Print() + fmt.Println("Tested Spots:", len(f.testedPath.coords)) + time.Sleep(time.Millisecond * 70) + } + if !f.IsWall(x-1, y) { + if t := f.testedPath.GetCoordAt(x-1, y); t != nil { + if t.dist+1 < wrkCoord.dist { + return + } + } + } + if !f.IsWall(x+1, y) { + if t := f.testedPath.GetCoordAt(x+1, y); t != nil { + if t.dist+1 < wrkCoord.dist { + return + } + } + } + if !f.IsWall(x, y-1) { + if t := f.testedPath.GetCoordAt(x, y-1); t != nil { + if t.dist+1 < wrkCoord.dist { + return + } + } + } + if !f.IsWall(x, y+1) { + if t := f.testedPath.GetCoordAt(x, y+1); t != nil { + if t.dist+1 < wrkCoord.dist { + return + } + } + } + f.testedPath.Append(wrkCoord) + // Try intelligently first + // (Attempt to move towards the exit) + if x > 0 { + f.Walk(x-1, y, wrkCoord.dist+1, maxDist, print) + } + if y > 0 { + f.Walk(x, y-1, wrkCoord.dist+1, maxDist, print) + } + f.Walk(x+1, y, wrkCoord.dist+1, maxDist, print) + f.Walk(x, y+1, wrkCoord.dist+1, maxDist, print) +} + +func (f *Floor) Solve(x, y, dist int, print bool) bool { + wrkCoord := Coord{x, y, dist} + if f.end.Is(x, y) { + return true + } + if f.IsWall(x, y) || f.testedPath.ContainsCoordXY(x, y) { + return false + } + // 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 false + } + } + } + if !f.IsWall(x+1, y) { + if t := f.testedPath.GetCoordAt(x+1, y); t != nil { + if t.dist+1 < wrkCoord.dist { + return false + } + } + } + if !f.IsWall(x, y-1) { + if t := f.testedPath.GetCoordAt(x, y-1); t != nil { + if t.dist+1 < wrkCoord.dist { + return false + } + } + } + if !f.IsWall(x, y+1) { + if t := f.testedPath.GetCoordAt(x, y+1); t != nil { + if t.dist+1 < wrkCoord.dist { + return false + } + } + } + if print { + f.dispCoord = &wrkCoord + ClearScreen() + f.Print() + fmt.Println("Tested Spots:", len(f.testedPath.coords)) + time.Sleep(time.Millisecond * 70) + } + f.testedPath.Append(wrkCoord) + // Try intelligently first + // (Attempt to move towards the exit) + if x > f.end.x && x > 0 { + if f.Solve(x-1, y, wrkCoord.dist+1, print) { + f.solvePath.Append(wrkCoord) + return true + } + } + if y > f.end.y && y > 0 { + if f.Solve(x, y-1, wrkCoord.dist+1, print) { + f.solvePath.Append(wrkCoord) + return true + } + } + if x < f.end.x { + if f.Solve(x+1, y, wrkCoord.dist+1, print) { + f.solvePath.Append(wrkCoord) + return true + } + } + if y < f.end.y { + if f.Solve(x, y+1, wrkCoord.dist+1, print) { + f.solvePath.Append(wrkCoord) + return true + } + } + // Intelligence failed us... Just find a move + if x > 0 { + if f.Solve(x-1, y, wrkCoord.dist+1, print) { + f.solvePath.Append(wrkCoord) + return true + } + } + if y > 0 { + if f.Solve(x, y-1, wrkCoord.dist+1, print) { + f.solvePath.Append(wrkCoord) + return true + } + } + // This is where it gets shaky... + // Since we have an infinite maze, this could run forever + // So we have a hard cutoff at: + var MaxInt = int(^uint(0) >> 1) + if len(f.testedPath.coords) >= MaxInt { + fmt.Println("Couldn't find a path.") + os.Exit(1) + } + if f.Solve(x+1, y, wrkCoord.dist+1, print) { + f.solvePath.Append(wrkCoord) + return true + } + if f.Solve(x, y+1, wrkCoord.dist+1, print) { + f.solvePath.Append(wrkCoord) + return true + } + return false +} + func (f *Floor) IsWall(x, y int) bool { sum := (x*x + 3*x + 2*x*y + y + y*y + f.seed) s := fmt.Sprintf("%b", sum) @@ -275,19 +297,14 @@ func (f *Floor) Print() { g.Add(color.Bold) r := color.New(color.BgRed) topY, topX := f.end.y+10, f.end.x+10 - if f.player.pos.y > f.end.y { - topY = f.player.pos.y + 5 - } - if f.player.pos.x > f.end.x { - topX = f.player.pos.x + 5 - } + for y := 0; y < topY; y++ { for x := 0; x < topX; x++ { - if f.player.pos.Is(x, y) { + if f.dispCoord != nil && f.dispCoord.Is(x, y) { g.Print("O") - } else if f.player.path.ContainsCoord(&Coord{x, y}) { + } else if f.solvePath.ContainsCoordXY(x, y) { g.Print(".") - } else if f.player.HasBeenAt(x, y) { + } else if f.testedPath.ContainsCoordXY(x, y) { r.Print(" ") } else { if f.end.Is(x, y) { diff --git a/day13/problem b/day13/problem new file mode 100644 index 0000000..2767b1b --- /dev/null +++ b/day13/problem @@ -0,0 +1,77 @@ +Advent of Code + +--- Day 13: A Maze of Twisty Little Cubicles --- + + You arrive at the first floor of this new building to discover a much less welcoming environment than the shiny atrium of the + last one. Instead, you are in a maze of twisty little cubicles, all alike. + + Every location in this area is addressed by a pair of non-negative integers (x,y). Each such coordinate is either a wall or an + open space. You can't move diagonally. The cube maze starts at 0,0 and seems to extend infinitely toward positive x and y; + negative values are invalid, as they represent a location outside the building. You are in a small waiting area at 1,1. + + While it seems chaotic, a nearby morale-boosting poster explains, the layout is actually quite logical. You can determine + whether a given x,y coordinate will be a wall or an open space using a simple system: + + • Find x*x + 3*x + 2*x*y + y + y*y. + • Add the office designer's favorite number (your puzzle input). + • Find the binary representation of that sum; count the number of bits that are 1. + + • If the number of bits that are 1 is even, it's an open space. + • If the number of bits that are 1 is odd, it's a wall. + + For example, if the office designer's favorite number were 10, drawing walls as # and open spaces as ., the corner of the + building containing 0,0 would look like this: + + 0123456789 + 0 .#.####.## + 1 ..#..#...# + 2 #....##... + 3 ###.#.###. + 4 .##..#..#. + 5 ..##....#. + 6 #...##.### + + Now, suppose you wanted to reach 7,4. The shortest route you could take is marked as O: + + 0123456789 + 0 .#.####.## + 1 .O#..#...# + 2 #OOO.##... + 3 ###O#.###. + 4 .##OO#OO#. + 5 ..##OOO.#. + 6 #...##.### + + Thus, reaching 7,4 would take a minimum of 11 steps (starting from your current location, 1,1). + + What is the fewest number of steps required for you to reach 31,39? + + Your puzzle answer was _____. + + The first half of this puzzle is complete! It provides one gold star: * + +--- Part Two --- + + How many locations (distinct x,y coordinates, including your starting location) can you reach in at most 50 steps? + + Your puzzle input is still 1364. + + Answer: _____________________ + +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/Binary_number + . https://en.wikipedia.org/wiki/Bit