Skip to content

Instantly share code, notes, and snippets.

@erikaderstedt
Created December 20, 2019 12:09
Show Gist options
  • Save erikaderstedt/e133d93cb4990321c930e1ddd2a50971 to your computer and use it in GitHub Desktop.
Save erikaderstedt/e133d93cb4990321c930e1ddd2a50971 to your computer and use it in GitHub Desktop.
import Foundation
struct Position: Hashable {
let x: Int
let y: Int
let depth: Int
var adjacent: [Position] {
return [Position(x: x-1, y: y, depth: depth),
Position(x: x+1, y: y, depth: depth),
Position(x: x, y: y-1, depth: depth),
Position(x: x, y: y+1, depth: depth)]
}
init(x: Int, y: Int, depth: Int = 0) {
self.x = x
self.y = y
self.depth = depth
}
}
enum Space: CustomStringConvertible {
case wall
case open
case portal(String)
case empty
init(_ s: Character) {
switch s {
case "#":
self = .wall
case ".":
self = .open
case " ":
self = .empty
case let a:
self = .portal(String(a))
}
}
var description: String {
switch self {
case .wall: return "#"
case .empty: return " "
case .open: return "."
case .portal(let s):
if s.count == 2 {
return s
} else {
return "?"
}
}
}
}
struct Grid {
let spaces: [[Space]]
let width: Int
let height: Int
let portals: [String: (Position, Position?)]
init(string s: String) {
let rows = s.components(separatedBy: .newlines)
self.height = rows.count
self.width = rows.reduce(0, { max($0, $1.count) })
let w = width
var g = rows.map({ (s: String) -> [Space] in
var a = Array<Space>(repeating: .empty, count: w)
for i in s.enumerated() {
a[i.offset] = Space(i.element)
}
return a
})
var p1 = [String: (Position, Position?)]()
// Outer portals
for c in 0..<width {
if case let Space.portal(s) = g[1][c], case let Space.portal(s2) = g[0][c] {
g[1][c] = .portal(s2 + s)
g[0][c] = .empty
p1[s2+s] = (Position(x: c, y: 2), nil)
}
if case let Space.portal(s) = g[height-2][c], case let Space.portal(s2) = g[height-1][c] {
g[height-2][c] = .portal(s + s2)
g[height-1][c] = .empty
p1[s+s2] = (Position(x: c, y: height-3), nil)
}
}
for r in 0..<height {
if case let Space.portal(s) = g[r][0], case let Space.portal(s2) = g[r][1] {
g[r][1] = .portal(s + s2)
g[r][0] = .empty
p1[s+s2] = (Position(x: 2, y: r), nil)
}
if case let Space.portal(s) = g[r][width-2], case let Space.portal(s2) = g[r][width-1] {
g[r][width-2] = .portal(s + s2)
g[r][width-1] = .empty
p1[s+s2] = (Position(x: width - 3, y: r), nil)
}
}
// Iterate over grid positions
for r in 4..<(height-4) {
for c in 4..<(width-4) {
if case let Space.portal(s) = g[r][c] {
if case let Space.portal(s2) = g[r+1][c] {
if r > height/2 {
g[r+1][c] = .portal(s + s2)
g[r][c] = .empty
p1[s+s2] = (p1[s+s2]!.0, Position(x: c, y: r+2))
} else {
g[r+1][c] = .empty
g[r][c] = .portal(s + s2)
p1[s+s2] = (p1[s+s2]!.0, Position(x: c, y: r-1))
}
} else if case let Space.portal(s2) = g[r][c+1] {
if c > width/2 {
g[r][c+1] = .portal(s + s2)
g[r][c] = .empty
p1[s+s2] = (p1[s+s2]!.0, Position(x: c+2, y: r))
} else {
g[r][c+1] = .empty
g[r][c] = .portal(s + s2)
p1[s+s2] = (p1[s+s2]!.0, Position(x: c-1, y: r))
}
}
}
}
}
self.portals = p1
self.spaces = g
}
}
func distance(to destination: Position, from source: Position, in grid: Grid, recursiveSpaces: Bool) -> Int {
var steps = 0
var leafStates = [source]
var oldStates = [Position]()
while true {
steps = steps + 1
var additionalStates = [Position]()
for leaf in leafStates {
let moves = leaf.adjacent.compactMap({
(p: Position) -> Position? in
switch grid.spaces[p.y][p.x] {
case .wall, .empty: return nil
case .open: return p
case .portal(let s):
guard let z = grid.portals[s] else { return nil }
if recursiveSpaces {
// Are we at the outer ring? If so, entering this portal will decrease depth.
if z.0.x == leaf.x && z.0.y == leaf.y {
if leaf.depth > 0, let o = z.1 {
return Position(x: o.x, y: o.y, depth: leaf.depth - 1)
} else {
return nil
}
} else {
return Position(x: z.0.x, y: z.0.y, depth: leaf.depth + 1)
}
} else {
return (z.0 == leaf) ? z.1 : z.0
}
}
})
for move in moves {
if oldStates.contains(move) { continue }
if move == destination {
return steps
}
additionalStates.append(move)
}
}
oldStates = leafStates
leafStates = additionalStates
}
}
let path = CommandLine.arguments[1]
let i = try! String(contentsOf: URL(fileURLWithPath: path))
let g = Grid(string: i)
let startPosition = g.portals["AA"]!.0
let endPosition = g.portals["ZZ"]!.0
print("Pt 1:", distance(to: endPosition, from: startPosition, in: g, recursiveSpaces: false))
print("Pt 2:", distance(to: endPosition, from: startPosition, in: g, recursiveSpaces: true))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment