Day 18 & Day 20 done
This commit is contained in:
parent
4f1712ceb0
commit
d4a45d2b67
@ -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
|
||||
}
|
||||
|
3
2019/day18/testinput
Normal file
3
2019/day18/testinput
Normal file
@ -0,0 +1,3 @@
|
||||
#########
|
||||
#b.A.@.a#
|
||||
#########
|
5
2019/day18/testinput2
Normal file
5
2019/day18/testinput2
Normal file
@ -0,0 +1,5 @@
|
||||
########################
|
||||
#f.D.E.e.C.b.A.@.a.B.c.#
|
||||
######################.#
|
||||
#d.....................#
|
||||
########################
|
5
2019/day18/testinput3
Normal file
5
2019/day18/testinput3
Normal file
@ -0,0 +1,5 @@
|
||||
########################
|
||||
#...............b.C.D.f#
|
||||
#.######################
|
||||
#.....@.a.B.c.d.A.e.F.g#
|
||||
########################
|
9
2019/day18/testinput4
Normal file
9
2019/day18/testinput4
Normal file
@ -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#
|
||||
#################
|
6
2019/day18/testinput5
Normal file
6
2019/day18/testinput5
Normal file
@ -0,0 +1,6 @@
|
||||
########################
|
||||
#@..............ac.GI.b#
|
||||
###d#e#f################
|
||||
###A#B#C################
|
||||
###g#h#i################
|
||||
########################
|
@ -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()
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
10
2019/day20/portal.go
Normal file
10
2019/day20/portal.go
Normal file
@ -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
|
||||
}
|
28
2019/day20/square.go
Normal file
28
2019/day20/square.go
Normal file
@ -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)
|
||||
}
|
37
2019/day20/testinput2
Normal file
37
2019/day20/testinput2
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user