diff --git a/day16/d16p1.swift b/day16/d16p1.swift index 5a08604..b381b74 100644 --- a/day16/d16p1.swift +++ b/day16/d16p1.swift @@ -58,7 +58,7 @@ struct Maze : CustomStringConvertible { 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") + print(" " + (0..(comparator: { $0.cost < $1.cost }) + q.insert((cell: start, cost: 0)) var visited: [Cell:Int] = [:] - while !q.isEmpty { - let e = q.removeLast() + while let e = q.pop() { if let node = edges[e.cell] { for (cell, cost) in node { if let prevCost = visited[cell] { @@ -123,7 +123,7 @@ struct Maze : CustomStringConvertible { } } 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]) -// print(maze) -print(maze.search()) +@main +struct AoC { + static func main() throws { + let maze = try Maze(fromFile: CommandLine.arguments[1]) + print(maze) + print("minCost: \(maze.search())") + } +} + diff --git a/day16/d16p2.swift b/day16/d16p2.swift index 17d1361..790d74e 100644 --- a/day16/d16p2.swift +++ b/day16/d16p2.swift @@ -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,34 +36,25 @@ 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 { 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 { let content = try String(contentsOfFile: f, encoding: .ascii) let lines = content.split(separator: "\n") @@ -114,11 +106,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(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 +124,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 +140,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)") + } +} + diff --git a/day16/heap.swift b/day16/heap.swift new file mode 100644 index 0000000..1bdfac7 --- /dev/null +++ b/day16/heap.swift @@ -0,0 +1,54 @@ +struct Heap { + 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 + } +} +