97 lines
2.9 KiB
Swift
97 lines
2.9 KiB
Swift
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 Map: CustomStringConvertible {
|
|
let walls: Set<Item>
|
|
var boxes: Set<Item> = []
|
|
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.forEach { b in s[b.y][b.x] = "🬴🬸" }
|
|
s[bot.y][bot.x] = "\u{001B}[91m🮿 \u{001B}[0m"
|
|
return (" " + (0..<w).map { " \($0 % 10)" }.joined() + "\n") +
|
|
s.enumerated().map {
|
|
i, row in String(format: "%3d ", i) + row.joined()
|
|
}.joined(separator: "\n")
|
|
}
|
|
var gps: Int { return 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 (x, char) in line.enumerated() {
|
|
switch char {
|
|
case "#": walls.insert(Item(x: x, y: y))
|
|
case "O": 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: [Item] = []
|
|
var next = bot.move(dir)
|
|
while boxes.contains(next) {
|
|
pushes.append(next)
|
|
next = next.move(dir)
|
|
}
|
|
if !walls.contains(next) {
|
|
pushes.forEach { boxes.remove($0) }
|
|
pushes.forEach { 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 {
|
|
//print(move)
|
|
map.move(move)
|
|
}
|
|
print(map)
|
|
print(map.gps)
|