130 lines
2.9 KiB
Swift
130 lines
2.9 KiB
Swift
|
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)
|