185 lines
5.0 KiB
Swift
185 lines
5.0 KiB
Swift
|
//
|
||
|
// File.swift
|
||
|
//
|
||
|
//
|
||
|
// Created by Max Nuding on 15s.12.21.
|
||
|
//
|
||
|
|
||
|
import Foundation
|
||
|
import Runner
|
||
|
import Collections
|
||
|
|
||
|
enum PacketType: String {
|
||
|
case value
|
||
|
case op
|
||
|
|
||
|
init(rawValue: String) {
|
||
|
switch rawValue {
|
||
|
case "100":
|
||
|
self = .value
|
||
|
default:
|
||
|
self = .op
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protocol Packet {
|
||
|
//var type: PacketType { get }
|
||
|
var version: Int { get }
|
||
|
}
|
||
|
|
||
|
struct ValuePacket: Packet {
|
||
|
//let type: PacketType
|
||
|
let version: Int
|
||
|
let value: Int
|
||
|
}
|
||
|
|
||
|
struct OperatorPacket: Packet {
|
||
|
//let type: PacketType
|
||
|
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 input = "620080001611562C8802118E34"
|
||
|
//let input = "D2FE28"
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
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 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
|
||
|
var value: Packet
|
||
|
var reminder: String
|
||
|
switch typeId {
|
||
|
case "100":
|
||
|
let (v, r) = parseLiteralPacketValue(bytes: bytes)
|
||
|
value = ValuePacket(version: version, value: v)
|
||
|
reminder = r
|
||
|
//case "110":
|
||
|
// break
|
||
|
default:
|
||
|
//fatalError("Unknown packet \(typeId)")
|
||
|
let (v, r) = parseOperatorPacker(bytes: bytes)
|
||
|
value = OperatorPacket(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}
|
||
|
}
|
||
|
}
|