AoC/2020/day24/main.swift

130 lines
2.9 KiB
Swift
Raw Normal View History

2020-12-24 11:47:12 +01:00
import Foundation
let input = loadData(day: 24)
let scanner = Scanner(string: input)
scanner.charactersToBeSkipped = nil
enum Direction: String, CaseIterable {
case east = "e"
case southEast = "se"
case southWest = "sw"
case west = "w"
case northWest = "nw"
case northEast = "ne"
}
extension Scanner {
func readDirection() -> Direction? {
for dir in Direction.allCases {
if string(dir.rawValue) {
return dir
}
}
return nil
}
}
struct Coord: Hashable {
var south: Int
var east: Int
static let zero = Coord(south: 0, east: 0)
mutating func move(_ direction: Direction) {
switch direction {
case .east:
east += 1
case .southEast:
south += 1
case .southWest:
south += 1
east -= 1
case .west:
east -= 1
case .northWest:
south -= 1
case .northEast:
south -= 1
east += 1
}
}
func neighbor(_ direction: Direction) -> Coord {
var result = self
result.move(direction)
return result
}
var neighbors: [Coord] {
Direction.allCases.map(neighbor)
}
}
func readCoordinate() -> Coord {
var current = Coord.zero
while let direction = scanner.readDirection() {
current.move(direction)
}
precondition(scanner.string("\n"))
return current
}
var floor: [Coord: Bool] = [:]
var northWestCorner = Coord.zero
var southEastCorner = Coord.zero
func expandRange(toInclude coord: Coord) {
northWestCorner.east = min(northWestCorner.east, coord.east)
northWestCorner.south = min(northWestCorner.south, coord.south)
southEastCorner.east = max(southEastCorner.east, coord.east)
southEastCorner.south = max(southEastCorner.south, coord.south)
}
while !scanner.isAtEnd {
let coord = readCoordinate()
expandRange(toInclude: coord)
floor[coord, default: false].toggle()
}
print("part 1:", floor.values.lazy.filter { $0 }.count)
func step() -> [Coord: Bool] {
var result = floor
for east in (northWestCorner.east - 1)...(southEastCorner.east + 1) {
for south in (northWestCorner.south - 1)...(southEastCorner.south + 1) {
let coord = Coord(south: south, east: east)
let isBlack = floor[coord, default: false]
let blackNeighbors = coord.neighbors
.lazy
.map { floor[$0, default: false] }
.filter { $0 }
.count
if isBlack && (blackNeighbors == 0 || blackNeighbors > 2) {
result[coord] = false
} else if !isBlack && blackNeighbors == 2 {
result[coord] = true
expandRange(toInclude: coord)
}
}
}
return result
}
for _ in 0..<100 {
floor = step()
}
print("part 2:", floor.values.lazy.filter { $0 }.count)