Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 2015/Sources/AdventOfCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ let allChallenges: [any AdventDay] = [
Day15(),
Day16(),
Day17(),
Day18(),
]

@main
Expand Down
95 changes: 95 additions & 0 deletions 2015/Sources/Day18.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import Foundation

struct Day18: AdventDay {
let data: String
let steps: Int
let width: Int
let height: Int

private var xRange: Range<Int> { 0..<width }
private var yRange: Range<Int> { 0..<height }
private var coordinates: [(Int, Int)] {
yRange.flatMap { y in
xRange.map { x in (x, y)
}
}
}
private var corners: [(Int, Int)] {
[
(0, 0),
(0, height - 1),
(width - 1, 0),
(width - 1, height - 1),
]
}

private var grid: [[Bool]] {
data.split(separator: /\n/).map {
$0.map {
$0 == "#"
}
}
}

init(data: String) {
self.init(data: data, steps: 100, width: 100, height: 100)
}

init(data: String, steps: Int, width: Int, height: Int) {
self.data = data
self.steps = steps
self.width = width
self.height = height
}

private func solve(_ initialState: [[Bool]], coordinates: [(Int, Int)]) -> Int {
var state = initialState
var nextState = state

for _ in 0..<steps {
for (x, y) in coordinates {
let isOn = state[y][x]
let neighbours = neighbours(x, y)

switch neighbours.count(where: { x, y in state[y][x] }) {
case 2 where isOn:
nextState[y][x] = true
case 3:
nextState[y][x] = true
default:
nextState[y][x] = false
}
}

state = nextState
}

return state.reduce(0) { count, row in
count + row.count { $0 }
}
}

func part1() -> Int {
solve(grid, coordinates: coordinates)
}

func part2() -> Int {
var state = grid
for (x, y) in corners {
state[y][x] = true
}
return solve(state, coordinates: coordinates.filter({ x, y in !isCorner(x, y) }))
}

private func isCorner(_ x: Int, _ y: Int) -> Bool {
corners.contains(where: { $0 == (x, y) })
}

private func neighbours(_ x: Int, _ y: Int) -> [(Int, Int)] {
Array(stride(from: y - 1, through: y + 1, by: 1)).flatMap { r in
Array(stride(from: x - 1, through: x + 1, by: 1)).map { c in
(c, r)
}.filter { (c, r) in (c, r) != (x, y) && xRange ~= c && yRange ~= r }
}
}
}
29 changes: 29 additions & 0 deletions 2015/Tests/Day18Tests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Testing

@testable import AoC2015

struct Day18Tests {
private let testData = """
.#.#.#
...##.
#....#
..#...
#.#..#
####..
""".replacingOccurrences(of: " ", with: "")

@Test func testPart1() {
#expect(Day18(data: testData, steps: 1, width: 6, height: 6).part1() == 11)
#expect(Day18(data: testData, steps: 2, width: 6, height: 6).part1() == 8)
#expect(Day18(data: testData, steps: 3, width: 6, height: 6).part1() == 4)
#expect(Day18(data: testData, steps: 4, width: 6, height: 6).part1() == 4)
}

@Test func testPart2() {
#expect(Day18(data: testData, steps: 1, width: 6, height: 6).part2() == 18)
#expect(Day18(data: testData, steps: 2, width: 6, height: 6).part2() == 18)
#expect(Day18(data: testData, steps: 3, width: 6, height: 6).part2() == 18)
#expect(Day18(data: testData, steps: 4, width: 6, height: 6).part2() == 14)
#expect(Day18(data: testData, steps: 5, width: 6, height: 6).part2() == 17)
}
}