diff --git a/aoc-py/solutions/__init__.py b/aoc-py/solutions/__init__.py index dd36706..da2b9a0 100644 --- a/aoc-py/solutions/__init__.py +++ b/aoc-py/solutions/__init__.py @@ -16,6 +16,7 @@ 'Day14', 'Day15', 'Day16', + 'Day17', ) from .day1 import Day1 @@ -34,6 +35,7 @@ from .day14 import Day14 from .day15 import Day15 from .day16 import Day16 +from .day17 import Day17 from ..solution import Solution @@ -41,7 +43,7 @@ Day1, Day2, Day3, Day4, Day5, Day6, Day7, Day8, Day9, Day10, Day11, Day12, Day13, Day14, Day15, - Day16, + Day16, Day17, ) del Solution \ No newline at end of file diff --git a/aoc-py/solutions/day17.py b/aoc-py/solutions/day17.py new file mode 100644 index 0000000..333a2dd --- /dev/null +++ b/aoc-py/solutions/day17.py @@ -0,0 +1,113 @@ +from __future__ import annotations +""" +Day 17: Clumsy Crucible + +https://adventofcode.com/2023/day/17 +""" +__all__ = ('Day17',) + +from typing import ClassVar +from heapq import heapify, heappop, heappush + +from ..solution import Solution + +class Day17(Solution): + NAME: ClassVar[str] = 'Clumsy Crucible' + ALL_DIRECTIONS: ClassVar[tuple[tuple[int, int], ...]]= ( + (0, -1), # up + (0, 1), # down + (-1, 0), # left + (1, 0), # right + ) + + def find_path(self, inp: str, *, is_part_two: bool) -> int: + grid = [ + [int(block) for block in row] + for row in inp.splitlines() + ] + n_rows = len(grid) + n_cols = len(grid[0]) + + traversed = set() + heapify(to_check := []) + # start at (0, 0) with a total heat of 0, and no direction + heappush(to_check, (0, 0, (0, 0), (0, 0))) + + # part 1: we can only move max 3 times in one direction + # part 2: we can only move max 10 times in one direction + max_dir_traversed = 10 if is_part_two else 3 + + while to_check: + heat, *set_entry = heappop(to_check) + dir_traversed, (row, col), (row_incr, col_incr) = set_entry + + if ( + # we've reched the end (bottom-right corner) + row == n_rows - 1 + and col == n_cols - 1 + # part 2: we need to have not turned for at least 4 blocks before we can end + and (dir_traversed >= 4 if is_part_two else True) + ): + return heat + + if (set_entry := tuple(set_entry)) not in traversed: + directions = [] + if ( + # part 2: we can only turn after moving at least 4 times in the same direction + dir_traversed >= 4 + # account for being on starting block + or row_incr == 0 + and col_incr == 0 + if is_part_two else True + ): + directions += [ + ((new_row_incr, new_col_incr), True) + for new_row_incr, new_col_incr in self.ALL_DIRECTIONS + if ( + # ensure we are not turning in the SAME direction + (new_row_incr != row_incr or new_col_incr != col_incr) + # ensure we are not turning in the direct OPPOSITE direction + and (new_row_incr != -row_incr or new_col_incr != -col_incr) + ) + ] + + if ( + # we can keep going in the same direction! + dir_traversed < max_dir_traversed + # cannot do this on starting block: no direction + and (row_incr != 0 or col_incr != 0) + ): + directions.append(((row_incr, col_incr), False)) + + for (row_incr, col_incr), changed_directions in directions: + new_row = row + row_incr + new_col = col + col_incr + + if new_row in range(n_rows) and new_col in range(n_cols): + heappush( + to_check, + ( + # add current heat of block + heat + grid[new_row][new_col], + # if we've changed directions, reset `dir_traversed` counter to 1 (fresh direction) + # else we add to the counter + 1 if changed_directions else dir_traversed + 1, + (new_row, new_col), + (row_incr, col_incr), + ) + ) + traversed.add(set_entry) + raise ValueError('Failed to traverse path') + + def part_one(self, inp: str) -> int: + return self.find_path(inp, is_part_two=False) + + def part_two(self, inp: str) -> int: + return self.find_path(inp, is_part_two=True) + + def run(self, inp: str) -> None: + print('Part 1:', p1 := self.part_one(inp)) + print('Part 2:', p2 := self.part_two(inp)) + + assert p1 == 724 + assert p2 == 877 \ No newline at end of file diff --git a/aoc-py/solutions/day3.py b/aoc-py/solutions/day3.py index ea02c58..6ba0457 100644 --- a/aoc-py/solutions/day3.py +++ b/aoc-py/solutions/day3.py @@ -20,7 +20,7 @@ def _symbol_adjacent( coordinates: list[tuple[int, int]], condition: Callable[[str], bool], ) -> list[tuple[int, int]]: - ncoords = len(coordinates) - 1 + n_coords = len(coordinates) - 1 symbols = [] for i, (row, col) in enumerate(coordinates): @@ -34,7 +34,7 @@ def _symbol_adjacent( (row - 1, col - 1), (row + 1, col - 1), ) - if i == ncoords: + if i == n_coords: indices += ( (row, col + 1), (row - 1, col + 1), @@ -55,19 +55,30 @@ def part_one(self, inp: str) -> int: ncols = len(arr[0]) total = 0 + # indices of the digits of the current number curr_indices = [] + # the digits of the current number curr_num = '' for y in range(nrows): for x in range(ncols): + # we hit a digit + # append digit to the current number we are tracking if (n := arr[y][x]).isnumeric(): curr_indices.append((y, x)) curr_num += n else: + # we hit a non digit + # if `curr_indices` is not empty, + # this marks the end of the current number we are tracking + # now we need to check if it is a "part number" by checking if there are symbols adjacent to it + # + # otherwise nothing happens if curr_indices and self._symbol_adjacent( nrows, ncols, arr, curr_indices, lambda c: not c.isnumeric() and c != '.', ): total += int(curr_num) + # reset the current number tracking (since we've finished with it) curr_indices = [] curr_num = '' return total @@ -80,6 +91,7 @@ def part_two(self, inp: str) -> int: for row in range(nrows): for col in range(ncols): + # we have hit a gear, find all adjacent digits (could lead us to a number) if arr[row][col] == '*' and (nums := self._symbol_adjacent( nrows, ncols, arr, [(row, col)], str.isnumeric, @@ -88,12 +100,18 @@ def part_two(self, inp: str) -> int: for y, x in nums: curr_num = '' + # backtrack to the start of the number (go back until we hit a non-number) while x > 0 and arr[y][x - 1].isnumeric(): x -= 1 + # once we've found the start of the number + # keep going forwards to fetch the whole number + # appending each digit to `curr_num` while x in range(ncols) and (n := arr[y][x]).isnumeric(): curr_num += n x += 1 + # maps the (row, col) position of the start of the number -> number num_map[(y, x)] = int(curr_num) + # if there are only 2 adjacent numbers, we add the product of them to the total if len(num_map) == 2: a, b = num_map.values() total += a * b diff --git a/inputs/day17.txt b/inputs/day17.txt new file mode 100644 index 0000000..e8ee175 --- /dev/null +++ b/inputs/day17.txt @@ -0,0 +1,141 @@ +112222221323213113333323322444342424324235235513233424441521421241545143411145334442432543231425554512423123113114232422242322323132322123322 +112131321311322131441423422433434243225123132352335324453212345444231322442352145135542552454212421113553344423244123342442124331123113111113 +223121133222231141414442234343234443245521353512543323315242154222243251314332452411341515252321341252252352412443224334334423431311331233132 +133112222131222314441334144443223232435425542512121331552541522242524442232241453321512312523525314513351154532323413141343411122222321112113 +322223332322123343222431144122111132424322253553532251352141344412415524331232153525524452112552433423233331423314432421441433324223121332321 +312131333112441323343144232223412255122131134231531222121445422642255246544344351542431215224223522125434152553131142332211443134312222231222 +211211333321123342334144132241233125151214321445224333231666326363643365422544542253625335334412231343533453513253132434122214434413233322212 +322212232222212211422412332251353342454542231312144243545346225253223636324233543632334462331141244415524124153133313132412224312222312331223 +322113211223113212213314342153433525433231555553242434356463363265563563645342336263523343224234524225243335241315411121134133421334443112231 +333131212121412343141114435445224133135245342435446335634363353522253334334325533246432443233534232533451511221534433232211341144331442123133 +233121313211411344244133551335321151454244115435646626433463222346345336345563626454252442243263621312153524415454353452143344333231232312131 +232232213113312132313315315212344412523141424345323355233264433452535644442424523432345335464524334555424255155321443253342432143244233443221 +222312133141411241134354244325155122343355365362644533322246255263355522354533563443355326646646264224131252312153525411224323444133232224333 +111244131344141222413414145434311435455622444665232334443532362335566323622352345532434556256524446255655425132232533115524333143121243132233 +111244311324141411414353111524355312152244242424233562353222623365626223436624662663526623323346624635656242241331544213244531411322111321212 +121424341321422213253122452544151441266334352453236555222246466353633533663225535523525353526632362352264321242524154533123152333422233411413 +214234314114323314314113432131313366255232425525656542422233234633675447633376624644353253553454655364562265441352535145552134124131432133133 +332424433411233341444552444214425242344536442265562456663425666554773446347346677757322356235433365252453666562542223545254213234341442413224 +224324313224121122231213334354554464222243262245563446443457534436665553377347573536453754242646426644353462646354224534444454111443421424223 +344113323141223424312532325552433532226426525244252545567446457377467376776453446546455346324422243324355266242461231132521221421334244122132 +121143243442251514121142215433466252326566252463554376575564647554466444643663736743735467656325234634366624462435225541155451213542412123321 +144144141413521534533235234243446622523424443543366754775333444667747447575555647346575553475442432323322363533534442254251333554224321322412 +133134234431121245414221455544552355254533333554765767646635755346377456576745633435554465565446753562643346625663554454551533413451121411332 +421312141414151251534535145545334635454544355454537445775735777476335374453663376445743543376477537535536333666253254644241451115445221211442 +214221242454533512544542256445353534624666364447645454474464366777664554366343344656674345645533773656334632346233242444145331553411113233212 +334433322134135114521523436665566232456236775457364736647343457564436443366747573543346756667346554433432625455645563443212442225353141432132 +414243144525445132532252446445235642322545533565647777645656674454736444665656347675347444337755333633734244653456533533542525542355122224112 +143433235535325323514555652344336622663673333376467736757366664764488787546574753344663735335476637633573446363332324444531212212313224243124 +233241124442243342213456255656564436473537636467545543637434558584544455845477658673347754347766475535563665626236564655445414511114453531231 +122413431433152134134564353264624256377643443645443356354755477477464754574766768774754774457343446345773673563325242632444445145241315521422 +444312155313125142555226522564323545655757443647535433658788465575486755656545676476875673454545753454675776324224234622254632245552231332241 +412315422413422234626446663654536753546573463454737467446466876468567577575758846484447444874336553567564657772425335653334653113522255423321 +321153543234545221364664522445565745475535455445777484645858586885856446845646587785657668865545453463765545366662426263665424333352132235312 +343333453445435345642254343233244467557455636777477667788648478884457487868658844576875548665767473445373444736746656464542536352221113114241 +341253351244311554666363623465443773334565554654657584758574446865455758468854764677665687585678646675377744747475523236366532643224332224132 +344411335532124555324434522354736737343764736657764444844874845687784647454874576766784678446644654643667667447774422566532425225453355452534 +335114112251353562263552553356673377666433457758767754655756747844667745777466447655544468855548654754473737743747666234326455632232214442144 +225222425444416653643232666446467743675765358546585488658774865746464678468857578676487567565468775655664634567643732532643543645445522444252 +221255251443542444423662624434743655647647485755544664645558854665855956758744886447777544574446664753567664347436446423353325652523452455435 +212553341123245524653233356344465547467357844646488645867475658779695667557996865565887876875848758555665345676567653345354543456553242425211 +124324252454252354346666635555463356466366764765564874466577697988975997976596879775875785557748456786436363464536777534565566666415253122423 +325111152445523442355232556376357645446586854565545874487796699987869778898789789758796467676847647687674374754666446655266222342222512121453 +422455452455632236346335347563767755665865664864458577856687697697897757575876756975777785766686667467465453645675354336442536246544213311553 +152431153324634643246634634574537534484787765688676758867558678897769985796587985868567587844664676845854866536343764366223224355552413433533 +455225414512433464335335554663376546777756858575857696858895899995956775869658676989888796556684445857454545543366637447263623326536514243141 +524115342224323436366656644773636343546686555477778786687586657565875696796775787557759768554777664886776547534776477347433234244365333451111 +215421333526242353653636465465357547566845467548755577777977975755678656957696899555969955788858768884546477536443756377733456462466513124145 +453343353526463442543557773466473748788746547586678875958676769698799659999768879799765585569898888576564856535336575675425634662626651252524 +253412222242626222432346567563634477887454588479765758656676778675685677967575678658786557595758658646657856867375547544646264635345664312123 +555512245345353565334333554535755475454656658475955558765596999697659889686775666956766657687698888564546674855535355576535662632423563514352 +452155524642465255567337367636637568458754776657699979565958998997688697897669678659885685585577665678484556547576343653353623364236355342114 +251134115535345254646474773557574754667855574897588975769766698986877969996889996798555695987767765456474685856753434536637534365446546512524 +313234422435246652257534377665385687648665645596788569696786789978786697996679987795559598588796966846754675786557357475557342553432222432235 +513131443646224422574544654546745887444758657899677778786859698688967688698899668788865595556967568456774646675464566566563565465344345254345 +425125562464224254466637537343365546548668599688675897685696699869799969676788969876779977789779687665687584686536777347563423253545665611435 +345451266444536222376475754743554648687454696595769789668789768977899967996966968676969576965995986698566866588446465347463675654666324235435 +451335564564336426355565754567747765474654777658589876896768999867979697679676878696679769696986599685888778787446443344556555455335666511243 +431112634665335644734656445467555484658575796976986567877688978866769989766996667677998695589967789886865766456575464745767465445354254533555 +512433632236422233776664544337856564786655959898796968679978969688866666876789969976769966596887996859656746868474343474555433522552365631223 +411221632443326333353345355538785468788457759675657968899779969977968978967766967698786999959959586566666855866545536435345655225424564333435 +125553244225343663554546647668554777488497895977977977989879976889886699798769789866786966768689855869648544654476666754667546332352656242232 +224355554426226427543567755487476465685779895867885986776697769877977788779899876877696866699898577596667865786755446366656764525345346652214 +543353654443226563375653756685788855864976688677656786798678878968897888877788787766767677667999799788777566658554736376453746656236265643521 +333454542665465244564546543385875577484956879587989997976867878688779877779997867998896676779588599977986446846457857575453375222324526534133 +434423626556545553466446475455488856664559988679798669679776696977988889799798779898769886668685688567568867666455766733573375324254424424333 +533225466264523655453666547787757785688796959979698979796876798987899788779899877998899786768966859997774474747556755453774466746452244254133 +233236435436355253673373463578478745456869858986757697679796869799978879987997889769887987869577565596997487848475864337675756432243623633531 +445443645256346277544755436848467557768969758685688896676868978787989887899889787997687789898558998998759547884884863344635566465645554432354 +124155465565526266556563654686677668556868898599757776778797887888988977897779988999686788676895975665899884754748675746476666446366566434122 +311322632636455664545565356654586555459659758788888769778889878778989888889879977768787868689659588787568645657844856674376747333534522446543 +524415343336265273534645664487885774877899958977789878799887987999778979989898899778678779769976887767897486768856865666347536432242456664542 +252235255524462643653563335787488774465758755559966788767688888799777989978789988799689669766798657798896668866556647757454363523233452432141 +554143266442623573465776464864468677779678896788767696997998798878978888989787987789877668977695698668769775476667456764644553534342535266444 +455413536263265454754374665847578657465756887596786796998797979999999888879788798976888688779769986569696858754764553336674566736535245655151 +512114234634634573767444646846458576775777999777987768878789699989887887989897788796676878897867796888566884847756544464756535425534226455315 +251156443463246365364555474875454467479655999887576896968768978899998788779887897789889979989657699786956644467567774435633753363336532323342 +141445426456345265535356746554548684745767559885568779778776768799879777888789989989668769966686595795575865744764765734475776446243665665312 +255313332435652474564576376656756744686675669655557676666679698877997977989998789697979798797968689787957447875468666344755353652563622632155 +224116423563545324743733743365788745646698588756789896999878687998797899999988899798788669896659858787568855455865464643565533564255544546532 +221516236634252257644567373678456477746958667867869687797688788679787789789998988796696699996767866797867748675884834535654553562232462446444 +222111335644233545747443657487587784678587675977665966796988869888777989989898699667968869699867555599766476745664645666775635552226635533431 +241152326523236647636365537758576574785686665687979566967789886979989789789999979697678878765757779675564544876778754766557633432263335432511 +452223665243224356754333346474474678677576675785985599688668677799798698998996677899997797955699567757685668857564347474546464442225462343142 +423515353434446536474447535756864467675796567988858996897968776977898767879978776999866889875659796568976567787558564335566653354235265322524 +424351365564252244474735573334647754865559586567858786889698899876966797798786877779998665988798657599446747744777353735666344633653366633443 +454144532463464222765445754444565446664875987986567866796969889878799879989896887889676866687857579687846848686587656737543663254363266523324 +331243324542246263764737537754874877567844787886598656877688968766898868666676679898686677588986579575675875455856366535446674342646656345133 +522321542452233626434565356637848767848544675968869858758769778767787777697696689977777857969959787976545865565865465336346655334223624541112 +114331336626665542353463356774785875587465466585655765656796669877699989689897868896886595898867759985887768874677765363433443254336656344332 +532434544636522322376363476457655864656568487987665885799596699786688688769896699999558576757757888564444674457475554463333463564635325254234 +124123254356322652374367365435367755646457565868966797857755878988869898778676899889996578895679979747465484577746366674577562655552665354451 +355345424663263264544445543664667585455685786595786779565869857888798889686986766559566865596859874668875885786376666576364542563332653314542 +325314554354552336545637736754754776744876584485778875668658679979997779896976785597858967597985685845674676484745335436656336624544354211412 +425414121655523344243575357763653747765444854467696766899955596658589979895566586798588797797556785654784457786766733553775344335552245323245 +321544144325652343334575753536347564556577486666588986855888695796688659778786685995788887556958887766558666576735457565562453342546642134133 +233224244454245562454544567466375464478684468664878689799895787598577866567558998656655957865877684666685656557544654575652253325644325245452 +223253325222626422232344673743536775484847887564746977799768676589955866865598778776977777898654875857658564566565663763455236326342652254352 +155252422533226225634556656344354654464548688886765789889959975988897978898976859996899559995865644767458876575545536564442453242363535453552 +153151534534626342354465665744754474644684754445886465878699957799687876669595855957865598785658456874788777337375336333556425554625213551524 +114131451313553442645435336474563565464846784747667764766877599656867686898598866888669594748747557867644446754575776643342566446456354515545 +341445155413444265442333676464335657464657465576446677768796997589788878566978557586958648788844587858746674657357537556434652546423525233113 +145555453252246666354553464435564354555574754444686748488899567775759856599667996866868645877786666486875457454653577562363344343353254313251 +143522431434132223562456236436453466574664556457854457784587698566798687887857966579455846478558567867734776443433564663645232333432153533433 +215322345321224335635562467377454444547736774447848748685756479895588579586889995685457756468565465686547776773475577554245422325222111541441 +341322153154514433556244432436566644465665747645578576487455785788898555578675846854548778777465446786763336667574642346555656344514521212431 +211411151325556343533222434533637444454335645656474784747848585745568857858857586746545487766766747654736763544346624246343635345312442334513 +332121414344532346433322666546554556647353675587785568474748578865644484548648855868467678744684584637444373533334343524243655231512424415224 +411524544331513352322333546664576374474473653477646577688768554444786477486457545666878757754774535354377435745536263264224334353132254241153 +422211332253552135663623352665455534446547756657668474757847555474767757448784477787467885884668463377464774543773322646356226324142454115544 +431332122243114415654234442342364454375577436734766474846574566767468866458556744747578844486837345647756635665625464262633224523334511522341 +224251132223221313453424254225224554354467533673735745545644556487888454588447577457458774746447435534547557657555442633434462324134522544331 +424111421545541554645653335265444456575347757673576457844645675546487464587674555767765667574773374336533537762544524643353364111515335224142 +223234224455241252363466635222362536376655666633577657645755565564687447466785887788764433664575544537646477666565263426556245422143414551114 +233332452421231352335626354345244256743777467745433457776474466468687755585566566647444755553777776556436777656425255542455334512253244152412 +422112411545554131124444622334525554545646447374565554535566388477546575685584468734376566346747454644555546562335642323543314133325522324241 +413442321241543521354365362526422436656465774657774354765753344464456467766744635674434544657564573463535455526336266353232123151224155411344 +242422423415151112523146354525226366645673535545673375435674565354736655476666475464746664643334676575555355355232463523215233452253211124213 +111143333432524413523436454324432264544544735574336367535553357366667456753344674646463476344445756576624653253446454635325453235235252223333 +332333142443531535113424432366223346242652573755667573455376754446553553657745475456557634557655643365263544656433326353222124154251511343123 +242134112312545355525535253554536455434525565747777346444335464356647647753474356773646733654433665663546244642563345314421211224122331322323 +412433132142141334132434543252622264425454662573753773655373564475543446637637735373477366634444666525263445424664645423315312343452144244311 +344332114244241351525142214566224356456452462326344354764475363746334746646337333637737763345332536642426666635663354355254312322132131323131 +131122332241242144224432125432263364233255422242226745577455737735337775364433433366454374736353664344646633533423434332335151212214413243223 +234242214234112333232155555512323532255465263462233466676735734343577734764766366647564433344543423333456355223241544213123541311212142221331 +141441132112224233454451555413534354322564665444243625534333444434643766364477757477656655464463335535563452453345443233435353224242123124343 +144331231314331344132242232413551365336335442534566254452555534665767376357467637446622223646365466462446552542232412554553522134322422114312 +134214113434441242441523343323532134326253624355324322446664554225677656353747222453535646643544355352452322613134131514514525142433223323211 +323134243341144411245145214232341245635335566666345653633536443645465225362662534525445362323226632632245641512241442214122313344131411414343 +212343323343433323221422422313553433425452324663245554536655554326624422435443626436552256464424624352636413242143535411323321222212134414313 +222324211434321334233532534143131311242353465646253224554652653653422435642254245663622245433355456445562454412533254545222221344143341311122 +322212341224344341433245241234425555552453423346256624666222456636325563532446545362553526364423256422345114245213224541344314433124323411131 +111312132413122144323135414215543225153444362322356525452452452356345555632553532222235354333665362624225212425414412334544114331333221143211 +122332344243113114144314534123511252331523132652452542663432643565426622464544366423233522464666352534215412214222541334342311424433423333321 +211223224243324444224441431521434131252451135224333623265442642225653523535653324236342424534552252111413255122212415314111112313322241221222 +212113121424143212343224342212325341112231455145336543333365466455536246252462365264643466655354352314115541222243143143414144342122431213211 +312323312132421232244144211445241435551135245234253243242342262455222645633446444243345655121234353431542515144235524141411311421233123132122 +131232212122341312221232341344525554312435154254534131334556526542542653426346455252353331523443244421444242555313322312124434113321333111133 +131133221312324313412312432243212515351341414545433521154451412333542424643536635512413124341333222141133451421242143433422134122332233323331 +231113221233222341232124132321211451441554134441534455422411151422513425131244433145554542343113552114533523252113111343313323213211121131123 +322213212211112222114333144342114324353155512533222525434225114221442421414253341433251532554455214224244211311142212433411431311232111313131 +212312221113133244413331343342212424534414254421312155554511123444113433511542332121333125153343511524122121333132411431123332413332322311213 \ No newline at end of file diff --git a/src/bin/day16.rs b/src/bin/day16.rs index 0cb040e..b83b306 100644 --- a/src/bin/day16.rs +++ b/src/bin/day16.rs @@ -35,6 +35,7 @@ impl Day16 { (starting_row_incr, starting_col_incr), )]); + #[allow(clippy::cast_sign_loss)] while let Some(( (mut row, mut col), (mut row_incr, mut col_incr) @@ -47,22 +48,23 @@ impl Day16 { .get(row) .and_then(|row| row.get(col)) { - let directions = if tile == b'-' - && row_incr != 0 - { - vec![(0, -1), (0, 1)] - } else if tile == b'|' - && col_incr != 0 - { - vec![(-1, 0), (1, 0)] - } else { - (row_incr, col_incr) = match tile { - b'/' => (-col_incr, -row_incr), - b'\\' => (col_incr, row_incr), - _ => (row_incr, col_incr), + let directions = + if tile == b'-' + && row_incr != 0 + { + vec![(0, -1), (0, 1)] + } else if tile == b'|' + && col_incr != 0 + { + vec![(-1, 0), (1, 0)] + } else { + (row_incr, col_incr) = match tile { + b'/' => (-col_incr, -row_incr), + b'\\' => (col_incr, row_incr), + _ => (row_incr, col_incr), + }; + vec![(row_incr, col_incr)] }; - vec![(row_incr, col_incr)] - }; for direction in directions { let entry = ((row, col), direction); @@ -88,6 +90,9 @@ impl Day16 { ) } + /// # Panics + /// + /// If the grid is empty pub fn part_two(&self, inp: T) -> usize { let grid = Self::get_grid(inp); @@ -99,24 +104,36 @@ impl Day16 { (0..n_rows) .map(|row| - Self::get_energized_amount(&grid, row, usize::MAX, 0, 1) + Self::get_energized_amount( + &grid, + row, usize::MAX, 0, 1 + ) .max( - Self::get_energized_amount(&grid, row, n_cols, 0, -1) + Self::get_energized_amount( + &grid, + row, n_cols, 0, -1 + ) ) ) .max() - .unwrap() - .max( + .and_then(|max_row| (0..n_cols) .map(|col| - Self::get_energized_amount(&grid, usize::MAX, col, 1, 0) + Self::get_energized_amount( + &grid, + usize::MAX, col, 1, 0 + ) .max( - Self::get_energized_amount(&grid, n_rows, col, -1, 0) + Self::get_energized_amount( + &grid, + n_rows, col, -1, 0 + ) ) ) .max() - .unwrap() + .map(|max_col| max_col.max(max_row)) ) + .unwrap() } } diff --git a/src/bin/day17.rs b/src/bin/day17.rs new file mode 100644 index 0000000..9033bce --- /dev/null +++ b/src/bin/day17.rs @@ -0,0 +1,146 @@ +//! Day 17: Clumsy Crucible +//! +//! +use std::{ + collections::{BinaryHeap, HashSet}, + cmp::Reverse, + fmt::Display, +}; +use aoc_2023::Solution; + +pub struct Day17; + +static ALL_DIRECTIONS: [(i8, i8); 4] = + [(0, -1), (0, 1), (-1, 0), (1, 0)]; + +impl Day17 { + /// # Panics + /// + /// If grid is empty + fn find_path(inp: T, is_part_two: bool) -> Option { + let grid = inp + .to_string() + .lines() + .map(|line| line + .chars() + .filter_map(|c| c.to_digit(10)) + .collect::>() + ) + .collect::>>(); + + let n_rows = grid.len(); + let n_cols = grid + .first() + .unwrap() + .len(); + let mut traversed = HashSet::new(); + let mut to_check = BinaryHeap::from([ + Reverse((0u32, 0u8, (0, 0), (0, 0))) + ]); + let max_dir_traversed = if is_part_two { 10 } else { 3 }; + + while let Some(Reverse((heat, dir_traversed, (row, col), (row_incr, col_incr)))) = to_check.pop() { + let set_entry = (dir_traversed, (row, col), (row_incr, col_incr)); + + if row == n_rows - 1 + && col == n_cols - 1 + && if is_part_two { dir_traversed >= 4 } else { true } + { + return Some(heat); + } + + if !traversed.contains(&set_entry) { + let mut directions = Vec::with_capacity(3); + + if if is_part_two { + dir_traversed >= 4 + || row_incr == 0 + && col_incr == 0 + } else { true } + { + directions.extend(ALL_DIRECTIONS + .iter() + .filter_map(|&(new_row_incr, new_col_incr)| + ( + (new_row_incr != row_incr + || new_col_incr != col_incr) + && (new_row_incr != -row_incr + || new_col_incr != -col_incr) + ) + .then_some(((new_row_incr, new_col_incr), true)) + ) + ); + } + + if dir_traversed < max_dir_traversed + && (row_incr != 0 || col_incr != 0) + { + directions.push(((row_incr, col_incr), false)); + } + + #[allow(clippy::cast_sign_loss)] + for ((row_incr, col_incr), changed_directions) in directions { + let new_row = row.wrapping_add(row_incr as usize); + let new_col = col.wrapping_add(col_incr as usize); + + if let Some(&new_heat) = grid + .get(new_row) + .and_then(|row| row.get(new_col)) + { + to_check.push(Reverse(( + heat + new_heat, + if changed_directions { 1 } else { dir_traversed + 1 }, + (new_row, new_col), + (row_incr, col_incr), + ))); + } + } + traversed.insert(set_entry); + } + } + None + } + + /// # Panics + /// + /// If no paths to the end are found (not possible) + pub fn part_one(&self, inp: T) -> u32 { + Self::find_path(inp, false) + .expect("No paths found") + } + + /// # Panics + /// + /// If no paths to the end are found (not possible) + pub fn part_two(&self, inp: T) -> u32 { + Self::find_path(inp, true) + .expect("No paths found") + } +} + +impl Solution for Day17 { + const NAME: &'static str = "Clumsy Crucible"; + + fn run(&self, inp: String) { + let p1 = self.part_one(&inp); + let p2 = self.part_two(&inp); + + println!("Part 1: {p1}"); + println!("Part 2: {p2}"); + + assert_eq!(p1, 724); + assert_eq!(p2, 877); + } +} + +fn main() { + aoc_2023::run_day(17, &Day17); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { main(); } +} \ No newline at end of file diff --git a/src/bin/day3.rs b/src/bin/day3.rs index 4083ede..a71d8e6 100644 --- a/src/bin/day3.rs +++ b/src/bin/day3.rs @@ -20,7 +20,7 @@ impl Day3 { A: AsRef<[(usize, usize)]>, { let coordinates = coordinates.as_ref(); - let ncoords = coordinates.len() - 1; + let n_coords = coordinates.len() - 1; coordinates .iter() @@ -37,7 +37,7 @@ impl Day3 { (*row + 1, col.wrapping_sub(1)), ]); } - if i == ncoords { + if i == n_coords { indices.extend([ (*row, *col + 1), (row.wrapping_sub(1), *col + 1),