Skip to content

Commit

Permalink
2024D21: part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
martapanc committed Dec 22, 2024
1 parent d2b918a commit 89b334f
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 4 deletions.
13 changes: 10 additions & 3 deletions 2024/src/2024/day21/day21.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {encodeFirstLevel, encodeSecondLevel, part1, part2} from "./day21";
import {encodeFirstLevel, encodeSecondLevel, part1 } from "./day21";
import { part2 } from "./extra";
import {readInputLineByLine} from "@utils/io";

describe('2024 Day 21', () => {
beforeEach(() => {
Expand All @@ -11,8 +13,13 @@ describe('2024 Day 21', () => {
});

test('Part 2', async () => {
expect(await part2('testInput1')).toEqual(31);
expect(await part2('input')).toEqual(29379307);
// expect(await part2('testInput1')).toEqual(31);
expect(part2(["671A",
"083A",
"582A",
"638A",
"341A"])).toEqual(204040805018350);

});

test('encode first level', () => {
Expand Down
46 changes: 45 additions & 1 deletion 2024/src/2024/day21/day21.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export async function part1(inputFile: string) {
}

export async function part2(inputFile: string) {
return await day21(inputFile);
return await day21(inputFile, calcComplexities2);
}

async function day21(inputFile: string, calcFn?: (lines: string[]) => number) {
Expand Down Expand Up @@ -47,6 +47,50 @@ function calcComplexities(lines: string[]) {
return complexityCount;
}

function calcComplexities2(lines: string[]) {
let complexityCount = 0;

for (const line of lines) {
let minLength = Infinity;
const num = Number.parseInt(line.replace('A', ''));

const level3List: string[] = [];
const level1 = encodeFirstLevel(line);
for (const l1 of level1) {
const level2 = encodeSecondLevel(l1);

for (const l2 of level2) {
const level3 = encodeSecondLevel(l2);

for (const l3 of level3) {
for (const l4 of encodeSecondLevel(l3)) {
for (const l5 of encodeSecondLevel(l4)) {
for (const l6 of encodeSecondLevel(l5)) {
for (const l7 of encodeSecondLevel(l6)) {
for (const l8 of encodeSecondLevel(l7)) {
for (const l9 of encodeSecondLevel(l8)) {
for (const l10 of encodeSecondLevel(l9)) {
if (l10.length < minLength) {
level3List.push(l10);
minLength = l10.length;
}
}
}
}
}
}
}
}
}
}
}

level3List.sort((a, b) => a.length - b.length);
complexityCount += num * level3List[0].length;
}
return complexityCount;
}

function uniqueLengths(arr: string[]): number[] {
const lengths = arr.map(str => str.length); // Get lengths of all strings
return [...new Set(lengths)]; // Use Set to get unique lengths and convert back to array
Expand Down
124 changes: 124 additions & 0 deletions 2024/src/2024/day21/extra.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* puzzles/2024/day21/solution.ts
*
* ~~ Keypad Conundrum ~~
* this is my solution for this advent of code puzzle
*
* by alex prosser
* 12/20/2024
*/

// directions given to bfs to traverse keypads
const BFS_DIRECTIONS = {
'^': { x: 0, y: -1 },
'>': { x: 1, y: 0 },
'v': { x: 0, y: 1 },
'<': { x: -1, y: 0 }
};

// normal keypad button positions
const KEYPAD: { [key: string]: { x: number, y: number } } = {
7: { x: 0, y: 0 },
8: { x: 1, y: 0 },
9: { x: 2, y: 0 },
4: { x: 0, y: 1 },
5: { x: 1, y: 1 },
6: { x: 2, y: 1 },
1: { x: 0, y: 2 },
2: { x: 1, y: 2 },
3: { x: 2, y: 2 },
X: { x: 0, y: 3 },
0: { x: 1, y: 3 },
A: { x: 2, y: 3 }
};

// direction keypad button positions
const DIRECTIONS: { [key: string]: { x: number, y: number } } = {
X: { x: 0, y: 0 },
'^': { x: 1, y: 0 },
A: { x: 2, y: 0 },
'<': { x: 0, y: 1 },
'v': { x: 1, y: 1 },
'>': { x: 2, y: 1 },
};

// generate all paths from one button to another
const getCommand = (input: { [key: string]: { x: number, y: number } }, start: string, end: string) => {
const queue = [{ ...input[start], path: '' }];
const distances: { [key: string]: number } = {};

if (start === end) return ['A'];

let allPaths: string[] = [];
while (queue.length) {
const current = queue.shift();
if (current === undefined) break;

// find all paths
if (current.x === input[end].x && current.y === input[end].y) allPaths.push(current.path + 'A');
if (distances[`${current.x},${current.y}`] !== undefined && distances[`${current.x},${current.y}`] < current.path.length) continue;

Object.entries(BFS_DIRECTIONS).forEach(([direction, vector]) => {
const position = { x: current.x + vector.x, y: current.y + vector.y };

// don't allow traversal into the blank areas
if (input.X.x === position.x && input.X.y === position.y) return;

// only traverse if there is a button to hit
const button = Object.values(input).find(button => button.x === position.x && button.y === position.y);
if (button !== undefined) {
const newPath = current.path + direction;
if (distances[`${position.x},${position.y}`] === undefined || distances[`${position.x},${position.y}`] >= newPath.length) {
queue.push({ ...position, path: newPath });
distances[`${position.x},${position.y}`] = newPath.length;
}
}
});
}

// sort from smallest to largest paths
return allPaths.sort((a, b) => a.length - b.length);
}

// find the smallest amount of button presses, given the robot and code to enter
const getKeyPresses = (input: { [key: string]: { x: number, y: number } }, code: string, robot: number, memo: { [key: string]: number }): number => {
const key = `${code},${robot}`;
if (memo[key] !== undefined) return memo[key];

let current = 'A';
let length = 0;
for (let i = 0; i < code.length; i++) {
// find the smallest move for each transition
const moves = getCommand(input, current, code[i]);
if (robot === 0) length += moves[0].length;
else length += Math.min(...moves.map(move => getKeyPresses(DIRECTIONS, move, robot - 1, memo)));
current = code[i];
}

memo[key] = length;
return length;
}

/**
* the code of part 1 of the puzzle
*/
const part1 = (keycodes: string[]) => {
const memo: { [key: string]: number } = {};

return keycodes.reduce((sum, code) => {
const numerical = parseInt((code.split('').filter(character => character.match(/\d/)).join('')));
return sum + numerical * getKeyPresses(KEYPAD, code, 2, memo);
}, 0);
};

/**
* the code of part 2 of the puzzle
*/
export const part2 = (keycodes: string[]) => {
const memo: { [key: string]: number } = {};

return keycodes.reduce((sum, code) => {
const numerical = parseInt((code.split('').filter(character => character.match(/\d/)).join('')));
return sum + numerical * getKeyPresses(KEYPAD, code, 25, memo);
}, 0);
}

0 comments on commit 89b334f

Please sign in to comment.