diff --git a/functions/src/day16.ts b/functions/src/day16.ts new file mode 100644 index 0000000..47f9065 --- /dev/null +++ b/functions/src/day16.ts @@ -0,0 +1,150 @@ +import Day from "./day"; +import Utils from "./utils"; + +class Day16 implements Day { + + part1(input: string[]): number | string { + + const packet = Packet.parse(input[0]); + + return Day16.recursiveSumVersionNumbers(packet); + } + + part2(input: string[]): number | string { + + return Packet.parse(input[0]).calculateValue().toString(); + } + + static recursiveSumVersionNumbers(packet: Packet): number { + + if (typeof packet.content === "bigint") { + return packet.version; + } else { + return packet.version + Utils.sum(packet.content.map(sp => Day16.recursiveSumVersionNumbers(sp))); + } + + } + +} + +class Packet { + + static readonly SUM_TYPE = 0 + static readonly PROD_TYPE = 1; + static readonly MIN_TYPE = 2; + static readonly MAX_TYPE = 3; + static readonly LITERAL_TYPE = 4; + static readonly GT_TYPE = 5; + static readonly LT_TYPE = 6; + static readonly EQ_TYPE = 7; + + static readonly VERSION_LENGTH = 3; + static readonly TYPE_LENGTH = 3; + static readonly LENGTH_TYPE_LENGTH = 1; + static readonly SUBPACKET_SIZE_LENGTH = 15; + static readonly SUBPACKET_COUNT_LENGTH = 11; + static readonly LITERAL_PART_LENGTH = 5; + + version: number; + type: number; + + content: Packet[] | bigint + binaryLength: number; + + constructor(version: number, type: number, content: Packet[] | bigint, binaryLength: number) { + this.version = version; + this.type = type; + this.content = content; + this.binaryLength = binaryLength; + } + + calculateValue(): bigint { + + if (typeof this.content === "bigint") return this.content; + + const subValues = this.content.map(c => c.calculateValue()); + + if (this.type === Packet.SUM_TYPE) { + return Utils.bigSum(subValues) + } else if (this.type === Packet.PROD_TYPE) { + return subValues.reduce((a, b) => a * b, 1n); + } else if (this.type === Packet.MIN_TYPE) { + return Utils.bigMin(subValues); + } else if (this.type === Packet.MAX_TYPE) { + return Utils.bigMax(subValues); + } else if (this.type === Packet.GT_TYPE) { + return subValues[0] > subValues[1] ? 1n : 0n; + } else if (this.type === Packet.LT_TYPE) { + return subValues[0] < subValues[1] ? 1n : 0n; + } else if (this.type === Packet.EQ_TYPE) { + return subValues[0] === subValues[1] ? 1n : 0n; + } else { + // Shouldn't happen + throw Error("Unsupported packet type: " + this.type); + } + } + + static parse(input: string): Packet { + return this.parseBinaryInput(Packet.toBinary(input)); + } + + static toBinary(input: string): string { + return input.split("").map(s => parseInt(s, 16).toString(2).padStart(4, "0")).join(""); + } + + static parseBinaryInput(binaryInput: string, start: number = 0): Packet { + + const version = parseInt(binaryInput.substr(start, Packet.VERSION_LENGTH), 2); + const type = parseInt(binaryInput.substr(start + Packet.VERSION_LENGTH, Packet.TYPE_LENGTH), 2); + + const contentStart = start + Packet.VERSION_LENGTH + Packet.TYPE_LENGTH; + + if (type === Packet.LITERAL_TYPE) { + // Literal value + let i = 0; + let binaryLiteral = "0b"; + do { + binaryLiteral += binaryInput.substr(contentStart + (Packet.LITERAL_PART_LENGTH * i) + 1, Packet.LITERAL_PART_LENGTH - 1); + i++; + } while(binaryInput[contentStart + (Packet.LITERAL_PART_LENGTH * (i - 1))] === "1") + return new Packet(version, type, BigInt(binaryLiteral), Packet.VERSION_LENGTH + Packet.TYPE_LENGTH + (Packet.LITERAL_PART_LENGTH * i)) + } else { + // Operator packets, so let's parse the subpackets: + const lengthType = binaryInput[contentStart]; + + if (lengthType === "0") { + const subpacketsLength = parseInt(binaryInput.substr(contentStart + Packet.LENGTH_TYPE_LENGTH, Packet.SUBPACKET_SIZE_LENGTH), 2); + + const firstPacketStart = contentStart + Packet.LENGTH_TYPE_LENGTH + Packet.SUBPACKET_SIZE_LENGTH; + let curStart = firstPacketStart; + const subPackets = []; + while (curStart - firstPacketStart < subpacketsLength) { + const subPacket = this.parseBinaryInput(binaryInput, curStart); + curStart += subPacket.binaryLength; + subPackets.push(subPacket); + } + + return new Packet(version, type, subPackets, curStart - start); + + } else { + + const subpacketCount = parseInt(binaryInput.substr(contentStart + Packet.LENGTH_TYPE_LENGTH, Packet.SUBPACKET_COUNT_LENGTH), 2); + + let curStart = contentStart + Packet.LENGTH_TYPE_LENGTH + Packet.SUBPACKET_COUNT_LENGTH; + const subPackets = []; + for (let i = 0; i < subpacketCount; i++) { + const subPacket = this.parseBinaryInput(binaryInput, curStart); + curStart += subPacket.binaryLength; + subPackets.push(subPacket); + } + + return new Packet(version, type, subPackets, curStart - start); + } + } + + } + +} + +export default Day16; + diff --git a/functions/src/index.ts b/functions/src/index.ts index 6808d8c..ea0b90b 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -17,6 +17,7 @@ import Day12 from "./day12"; import Day13 from "./day13"; import Day14 from "./day14"; import Day15 from "./day15"; +import Day16 from "./day16"; // // Start writing Firebase Functions @@ -48,6 +49,7 @@ export const day = { 13: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day13(), request, response) }), 14: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day14(), request, response) }), 15: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day15(), request, response) }), + 16: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day16(), request, response) }), } diff --git a/input/day/16-example.http b/input/day/16-example.http new file mode 100644 index 0000000..1da86c7 --- /dev/null +++ b/input/day/16-example.http @@ -0,0 +1,48 @@ +POST http://localhost:5001/advent-of-code-2021-911a8/europe-west1/day-16 +Content-Type: text/plain + +D2FE28 + +### + +POST http://localhost:5001/advent-of-code-2021-911a8/europe-west1/day-16 +Content-Type: text/plain + +38006F45291200 + +### + +POST http://localhost:5001/advent-of-code-2021-911a8/europe-west1/day-16 +Content-Type: text/plain + +EE00D40C823060 + +### + +POST http://localhost:5001/advent-of-code-2021-911a8/europe-west1/day-16 +Content-Type: text/plain + +8A004A801A8002F478 + +### + +POST http://localhost:5001/advent-of-code-2021-911a8/europe-west1/day-16 +Content-Type: text/plain + +620080001611562C8802118E34 + +### + +POST http://localhost:5001/advent-of-code-2021-911a8/europe-west1/day-16 +Content-Type: text/plain + +C0015000016115A2E0802F182340 + +### + +POST http://localhost:5001/advent-of-code-2021-911a8/europe-west1/day-16 +Content-Type: text/plain + +A0016C880162017C3686B18A3D4780 + +### diff --git a/input/day/16.http b/input/day/16.http new file mode 100644 index 0000000..e47aacf --- /dev/null +++ b/input/day/16.http @@ -0,0 +1,7 @@ +POST http://localhost:5001/advent-of-code-2021-911a8/europe-west1/day-16 +Content-Type: text/plain + +4054460802532B12FEE8B180213B19FA5AA77601C010E4EC2571A9EDFE356C7008E7B141898C1F4E50DA7438C011D005E4F6E727B738FC40180CB3ED802323A8C3FED8C4E8844297D88C578C26008E004373BCA6B1C1C99945423798025800D0CFF7DC199C9094E35980253FB50A00D4C401B87104A0C8002171CE31C41201062C01393AE2F5BCF7B6E969F3C553F2F0A10091F2D719C00CD0401A8FB1C6340803308A0947B30056803361006615C468E4200E47E8411D26697FC3F91740094E164DFA0453F46899015002A6E39F3B9802B800D04A24CC763EDBB4AFF923A96ED4BDC01F87329FA491E08180253A4DE0084C5B7F5B978CC410012F9CFA84C93900A5135BD739835F00540010F8BF1D22A0803706E0A47B3009A587E7D5E4D3A59B4C00E9567300AE791E0DCA3C4A32CDBDC4830056639D57C00D4C401C8791162380021108E26C6D991D10082549218CDC671479A97233D43993D70056663FAC630CB44D2E380592FB93C4F40CA7D1A60FE64348039CE0069E5F565697D59424B92AF246AC065DB01812805AD901552004FDB801E200738016403CC000DD2E0053801E600700091A801ED20065E60071801A800AEB00151316450014388010B86105E13980350423F447200436164688A4001E0488AC90FCDF31074929452E7612B151803A200EC398670E8401B82D04E31880390463446520040A44AA71C25653B6F2FE80124C9FF18EDFCA109275A140289CDF7B3AEEB0C954F4B5FC7CD2623E859726FB6E57DA499EA77B6B68E0401D996D9C4292A881803926FB26232A133598A118023400FA4ADADD5A97CEEC0D37696FC0E6009D002A937B459BDA3CC7FFD65200F2E531581AD80230326E11F52DFAEAAA11DCC01091D8BE0039B296AB9CE5B576130053001529BE38CDF1D22C100509298B9950020B309B3098C002F419100226DC + +### +