// // File.swift // // // Created by Max Nuding on 02.12.21. // import Foundation import Runner struct Day03: Runnable { let inputPath: String func run() { let input = try! String(contentsOfFile: inputPath) let commands = input .components(separatedBy: .newlines) .filter { !$0.isEmpty } runA(commands) runB(commands) } func runA(_ commands: [String]) { let neededForMajority = commands.count / 2 let lastBitIndex = commands.first!.count var countsOnes = [Int](repeating: 0, count: lastBitIndex) // There's probably some clever bitwise operation to solve this for command in commands { let m = command.map { Bool.init(digit: $0)! }.map { $0 ? 1 : 0 } countsOnes.enumerated().forEach { (idx, elem) in countsOnes[idx] += m[idx] } } let dec = countsOnes.indices.reduce((gamma: 0, episolon: 0), { partialResult, idx in let isOne = countsOnes[idx] > neededForMajority let val = Int(truncating: NSDecimalNumber(decimal: pow(2, countsOnes.count - idx - 1))) var (gamma, episolon) = partialResult if isOne { gamma += val } else { episolon += val } return (gamma, episolon) }) print(dec.episolon * dec.gamma) } func runB(_ commands: [String]) { // Incredibly naive, iterates through the list many, many times // Still runs plenty fast (~70ms on my machine) // Too tired to optimize this let lastBitIndex = commands.first!.count var countsOnes = [Int](repeating: 0, count: lastBitIndex) for command in commands { let m = command.map { Bool.init(digit: $0)! }.map { $0 ? 1 : 0 } countsOnes.enumerated().forEach { (idx, elem) in countsOnes[idx] += m[idx] } } var oxygenCommands = commands var scrubberCommands = commands for pos in 0..= neededForMajorityOxygen let digit = Bool(digit: command[idx]) let keepOxygen = digit == isMostCommon if !keepOxygen, oxygenCommands.count > 1, let coi = oxygenCommands.firstIndex(of: command){ oxygenCommands.remove(at: coi) } } let neededForMajorityScrubber = Int(ceil(Double(scrubberCommands.count) / 2.0)) for command in scrubberCommands { let idx = command.index(command.startIndex, offsetBy: pos) let isLeastCommon = countsOnesScrubber[pos] < neededForMajorityScrubber let digit = Bool(digit: command[idx]) let keepScrubber = digit == isLeastCommon if !keepScrubber, scrubberCommands.count > 1, let ci = scrubberCommands.firstIndex(of: command) { scrubberCommands.remove(at: ci) } } } let finalOxygenNumber = oxygenCommands.first! let finalScruberNumber = scrubberCommands.first! print(ToDecimal(num: finalOxygenNumber) * ToDecimal(num: finalScruberNumber)) } private func ToDecimal(num: String) -> Int { var pot = -1 var val = 0 for c in num.reversed() { pot += 1 if c == "0" { continue } val += Int(truncating: NSDecimalNumber(decimal: pow(2, pot))) } return val } } extension Bool { init?(digit: Character) { switch digit { case "0": self = false case "1": self = true default: return nil } } }