2022 Day 14
This commit is contained in:
parent
da7a3946d4
commit
c47dee812e
1 changed files with 155 additions and 0 deletions
155
2022/day14.swift
Normal file
155
2022/day14.swift
Normal file
|
@ -0,0 +1,155 @@
|
|||
import Foundation
|
||||
import Darwin
|
||||
|
||||
|
||||
struct Point: Hashable {
|
||||
var x: Int
|
||||
var y: Int
|
||||
}
|
||||
|
||||
extension Scanner {
|
||||
func point() -> Point? {
|
||||
guard let x = scanInt(), scanString(",") != nil, let y = scanInt() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Point(x: x, y: y)
|
||||
}
|
||||
|
||||
func line() -> [Point] {
|
||||
var result: [Point] = []
|
||||
while let point = point() {
|
||||
result.append(point)
|
||||
guard scanString("->") != nil else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
let input = try String(contentsOf: URL(fileURLWithPath: "day14.input"))
|
||||
let scanner = Scanner(string: input)
|
||||
|
||||
var map: Set<Point> = []
|
||||
|
||||
func range(_ a: Int, _ b: Int) -> ClosedRange<Int> {
|
||||
min(a, b)...max(a, b)
|
||||
}
|
||||
|
||||
var maxY = 0
|
||||
var minY = Int.max
|
||||
var maxX = 0
|
||||
var minX = Int.max
|
||||
|
||||
while !scanner.isAtEnd {
|
||||
let line = scanner.line()
|
||||
var current = line[0]
|
||||
for point in line.dropFirst() {
|
||||
if current.x == point.x {
|
||||
for y in range(current.y, point.y) {
|
||||
map.insert(Point(x: current.x, y: y))
|
||||
if y > maxY {
|
||||
maxY = y
|
||||
}
|
||||
if y < minY {
|
||||
minY = y
|
||||
}
|
||||
}
|
||||
maxX = max(current.x, maxX)
|
||||
minX = min(current.x, minX)
|
||||
} else if current.y == point.y {
|
||||
for x in range(current.x, point.x) {
|
||||
map.insert(Point(x: x, y: current.y))
|
||||
if x > maxX {
|
||||
maxX = x
|
||||
}
|
||||
if x < minX {
|
||||
minX = x
|
||||
}
|
||||
}
|
||||
maxY = max(current.y, maxY)
|
||||
minY = min(current.y, minY)
|
||||
} else {
|
||||
fatalError("Not horizontal/vertical")
|
||||
}
|
||||
current = point
|
||||
}
|
||||
}
|
||||
let walls = map
|
||||
|
||||
func blocked(_ point: Point) -> Bool {
|
||||
if map.contains(point) {
|
||||
return true
|
||||
}
|
||||
|
||||
return point.y >= maxY + 2
|
||||
}
|
||||
|
||||
extension Point {
|
||||
func down() -> Point? {
|
||||
var copy = self
|
||||
copy.y += 1
|
||||
if !blocked(copy) {
|
||||
return copy
|
||||
}
|
||||
|
||||
copy.x -= 1
|
||||
if !blocked(copy) {
|
||||
return copy
|
||||
}
|
||||
|
||||
copy.x = x + 1
|
||||
if !blocked(copy) {
|
||||
return copy
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
let inlet = Point(x: 500, y: 0)
|
||||
var part1: Int? = nil
|
||||
|
||||
func simulate() {
|
||||
var position = inlet
|
||||
|
||||
while let next = position.down() {
|
||||
position = next
|
||||
if position.y >= maxY, part1 == nil {
|
||||
part1 = count
|
||||
}
|
||||
}
|
||||
|
||||
map.insert(position)
|
||||
}
|
||||
|
||||
|
||||
var count = 0
|
||||
while !map.contains(inlet) {
|
||||
simulate()
|
||||
count += 1
|
||||
show()
|
||||
usleep(3000)
|
||||
}
|
||||
|
||||
print("Part 2:", count)
|
||||
|
||||
func show() {
|
||||
var line = "\u{1b}[2J\u{1b}[H"
|
||||
for y in 0...(maxY + 2) {
|
||||
for x in (minX-30)...(maxX+30) {
|
||||
let point = Point(x:x, y:y)
|
||||
let isWall = walls.contains(point) || y >= maxY + 2
|
||||
let sand = map.contains(point)
|
||||
line += isWall ? "#" : sand ? "o" : " "
|
||||
}
|
||||
line += "\n"
|
||||
}
|
||||
line += "\n"
|
||||
if let part1 {
|
||||
line += "Part 1: \(part1)\n"
|
||||
}
|
||||
var data = Array(line.utf8)
|
||||
write(STDOUT_FILENO, &data, data.count)
|
||||
}
|
Loading…
Add table
Reference in a new issue