From c7ba31b24d2179627671b0ea5cc65b9351d36321 Mon Sep 17 00:00:00 2001 From: Pascal Syma Date: Fri, 20 Dec 2024 14:40:52 +0100 Subject: [PATCH] Solve 2024-20 --- 2024/20/2024-20.run.xml | 7 +++ 2024/20/index.test.ts | 28 ++++++++++ 2024/20/index.ts | 120 ++++++++++++++++++++++++++++++++++++++++ 2024/20/run.ts | 12 ++++ bun.lockb | Bin 23246 -> 24046 bytes package.json | 2 + 6 files changed, 169 insertions(+) create mode 100644 2024/20/2024-20.run.xml create mode 100644 2024/20/index.test.ts create mode 100644 2024/20/index.ts create mode 100644 2024/20/run.ts diff --git a/2024/20/2024-20.run.xml b/2024/20/2024-20.run.xml new file mode 100644 index 0000000..1a5596a --- /dev/null +++ b/2024/20/2024-20.run.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/2024/20/index.test.ts b/2024/20/index.test.ts new file mode 100644 index 0000000..feb69c4 --- /dev/null +++ b/2024/20/index.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from 'bun:test'; +import { solveFirst, solveSecond } from './index.ts'; + +describe('2024-20', () => { + const testInput = `############### +#...#...#.....# +#.#.#.#.#.###.# +#S#...#.#.#...# +#######.#.#.### +#######.#.#...# +#######.#.###.# +###..E#...#...# +###.#######.### +#...###...#...# +#.#####.#.###.# +#.#...#.#.#...# +#.#.#.#.#.#.### +#...#...#...### +###############`; + it('first', () => { + expect(solveFirst(testInput, 1)).toBe(14 + 14 + 2 + 4 + 2 + 3 + 5); + }); + it('second', () => { + expect(solveSecond(testInput, 50)).toBe( + 32 + 31 + 29 + 39 + 25 + 23 + 20 + 19 + 12 + 14 + 12 + 22 + 4 + 3 + ); + }); +}); diff --git a/2024/20/index.ts b/2024/20/index.ts new file mode 100644 index 0000000..6a48361 --- /dev/null +++ b/2024/20/index.ts @@ -0,0 +1,120 @@ +import { Presets, SingleBar } from 'cli-progress'; +import { replaceChar } from '../../utils/array.ts'; +import { + type Coordinate, + type Coordinates, + findCoordsOfDigit, + getAdjacent, + inBounds, + isCoordinateEqual +} from '../../utils/coordinates.ts'; + +export function solveFirst(input: string, minSave = 100): number { + return solve(input, 2, minSave); +} +export function solveSecond(input: string, minSave = 100): number { + return solve(input, 20, minSave); +} + +function findPath( + rows: string[], + start: Coordinate, + end: Coordinate +): Coordinates { + let [x, y] = start; + const path: Coordinates = []; + while (x !== end[0] || y !== end[1]) { + const last = path.slice(-1)[0] || [-1, -1]; + path.push([x, y]); + + const { top, right, down, left } = getAdjacent(rows, x, y); + + if (top === '.' && last[1] !== y - 1) { + y--; + } else if (right === '.' && last[0] !== x + 1) { + x++; + } else if (down === '.' && last[1] !== y + 1) { + y++; + } else if (left === '.' && last[0] !== x - 1) { + x--; + } + } + return path; +} + +function getCheatDirections(maxDistance: number) { + const cheatDirections: Coordinates = []; + for (let x = -maxDistance; x <= maxDistance; x++) { + for (let y = -maxDistance; y <= maxDistance; y++) { + const d = Math.abs(x) + Math.abs(y); + if (d > 0 && d <= maxDistance) { + cheatDirections.push([x, y]); + } + } + } + return cheatDirections; +} + +function solve(input: string, maxDistance: number, minSave: number): number { + const rows = input.split('\n'); + + const start = findCoordsOfDigit(rows, 'S')[0]; + const end = findCoordsOfDigit(rows, 'E')[0]; + + replaceChar(rows, start[0], start[1], '.'); + replaceChar(rows, end[0], end[1], '.'); + + const path = findPath(rows, start, end); + const totalLength = path.length; + + const cheatDirections = getCheatDirections(maxDistance); + const isInBounds = inBounds(rows[0].length, rows.length); + const bar = + process.env.NODE_ENV === 'test' + ? null + : new SingleBar({}, Presets.shades_classic); + bar?.start(totalLength, 0); + + const cheats = path.flatMap((cell, i) => { + const [x, y] = cell; + const downgrade = path.slice(0, i + 3); + + const potentialSpots = cheatDirections + .map(([dx, dy]) => [x + dx, y + dy] as Coordinate) + .filter( + (c) => + isInBounds(c) && + rows[c[1]][c[0]] === '.' && + !downgrade.some((d) => isCoordinateEqual(c, d)) + ); + bar?.increment(); + + return potentialSpots + .map((newStart) => { + const end = path.findIndex((p) => isCoordinateEqual(p, newStart)); + return { + newStart, + cell, + length: + (end < 0 ? totalLength : end) - + (i + + (Math.abs(cell[0] - newStart[0]) + + Math.abs(cell[1] - newStart[1]))) + }; + }) + .filter((d) => d.length >= minSave); + }); + + bar?.start(cheats.length, 0); + const existing = new Set(); + for (let cheat of cheats) { + bar?.increment(); + const key = `${cheat.cell.join(',')}-${cheat.newStart.join(',')}`; + if (existing.has(key)) { + continue; + } + existing.add(key); + } + bar?.stop(); + return existing.size; +} diff --git a/2024/20/run.ts b/2024/20/run.ts new file mode 100644 index 0000000..88783cd --- /dev/null +++ b/2024/20/run.ts @@ -0,0 +1,12 @@ +import { join } from 'path'; +import { solveFirst, solveSecond } from './index.ts'; + +const input = await Bun.file(join(__dirname, 'input.txt')).text(); + +const firstAnswer = solveFirst(input); + +console.log(firstAnswer); + +const secondAnswer = solveSecond(input); + +console.log(secondAnswer); diff --git a/bun.lockb b/bun.lockb index 24b152fd8a65770e3ecddafa215ae2d243db0332..171d588a96cc4d372b60af509ddf8bfc284837bc 100755 GIT binary patch delta 3880 zcmds4dr*|u760zC3;XeYDDr`phlt2yVIi;(V87)NL>^6OO?V^_K`3GfPzV_{RnXXR zjM|tUG72?`PGdsDAK*|L6FY&%Kcr()O)-sVB}2@#qfw$}rZur*f9Ko%lAVtEv;CuY z=IrmDbI(2Z+X8lGL{e7y#Vd(ACn7{%#>jLEyIlS>6ux z1va{H(jZAoz>focfWts<;F}&!Z0=s)(ALn@yuPiqrxo_9jFKd;g~A{@JmE6X513|h zPS|YCYKNZG($>+)80rbaxF?VuoCfl|DHqmyIUO6JH6pV24y^E~pNwUIPq((Wb~bc( z--4X^llb9nguzFD;1!qr7rW5CnI8Ts?SUfo;)%G*)hm+Av4jOr%k>NE(@Fo%^7X-3Ir}q z)90~u;2s3$rOiG{idmI!Kvn>mfoF?c8ZfJ36V2fFI4QC!Cn1D<6@+HJ4_pa24~jP1 z#4OFos$r=Yg?roOR>ZhiGX&R6zXw-9b)Gr$0(wq|cr%ZX;-ktt%ucrqnj^oZ0Uy;6 zD$@lYyIhI$V|6b&;-6&(!w$=I#AK8I49>X%Zzc|hUW740(LOeLBRJN?wtZ~k4^-o) z${q+A%gma_(6`*2Y?D6&$A{)c%>4jCWzJLMYQZ_cnJC)VCJ%w*btFDeagCG!RU}YN zfGTgm9pb}<9u8xWW+1x&8Cw$Uu_4r-Y6I=265OE;bV<$;lQa{k%0^r>XT+khEeE(v zOmQy09UMnkaP0ji!0}B$P$gfgG$zu1quC$@(uE+qp(K#PgYDuMR1>UY7%+$-Xab>jT zjy!v{OYV+r4N=n?BlBxOYzIzid^wPtJF;E{i1CLY)_+7lSM;^u{_g|a>+~oZ4zOo2 z>8I8^#8{#d1q|F-Y?&{xOe}$!#8j=LRtt|0NUv5 zRaG>U;74@{Wg?q?lTby~iGJjlSXRN;UT?j}<@u-to`--t`*I{AEaNPS4sW zmo2@zDtF#21wC9Rv#Ko zYV0EW=!aZkWU)g7lyq?l}>T-f`I`f#G#+;Nkmy9j)m=Toi>N{L`(6!tl z2W`tM?Zx*-dto{MI43z@Id?g)3qhQA_!e;aAUzD?Jm$9)WrGw3;%rzBiUq9z#er6W z`29td!Sx)M77~{ji$VO(@^&5o;bON)sp!OmR)P4j#V3S6oczVpZalw}iJ&A9Kfd`O zek1wai~>c2_$+wuyjKT^&xFrviWxND0_-&=h}XsUN*iMHwy}@w0m9;iE_=bNvIWLG z7qTVh9R>J7+fquNjz%kX)Tb9h_>Q?6@0-i1ji-{0zVAkjm_>c6CDoFiA=#N7M}>TM(D+&hrGW+=HWu2Jg3>BunfH#ubG%P z9h$u!?^4Is_4>T3@?1sTtv}9vx7_QL_>dBz*Xu)@7dp}pN8}qhtXB54k)Fi(S|4XU zfBT!~HoRZmf7BrIAmNKJOew|6!<)e&efpYePQL$Yz+sa?G(m!7__p$YboyHCmX5$0 zteoXMf?q)cY1e1Sf2 zRvQ=Nw6$14t#_a}U7V%U#Vrx~e}j1R;n|AnkF=kQ~7G+2`Fi+_O0on0L*UCrIybgpEDkDFlZcFFSrk#2(Sl;jk5 mHW}BDCdy-IS*_RDj_ODe<34&HWveBVwwFd~`bTP##lHa+;=&97 delta 3534 zcmcImdu&u?7Qf%6Go9OMnW3en*RczzVrIN?_4?{PVW~Z{<-SX2QTP}yeA*Y|9H>odro!);@$bjUdkE&q$JV) zx9js$BVCd)YPWY3#E&g72}$biTVee6;Omv7;Z9%=@Id#ytNZc4Mo7{O&`W`te=~48 zaD#<+%aSw;^y9#8;BCNZz+0x7cvb(3?%wW|t5)>J*2l13I7O0_UqN69Kdf*RI0HD# zVJZv+!)qZYt?unx&iFF;Iha2W%m(grntCrCS{+mV}y5dIkqfpAGjgF95Oqx>{|DXY0HMAcl&7~`^fmaWn%-SN4MqRq- zq$IvilBVcN1`@=H7RKt`pjtppqo^w+4pLIlkshIg?2G_)_UvAOg;JCR! z028bbv;-KlLZwABW`i^UZ;VVB^0mM`7&E^Pz@yIiULEtg_11hnZOwMaS+mLFOvP+b zv&Co3nm9qm&aD=IDrWgb0G3+}V4BAd1%*;40dxhvHo7d<+%>_?SciT-vyY`TdmI0DL?O0kZ)`fH{Ec z0M`TfTEi{MuRp)^+|)>!fGhxef_vGxA^gBxj6VbL6{Z5RCFyo2D&p&j@45m2hlRtyZnK9R9uC_pmb+i;^n6{h1CNcQCEl*{h+z}ol7pTJrLvEp*`sK8vK1b!v;N9@HeS3GT9DiZ4ODwDKSNS!G-mR}jam;9_ z^mRf8-Vfio;J{e-;PX#IChV{9W2YXvy;_1PWK#g$!4&Mp{h#C#Z<*kw2EZB)e;IsqxW zzgjYW@w&l2ZzrTerj)UYVM|Uvltd|94sYrAzCeuR$M)v|Gi^-dlTnH;J8?fg9|}c?ab;=lbl< z^AG87PE0nOsWJo%PhO<xH?yP9wE+5Kl-_PHnKA9QVi6h{iijL>*<)0{VO&aOe! z?T+-*qZ`)keCy;muNE-E{~XmURDE_w+R^^TO}Bl1?7eSx+f026)wtcE?irZqtK8*_ zrif($Ha8&IJ?WKR@t1W)jqgCS+8^|zpsR5>yx9B8uZDUK-}2b+WswgKiuC_|ZC=rP zbHv|NP^0s6|D?^lnR~*h`+))wy;%UH(F3?8D8= zIqXQCKS+C8ul3oz_4e|vM~nB@YO>%1%A<(RwW`sI1yo%N7^ zQEo)T?V!M;-<^13;J0gDkICY}updDUK)`I{zq|A=`1ZjVX`xMv?z`q~1QGYr@TPF} oNpb)5i7{(>UKeGyMd{AA{KQDxLmsNVEj7`#v_K>d-C8F82Nt?OqyPW_ diff --git a/package.json b/package.json index 4c76cca..f053467 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,10 @@ }, "devDependencies": { "@types/bun": "latest", + "@types/cli-progress": "^3.11.6", "@types/javascript-astar": "^0.0.35", "@types/prompts": "^2.4.9", + "cli-progress": "^3.12.0", "p-limit": "^6.1.0", "prettier": "^3.4.0", "prettier-plugin-organize-imports": "^4.1.0",