Day 13 Complete

This commit is contained in:
Brian Buller 2016-12-13 11:09:54 -06:00
parent 529cd54788
commit f9fe534a3b
3 changed files with 285 additions and 191 deletions

Binary file not shown.

View File

@ -15,33 +15,35 @@ import (
// Puzzle 1 Test Input: 10 7 4 // Puzzle 1 Test Input: 10 7 4
// Puzzle 2: 101 < x < 128 ?? 103 // Puzzle 2: 101 < x < 128 ?? 103
func main() { func main() {
mode := "solve"
if len(os.Args) < 4 { if len(os.Args) < 4 {
fmt.Println("Usage: day13 <seed> <dest-x> <dest-y>") fmt.Println("Usage: day13 <seed> <dest-x> <dest-y>")
os.Exit(1) os.Exit(1)
} }
seed := atoi(os.Args[1]) seed := atoi(os.Args[1])
destX, destY := atoi(os.Args[2]), atoi(os.Args[3]) 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 := CreateFloor(1, 1, destX, destY, seed)
//f.Print() switch mode {
//SolveMaze(f) case "solve":
fmt.Println("WalkFloor(50):", f.player.WalkFloor(50)) if f.Solve(f.start.x, f.start.y, 0, true) {
f.Print() f.dispCoord = f.end
} }
ClearScreen()
// SolveMaze finds a solution to the maze f.Print()
func SolveMaze(f *Floor) { fmt.Println("Shortest Path:", len(f.solvePath.coords))
for !f.player.FoundExit() { case "walk":
time.Sleep(time.Millisecond * 100) dist := 50
ClearScreen() f.Walk(f.start.x, f.start.y, 0, dist, true)
f.player.MoveToExit() fmt.Println("Within", dist, "steps: ", len(f.testedPath.coords))
f.Print()
} }
fmt.Println("Found the exit in", f.player.NumSteps(), "steps")
} }
type Coord struct { type Coord struct {
x, y int x, y, dist int
} }
func (c *Coord) Is(x, y int) bool { 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 { func NewCoord(x, y int) *Coord {
return &Coord{x, y} return &Coord{x, y, -1}
}
type Player struct {
pos *Coord
currentFloor *Floor
visited []Coord
path Path
}
// WalkFloor determines how many places you can get to by taking
// <dist> 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)
} }
type Path struct { type Path struct {
@ -228,7 +68,12 @@ func (p *Path) Append(c Coord) {
func (p *Path) Pop() Coord { func (p *Path) Pop() Coord {
var ret Coord var ret Coord
prePop := len(p.coords)
ret, p.coords = p.coords[len(p.coords)-1], p.coords[:len(p.coords)-1] 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 return ret
} }
@ -240,12 +85,31 @@ func (p *Path) ContainsCoord(c *Coord) bool {
} }
return false 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 { type Floor struct {
player *Player
start *Coord start *Coord
end *Coord end *Coord
seed int seed int
testedPath Path
solvePath Path
dispCoord *Coord
} }
func CreateFloor(stX, stY, endX, endY, seed int) *Floor { 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), end: NewCoord(endX, endY),
seed: seed, seed: seed,
} }
f.player = &Player{pos: f.start}
f.player.currentFloor = &f
return &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 { func (f *Floor) IsWall(x, y int) bool {
sum := (x*x + 3*x + 2*x*y + y + y*y + f.seed) sum := (x*x + 3*x + 2*x*y + y + y*y + f.seed)
s := fmt.Sprintf("%b", sum) s := fmt.Sprintf("%b", sum)
@ -275,19 +297,14 @@ func (f *Floor) Print() {
g.Add(color.Bold) g.Add(color.Bold)
r := color.New(color.BgRed) r := color.New(color.BgRed)
topY, topX := f.end.y+10, f.end.x+10 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 y := 0; y < topY; y++ {
for x := 0; x < topX; x++ { 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") g.Print("O")
} else if f.player.path.ContainsCoord(&Coord{x, y}) { } else if f.solvePath.ContainsCoordXY(x, y) {
g.Print(".") g.Print(".")
} else if f.player.HasBeenAt(x, y) { } else if f.testedPath.ContainsCoordXY(x, y) {
r.Print(" ") r.Print(" ")
} else { } else {
if f.end.Is(x, y) { if f.end.Is(x, y) {

77
day13/problem Normal file
View File

@ -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