d16
This commit is contained in:
160
day16/d16p1.debug.swift
Normal file
160
day16/d16p1.debug.swift
Normal file
@@ -0,0 +1,160 @@
|
||||
import Foundation
|
||||
|
||||
typealias CellCost = (cell: Cell, cost: Int)
|
||||
|
||||
enum Dir : String, CaseIterable, CustomStringConvertible {
|
||||
case n = "▲"
|
||||
case s = "▼"
|
||||
case e = "▶"
|
||||
case w = "◀"
|
||||
var description: String { return self.rawValue }
|
||||
func turnCost(to dir: Self) -> Int? {
|
||||
if self == dir { return 0 }
|
||||
if (self == .n && dir == .s) || (self == .s && dir == .n) ||
|
||||
(self == .e && dir == .w) || (self == .w && dir == .e) { return nil }
|
||||
return 1000
|
||||
}
|
||||
}
|
||||
|
||||
struct Cell : Hashable, CustomStringConvertible {
|
||||
let (i, j): (Int, Int)
|
||||
let dir: Dir
|
||||
var description: String { return "(\(i),\(j))\(dir)" }
|
||||
func neighbor(_ dir: Dir) -> CellCost? {
|
||||
if let cost = self.dir.turnCost(to: dir) {
|
||||
let c = cost + 1
|
||||
switch dir {
|
||||
case Dir.n: return (cell: Cell(i: i-1, j: j, dir: dir), cost: c)
|
||||
case Dir.s: return (cell: Cell(i: i+1, j: j, dir: dir), cost: c)
|
||||
case Dir.e: return (cell: Cell(i: i, j: j+1, dir: dir), cost: c)
|
||||
case Dir.w: return (cell: Cell(i: i, j: j-1, dir: dir), cost: c)
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct QueueEntry : Hashable, CustomStringConvertible {
|
||||
let cell: Cell
|
||||
let origin: Cell
|
||||
let costSoFar: Int
|
||||
var description: String { return "\(cell) from \(origin) @\(costSoFar)" }
|
||||
}
|
||||
|
||||
struct IJ : Hashable { //TODO: delete
|
||||
let i: Int
|
||||
let j: Int
|
||||
}
|
||||
|
||||
struct CellPair : Hashable {
|
||||
let a: Cell
|
||||
let b: Cell
|
||||
}
|
||||
|
||||
struct Maze : CustomStringConvertible {
|
||||
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 {
|
||||
return "┌" + String(repeating: "─", count: w*2) + "┐\n" +
|
||||
walls.map {
|
||||
"│" + $0.map { $0 ? "██" : " " }.joined() + "│"
|
||||
}.joined(separator: "\n") +
|
||||
"\n└" + String(repeating: "─", count: w*2) + "┘"
|
||||
}
|
||||
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)
|
||||
walls = lines.map { line in line.map { cell in cell == "#" } }
|
||||
}
|
||||
|
||||
func neighbors(of cell: Cell) -> [CellCost] {
|
||||
return Dir.allCases
|
||||
.compactMap { cell.neighbor($0) }
|
||||
.filter { nb in !walls[nb.cell.i][nb.cell.j] }
|
||||
}
|
||||
|
||||
func graph() -> [Cell:[CellCost]] {
|
||||
var edges: [Cell:[(Cell, Int)]] = [start: []]
|
||||
var q: [QueueEntry] = [
|
||||
QueueEntry(cell: start, origin: start, costSoFar: 0)
|
||||
]
|
||||
var seen: Set<CellPair> = []
|
||||
while !q.isEmpty {
|
||||
let e = q.removeLast()
|
||||
var origin = e.origin
|
||||
var costSoFar = e.costSoFar
|
||||
let nbs = neighbors(of: e.cell)
|
||||
//xprint(q: q, cell: e.cell)
|
||||
//print("q: \(q)")
|
||||
//print("c: \(e.cell) -> \(nbs)")
|
||||
if ends.contains(e.cell) || (nbs.count > 1 && e.cell != start) {
|
||||
if seen.contains(CellPair(a: origin, b: e.cell)) {
|
||||
continue
|
||||
}
|
||||
let upd = edges[origin, default: []] + [(e.cell, costSoFar)]
|
||||
edges[origin] = upd
|
||||
seen.insert(CellPair(a: origin, b: e.cell))
|
||||
origin = e.cell
|
||||
costSoFar = 0
|
||||
//print("!!!! \(e.cell)")
|
||||
//edges.forEach { k, v in print(" - \(k): \(v)") }
|
||||
}
|
||||
if !ends.contains(e.cell) {
|
||||
nbs.map { cell, cost in
|
||||
QueueEntry(
|
||||
cell: cell, origin: origin, costSoFar: costSoFar + cost
|
||||
)
|
||||
}.forEach { q.append($0) }
|
||||
}
|
||||
}
|
||||
ends.forEach { edges[$0] = [] }
|
||||
return edges
|
||||
}
|
||||
|
||||
func xprint(q: [QueueEntry], cell: Cell) {
|
||||
let qx = Set(q.map { IJ(i: $0.cell.i, j: $0.cell.j) })
|
||||
let s: [[String]] = walls.enumerated().map { i, r in
|
||||
[String(format: "%02d ", i)] + r.enumerated().map { j, c in
|
||||
if c { return "██" }
|
||||
if cell.i == i && cell.j == j { return "🯇🯈" }
|
||||
if qx.contains(IJ(i: i, j: j)) { return "▒▒" }
|
||||
return " "
|
||||
}
|
||||
}
|
||||
print(" 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4")
|
||||
print(s.map { $0.joined() }.joined(separator: "\n"))
|
||||
}
|
||||
|
||||
func search() -> Int {
|
||||
let edges = graph()
|
||||
edges.forEach { k, v in print(" - \(k): \(v)") }
|
||||
|
||||
var q: [CellCost] = [(cell: start, cost: 0)]
|
||||
var visited: [Cell:Int] = [:]
|
||||
while !q.isEmpty {
|
||||
let e = q.removeLast()
|
||||
print("visiting \(e)")
|
||||
for (cell, cost) in edges[e.cell]! {
|
||||
if let prevCost = visited[cell] {
|
||||
if e.cost + cost >= prevCost {
|
||||
continue
|
||||
}
|
||||
}
|
||||
visited[cell] = e.cost + cost
|
||||
q.append((cell: cell, cost: e.cost + cost))
|
||||
}
|
||||
}
|
||||
print(visited)
|
||||
return ends.compactMap { visited[$0] }.min()!
|
||||
}
|
||||
}
|
||||
|
||||
let maze = try Maze(fromFile: CommandLine.arguments[1])
|
||||
print(maze)
|
||||
print(maze.search())
|
Reference in New Issue
Block a user