[TASK] Implemented Day 16
This commit is contained in:
150
functions/src/day16.ts
Normal file
150
functions/src/day16.ts
Normal file
@@ -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;
|
||||
|
||||
@@ -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) }),
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user