d15
This commit is contained in:
114
day15/d15p2.swift
Normal file
114
day15/d15p2.swift
Normal file
@@ -0,0 +1,114 @@
|
||||
import Foundation
|
||||
|
||||
enum Move: Character, CustomStringConvertible {
|
||||
case n = "^"
|
||||
case s = "v"
|
||||
case e = ">"
|
||||
case w = "<"
|
||||
var description: String { return String(self.rawValue) }
|
||||
var delta: (Int, Int) {
|
||||
switch self {
|
||||
case .n: return (0, -1)
|
||||
case .s: return (0, 1)
|
||||
case .e: return (1, 0)
|
||||
case .w: return (-1, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Item: Hashable, CustomStringConvertible {
|
||||
let (x, y): (Int, Int)
|
||||
var description: String { return "(\(x), \(y))" }
|
||||
var gps: Int { return x + 100*y }
|
||||
func move(_ m: Move) -> Item {
|
||||
let (dx, dy) = m.delta
|
||||
return Item(x: x + dx, y: y + dy)
|
||||
}
|
||||
}
|
||||
|
||||
struct Boxes {
|
||||
var boxes: Set<Item> = []
|
||||
func at(x: Int, y: Int) -> Item? {
|
||||
if boxes.contains(Item(x: x, y: y)) { return Item(x: x, y: y) }
|
||||
if boxes.contains(Item(x: x-1, y: y)) { return Item(x: x-1, y: y) }
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
struct Map: CustomStringConvertible {
|
||||
let walls: Set<Item>
|
||||
var boxes: Boxes = Boxes()
|
||||
var bot = Item(x: 0, y: 0)
|
||||
var (w, h): (Int, Int)
|
||||
var description: String {
|
||||
var s: [[String]] = Array(
|
||||
repeating: Array(repeating: " ", count: w*2), count: h
|
||||
)
|
||||
walls.forEach { w in s[w.y][w.x] = "██" }
|
||||
boxes.boxes.forEach { b in s[b.y][b.x] = "🮇🮀"; s[b.y][b.x+1] = "🮀▌" }
|
||||
s[bot.y][bot.x] = "🮕🮕"
|
||||
return s.map { row in row.joined() }.joined(separator: "\n")
|
||||
}
|
||||
var gps: Int { return boxes.boxes.reduce(0) { s, box in s + box.gps } }
|
||||
|
||||
init(from s: ArraySlice<String.SubSequence>) {
|
||||
var walls: Set<Item> = []
|
||||
h = s.count
|
||||
w = s[0].count
|
||||
for (y, line) in s.enumerated() {
|
||||
for (x0, char) in line.enumerated() {
|
||||
let x = 2*x0
|
||||
switch char {
|
||||
case "#": walls.formUnion([Item(x: x, y: y), Item(x: x+1, y: y)])
|
||||
case "O": boxes.boxes.insert(Item(x: x, y: y))
|
||||
case "@": bot = Item(x: x, y: y)
|
||||
default: ()
|
||||
}
|
||||
}
|
||||
}
|
||||
self.walls = walls
|
||||
}
|
||||
|
||||
mutating func move(_ dir: Move) {
|
||||
var pushes: Set<Item> = []
|
||||
var nexts: Set<Item> = [bot.move(dir)]
|
||||
while !nexts.isEmpty {
|
||||
let next = nexts.removeFirst()
|
||||
if let box = boxes.at(x: next.x, y: next.y) {
|
||||
pushes.insert(box)
|
||||
let movedBox = box.move(dir)
|
||||
switch dir {
|
||||
case Move.w: nexts.insert(movedBox)
|
||||
case Move.e: nexts.insert(Item(x: movedBox.x+1, y: movedBox.y))
|
||||
case Move.n, Move.s:
|
||||
nexts.insert(movedBox)
|
||||
nexts.insert(Item(x: movedBox.x+1, y: movedBox.y))
|
||||
}
|
||||
continue
|
||||
}
|
||||
if walls.contains(next) {
|
||||
return
|
||||
}
|
||||
}
|
||||
pushes.forEach { boxes.boxes.remove($0) }
|
||||
pushes.forEach { boxes.boxes.insert($0.move(dir)) }
|
||||
bot = bot.move(dir)
|
||||
}
|
||||
}
|
||||
|
||||
func readInput(_ filePath: String) throws -> (Map, [Move]) {
|
||||
let content = try String(contentsOfFile: filePath, encoding: .ascii)
|
||||
let lines = content.split(separator: "\n", omittingEmptySubsequences: false)
|
||||
let blankIdx = lines.firstIndex(of: "")!
|
||||
let moves = lines.suffix(from: blankIdx + 1).joined().map { Move(rawValue: $0)! }
|
||||
return (Map(from: lines.prefix(blankIdx)), moves)
|
||||
}
|
||||
|
||||
var (map, moves) = try readInput(CommandLine.arguments[1])
|
||||
print(map)
|
||||
print(moves)
|
||||
for move in moves {
|
||||
map.move(move)
|
||||
}
|
||||
print(map)
|
||||
print(map.gps)
|
Reference in New Issue
Block a user