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) } 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) } } 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 } 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 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 } 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:] if st.Pos.Z&allKeys == allKeys { return st.Steps } 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.Coordinate{X: next.X, Y: next.Y}] if ok && next.Z&door != door { continue } key, ok := keys[helpers.Coordinate{X: next.X, Y: next.Y}] if ok { next.Z |= key } queue = append(queue, state{Pos: next, Steps: st.Steps + 1}) } } }