2021-12-03 19:48:34 +00:00
|
|
|
//
|
|
|
|
// File.swift
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Created by Max Nuding on 02.12.21.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
2021-12-05 10:43:02 +00:00
|
|
|
import Runner
|
2021-12-03 19:48:34 +00:00
|
|
|
|
2021-12-05 10:43:02 +00:00
|
|
|
struct Day03: Runnable {
|
2021-12-03 19:48:34 +00:00
|
|
|
let inputPath: String
|
|
|
|
|
|
|
|
func run() {
|
|
|
|
let input = try! String(contentsOfFile: inputPath)
|
|
|
|
let commands = input
|
|
|
|
.components(separatedBy: .newlines)
|
|
|
|
.filter { !$0.isEmpty }
|
|
|
|
runA(commands)
|
2021-12-03 20:50:50 +00:00
|
|
|
runB(commands)
|
2021-12-03 19:48:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func runA(_ commands: [String]) {
|
|
|
|
let neededForMajority = commands.count / 2
|
|
|
|
let lastBitIndex = commands.first!.count
|
|
|
|
var countsOnes = [Int](repeating: 0, count: lastBitIndex)
|
2021-12-03 20:50:50 +00:00
|
|
|
// There's probably some clever bitwise operation to solve this
|
2021-12-03 19:48:34 +00:00
|
|
|
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)
|
|
|
|
})
|
2021-12-03 20:50:50 +00:00
|
|
|
print(dec.episolon * dec.gamma)
|
2021-12-03 19:48:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func runB(_ commands: [String]) {
|
2021-12-03 20:50:50 +00:00
|
|
|
// Incredibly naive, iterates through the list many, many times
|
|
|
|
// Still runs plenty fast (~70ms on my machine)
|
|
|
|
// Too tired to optimize this
|
2021-12-03 19:48:34 +00:00
|
|
|
|
2021-12-03 20:50:50 +00:00
|
|
|
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
|
2021-12-03 19:48:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension Bool {
|
|
|
|
init?(digit: Character) {
|
|
|
|
switch digit {
|
|
|
|
case "0":
|
|
|
|
self = false
|
|
|
|
case "1":
|
|
|
|
self = true
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|