use a heap for marginally faster execution
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
typealias CellCost = (cell: Cell, cost: Int)
|
||||
typealias PathCostFromStart = [Cell:(Int, [Cell])]
|
||||
|
||||
enum Dir : String, CaseIterable, CustomStringConvertible {
|
||||
case n = "▲"
|
||||
@@ -35,39 +36,31 @@ struct Cell : Hashable, CustomStringConvertible {
|
||||
}
|
||||
}
|
||||
|
||||
struct QueueEntry : Hashable, CustomStringConvertible {
|
||||
struct QueueEntry : Hashable {
|
||||
let cell: Cell
|
||||
let origin: Cell
|
||||
let costSoFar: Int
|
||||
let path: [Cell]
|
||||
var description: String { return "\(cell) from \(origin) @\(costSoFar)" }
|
||||
}
|
||||
|
||||
struct CellPair : Hashable, CustomStringConvertible {
|
||||
struct CellPair : Hashable {
|
||||
let a: Cell
|
||||
let b: Cell
|
||||
var description: String { return "\(a),\(b)" }
|
||||
}
|
||||
|
||||
struct Maze : CustomStringConvertible {
|
||||
struct Maze {
|
||||
let walls: [[Bool]]
|
||||
let (w, h): (Int, Int)
|
||||
var start: Cell { return Cell(i: h-2, j: 1, dir: Dir.e) }
|
||||
var ends: Set<Cell> {
|
||||
return Set(Dir.allCases.map { Cell(i: 1, j: w-2, dir: $0) })
|
||||
}
|
||||
var description: String {
|
||||
let s: [[String]] = walls.enumerated().map { i, r in
|
||||
[String(format: "%3d ", i)] + r.map { $0 ? "██" : " " }
|
||||
}
|
||||
print(" 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4")
|
||||
return s.map { $0.joined() }.joined(separator: "\n")
|
||||
}
|
||||
let start: Cell
|
||||
let ends: Set<Cell>
|
||||
init(fromFile f: String) throws {
|
||||
let content = try String(contentsOfFile: f, encoding: .ascii)
|
||||
let lines = content.split(separator: "\n")
|
||||
(h, w) = (lines.count, lines[0].count)
|
||||
let (h, w) = (lines.count, lines[0].count)
|
||||
(self.h, self.w) = (h, w)
|
||||
walls = lines.map { line in line.map { cell in cell == "#" } }
|
||||
start = Cell(i: h-2, j: 1, dir: Dir.e)
|
||||
ends = Set(Dir.allCases.map { Cell(i: 1, j: w-2, dir: $0) })
|
||||
}
|
||||
|
||||
func neighbors(of cell: Cell) -> [CellCost] {
|
||||
@@ -114,11 +107,11 @@ struct Maze : CustomStringConvertible {
|
||||
|
||||
}
|
||||
|
||||
func search(graph edges: [Cell:[CellCost]], start: Cell) -> [Cell:(Int, [Cell])] {
|
||||
var q: [CellCost] = [(cell: start, cost: 0)]
|
||||
var visited: [Cell:(Int, [Cell])] = [:]
|
||||
while !q.isEmpty {
|
||||
let e = q.removeLast()
|
||||
func search(graph edges: [Cell:[CellCost]], start: Cell) -> PathCostFromStart {
|
||||
var q = Heap<CellCost>(comparator: { l, r in l.cost < r.cost })
|
||||
q.insert((cell: start, cost: 0))
|
||||
var visited: PathCostFromStart = [:]
|
||||
while let e = q.pop() {
|
||||
if let node = edges[e.cell] {
|
||||
for (cell, cost) in node {
|
||||
let newCost = e.cost + cost
|
||||
@@ -132,14 +125,14 @@ func search(graph edges: [Cell:[CellCost]], start: Cell) -> [Cell:(Int, [Cell])]
|
||||
}
|
||||
}
|
||||
visited[cell] = (newCost, [e.cell])
|
||||
q.append((cell: cell, cost: newCost))
|
||||
q.insert((cell: cell, cost: newCost))
|
||||
}
|
||||
}
|
||||
}
|
||||
return visited
|
||||
}
|
||||
|
||||
func trace(_ visited: [Cell:(Int, [Cell])], from: Cell, to: Cell) -> [[Cell]] {
|
||||
func trace(_ visited: PathCostFromStart, from: Cell, to: Cell) -> [[Cell]] {
|
||||
if from == to {
|
||||
return [[from]]
|
||||
}
|
||||
@@ -148,16 +141,22 @@ func trace(_ visited: [Cell:(Int, [Cell])], from: Cell, to: Cell) -> [[Cell]] {
|
||||
}
|
||||
}
|
||||
|
||||
let maze = try Maze(fromFile: CommandLine.arguments[1])
|
||||
let (graph, pathPairs) = maze.graph()
|
||||
let visited = search(graph: graph, start: maze.start)
|
||||
let minCost = maze.ends.compactMap { visited[$0] }.map { $0.0 }.min()!
|
||||
print("minCost: \(minCost)")
|
||||
let seats = maze.ends
|
||||
.filter { visited[$0, default: (0, [])].0 == minCost }
|
||||
.flatMap { trace(visited, from: $0, to: maze.start) }
|
||||
.map { zip($0, $0.dropFirst()) }
|
||||
.map { $0.flatMap { pathPairs[CellPair(a: $0.0, b: $0.1)]! } }
|
||||
.map { $0.map { cell in Cell(i: cell.i, j: cell.j, dir: Dir.n) } }
|
||||
.flatMap { $0 }
|
||||
print("seats: \(Set(seats).count)")
|
||||
@main
|
||||
struct AoC {
|
||||
static func main() throws {
|
||||
let maze = try Maze(fromFile: CommandLine.arguments[1])
|
||||
let (graph, pathPairs) = maze.graph()
|
||||
let visited = search(graph: graph, start: maze.start)
|
||||
let minCost = maze.ends.compactMap { visited[$0] }.map { $0.0 }.min()!
|
||||
print("minCost: \(minCost)")
|
||||
let seats = maze.ends
|
||||
.filter { visited[$0, default: (0, [])].0 == minCost }
|
||||
.flatMap { trace(visited, from: $0, to: maze.start) }
|
||||
.map { zip($0, $0.dropFirst()) }
|
||||
.map { $0.flatMap { pathPairs[CellPair(a: $0.0, b: $0.1)]! } }
|
||||
.map { $0.map { cell in Cell(i: cell.i, j: cell.j, dir: Dir.n) } }
|
||||
.flatMap { $0 }
|
||||
print("seats: \(Set(seats).count)")
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user