use a heap for marginally faster execution
This commit is contained in:
@@ -58,7 +58,7 @@ struct Maze : CustomStringConvertible {
|
|||||||
let s: [[String]] = walls.enumerated().map { i, r in
|
let s: [[String]] = walls.enumerated().map { i, r in
|
||||||
[String(format: "%3d ", i)] + r.map { $0 ? "██" : " " }
|
[String(format: "%3d ", i)] + r.map { $0 ? "██" : " " }
|
||||||
}
|
}
|
||||||
print(" 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4")
|
print(" " + (0..<w).map { " \($0 % 10)" }.joined())
|
||||||
return s.map { $0.joined() }.joined(separator: "\n")
|
return s.map { $0.joined() }.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
init(fromFile f: String) throws {
|
init(fromFile f: String) throws {
|
||||||
@@ -111,10 +111,10 @@ struct Maze : CustomStringConvertible {
|
|||||||
let edges = graph()
|
let edges = graph()
|
||||||
// edges.forEach { k, v in print(" - \(k): \(v)") }
|
// edges.forEach { k, v in print(" - \(k): \(v)") }
|
||||||
|
|
||||||
var q: [CellCost] = [(cell: start, cost: 0)]
|
var q = Heap<CellCost>(comparator: { $0.cost < $1.cost })
|
||||||
|
q.insert((cell: start, cost: 0))
|
||||||
var visited: [Cell:Int] = [:]
|
var visited: [Cell:Int] = [:]
|
||||||
while !q.isEmpty {
|
while let e = q.pop() {
|
||||||
let e = q.removeLast()
|
|
||||||
if let node = edges[e.cell] {
|
if let node = edges[e.cell] {
|
||||||
for (cell, cost) in node {
|
for (cell, cost) in node {
|
||||||
if let prevCost = visited[cell] {
|
if let prevCost = visited[cell] {
|
||||||
@@ -123,7 +123,7 @@ struct Maze : CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
visited[cell] = e.cost + cost
|
visited[cell] = e.cost + cost
|
||||||
q.append((cell: cell, cost: e.cost + cost))
|
q.insert((cell: cell, cost: e.cost + cost))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,6 +131,12 @@ struct Maze : CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let maze = try Maze(fromFile: CommandLine.arguments[1])
|
@main
|
||||||
// print(maze)
|
struct AoC {
|
||||||
print(maze.search())
|
static func main() throws {
|
||||||
|
let maze = try Maze(fromFile: CommandLine.arguments[1])
|
||||||
|
print(maze)
|
||||||
|
print("minCost: \(maze.search())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
typealias CellCost = (cell: Cell, cost: Int)
|
typealias CellCost = (cell: Cell, cost: Int)
|
||||||
|
typealias PathCostFromStart = [Cell:(Int, [Cell])]
|
||||||
|
|
||||||
enum Dir : String, CaseIterable, CustomStringConvertible {
|
enum Dir : String, CaseIterable, CustomStringConvertible {
|
||||||
case n = "▲"
|
case n = "▲"
|
||||||
@@ -35,34 +36,25 @@ struct Cell : Hashable, CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct QueueEntry : Hashable, CustomStringConvertible {
|
struct QueueEntry : Hashable {
|
||||||
let cell: Cell
|
let cell: Cell
|
||||||
let origin: Cell
|
let origin: Cell
|
||||||
let costSoFar: Int
|
let costSoFar: Int
|
||||||
let path: [Cell]
|
let path: [Cell]
|
||||||
var description: String { return "\(cell) from \(origin) @\(costSoFar)" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CellPair : Hashable, CustomStringConvertible {
|
struct CellPair : Hashable {
|
||||||
let a: Cell
|
let a: Cell
|
||||||
let b: Cell
|
let b: Cell
|
||||||
var description: String { return "\(a),\(b)" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Maze : CustomStringConvertible {
|
struct Maze {
|
||||||
let walls: [[Bool]]
|
let walls: [[Bool]]
|
||||||
let (w, h): (Int, Int)
|
let (w, h): (Int, Int)
|
||||||
var start: Cell { return Cell(i: h-2, j: 1, dir: Dir.e) }
|
var start: Cell { return Cell(i: h-2, j: 1, dir: Dir.e) }
|
||||||
var ends: Set<Cell> {
|
var ends: Set<Cell> {
|
||||||
return Set(Dir.allCases.map { Cell(i: 1, j: w-2, dir: $0) })
|
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")
|
|
||||||
}
|
|
||||||
init(fromFile f: String) throws {
|
init(fromFile f: String) throws {
|
||||||
let content = try String(contentsOfFile: f, encoding: .ascii)
|
let content = try String(contentsOfFile: f, encoding: .ascii)
|
||||||
let lines = content.split(separator: "\n")
|
let lines = content.split(separator: "\n")
|
||||||
@@ -114,11 +106,11 @@ struct Maze : CustomStringConvertible {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func search(graph edges: [Cell:[CellCost]], start: Cell) -> [Cell:(Int, [Cell])] {
|
func search(graph edges: [Cell:[CellCost]], start: Cell) -> PathCostFromStart {
|
||||||
var q: [CellCost] = [(cell: start, cost: 0)]
|
var q = Heap<CellCost>(comparator: { l, r in l.cost < r.cost })
|
||||||
var visited: [Cell:(Int, [Cell])] = [:]
|
q.insert((cell: start, cost: 0))
|
||||||
while !q.isEmpty {
|
var visited: PathCostFromStart = [:]
|
||||||
let e = q.removeLast()
|
while let e = q.pop() {
|
||||||
if let node = edges[e.cell] {
|
if let node = edges[e.cell] {
|
||||||
for (cell, cost) in node {
|
for (cell, cost) in node {
|
||||||
let newCost = e.cost + cost
|
let newCost = e.cost + cost
|
||||||
@@ -132,14 +124,14 @@ func search(graph edges: [Cell:[CellCost]], start: Cell) -> [Cell:(Int, [Cell])]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
visited[cell] = (newCost, [e.cell])
|
visited[cell] = (newCost, [e.cell])
|
||||||
q.append((cell: cell, cost: newCost))
|
q.insert((cell: cell, cost: newCost))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return visited
|
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 {
|
if from == to {
|
||||||
return [[from]]
|
return [[from]]
|
||||||
}
|
}
|
||||||
@@ -148,16 +140,22 @@ func trace(_ visited: [Cell:(Int, [Cell])], from: Cell, to: Cell) -> [[Cell]] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let maze = try Maze(fromFile: CommandLine.arguments[1])
|
@main
|
||||||
let (graph, pathPairs) = maze.graph()
|
struct AoC {
|
||||||
let visited = search(graph: graph, start: maze.start)
|
static func main() throws {
|
||||||
let minCost = maze.ends.compactMap { visited[$0] }.map { $0.0 }.min()!
|
let maze = try Maze(fromFile: CommandLine.arguments[1])
|
||||||
print("minCost: \(minCost)")
|
let (graph, pathPairs) = maze.graph()
|
||||||
let seats = maze.ends
|
let visited = search(graph: graph, start: maze.start)
|
||||||
.filter { visited[$0, default: (0, [])].0 == minCost }
|
let minCost = maze.ends.compactMap { visited[$0] }.map { $0.0 }.min()!
|
||||||
.flatMap { trace(visited, from: $0, to: maze.start) }
|
print("minCost: \(minCost)")
|
||||||
.map { zip($0, $0.dropFirst()) }
|
let seats = maze.ends
|
||||||
.map { $0.flatMap { pathPairs[CellPair(a: $0.0, b: $0.1)]! } }
|
.filter { visited[$0, default: (0, [])].0 == minCost }
|
||||||
.map { $0.map { cell in Cell(i: cell.i, j: cell.j, dir: Dir.n) } }
|
.flatMap { trace(visited, from: $0, to: maze.start) }
|
||||||
.flatMap { $0 }
|
.map { zip($0, $0.dropFirst()) }
|
||||||
print("seats: \(Set(seats).count)")
|
.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)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
54
day16/heap.swift
Normal file
54
day16/heap.swift
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
struct Heap<T> {
|
||||||
|
var comparator: ((_ l: T,_ r: T) -> Bool)
|
||||||
|
var heap: [T] = []
|
||||||
|
var isEmpty: Bool { return heap.isEmpty }
|
||||||
|
|
||||||
|
private mutating func bubbleUp(idx: Int) {
|
||||||
|
let parent = (idx - 1) / 2
|
||||||
|
if idx <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if comparator(heap[idx], heap[parent]) {
|
||||||
|
heap.swapAt(parent, idx)
|
||||||
|
bubbleUp(idx: parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private mutating func heapify(_ idx: Int) {
|
||||||
|
let left = idx * 2 + 1
|
||||||
|
let right = idx * 2 + 2
|
||||||
|
var comp = idx
|
||||||
|
|
||||||
|
if heap.count > left && comparator(heap[left], heap[comp]) {
|
||||||
|
comp = left
|
||||||
|
}
|
||||||
|
if heap.count > right && comparator(heap[right], heap[comp]) {
|
||||||
|
comp = right
|
||||||
|
}
|
||||||
|
if comp != idx {
|
||||||
|
heap.swapAt(comp, idx)
|
||||||
|
heapify(comp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func insert(_ item: T) {
|
||||||
|
heap.append(item)
|
||||||
|
bubbleUp(idx: heap.count-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func pop() -> T? {
|
||||||
|
let item: T? = heap.first
|
||||||
|
if heap.count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if heap.count > 1 {
|
||||||
|
heap[0] = heap[heap.count-1]
|
||||||
|
heap.removeLast()
|
||||||
|
heapify(0)
|
||||||
|
} else if heap.count == 1{
|
||||||
|
heap.removeLast()
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user