2019-12-18 14:07:19 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-11-03 21:09:13 +00:00
|
|
|
"bufio"
|
2020-11-03 11:02:36 +00:00
|
|
|
"fmt"
|
2020-11-03 21:09:13 +00:00
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"os"
|
2019-12-18 14:07:19 +00:00
|
|
|
|
|
|
|
helpers "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
|
|
)
|
|
|
|
|
2020-11-03 21:09:13 +00:00
|
|
|
type state struct {
|
|
|
|
Pos helpers.Coordinate3d
|
|
|
|
Steps int
|
|
|
|
}
|
|
|
|
|
2019-12-18 14:07:19 +00:00
|
|
|
func main() {
|
|
|
|
file := "input"
|
|
|
|
if helpers.GetArgNumber(1) != "" {
|
|
|
|
file = helpers.GetArgNumber(1)
|
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
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)
|
|
|
|
}
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
|
|
|
|
2020-11-03 21:09:13 +00:00
|
|
|
func part1(r io.Reader) {
|
|
|
|
vault, doors, keys, start, allkeys := readVault(r)
|
|
|
|
fmt.Println(search(vault, doors, keys, start, allkeys, 0))
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
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}]
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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}]
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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}]
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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}]
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-03 21:09:13 +00:00
|
|
|
total += search(vault, doors, keys, helpers.Coordinate{X: start.X - 1, Y: start.Y + 1}, allKeys, haveKeys)
|
|
|
|
|
|
|
|
fmt.Println(total)
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
|
|
|
|
2020-11-03 21:09:13 +00:00
|
|
|
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
|
|
|
|
}
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
x++
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
y++
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
if err := scan.Err(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
return v, doors, keys, start, allkeys
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
|
|
|
|
2020-11-03 21:09:13 +00:00
|
|
|
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)
|
2020-11-03 11:02:36 +00:00
|
|
|
var st state
|
|
|
|
for {
|
|
|
|
st, queue = queue[0], queue[1:]
|
2020-11-03 21:09:13 +00:00
|
|
|
if st.Pos.Z&allKeys == allKeys {
|
|
|
|
return st.Steps
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
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
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
door, ok := doors[helpers.Coordinate{X: next.X, Y: next.Y}]
|
|
|
|
if ok && next.Z&door != door {
|
|
|
|
continue
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
2020-11-03 21:09:13 +00:00
|
|
|
|
|
|
|
key, ok := keys[helpers.Coordinate{X: next.X, Y: next.Y}]
|
2020-11-03 11:02:36 +00:00
|
|
|
if ok {
|
2020-11-03 21:09:13 +00:00
|
|
|
next.Z |= key
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
2019-12-18 14:07:19 +00:00
|
|
|
|
2020-11-03 21:09:13 +00:00
|
|
|
queue = append(queue, state{Pos: next, Steps: st.Steps + 1})
|
|
|
|
}
|
2020-11-03 11:02:36 +00:00
|
|
|
}
|
|
|
|
}
|