aoc2021/Sources/16/16.swift
2021-12-16 08:07:37 +01:00

208 lines
5.8 KiB
Swift

//
// File.swift
//
//
// Created by Max Nuding on 15s.12.21.
//
import Foundation
import Runner
import Collections
enum OperatorType: Int {
case sum = 0
case product = 1
case minimum = 2
case maximum = 3
case value = 4
case greaterThan = 5
case lessThan = 6
case equal = 7
}
protocol Packet {
var version: Int { get }
var type: OperatorType { get }
}
struct ValuePacket: Packet {
let type: OperatorType = .value
let version: Int
let value: Int
}
struct OperatorPacket: Packet {
let type: OperatorType
let version: Int
let subPackets: [Packet]
}
class Day16: Runnable {
let inputPath: String
required init(inputPath: String) {
self.inputPath = inputPath
}
public func run() {
let input = try! String(contentsOfFile: inputPath)
let parts = input
.trimmingCharacters(in: .newlines)
let bytes = parts.ToBinary().joined()
let packets = parsePacket(bytes: bytes)
let p: Packet = packets.0
let totalVersion = getPacketVersion(p: p)
print(totalVersion)
print(getPacketValue(p: p))
}
func getPacketVersion(p: Packet) -> Int {
//print("Found packet: \(p)")
if let packet = p as? OperatorPacket {
return packet.version + packet.subPackets.map(getPacketVersion).reduce(0, +)
} else {
return p.version
}
}
func getPacketValue(p: Packet) -> Int {
var spValues = [Int]()
if let packet = p as? OperatorPacket {
spValues = packet.subPackets.map(getPacketValue)
} else if let packet = p as? ValuePacket {
spValues = [packet.value]
} else {
fatalError()
}
switch p.type {
case .sum:
return spValues.reduce(0, +)
case .product:
return spValues.reduce(1, *)
case .minimum:
return spValues.min()!
case .maximum:
return spValues.max()!
case .greaterThan:
return spValues.first! > spValues.last! ? 1 : 0
case .lessThan:
return spValues.first! < spValues.last! ? 1 : 0
case .equal:
return spValues.first! == spValues.last! ? 1 : 0
case .value:
return spValues.first!
}
}
func parsePacket(bytes: String) -> (Packet, String) {
let version = bytes[bytes.startIndex..<bytes.index(bytes.startIndex, offsetBy: 3)].description.binaryToDecimal()
let typeId = bytes[bytes.index(bytes.startIndex, offsetBy: 3)..<bytes.index(bytes.startIndex, offsetBy: 6)].description.binaryToDecimal()
var value: Packet
var reminder: String
switch typeId {
case 4:
let (v, r) = parseLiteralPacketValue(bytes: bytes)
value = ValuePacket(version: version, value: v)
reminder = r
default:
let (v, r) = parseOperatorPacker(bytes: bytes)
value = OperatorPacket(type: OperatorType(rawValue: typeId)!, version: version, subPackets: v)
reminder = r
}
return (value, reminder)
}
func parseOperatorPacker(bytes: String) -> ([Packet], String) {
let startNextBitsIndex = bytes.index(bytes.startIndex, offsetBy: 7)
let lengthTypeId = bytes[bytes.index(bytes.startIndex, offsetBy: 6)..<startNextBitsIndex].description
let isTotalLength = lengthTypeId == "0"
let nextBitsLength = isTotalLength ? 15 : 11
let endNextBitsIndex = bytes.index(startNextBitsIndex, offsetBy: nextBitsLength)
let nextBits = bytes[startNextBitsIndex..<endNextBitsIndex].description
var number = nextBits.binaryToDecimal()
var reminder = bytes[endNextBitsIndex...].description
var subPackets = [Packet]()
while number > 0 {
let (p, rem) = parsePacket(bytes: reminder)
number -= isTotalLength ? (reminder.count - rem.count) : 1
subPackets.append(p)
reminder = rem
}
return (subPackets, reminder)
}
func parseLiteralPacketValue(bytes: String) -> (Int, String) {
var index = bytes.index(bytes.startIndex, offsetBy: 6)
var ei = index
var bits = ""
var reminder = ""
while true {
let si = bytes.index(index, offsetBy: 1)
ei = bytes.index(si, offsetBy: 4)
bits += bytes[si..<ei].description
if bytes[index] == "0" {
reminder = bytes[ei...].description
break
}
index = bytes.index(ei, offsetBy: 0)
}
return (bits.binaryToDecimal(), reminder)
}
}
extension String {
func ToBinary() -> [String] {
self.map {$0.toBinary()!}
}
func binaryToDecimal() -> Int {
self.reversed().enumerated().reduce(0, { p, cur in
p + (cur.element == "0" ? 0 : Int(pow(2.0, Double(cur.offset))))
})
}
}
extension Character {
func toBinary() -> String? {
switch self {
case "0":
return "0000"
case "1":
return "0001"
case "2":
return "0010"
case "3":
return "0011"
case "4":
return "0100"
case "5":
return "0101"
case "6":
return "0110"
case "7":
return "0111"
case "8":
return "1000"
case "9":
return "1001"
case "A":
return "1010"
case "B":
return "1011"
case "C":
return "1100"
case "D":
return "1101"
case "E":
return "1110"
case "F":
return "1111"
default:
return nil}
}
}