Skip to content

Commit

Permalink
Day 11
Browse files Browse the repository at this point in the history
  • Loading branch information
CraigSiemens committed Dec 17, 2023
1 parent 57b4e10 commit bc40056
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 25 deletions.
1 change: 1 addition & 0 deletions Sources/AdventOfCode2023/AdventOfCode2023.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public struct AdventOfCode2023: Year {
Day8(),
Day9(),
Day10(),
Day11(),
// {DAYS}
]

Expand Down
41 changes: 41 additions & 0 deletions Sources/AdventOfCode2023/Day11/Day11.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Algorithms
import Foundation
import Utilities

public struct Day11: Day {
public func part1Solution(for input: Input) -> Int {
solution(for: input, expansion: 2)
}

public func part2Solution(for input: Input) -> Int {
solution(for: input, expansion: 1000000)
}

func solution(for input: Input, expansion: Int) -> Int {
let grid = Grid(input.lines.characters) { _, input in
input == "." ? nil : ()
}

let range = grid.keys.pointRange()

var xSpace = IndexRangeSet(ranges: [range.x])
var ySpace = IndexRangeSet(ranges: [range.y])

for point in grid.keys {
xSpace.remove(point.x)
ySpace.remove(point.y)
}

return grid.keys
.sorted()
.combinations(ofCount: 2)
.map { combination in
let range = combination.pointRange()

return combination[0].manhattanDistance(to: combination[1])
+ xSpace.map { $0.overlaps(range.x) ? $0.count : 0 }.sum() * (expansion - 1)
+ ySpace.map { $0.overlaps(range.y) ? $0.count : 0 }.sum() * (expansion - 1)
}
.sum()
}
}
140 changes: 140 additions & 0 deletions Sources/AdventOfCode2023/Day11/input.txt

Large diffs are not rendered by default.

24 changes: 0 additions & 24 deletions Sources/AdventOfCode2023/Day5/Day5.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,27 +88,3 @@ public struct Day5: Day {
return minValue(for: seedRanges, almanac: almanac)
}
}

private extension ClosedRange where Bound == Int {
func offset(_ amount: Bound) -> Self {
(lowerBound + amount)...(upperBound + amount)
}

func split(by other: Self) -> (overlap: Self, lowerRemainder: Self?, upperRemainder: Self?)? {
guard overlaps(other) else { return nil }

let lowerRemainder = lowerBound < other.lowerBound
? lowerBound...other.lowerBound - 1
: nil

let upperRemainder = upperBound > other.upperBound
? other.upperBound + 1...upperBound
: nil

return (
clamped(to: other),
lowerRemainder,
upperRemainder
)
}
}
37 changes: 36 additions & 1 deletion Sources/Utilities/Data Structures/IndexRangeSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@ public struct IndexRangeSet {

public init() {}

public mutating func insert(_ range: ClosedRange<Int>) {
public init(ranges: [Element]) {
for range in ranges {
insert(range)
}
}

public mutating func insert(_ value: Element.Bound) {
insert(value...value)
}

public mutating func insert(_ range: Element) {
var range = range

for i in ranges.indices.reversed()
Expand All @@ -19,6 +29,31 @@ public struct IndexRangeSet {

ranges.append(range)
}

public mutating func remove(_ value: Element.Bound) {
remove(value...value)
}

public mutating func remove(_ range: Element) {
var newRanges: [Element] = []

for i in ranges.indices.reversed() {
guard let (_, lower, upper) = ranges[i].split(by: range)
else { continue}

if let lower {
newRanges.append(lower)
}

if let upper {
newRanges.append(upper)
}

ranges.remove(at: i)
}

ranges.append(contentsOf: newRanges)
}
}

extension IndexRangeSet: Collection {
Expand Down
7 changes: 7 additions & 0 deletions Sources/Utilities/Extensions/ClosedRange+Offset.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

public extension ClosedRange where Bound == Int {
func offset(_ amount: Bound) -> Self {
(lowerBound + amount)...(upperBound + amount)
}
}
26 changes: 26 additions & 0 deletions Sources/Utilities/Extensions/ClosedRange+Split.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Foundation

public extension ClosedRange where Bound == Int {
/// Splits a range based on the overlap with another range
/// - Parameter other: The other range to use for splitting.
/// - Returns: `nil` if the ranges don't overlap. Otherwise it returns a tuple containing the
/// range of the overlap and optionally the lower and/or upper remainders that are
/// outside the overlapping range.
func split(by other: Self) -> (overlap: Self, lowerRemainder: Self?, upperRemainder: Self?)? {
guard overlaps(other) else { return nil }

let lowerRemainder = lowerBound < other.lowerBound
? lowerBound...other.lowerBound - 1
: nil

let upperRemainder = upperBound > other.upperBound
? other.upperBound + 1...upperBound
: nil

return (
clamped(to: other),
lowerRemainder,
upperRemainder
)
}
}
13 changes: 13 additions & 0 deletions Sources/Utilities/Geometry/Point.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,19 @@ extension Collection {
}
}

// MARK: - Ranges
extension Collection where Element == Point {
public func pointRange() -> (x: ClosedRange<Int>, y: ClosedRange<Int>) {
let extremes = extremes()

return (
extremes.min.x...extremes.max.x,
extremes.min.y...extremes.max.y
)
}
}

// MARK: - Grid String
extension Set where Element == Point {
public func gridString(point: String = "#", space: String = " ") -> String {
let (min, max) = extremes()
Expand Down
31 changes: 31 additions & 0 deletions Tests/AdventOfCode2023Tests/Day11Tests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Utilities
import XCTest
@testable import AdventOfCode2023

final class Day11Tests: XCTestCase {
let day = Day11()

let input: Input = """
...#......
.......#..
#.........
..........
......#...
.#........
.........#
..........
.......#..
#...#.....
"""

func testPart1() {
XCTAssertEqual(day.part1Solution(for: input), 374)
XCTAssertEqual(day.part1Solution(), 9639160)
}

func testPart2() {
XCTAssertEqual(day.solution(for: input, expansion: 10), 1030)
XCTAssertEqual(day.solution(for: input, expansion: 100), 8410)
XCTAssertEqual(day.part2Solution(), 752936133304)
}
}

0 comments on commit bc40056

Please sign in to comment.