From d4a45d2b674b0fe2f6ddb7207f6230cd941096b6 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Tue, 3 Nov 2020 15:09:13 -0600 Subject: [PATCH] Day 18 & Day 20 done --- 2019/day18/main.go | 251 ++++++++++++++++++-------------------- 2019/day18/testinput | 3 + 2019/day18/testinput2 | 5 + 2019/day18/testinput3 | 5 + 2019/day18/testinput4 | 9 ++ 2019/day18/testinput5 | 6 + 2019/day18/vault.go | 62 ---------- 2019/day20/main.go | 274 +++++++++++++++++++++++++++++++----------- 2019/day20/maze.go | 236 ++++-------------------------------- 2019/day20/portal.go | 10 ++ 2019/day20/square.go | 28 +++++ 2019/day20/testinput2 | 37 ++++++ 12 files changed, 447 insertions(+), 479 deletions(-) create mode 100644 2019/day18/testinput create mode 100644 2019/day18/testinput2 create mode 100644 2019/day18/testinput3 create mode 100644 2019/day18/testinput4 create mode 100644 2019/day18/testinput5 delete mode 100644 2019/day18/vault.go create mode 100644 2019/day20/portal.go create mode 100644 2019/day20/square.go create mode 100644 2019/day20/testinput2 diff --git a/2019/day18/main.go b/2019/day18/main.go index e63bf99..57ff4e3 100644 --- a/2019/day18/main.go +++ b/2019/day18/main.go @@ -1,170 +1,153 @@ package main import ( + "bufio" "fmt" + "io" + "log" + "os" helpers "git.bullercodeworks.com/brian/adventofcode/helpers" ) +type state struct { + Pos helpers.Coordinate3d + Steps int +} + func main() { file := "input" if helpers.GetArgNumber(1) != "" { file = helpers.GetArgNumber(1) } - v := NewVault(file) - part1(v) + f, err := os.Open(file) + if err != nil { + log.Fatal(err) + } + defer f.Close() + + part := helpers.GetArgNumber(2) + if part != "2" { + part1(f) + } else { + part2(f) + } } -var keylist int -var allNodes map[string]*Node - -func part1(v *Vault) { - keylist = v.keylist - allNodes = make(map[string]*Node) - for k, v := range v.vault { - if v { - x, y, z := unc(k) - n := Node{ - X: x, - Y: y, - Z: z, - steps: helpers.MAX_INT, - } - allNodes[c([]int{x, y})] = &n - } - } - for _, v := range allNodes { - n, ok := allNodes[c([]int{v.X, v.Y + 1})] - if ok { - v.N = n - } - e, ok := allNodes[c([]int{v.X + 1, v.Y})] - if ok { - v.E = e - } - s, ok := allNodes[c([]int{v.X, v.Y - 1})] - if ok { - v.S = s - } - w, ok := allNodes[c([]int{v.X - 1, v.Y})] - if ok { - v.W = w - } - } - start := allNodes[c([]int{v.start.X, v.start.Y})] - start.steps = 0 - ProcessNode(start, 0, 0) +func part1(r io.Reader) { + vault, doors, keys, start, allkeys := readVault(r) + fmt.Println(search(vault, doors, keys, start, allkeys, 0)) } -type Node struct { - X, Y, Z int - N, E, S, W *Node - steps int - visited bool +func part2(r io.Reader) { + vault, doors, keys, start, allKeys := readVault(r) + directions := []helpers.Coordinate{{X: 0, Y: -1}, {X: 1, Y: 0}, {X: 0, Y: 1}, {X: -1, Y: 0}} + vault[start] = false + for _, d := range directions { + vault[helpers.Coordinate{X: start.X + d.X, Y: start.Y + d.Y}] = false + } + total := 0 + + haveKeys := allKeys + for x := 0; x < start.X; x++ { + for y := 0; y < start.Y; y++ { + haveKeys ^= keys[helpers.Coordinate{X: x, Y: y}] + } + } + + total += search(vault, doors, keys, helpers.Coordinate{X: start.X - 1, Y: start.Y - 1}, allKeys, haveKeys) + + haveKeys = allKeys + for x := start.X + 1; x <= start.X*2; x++ { + for y := 0; y < start.Y; y++ { + haveKeys ^= keys[helpers.Coordinate{X: x, Y: y}] + } + } + + total += search(vault, doors, keys, helpers.Coordinate{X: start.X + 1, Y: start.Y - 1}, allKeys, haveKeys) + + haveKeys = allKeys + for x := start.X + 1; x <= start.X*2; x++ { + for y := start.Y + 1; y <= start.Y*2; y++ { + haveKeys ^= keys[helpers.Coordinate{X: x, Y: y}] + } + } + + total += search(vault, doors, keys, helpers.Coordinate{X: start.X + 1, Y: start.Y + 1}, allKeys, haveKeys) + + haveKeys = allKeys + for x := 0; x < start.X; x++ { + for y := start.Y + 1; y <= start.Y*2; y++ { + haveKeys ^= keys[helpers.Coordinate{X: x, Y: y}] + } + } + + total += search(vault, doors, keys, helpers.Coordinate{X: start.X - 1, Y: start.Y + 1}, allKeys, haveKeys) + + fmt.Println(total) } -func ProcessNode(n *Node, steps, keys int) { - if n.Z&keylist == keylist { - fmt.Println("Steps to all Keys:", steps) - return - } - for _, neighbor := range []*Node{n.N, n.E, n.S, n.W} { - if neighbor == nil { - continue - } - wrk, ok := allNodes[c([]int{neighbor.X, neighbor.Y})] - if ok { - if n.steps+1 < wrk.steps { - wrk.steps = n.steps + 1 - if !wrk.visited { - ProcessNode(wrk, n.steps+1, keys) +func readVault(r io.Reader) (map[helpers.Coordinate]bool, map[helpers.Coordinate]int, map[helpers.Coordinate]int, helpers.Coordinate, int) { + scan := bufio.NewScanner(r) + var start helpers.Coordinate + var x, y, allkeys int + v := make(map[helpers.Coordinate]bool) + doors, keys := make(map[helpers.Coordinate]int), make(map[helpers.Coordinate]int) + for scan.Scan() { + x = 0 + for _, c := range scan.Text() { + if c != '#' { + v[helpers.Coordinate{X: x, Y: y}] = true + if c == '@' { + start = helpers.Coordinate{X: x, Y: y} + } else if c != '.' { + if c < 'a' { + k := 1 << (c - 'A') + doors[helpers.Coordinate{X: x, Y: y}] = k + allkeys |= k + } else { + k := 1 << (c - 'a') + keys[helpers.Coordinate{X: x, Y: y}] = k + allkeys |= k + } } } + x++ } + y++ } + if err := scan.Err(); err != nil { + log.Fatal(err) + } + return v, doors, keys, start, allkeys } -type state struct { - X, Y int - keys int - steps int -} - -func FindPath(vault map[string]bool, doors, keys map[string]int, start helpers.Coordinate, keylist, currkeys int) int { - queue := []state{state{X: start.X, Y: start.Y, keys: currkeys}} - visited := make(map[string]bool) +func search(vault map[helpers.Coordinate]bool, doors, keys map[helpers.Coordinate]int, start helpers.Coordinate, allKeys, haveKeys int) int { + directions := []helpers.Coordinate{{X: 0, Y: -1}, {X: 1, Y: 0}, {X: 0, Y: 1}, {X: -1, Y: 0}} + queue, visited := []state{{Pos: helpers.Coordinate3d{X: start.X, Y: start.Y, Z: haveKeys}}}, make(map[helpers.Coordinate3d]bool) var st state for { st, queue = queue[0], queue[1:] - fmt.Println(st.keys&keylist, keylist) - if st.keys&keylist == keylist { - return st.steps + if st.Pos.Z&allKeys == allKeys { + return st.Steps } - visited[c([]int{st.X, st.Y, st.keys})] = true - for _, v := range []*helpers.Coordinate{ - start.GetNorthCoord(), start.GetEastCoord(), - start.GetSouthCoord(), start.GetWestCoord(), - } { - wrk := helpers.NewCoordinate3d(v.X, v.Y, st.keys) - if !vault[helpers.NewCoordinate(wrk.X, wrk.Y).String()] || visited[wrk.String()] { - continue // Already been here, or can't be here + visited[st.Pos] = true + for _, d := range directions { + next := helpers.Coordinate3d{X: st.Pos.X + d.X, Y: st.Pos.Y + d.Y, Z: st.Pos.Z} + if !vault[helpers.Coordinate{X: next.X, Y: next.Y}] || visited[next] { + continue } - door, ok := doors[helpers.NewCoordinate(wrk.X, wrk.Y).String()] - if ok && wrk.Z&door != door { - fmt.Println("Hit door") - continue // Space exists, but we can't open the door + door, ok := doors[helpers.Coordinate{X: next.X, Y: next.Y}] + if ok && next.Z&door != door { + continue } - key, ok := keys[helpers.NewCoordinate(wrk.X, wrk.Y).String()] + + key, ok := keys[helpers.Coordinate{X: next.X, Y: next.Y}] if ok { - fmt.Println("Getting Key") - wrk.Z |= key // Pick up the key + next.Z |= key } - queue = append(queue, state{X: wrk.X, Y: wrk.Y, keys: wrk.Z, steps: st.steps + 1}) + + queue = append(queue, state{Pos: next, Steps: st.Steps + 1}) } } } - -func isDoor(b byte) bool { - return 'A' <= b && b <= 'Z' -} -func isKey(b byte) bool { - return 'a' <= b && b <= 'z' -} - -func keyToDoor(b byte) byte { - return (b - 'a') + 'A' -} - -func doorToKey(b byte) byte { - return (b - 'A') + 'a' -} - -func keyInt(b byte) int { - return 1 << (b - 'a') -} - -func doorInt(b byte) int { - return 1 << (b - 'A') -} - -func c(vals []int) string { - if len(vals) == 2 { - return fmt.Sprintf("[%d, %d]", vals[0], vals[1]) - } else if len(vals) == 3 { - return fmt.Sprintf("[%d, %d, %d]", vals[0], vals[1], vals[2]) - } - return "" -} - -func unc(c string) (int, int, int) { - var x, y, z int - _, err := fmt.Sscanf(c, "[%d, %d, %d]", &x, &y, &z) - if err == nil { - return x, y, z - } - _, err = fmt.Sscanf(c, "[%d, %d]", &x, &y) - if err != nil { - panic(err) - } - return x, y, 0 -} diff --git a/2019/day18/testinput b/2019/day18/testinput new file mode 100644 index 0000000..33802e1 --- /dev/null +++ b/2019/day18/testinput @@ -0,0 +1,3 @@ +######### +#b.A.@.a# +######### diff --git a/2019/day18/testinput2 b/2019/day18/testinput2 new file mode 100644 index 0000000..af485bc --- /dev/null +++ b/2019/day18/testinput2 @@ -0,0 +1,5 @@ +######################## +#f.D.E.e.C.b.A.@.a.B.c.# +######################.# +#d.....................# +######################## diff --git a/2019/day18/testinput3 b/2019/day18/testinput3 new file mode 100644 index 0000000..b650235 --- /dev/null +++ b/2019/day18/testinput3 @@ -0,0 +1,5 @@ +######################## +#...............b.C.D.f# +#.###################### +#.....@.a.B.c.d.A.e.F.g# +######################## diff --git a/2019/day18/testinput4 b/2019/day18/testinput4 new file mode 100644 index 0000000..d5d888b --- /dev/null +++ b/2019/day18/testinput4 @@ -0,0 +1,9 @@ +################# +#i.G..c...e..H.p# +########.######## +#j.A..b...f..D.o# +########@######## +#k.E..a...g..B.n# +########.######## +#l.F..d...h..C.m# +################# diff --git a/2019/day18/testinput5 b/2019/day18/testinput5 new file mode 100644 index 0000000..ee6598e --- /dev/null +++ b/2019/day18/testinput5 @@ -0,0 +1,6 @@ +######################## +#@..............ac.GI.b# +###d#e#f################ +###A#B#C################ +###g#h#i################ +######################## diff --git a/2019/day18/vault.go b/2019/day18/vault.go deleted file mode 100644 index 1b44222..0000000 --- a/2019/day18/vault.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - - helpers "git.bullercodeworks.com/brian/adventofcode/helpers" -) - -type Vault struct { - maxX, maxY int - start helpers.Coordinate - vault map[string]bool - keys map[string]int - doors map[string]int - keylist int -} - -func NewVault(file string) *Vault { - inp := helpers.FileToBytes(file) - wrk := bytes.Split(inp, []byte{'\n'}) - - v := Vault{ - vault: make(map[string]bool), - keys: make(map[string]int), - doors: make(map[string]int), - } - v.maxY = len(wrk) - v.maxX = len(wrk[0]) - for y, yv := range wrk { - for x, xv := range yv { - if xv == '@' { - v.start = *helpers.NewCoordinate(x, y) - xv = '.' - } - v.vault[helpers.NewCoordinate(x, y).String()] = (xv != '#') - if isKey(xv) { - k := keyInt(xv) - v.keys[helpers.NewCoordinate(x, y).String()] = k - v.keylist |= k - } else if isDoor(xv) { - d := doorInt(xv) - v.doors[helpers.NewCoordinate(x, y).String()] = d - v.keylist |= d - } - } - } - return &v -} - -func (v *Vault) Print() { - for y := 0; y < v.maxY; y++ { - for x := 0; x < v.maxX; x++ { - if v.vault[helpers.NewCoordinate(x, y).String()] { - fmt.Print(".") - } else { - fmt.Print("#") - } - } - fmt.Println() - } -} diff --git a/2019/day20/main.go b/2019/day20/main.go index 42aa1b5..d683146 100644 --- a/2019/day20/main.go +++ b/2019/day20/main.go @@ -1,94 +1,224 @@ package main import ( - "bufio" "fmt" - "os" - "time" + "io/ioutil" + "strings" - helpers "git.bullercodeworks.com/brian/adventofcode/helpers" + h "git.bullercodeworks.com/brian/adventofcode/helpers" ) -var maze *Maze +type State struct { + Position h.Coordinate3d + Distance int +} + +var filename string +var directions []h.Coordinate3d func main() { - file := "input" - var manual bool - if helpers.GetArgNumber(1) != "" { - file = helpers.GetArgNumber(1) + filename = "input" + if h.GetArgNumber(1) != "" { + filename = h.GetArgNumber(1) } - if helpers.GetArgNumber(2) == "manual" { - manual = true + + directions = []h.Coordinate3d{{X: 0, Y: -1, Z: 0}, {X: 1, Y: 0, Z: 0}, {X: 0, Y: 1, Z: 0}, {X: -1, Y: 0, Z: 0}} + part := h.GetArgNumber(2) + if part != "2" { + part1(filename) + } else { + part2(filename) } - maze = NewMaze(file) - if manual { - runmanual(maze) - } - part1() - //part2(inp) } -func part1() { - maze.GetStart().steps = 0 - ProcessNode(maze.GetStart(), 0) - fmt.Println("Distance:", maze.GetEnd().steps) -} - -func ProcessNode(m *MazeCoord, steps int) { - fmt.Println("Processing Node", m) - if maze.IsEnd(m.X, m.Y) { - fmt.Println(" End") - return - } - for _, neighbor := range []*MazeCoord{m.N, m.E, m.S, m.W} { - if neighbor == nil { - continue - } - if m.steps+1 < neighbor.steps { - neighbor.steps = m.steps + 1 - if !neighbor.visited { - ProcessNode(neighbor, m.steps+1) +func part1(filename string) { + maze := loadMaze(filename) + queue, visited := []State{{Position: maze.Start}}, map[h.Coordinate3d]bool{maze.Start: true} + var st State + for { + st, queue = queue[0], queue[1:] + for _, d := range directions { + next := h.Coordinate3d{X: st.Position.X + d.X, Y: st.Position.Y + d.Y, Z: 0} + if next == maze.End { + fmt.Println(st.Distance + 1) + return + } + if maze.MazeMap[next] && !visited[next] { + visited[next] = true + p, ok := maze.Portals[next] + if ok { + next = p.To + } + queue = append(queue, State{next, st.Distance + 1}) } } } - m.visited = true } -func runmanual(m *Maze) { - var err error - var inp string - m.Print() +func part2(filename string) { + maze := loadMaze(filename) + queue, visited := []State{{Position: maze.Start}}, map[h.Coordinate3d]bool{{X: maze.Start.X, Y: maze.Start.Y, Z: 0}: true} + var st State for { - reader := bufio.NewReader(os.Stdin) - inp, err = reader.ReadString('\n') - if err != nil { - fmt.Println("Input Error:", err.Error()) - continue + st, queue = queue[0], queue[1:] + for _, d := range directions { + next := h.Coordinate3d{X: st.Position.X + d.X, Y: st.Position.Y + d.Y, Z: st.Position.Z} + if next == maze.End { + fmt.Println(st.Distance + 1) + return + } + if maze.MazeMap[h.Coordinate3d{X: next.X, Y: next.Y, Z: 0}] && !visited[next] { + visited[next] = true + p, ok := maze.Portals[h.Coordinate3d{X: next.X, Y: next.Y, Z: 0}] + if ok && (st.Position.Z > 0 || !p.Outer) { + next = h.Coordinate3d{X: p.To.X, Y: p.To.Y, Z: st.Position.Z} + if p.Outer { + next.Z-- + } else { + next.Z++ + } + visited[next] = true + } + queue = append(queue, State{next, st.Distance + 1}) + } } - var valid bool - switch inp[0] { - case 'N', 'n': - valid = m.MoveNorth() - case 'E', 'e': - valid = m.MoveEast() - case 'S', 's': - valid = m.MoveSouth() - case 'W', 'w': - valid = m.MoveWest() - case 'Q', 'q': - fmt.Println("Quitting") - break - } - fmt.Println(helpers.CLEAR_SCREEN) - m.Print() - if m.IsDone() { - fmt.Println("DONE!") - break - } - if !valid { - fmt.Println("Invalid Move") - } - time.Sleep(time.Second / 2) - } } + +func loadMaze(filename string) Maze { + ret := Maze{ + MazeMap: make(map[h.Coordinate3d]bool), + Outer: Square{}, + Inner: Square{}, + Portals: make(map[h.Coordinate3d]Portal), + } + input, _ := ioutil.ReadFile(filename) + lines := make([]string, 0) + for _, line := range strings.Split(string(input), "\n") { + lines = append(lines, line) + } + //outer, inner := Square{Tl: h.Coordinate3d{X: 2, Y: 2, Z: 0}, Br: h.Coordinate3d{X: len(lines[0]) - 3, Y: len(lines) - 3, Z: 0}}, Square{} + // Find the outer top-left + var done bool + for y := 0; y < len(lines); y++ { + for x := 0; x < len(lines[y]); x++ { + if lines[y][x] == '#' || lines[y][x] == '.' { + ret.Outer.Tl = h.Coordinate3d{X: x, Y: y, Z: 0} + done = true + break + } + } + if done { + break + } + } + // Now find the outer bottom-righ + done = false + for y := len(lines) - 1; y > ret.Outer.Tl.Y; y-- { + for x := len(lines[y]) - 1; x > ret.Outer.Tl.X; x-- { + if lines[y][x] == '#' || lines[y][x] == '.' { + ret.Outer.Br = h.Coordinate3d{X: x, Y: y, Z: 0} + done = true + break + } + } + if done { + break + } + } + + // Find the inner top-left + done = false + for y := ret.Outer.Tl.Y; y < ret.Outer.Br.Y; y++ { + for x := ret.Outer.Tl.X; x < ret.Outer.Br.X; x++ { + if lines[y][x] != '#' && lines[y][x] != '.' { + // Found it. + ret.Inner.Tl = h.Coordinate3d{X: x - 1, Y: y - 1, Z: 0} + done = true + break + } + } + if done { + break + } + } + // Ok, now find the inner bottom-right + done = false + for y := ret.Outer.Br.Y - 1; y > ret.Outer.Tl.Y; y-- { + for x := ret.Outer.Br.X - 1; x > ret.Outer.Tl.X; x-- { + if lines[y][x] != '#' && lines[y][x] != '.' { + // Found it + ret.Inner.Br = h.Coordinate3d{X: x + 1, Y: y + 1, Z: 0} + done = true + break + } + } + if done { + break + } + } + + for y := ret.Outer.Tl.Y; y <= ret.Outer.Br.Y; y++ { + for x := ret.Outer.Tl.X; x <= ret.Outer.Br.X; x++ { + if lines[y][x] == '.' { + ret.MazeMap[h.Coordinate3d{X: x, Y: y, Z: 0}] = true + + var label string + var pos h.Coordinate3d + var outerPortal bool + if y == ret.Outer.Tl.Y { + label = lines[y-2][x:x+1] + lines[y-1][x:x+1] + pos = h.Coordinate3d{X: x, Y: y - 1, Z: 0} + outerPortal = true + } else if y == ret.Outer.Br.Y { + label = lines[y+1][x:x+1] + lines[y+2][x:x+1] + pos = h.Coordinate3d{X: x, Y: y + 1, Z: 0} + outerPortal = true + } else if x == ret.Outer.Tl.X { + label = lines[y][x-2 : x] + pos = h.Coordinate3d{X: x - 1, Y: y, Z: 0} + outerPortal = true + } else if x == ret.Outer.Br.X { + label = lines[y][x+1 : x+3] + pos = h.Coordinate3d{X: x + 1, Y: y, Z: 0} + outerPortal = true + } else if y == ret.Inner.Br.Y && x > ret.Inner.Tl.X && x < ret.Inner.Br.X { + label = lines[y-2][x:x+1] + lines[y-1][x:x+1] + pos = h.Coordinate3d{X: x, Y: y - 1, Z: 0} + } else if y == ret.Inner.Tl.Y && x > ret.Inner.Tl.X && x < ret.Inner.Br.X { + label = lines[y+1][x:x+1] + lines[y+2][x:x+1] + pos = h.Coordinate3d{X: x, Y: y + 1, Z: 0} + } else if x == ret.Inner.Br.X && y > ret.Inner.Tl.Y && y < ret.Inner.Br.Y { + label = lines[y][x-2 : x] + pos = h.Coordinate3d{X: x - 1, Y: y, Z: 0} + } else if x == ret.Inner.Tl.X && y > ret.Inner.Tl.Y && y < ret.Inner.Br.Y { + label = lines[y][x+1 : x+3] + pos = h.Coordinate3d{X: x + 1, Y: y, Z: 0} + } + + if label == "AA" { + ret.Start = h.Coordinate3d{X: x, Y: y, Z: 0} + } else if label == "ZZ" { + ret.End = h.Coordinate3d{X: x, Y: y, Z: 0} + } else if label != "" { + ret.Portals[pos] = Portal{Label: label, From: h.Coordinate3d{X: x, Y: y, Z: 0}, Outer: outerPortal} + ret.MazeMap[pos] = true + } + } else { + // Make sure we don't overwrite anything + if _, ok := ret.MazeMap[h.Coordinate3d{X: x, Y: y, Z: 0}]; !ok { + ret.MazeMap[h.Coordinate3d{X: x, Y: y, Z: 0}] = false + } + } + } + } + for i, p1 := range ret.Portals { + for _, p2 := range ret.Portals { + if p1.Label == p2.Label && p1.From != p2.From { + p1.To = p2.From + ret.Portals[i] = p1 + } + } + } + return ret +} diff --git a/2019/day20/maze.go b/2019/day20/maze.go index 5cccd72..ae4e22e 100644 --- a/2019/day20/maze.go +++ b/2019/day20/maze.go @@ -1,228 +1,42 @@ package main import ( - "bytes" "fmt" - helpers "git.bullercodeworks.com/brian/adventofcode/helpers" + h "git.bullercodeworks.com/brian/adventofcode/helpers" ) +type MazeMap map[h.Coordinate3d]bool + type Maze struct { - start *MazeCoord - end *MazeCoord - maze map[string]*MazeCoord - portals map[string]*Portal - - current *MazeCoord - - BotX, BotY int - TopX, TopY int + MazeMap + Outer Square + Inner Square + Portals map[h.Coordinate3d]Portal + Start h.Coordinate3d + End h.Coordinate3d } -func NewMaze(file string) *Maze { - inp := helpers.FileToBytes(file) - lines := bytes.Split(inp, []byte{'\n'}) - m := Maze{ - maze: make(map[string]*MazeCoord), - portals: make(map[string]*Portal), - } - for yk, yv := range lines { - for xk, xv := range yv { - if xv == '.' || xv == '#' { - m.maze[c(xk, yk)] = &MazeCoord{ - X: xk, - Y: yk, - Value: xv, - steps: helpers.MAX_INT, - } - } - } - } - // Now hook up neighbor coords - for _, v := range m.maze { - if v.X < m.BotX { - m.BotX = v.X - } - if v.X > m.TopX { - m.TopX = v.X - } - if v.Y < m.BotY { - m.BotY = v.Y - } - if v.Y > m.TopY { - m.TopY = v.Y - } - var d *MazeCoord - var ok bool - // Hook up north - if d, ok = m.maze[c(v.X, v.Y-1)]; ok { - v.N = d - } else if v.Value == '.' { - // North Portal - name := string([]byte{lines[v.Y-2][v.X], lines[v.Y-1][v.X]}) - var p *Portal - if p, ok = m.portals[name]; ok { - v.N = m.maze[c(p.X1, p.Y1)] - m.maze[c(p.X1, p.Y1)].S = v - p.X2 = v.X - p.Y2 = v.Y - } else { - m.portals[name] = &Portal{ - Name: name, - X1: v.X, - Y1: v.Y, - } - } - } - // Hook up east - if d, ok = m.maze[c(v.X+1, v.Y)]; ok { - v.E = d - } else if v.Value == '.' { - // East Portal - name := string([]byte{lines[v.Y][v.X+1], lines[v.Y][v.X+2]}) - var p *Portal - if p, ok = m.portals[name]; ok { - v.E = m.maze[c(p.X1, p.Y1)] - m.maze[c(p.X1, p.Y1)].W = v - p.X2 = v.X - p.Y2 = v.Y - } else { - m.portals[name] = &Portal{ - Name: name, - X1: v.X, - Y1: v.Y, - } - } - } - // Hook up south - if d, ok = m.maze[c(v.X, v.Y+1)]; ok { - v.S = d - } else if v.Value == '.' { - // South Portal - name := string([]byte{lines[v.Y+1][v.X], lines[v.Y+2][v.X]}) - var p *Portal - if p, ok = m.portals[name]; ok { - v.S = m.maze[c(p.X1, p.Y1)] - m.maze[c(p.X1, p.Y1)].N = v - fmt.Println(v.S, "<=>", m.maze[c(p.X1, p.Y1)].N) - p.X2 = v.X - p.Y2 = v.Y - } else { - m.portals[name] = &Portal{ - Name: name, - X1: v.X, - Y1: v.Y, - } - } - } - // Hook up west - if d, ok = m.maze[c(v.X-1, v.Y)]; ok { - v.W = d - } else if v.Value == '.' { - // West Portal - name := string([]byte{lines[v.Y][v.X-2], lines[v.Y][v.X-1]}) - var p *Portal - if p, ok = m.portals[name]; ok { - v.W = m.maze[c(p.X1, p.Y1)] - m.maze[c(p.X1, p.Y1)].E = v - p.X2 = v.X - p.Y2 = v.Y - } else { - m.portals[name] = &Portal{ - Name: name, - X1: v.X, - Y1: v.Y, - } - } - } - - } - - st := m.portals["AA"] - m.current = m.maze[c(st.X1, st.Y1)] - return &m -} - -func (m *Maze) MoveNorth() bool { - if m.current.N != nil && m.current.N.Value == '.' { - m.current = m.current.N - return true - } - return false -} - -func (m *Maze) MoveEast() bool { - if m.current.E != nil && m.current.E.Value == '.' { - m.current = m.current.E - return true - } - return false -} - -func (m *Maze) MoveSouth() bool { - if m.current.S != nil && m.current.S.Value == '.' { - m.current = m.current.S - return true - } - return false -} - -func (m *Maze) MoveWest() bool { - if m.current.W != nil && m.current.W.Value == '.' { - m.current = m.current.W - return true - } - return false -} - -func (m *Maze) Print() { - for y := m.BotY; y <= m.TopY; y++ { - for x := m.BotX; x <= m.TopX; x++ { - if m.current.X == x && m.current.Y == y { - fmt.Print("%") - } else { - if v, ok := m.maze[c(x, y)]; ok { - fmt.Print(string(v.Value)) +func (m *Maze) Print(visited map[h.Coordinate3d]bool) { + for y := m.Outer.Tl.Y - 1; y < m.Outer.Br.Y+1; y++ { + for x := m.Outer.Tl.X - 1; x < m.Outer.Br.X+1; x++ { + c := h.Coordinate3d{X: x, Y: y, Z: 0} + if _, pok := m.Portals[c]; pok { + fmt.Print("*") + //} else if m.Inner.IsInBorder(c) { + // fmt.Print("$") + //} else if m.Inner.ContainsButNotBorder(c) { + // fmt.Print(" ") + } else if m.MazeMap[c] { + if v, ok := visited[c]; ok && v { + fmt.Print(h.FILL_CHAR) } else { - fmt.Print(" ") + fmt.Print(".") } + } else { + fmt.Print("#") } } fmt.Println() } } - -func (m *Maze) GetStart() *MazeCoord { - start := m.portals["AA"] - return m.maze[c(start.X1, start.Y1)] -} - -func (m *Maze) IsDone() bool { - return m.IsEnd(m.current.X, m.current.Y) -} - -func (m *Maze) GetEnd() *MazeCoord { - end := m.portals["ZZ"] - return m.maze[c(end.X1, end.Y1)] -} - -func (m *Maze) IsEnd(x, y int) bool { - return x == m.GetEnd().X && y == m.GetEnd().Y -} - -type Portal struct { - Name string - X1, Y1, X2, Y2 int -} - -type MazeCoord struct { - X, Y int - N, E, S, W *MazeCoord - Value byte - visited bool - steps int -} - -func c(x, y int) string { - return fmt.Sprintf("[%d, %d]", x, y) -} diff --git a/2019/day20/portal.go b/2019/day20/portal.go new file mode 100644 index 0000000..b622ea9 --- /dev/null +++ b/2019/day20/portal.go @@ -0,0 +1,10 @@ +package main + +import h "git.bullercodeworks.com/brian/adventofcode/helpers" + +type Portal struct { + Label string + From h.Coordinate3d + To h.Coordinate3d + Outer bool +} diff --git a/2019/day20/square.go b/2019/day20/square.go new file mode 100644 index 0000000..5756e3b --- /dev/null +++ b/2019/day20/square.go @@ -0,0 +1,28 @@ +package main + +import h "git.bullercodeworks.com/brian/adventofcode/helpers" + +type Square struct { + Tl h.Coordinate3d + Br h.Coordinate3d +} + +func (s *Square) Contains(p h.Coordinate3d) bool { + return p.X >= s.Tl.X && p.Y >= s.Tl.Y && p.X <= s.Br.X && p.Y <= s.Br.Y +} + +func (s *Square) IsInBorder(p h.Coordinate3d) bool { + if !s.Contains(p) { + return false + } + if p.Y == s.Tl.Y || p.Y == s.Br.Y { + return p.X >= s.Tl.X || p.X <= s.Br.X + } else if p.X == s.Tl.X || p.X == s.Br.X { + return p.Y >= s.Tl.Y || p.Y <= s.Br.Y + } + return false +} + +func (s *Square) ContainsButNotBorder(p h.Coordinate3d) bool { + return s.Contains(p) && !s.IsInBorder(p) +} diff --git a/2019/day20/testinput2 b/2019/day20/testinput2 new file mode 100644 index 0000000..d051133 --- /dev/null +++ b/2019/day20/testinput2 @@ -0,0 +1,37 @@ + Z L X W C + Z P Q B K + ###########.#.#.#.#######.############### + #...#.......#.#.......#.#.......#.#.#...# + ###.#.#.#.#.#.#.#.###.#.#.#######.#.#.### + #.#...#.#.#...#.#.#...#...#...#.#.......# + #.###.#######.###.###.#.###.###.#.####### + #...#.......#.#...#...#.............#...# + #.#########.#######.#.#######.#######.### + #...#.# F R I Z #.#.#.# + #.###.# D E C H #.#.#.# + #.#...# #...#.# + #.###.# #.###.# + #.#....OA WB..#.#..ZH + #.###.# #.#.#.# +CJ......# #.....# + ####### ####### + #.#....CK #......IC + #.###.# #.###.# + #.....# #...#.# + ###.### #.#.#.# +XF....#.# RF..#.#.# + #####.# ####### + #......CJ NM..#...# + ###.#.# #.###.# +RE....#.# #......RF + ###.### X X L #.#.#.# + #.....# F Q P #.#.#.# + ###.###########.###.#######.#########.### + #.....#...#.....#.......#...#.....#.#...# + #####.#.###.#######.#######.###.###.#.#.# + #.......#.......#.#.#.#.#...#...#...#.#.# + #####.###.#####.#.#.#.#.###.###.#.###.### + #.......#.....#.#...#...............#...# + #############.#.#.###.################### + A O F N + A A D M