aoc2021/Sources/03/03.swift
2021-12-05 11:43:49 +01:00

135 lines
4.7 KiB
Swift

//
// 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..<lastBitIndex {
var countsOnesOxygen = [Int](repeating: 0, count: lastBitIndex)
for command in oxygenCommands {
let m = command.map { Bool.init(digit: $0)! }.map { $0 ? 1 : 0 }
countsOnesOxygen.enumerated().forEach { (idx, elem) in
countsOnesOxygen[idx] += m[idx]
}
}
var countsOnesScrubber = [Int](repeating: 0, count: lastBitIndex)
for command in scrubberCommands {
let m = command.map { Bool.init(digit: $0)! }.map { $0 ? 1 : 0 }
countsOnesScrubber.enumerated().forEach { (idx, elem) in
countsOnesScrubber[idx] += m[idx]
}
}
let neededForMajorityOxygen = Int(ceil(Double(oxygenCommands.count) / 2.0))
for command in oxygenCommands {
let idx = command.index(command.startIndex, offsetBy: pos)
let isMostCommon = countsOnesOxygen[pos] >= 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
}
}
}