193 lines
5.3 KiB
Swift
193 lines
5.3 KiB
Swift
//
|
|
// File.swift
|
|
//
|
|
//
|
|
// Created by Max Nuding on 15.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 }
|
|
|
|
func getTotalVersion() -> Int
|
|
func getValue() -> Int
|
|
}
|
|
|
|
struct ValuePacket: Packet {
|
|
let version: Int
|
|
let value: Int
|
|
|
|
func getTotalVersion() -> Int { version }
|
|
func getValue() -> Int { value }
|
|
}
|
|
|
|
struct OperatorPacket: Packet {
|
|
let type: OperatorType
|
|
let version: Int
|
|
let subPackets: [Packet]
|
|
|
|
func getTotalVersion() -> Int { version + subPackets.map { $0.getTotalVersion() }.reduce(0, +) }
|
|
func getValue() -> Int {
|
|
let spValues = subPackets.map { $0.getValue() }
|
|
switch 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:
|
|
fatalError()
|
|
}
|
|
}
|
|
}
|
|
|
|
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.bits().joined()
|
|
let packets = parsePacket(bytes: bytes)
|
|
let p: Packet = packets.0
|
|
print(p.getTotalVersion())
|
|
print(p.getValue())
|
|
}
|
|
|
|
func parsePacket<S>(bytes: S) -> (Packet, S.SubSequence) where S:StringProtocol {
|
|
let version = bytes[bytes.startIndex..<bytes.index(bytes.startIndex, offsetBy: 3)].binaryToDecimal()
|
|
let typeId = bytes[bytes.index(bytes.startIndex, offsetBy: 3)..<bytes.index(bytes.startIndex, offsetBy: 6)].binaryToDecimal()
|
|
var value: Packet
|
|
var reminder: S.SubSequence
|
|
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<S>(bytes: S) -> ([Packet], S.SubSequence) where S:StringProtocol {
|
|
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]
|
|
|
|
var number = nextBits.binaryToDecimal()
|
|
var reminder = bytes[endNextBitsIndex...]
|
|
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<S>(bytes: S) -> (Int, S.SubSequence) where S:StringProtocol {
|
|
var index = bytes.index(bytes.startIndex, offsetBy: 6)
|
|
var ei = index
|
|
var bits: Substring = ""
|
|
var reminder: S.SubSequence = ""
|
|
while true {
|
|
let si = bytes.index(index, offsetBy: 1)
|
|
ei = bytes.index(si, offsetBy: 4)
|
|
bits += bytes[si..<ei]
|
|
if bytes[index] == "0" {
|
|
reminder = bytes[ei...]
|
|
break
|
|
}
|
|
index = bytes.index(ei, offsetBy: 0)
|
|
}
|
|
return (bits.binaryToDecimal(), reminder)
|
|
}
|
|
}
|
|
|
|
extension StringProtocol {
|
|
func bits() -> [String] {
|
|
self.map { $0.bits }
|
|
}
|
|
|
|
func binaryToDecimal() -> Int { Int(self, radix: 2)! }
|
|
}
|
|
|
|
extension Character {
|
|
var bits: String {
|
|
switch self.hexDigitValue! {
|
|
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 10:
|
|
return "1010"
|
|
case 11:
|
|
return "1011"
|
|
case 12:
|
|
return "1100"
|
|
case 13:
|
|
return "1101"
|
|
case 14:
|
|
return "1110"
|
|
case 15:
|
|
return "1111"
|
|
default:
|
|
fatalError()
|
|
}
|
|
}
|
|
}
|