|
| 1 | +import ArgumentParser |
| 2 | +import Foundation |
| 3 | + |
| 4 | +struct Day8: ParsableCommand { |
| 5 | + static let configuration = CommandConfiguration( |
| 6 | + abstract: "Day 8: Resonant Collinearity", |
| 7 | + subcommands: [Part1.self] |
| 8 | + ) |
| 9 | + |
| 10 | + struct Position: Hashable, Codable { |
| 11 | + let x: Int |
| 12 | + let y: Int |
| 13 | + } |
| 14 | + |
| 15 | + struct Map: Codable { |
| 16 | + let frequencies: [String: [Position]] |
| 17 | + let width: Int |
| 18 | + let height: Int |
| 19 | + |
| 20 | + init(contents: String) { |
| 21 | + let lines = contents.split(separator: "\n") |
| 22 | + self.width = lines[0].count |
| 23 | + self.height = lines.count |
| 24 | + var frequencies: [String: [Position]] = [:] |
| 25 | + for (yIndex, line) in lines.enumerated() { |
| 26 | + for (xIndex, char) in line.enumerated() where char != "." { |
| 27 | + frequencies[String(char), default: []].append(Position(x: xIndex, y: yIndex)) |
| 28 | + } |
| 29 | + } |
| 30 | + self.frequencies = frequencies |
| 31 | + } |
| 32 | + |
| 33 | + func antinodes() -> [String: Set<Position>] { |
| 34 | + var antinodes = [String: Set<Position>]() |
| 35 | + for frequency in frequencies.keys { |
| 36 | + let frequencyAntinodes = self.antinodes(for: frequency) |
| 37 | + let filtered = frequencyAntinodes.filter { $0.x >= 0 && $0.x < width && $0.y >= 0 && $0.y < height } |
| 38 | + antinodes[frequency] = Set(filtered) |
| 39 | + } |
| 40 | + return antinodes |
| 41 | + } |
| 42 | + |
| 43 | + func antinodes(for frequency: String) -> [Position] { |
| 44 | + let positions = frequencies[frequency]! |
| 45 | + var frequencyAntinodes = [Position]() |
| 46 | + for firstIndex in 0..<positions.count - 1 { |
| 47 | + for secondIndex in firstIndex+1..<positions.count { |
| 48 | + let antinodes = antinodes(for: positions[firstIndex], and: positions[secondIndex]) |
| 49 | + frequencyAntinodes.append(contentsOf: antinodes) |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + return frequencyAntinodes |
| 54 | + } |
| 55 | + |
| 56 | + func antinodes(for first: Position, and second: Position) -> [Position] { |
| 57 | + let (left, right) = first.x <= second.x ? (first, second) : (second, first) |
| 58 | + let rise = abs(right.y - left.y) |
| 59 | + let run = abs(right.x - left.x) |
| 60 | + switch run { |
| 61 | + case 0: |
| 62 | + fatalError("Vertical line") |
| 63 | + break |
| 64 | + default: |
| 65 | + let leftAntinode: Position |
| 66 | + let rightAntinode: Position |
| 67 | + if left.y > right.y { |
| 68 | + leftAntinode = Position(x: left.x - run, y: left.y + rise) |
| 69 | + rightAntinode = Position(x: right.x + run, y: right.y - rise) |
| 70 | + } else if left.y < right.y { |
| 71 | + leftAntinode = Position(x: left.x - run, y: left.y - rise) |
| 72 | + rightAntinode = Position(x: right.x + run, y: right.y + rise) |
| 73 | + } else { |
| 74 | + leftAntinode = Position(x: left.x - run, y: left.y) |
| 75 | + rightAntinode = Position(x: right.x + run, y: right.y) |
| 76 | + } |
| 77 | + return [leftAntinode, rightAntinode] |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + func printMapOfAntinodes(for frequency: String) { |
| 82 | + let antinodes = self.antinodes() |
| 83 | + let antinodesForFrequency = antinodes[frequency]! |
| 84 | + var map = Array(repeating: Array(repeating: ".", count: width), count: height) |
| 85 | + for antinode in antinodesForFrequency { |
| 86 | + map[antinode.y][antinode.x] = "X" |
| 87 | + } |
| 88 | + for frequencyPosition in frequencies[frequency]! { |
| 89 | + map[frequencyPosition.y][frequencyPosition.x] = frequency |
| 90 | + } |
| 91 | + print(map.map { $0.joined() }.joined(separator: "\n")) |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + struct Part1: ParsableCommand { |
| 96 | + @OptionGroup var options: CommonOptions |
| 97 | + |
| 98 | + mutating func run() throws { |
| 99 | + let contents = try String(contentsOf: options.input, encoding: .utf8) |
| 100 | + let map = Map(contents: contents) |
| 101 | + let antinodes = map.antinodes() |
| 102 | + print(antinodes.count) |
| 103 | + let antinodesSet = antinodes.reduce(into: Set<Position>()) { $0.formUnion($1.value) } |
| 104 | + print(antinodesSet.count) |
| 105 | + } |
| 106 | + } |
| 107 | +} |
0 commit comments