diff --git a/day20/d20p2.swift b/day20/d20p2.swift index a768491..eed3a99 100644 --- a/day20/d20p2.swift +++ b/day20/d20p2.swift @@ -3,72 +3,18 @@ import Foundation struct Coord : Hashable, CustomStringConvertible { let (x, y): (Int, Int) var description: String { return "(\(x), \(y))" } + func dist(to: Coord) -> Int { return abs(x - to.x) + abs(y - to.y) } } -struct Queue { - var arr: [T?] - var front: Int = 0 - var back: Int = 0 - init(size: Int) { - arr = Array(repeating: nil, count: size) - } - mutating func push(_ element: T) { - arr[back] = element - back += 1 - } - mutating func pop() -> T? { - if front < back { - front += 1 - return arr[front-1] - } - return nil - } -} - -struct Maze : CustomStringConvertible { +struct Maze { let (w, h): (Int, Int) let walls: Set let (start, end): (Coord, Coord) let cheatMax: Int - var description: String { - var s = Array(repeating: Array(repeating: " ", count: w), count: h) - walls.forEach { wall in - s[wall.y][wall.x] = "██" - } - path.forEach { cell, cost in - s[cell.y][cell.x] = String(format: "%2d", cost%100) - } - let hdr = " " + (0.. Bool { - return cell.x >= 0 && cell.x < w && cell.y >= 0 && cell.y < h - } - func isPath(_ cell: Coord) -> Bool { - return !walls.contains(cell) - } - func isWall(_ cell: Coord) -> Bool { - return walls.contains(cell) + return cell.x >= 0 && cell.x < w && cell.y >= 0 && cell.y < h && + !walls.contains(cell) } func neighbors(of cell: Coord) -> [Coord] { @@ -76,49 +22,20 @@ struct Maze : CustomStringConvertible { .map { Coord(x: cell.x + $0, y: cell.y + $1) }.filter(valid) } - /* - func cheats(from entryCell: Coord) -> [Coord: Int] { - var q = Queue<(Coord, Int)>(size: cheatMax*cheatMax*2*2) - q.push((entryCell, 0)) - var seen: Set = [entryCell] - var jumps: [Coord: Int] = [:] - while let (cell, dist) = q.pop() { - neighbors(of: cell).filter { !seen.contains($0) }.forEach { nb in - seen.insert(nb) - if isPath(nb) { - if dist + 1 <= cheatMax { - //print("\(cell) -> \(nb) . \(dist + 1)") - jumps[nb] = min(jumps[nb] ?? Int.max, dist + 1) - } - } else if dist < cheatMax { - //print("\(cell) -> \(nb) # \(dist + 1)") - q.push((nb, dist + 1)) - } - } - } - return jumps - } - */ func cheats(from entry: Coord) -> [Coord: Int] { - let jumpList: [(Int, Int)] = (2...cheatMax).flatMap { dist in - (0.. [(Int, Int)] in + (0.. [(Int, Int)] in + [(d, dist-d), (dist-d, -d), (-d, d-dist), (d-dist, d)] } } - let jumpCoords: [Coord] = jumpList - .map { dx, dy in Coord(x: entry.x + dx, y: entry.y + dy) } - .filter(valid).filter(isPath) - var res: [Coord: Int] = [:] - jumpCoords.forEach { end in - res[end] = abs(end.x - entry.x) + abs(end.y - entry.y) - } - return res + .map { dx, dy -> Coord in Coord(x: entry.x + dx, y: entry.y + dy) } + .filter(valid) + .map { end -> (Coord, Int) in (end, entry.dist(to: end)) } + ) } - func cheats() -> [(Coord, Coord, Int, Int)] { + func cheats() -> [Int] { var cost: [Coord: Int] = [start: 0] var jumps: [(Coord, Coord, Int)] = [] var cell = start @@ -131,16 +48,13 @@ struct Maze : CustomStringConvertible { .map { dest, dist in (cell, dest, dist) } jumps.append(contentsOf: newCheats) // add cost to table - let next = neighbors(of: cell) - .filter(isPath) - .filter { $0 != prev }[0] + let next = neighbors(of: cell).filter { $0 != prev }[0] currentCost += 1 cost[next] = currentCost prev = cell cell = next - //print("looking at \(cell) \(cost[cell]!)") } - return jumps.map { from, to, dist in (from, to, cost[to]! - cost[from]! - dist, dist) } + return jumps.map { from, to, dist in cost[to]! - cost[from]! - dist } } } @@ -168,19 +82,5 @@ func readInput(_ filePath: String, _ cheatMax: Int) throws -> Maze { } let maze = try readInput(CommandLine.arguments[1], Int(CommandLine.arguments[2])!) -print(maze) -// print(maze.cheats(from: maze.start)) -let cheats = maze.cheats().filter { _, _, saving, _ in saving > 0 } -let hist = cheats.reduce(into: [:]) { n, t in n[t.2, default: 0] += 1 } -hist.keys.sorted().forEach { print("\(hist[$0]!) - \($0)") } -print(cheats.filter { $0.2 >= 100 }.count) - -/* -cheats - .filter { _, _, saving, _ in saving == 72 } - .forEach { from, to, save, dist in - print("\(path[from]!) -> \(path[to]!) = \(dist)(\(save))") - } -print() -*/ +print(maze.cheats().filter { $0 >= 100 }.count)