From 8760e4728316ff48eec0d22abbe8b2c3c215700a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sun, 17 Dec 2023 09:37:31 +0100 Subject: [PATCH] 2023 day 17. --- 2023/day17.py | 226 +++++++++++++++++++++++++++++++++++++++++- 2023/inputs/day17.txt | 141 ++++++++++++++++++++++++++ 2023/tests/day17.txt | 13 +++ 3 files changed, 377 insertions(+), 3 deletions(-) diff --git a/2023/day17.py b/2023/day17.py index 992bf35..250af9b 100644 --- a/2023/day17.py +++ b/2023/day17.py @@ -1,13 +1,233 @@ +from __future__ import annotations + +import heapq +import os import sys from collections import defaultdict from dataclasses import dataclass +from typing import Literal, TypeAlias -lines = sys.stdin.read().splitlines() +VERBOSE = os.getenv("AOC_VERBOSE") == "True" + +Direction: TypeAlias = Literal[">", "<", "^", "v"] + + +@dataclass(frozen=True, order=True) +class Label: + row: int + col: int + + direction: Direction + count: int + + parent: Label | None = None + + +# mappings from direction to row shift / col shift / opposite direction +MAPPINGS: dict[Direction, tuple[int, int, Direction]] = { + ">": (0, +1, "<"), + "<": (0, -1, ">"), + "v": (+1, 0, "^"), + "^": (-1, 0, "v"), +} + + +def print_shortest_path( + grid: list[list[int]], + target: tuple[int, int], + per_cell: dict[tuple[int, int], list[tuple[Label, int]]], +): + assert len(per_cell[target]) == 1 + label = per_cell[target][0][0] + + path: list[Label] = [] + while True: + path.insert(0, label) + if label.parent is None: + break + label = label.parent + + p_grid = [[str(c) for c in r] for r in grid] + + for i in range(len(grid)): + for j in range(len(grid[0])): + if per_cell[i, j]: + p_grid[i][j] = f"\033[94m{grid[i][j]}\033[0m" + + prev_label = path[0] + for label in path[1:]: + for r in range( + min(prev_label.row, label.row), max(prev_label.row, label.row) + 1 + ): + for c in range( + min(prev_label.col, label.col), + max(prev_label.col, label.col) + 1, + ): + if (r, c) != (prev_label.row, prev_label.col): + p_grid[r][c] = f"\033[93m{grid[r][c]}\033[0m" + + p_grid[label.row][label.col] = f"\033[91m{grid[label.row][label.col]}\033[0m" + + prev_label = label + + p_grid[0][0] = f"\033[92m{grid[0][0]}\033[0m" + + print("\n".join("".join(row) for row in p_grid)) + + +def shortest_many_paths(grid: list[list[int]]) -> dict[tuple[int, int], int]: + n_rows, n_cols = len(grid), len(grid[0]) + + visited: dict[tuple[int, int], tuple[Label, int]] = {} + + queue: list[tuple[int, Label]] = [ + (0, Label(row=n_rows - 1, col=n_cols - 1, direction="^", count=0)) + ] + + while queue and len(visited) != n_rows * n_cols: + distance, label = heapq.heappop(queue) + + if (label.row, label.col) in visited: + continue + + visited[label.row, label.col] = (label, distance) + + for direction, (c_row, c_col, i_direction) in MAPPINGS.items(): + if label.direction == i_direction: + continue + else: + row, col = (label.row + c_row, label.col + c_col) + + # exclude labels outside the grid or with too many moves in the same + # direction + if row not in range(0, n_rows) or col not in range(0, n_cols): + continue + + heapq.heappush( + queue, + ( + distance + + sum( + grid[r][c] + for r in range(min(row, label.row), max(row, label.row) + 1) + for c in range(min(col, label.col), max(col, label.col) + 1) + ) + - grid[row][col], + Label( + row=row, + col=col, + direction=direction, + count=0, + parent=label, + ), + ), + ) + + return {(r, c): visited[r, c][1] for r in range(n_rows) for c in range(n_cols)} + + +def shortest_path( + grid: list[list[int]], + min_straight: int, + max_straight: int, + lower_bounds: dict[tuple[int, int], int], +) -> int: + n_rows, n_cols = len(grid), len(grid[0]) + + target = (len(grid) - 1, len(grid[0]) - 1) + + # for each tuple (row, col, direction, count), the associated label when visited + visited: dict[tuple[int, int, str, int], Label] = {} + + # list of all visited labels for a cell (with associated distance) + per_cell: dict[tuple[int, int], list[tuple[Label, int]]] = defaultdict(list) + + # need to add two start labels, otherwise one of the two possible direction will + # not be possible + queue: list[tuple[int, int, Label]] = [ + (lower_bounds[0, 0], 0, Label(row=0, col=0, direction="^", count=0)), + (lower_bounds[0, 0], 0, Label(row=0, col=0, direction="<", count=0)), + ] + + while queue: + _, distance, label = heapq.heappop(queue) + + if (label.row, label.col, label.direction, label.count) in visited: + continue + + visited[label.row, label.col, label.direction, label.count] = label + per_cell[label.row, label.col].append((label, distance)) + + if (label.row, label.col) == target: + break + + for direction, (c_row, c_col, i_direction) in MAPPINGS.items(): + # cannot move in the opposite direction + if label.direction == i_direction: + continue + + # other direction, move 'min_straight' in the new direction + elif label.direction != direction: + row, col, count = ( + label.row + min_straight * c_row, + label.col + min_straight * c_col, + min_straight, + ) + + # same direction, too many count + elif label.count == max_straight: + continue + + # same direction, keep going and increment count + else: + row, col, count = ( + label.row + c_row, + label.col + c_col, + label.count + 1, + ) + # exclude labels outside the grid or with too many moves in the same + # direction + if row not in range(0, n_rows) or col not in range(0, n_cols): + continue + + distance_to = ( + distance + + sum( + grid[r][c] + for r in range(min(row, label.row), max(row, label.row) + 1) + for c in range(min(col, label.col), max(col, label.col) + 1) + ) + - grid[label.row][label.col] + ) + + heapq.heappush( + queue, + ( + distance_to + lower_bounds[row, col], + distance_to, + Label( + row=row, + col=col, + direction=direction, + count=count, + parent=label, + ), + ), + ) + + if VERBOSE: + print_shortest_path(grid, target, per_cell) + + return per_cell[target][0][1] + + +data = [[int(c) for c in r] for r in sys.stdin.read().splitlines()] +estimates = shortest_many_paths(data) # part 1 -answer_1 = ... +answer_1 = shortest_path(data, 1, 3, lower_bounds=estimates) print(f"answer 1 is {answer_1}") # part 2 -answer_2 = ... +answer_2 = shortest_path(data, 4, 10, lower_bounds=estimates) print(f"answer 2 is {answer_2}") diff --git a/2023/inputs/day17.txt b/2023/inputs/day17.txt index e69de29..cd9526d 100644 --- a/2023/inputs/day17.txt +++ b/2023/inputs/day17.txt @@ -0,0 +1,141 @@ +221111112123113232223221211223431421234233422312143231535322415224214525132414123325214251231222122422133431331343133332311121121111311212121 +211122211133211332121231312344123432112311144414235315524424354544513135154222442511331423512341112233243413331223414221112333312313311121222 +111212111232312122122123411211113414114234314333354231345141532533115144311545125122533135244454413423434214432314333422122112211321213211221 +221122231321333121323232442421144114441343141354135215251552232314544323421141432422435232132143452331432111434413312222323323231113332322112 +212122131213123131322142314241332133211314312151331433432332431514521525112255111215513512245345521414332333244343214111231211212222232132121 +111123113213211133332414213142233142114244132523312543113125235534453513332535213451144531421355514341443143344424114321214311321221332231122 +112232122333123313114141331431132343312335514212512215243544431435312315425532253451451424215231324153324242323341231323211431231222232132212 +121221332233222231224131442232331124411415411541512445455233142254145354542423112414245455423225113152214424322124242344141423221131222133112 +211123323211333211122314412111342425313421345245115433154452323343413321555222415233151414255251123112353514242234244442133421312212111322222 +232133222211233221241431322213431215215314441523115315155443414664425562236642232244533532341552323154232254333122312313433132133213322132312 +332122221133112432112231324243222522332235351415545113452424343642435522252532463642525245122225514355233335325522121244421224342323122333122 +231311332332333311142322323434233412522544532552431113445423235665326565653443644224346253235333445334123523245544212243312322414431222113122 +122113122311421231214414223424521424431215345444452232652434632252546664645223366463664553556333515312321524455422314423441131212221221121323 +231323311232443422131123331141154145245552322414463423443346436652335432353526466633423445326431251241142425334132444331141434141133313111113 +231311222134334243444322213433553532534451411364562625422353666662653235435633433565255342253426455134333221524133151344131311212244222122333 +132232112333224443233223312255345414421123446442243633255356525652562423526325222352324654435532532654151225513443514254413134323124321131132 +133213332244341333133313444152512133151134664256455526646556226645443652443546256252553342226336242264151412435525415235424141232322311112313 +111223321312234111341444545422525542424562426452465363266654265325642426426352536422552462335564225366212223324455311535434233112421444133313 +323123314344433441342531354154112553444434246346544445256444252325244354652455262524433434453336443355345545141425544544451134433213344232121 +232323344342212142342535212232424335564556533435262656432256432632674366467533224665223363444445442422554345422151544324222334132334241332111 +133223232423323122314421354132153323254633422354453624556642463576647564575576343736322665463634555555225562532253215114313324232121332221123 +331322234312332132433215545232512544454244642632334546423456776463666745433757744477763344654443452223655354553331221153325121221421414211332 +122132242244123121334125321145424442352326565635423643764456355733534673435777746775553553364635436534345663326145555313115543214411244344131 +312123333123124214515442233554454542222665262323236376635734563356477573644544675665563356443624333623523336222421233545254544423322243111222 +311243114143323115332443421523223242245562522433655444353365443474337667576333336646545433666422464425646455445652335253232523231413322143113 +133434112144114322332345224112265663553552465365755633676643745674644466773357577454633466534767353424364562642263521511222454451232431421424 +142311311341424423425235325432562442553254655544743656556735456573764375533444333666374774655744745442245452322446543531413444414212342241443 +134143333132352313214413231335362455625526633774433343335747733336566357473646567536775776563474755364332323264455244212452324512132442122414 +423211433444515553145523455263344624424646753734337534636453463544467353437475546547537434553466757356653446546523522325334433152253422114144 +411412434411121221223155546643465236344343444543563644466477774455666645777455746776755565476675463573544246625325636414552153132333432133321 +414244324443524134245345463335543235522337656757743453666445755586586558555887565664547564676656535555776432366432655241141233141324331214242 +334111441133223534125246255425436443226644375557576354545647876867744557865447764875433334334747354774743236664256233264411542215324213412233 +133142311251453243235345634326326343467357363744654765765757678555774467564575668747885356647463436374736324566526243456351525133213342433414 +112312311555553421155326422255352435364445357373733435655874445684648657866648647656784486734357564563733474336662426235315535325111432244213 +244143214344524313125466464646325536745353665655344684768788458848875777445665687444755744756453353477333343553262656436423511342524215323333 +333314412555144321544534226455643245534546563776756476757645657788765555574746757664787576865447374633677654353555325256556443511355152242122 +321433455351154545564233552662355754555437564353547875644585466874446886466586654865565574855546776336567735344254222666646434311553441541334 +333334413525333452145225266255227546755475333636764564648666746865456654576488454564888664678554366574375343753635252522546323415443433232344 +144331343514123354465665522663264635447667474784886464446756556465466788676875567688847856686657447537454544656464224562432445235433221322332 +413221315152411413456453353266446755454477455455845764565878868865484664768588464447485687544868664464735564344572463532326262133411252252214 +434235535222444454223342363325363577345366377487855678686567757856656769568577654585567784567856668857366437576764253256322235431222125532333 +444322241221114243442636622447465374654467877866478847876664676659558887889886755646546474647874754575456466736354266522623653652345533122444 +341411442531322555334253534334767677753757676466478446788645785757666855569997799956777644467884687447476455645755543433542465554221251523113 +232313541154334642335436446354433666474774867665664666584896595566586557778559586655766564657765878457675433537646574526446566554531313253431 +312354232231515426526564464764375356764454687578884457676665986889859869559679968798856886756557745745755633633734763536256362344231244151441 +241224131434342656222526627647774473645767657785755475877657876985775985657596589799776695864657684556446545774556543545346266263444411334114 +241425521451526266266526363676774635577844868476448547889685557867586598555588955985995978468474475847845677747367566243254444242133353434121 +411445444141354464243464476644565546584874464677855675998855879858667958789756969687859778996654667878645735577745647522242422324255123223231 +122334143235166252352325336773467435445888565856769776978898697696695975578868569997865868785466564475758666563545746743224466256241432414534 +333453331534554533436454464665345374477484876446597887798687857599987766989575755657555988856754574877476747646634636365523644365231541333145 +351243555144622636342425667547633664676555545778897559768759887965777887956896958657967669756767757884786444443537755744563364344644353121231 +453535141543334326442335357736767744846555585549655757787788768867688876678887886866975676969956587676474777443554534364264362353563244324131 +243141131441454666652234665456466648874476884569765665967696667989879677897789985689999669677988465888646687854363374334235526254461321541534 +224442441455345463462267767346445748784554687786596678665675776988899766876779789765678595688769746858666875636657676446455324343525245243154 +422331243324543522445343376745477644668775585687865775858569696979969866887787667777885587987665968887857486467566755543346465563546453522251 +324544511135434245522553353446556747878686845787988759785668767968966688979968789796897896579769997685566757666767767433425466556462255515512 +123534122413362355542733337464368674677788667586787675578998778796667897866887799768975579796679897744786768566346644757372253233665444354312 +211555134263235454354664566336365876654558769687758697668696669877866966779888798878966887699667986874487555567556557543734524653343333325544 +354312451165533323425547433574768865748745779997658978799696966768699676697868978966899958669589755966448467688335367654365666266663412434412 +523215222343532535644535645764774574767865689797688697967886878769996797969968988777979999855977785957455577778855537754735424365422244421541 +312543142165252532233657654763685644885577778996775957666769788676886669776797686898876876596796998858477644847465547774756423366345511551524 +412314233163346653324366543367368447668784977866789766776876678867869897989787889788997998777857679778857556558866475537774463654224623414342 +113131235344463344627665353344666847565845688657576658979698967879988989887989699687968798798978969789578486647453735365334663463523555551532 +223211131233542556344447434536466668467446996959559966799767796787797889979888967666687676655785787777444774767643745753545425262645464141325 +435514122332566223526657333346644545688457597667795979866686769989779788999797899768888879965889996976656476767864446645456224232426564513323 +453145222264234464565637476536488455686686859588658997996876986899977778797797898988998677698788669657785458876553356763377225462555232342114 +341153235525264666334663447737887676878648779856695779867869968887779997777989987776668798975785598777658864458567573434673563432356425432521 +544322112662522623664645567637658585647797556887575969667976677878989899799988798786898888788868566786957466667747455635456322534362322343542 +143235433345625465644543477645646555676669685989566697986997799779988899979777788789696688787566688669765778477544363735446746546246443421423 +214121314423525422346763545665864746646699659669557678698787899779887797897977787787886866989995899975754646774687545545446662646662233352331 +342411323423564425534355477345784848675477789756986687698968979897877897887889797966998969677897888768778658455484656746555623354626425212542 +515411425264662333257767547456787845886568897866679687686976878797778979978977787777978688879989765998576648545787654676776764633632643353352 +111321555243253666276657477435656765856688658757677967667879798888899879897778877798766778668657768889647788887543775375465753445636342542251 +343513142565442562357776474337675854584865785688576696786679888887777899887998889778687888789799857559564444744754664743436526224356653125351 +122242125436564354476646576333776756485576788656759799797699777887978878798888979896699987789659686767745458557847447447567633326222225155215 +455444253653265624243756373366854467565699676887698976699777967788887887889889797779877787769986879998668845745646645566547536344642235255545 +315425355524562466573373356636658777745759876799987999896887969779999897778889997977887679689577558855777866444653457773747344446235655215213 +115334354463445432435633734444688676546647598855858879887779689999997878899988988879999879999777767788665448478475734465667444625425652511125 +423411425234345634336576473345866675774576979869777886698976977897988887977797766977786888869588889867745764887467573766733263234444532314454 +523413443263255443224473454473575744675456758899697768777696889679779888989777889996776677885567878586665464667446677446764344625543321543221 +113353121532262463346334355653374758746555997785679598967897788688988877988888786779686878579969886975866666586844547545675452536566535332324 +321152241456434226534577664553648557885775697588988668787876686669967879779689998899678786978589687557654757664836575675773232646463435333134 +123141441456266452537765766574457654685844678986885875998696989688668898678896689699867897995656579677575564685877636543377342623256425441213 +344233152524565546362645357374584568668744656995676766869867989679766686879896876888897797886776857987865584747665354765372565663253511131111 +355153434133422632334654364334747458567566899969777758869997777689797876986997777787989985958885566864864845655567756733732566325625225413231 +114431353433356242653544373556565467666855556587979695999999689667779896886888997787978767787986778577566445488746747633556553432555424332231 +443543233545335352542436735666744686746585788778866655655677688866968869799897786978685989689787689848667458766764735673454226425322412231155 +414355431112224332333536567756464445848844877875766875865897996877996869768866677888877996979995567668565865783377745354646645534346253324133 +235451144332323523245644735665365844574777848566868797985689697677667968866787879896785986555795688648456567646335455544436524336535152152454 +213214541332444656264344777667653574675778476495676877688778586979999987688988887996769576885897886455875567755577335555626323262352541511151 +214355111153342562633346464753443458467464555568875756857877755968688676797898667685566856677777477465545885873553353573322626226345431542534 +133424543523226436322465347674534665668856744848977999676687787759787776766866886677587768686985446566676585734473334346356524435644324334213 +233443124133452345365245657434633446478686854558897577686999798695965978595977756565987765889685575677688745747663637756234232664443224433132 +114432125515122654243223363455347344588585556865566995966979598889779887667558865558578876587887865444546887367574447573544536625255551415123 +434113123125243252425646663763563543566558757575589866796899878699876675856886688756595756876645848448588436747433334343354534565424113333531 +415312423223134536652245255773674746354458885764844965556857797567878858669856979896855655695458867477655447537744767362442266544221212122552 +432512134334552653555622625755577434777566767668486647885577585887856665685769698986899798855886584754444373433343573254626456326513345341343 +431322251453143463243535627373334676534885555587787554879555888765755898796875655597577677855765774857676754436743376254244624224241134511122 +322425542152425626564554236756675764577786446454486686665587756855886689567858565856878676776788556846733653543437654263566562445551511334241 +222223414252541366223232362647453543576548647485456688465857855988788666969886858886768555585874474784675675566734762422536334245533323224411 +324135241332225346366256336534777643553637774887787778666574986996558665786975969978558676864565768444664734344357655435246465431522134143321 +221323125312133154545435263334564433764747474848857475888775787589885579677589858466776877557474765543755543564367423324352546545121132245434 +213314154253123446564546356556467653376666744784865888647454856456888767555755578668748875855656486575537353636645665336243452342352114255414 +333232431543244252356436446656467664374744743685777866677748867474847578548578655777544778684665564664734446566644435356345644415515215142143 +344433553315244454453563264264354635567373545656455675855777788575777684784447475646545664868786565736755346673526535554323634322414514352112 +113142221324341425364462435555657767377466773576874644586487667677855647887778748476758577885888545363434446576222246365246422255515533414312 +334143221411221412556554466362455334477466743455558748678667485845867877867556667646645474755535636655457455346243542562444555142215215133133 +331133333511344143453426662645223444354457377343347746856547845774856555545884657654774468848665664746356454463442342232555555252322425422412 +212142143434511513315234562342634323655755745535646646644656874568567575668648558647756875637564343653357754556364644633342525225545432342244 +413234344425515111442134642532565452576573464334654673886665444576757766558688765487844745663765763467677372663235265336344545215343231341343 +414233433441132244243434533566343434234434566767437744358866655577764554887858864688567346565453446637656663656236565326615313522245121333311 +341324242355122322351314564564323664343747636567773667357475856548787648476867557854354655537456333747467446363535464234255241534332331224232 +412433321333545115253524555262526323324574736534733737337567467475487578777646756657444546744457673563474344444434332444423253424153411342342 +241424222341541433424353364423642226426364566775356555774454377676457363477374373465736756635446356575644323425256265445553534243521441241421 +334313222442411122141435216232435544224245754764566747755357353566367536334375636774736456346447733573566443353366446124441231122554224433334 +334243243213212134441314442352554533355435634343465446373345433743555567474657664373375434757476357423664634424235654513214124232421234224124 +231214242234133322134344454262434464532444624575453673735666647554467675666757344344434676474556555452264224662625642151241421541121214411243 +133422321412321521315415414152632465242663446423546456476453447536464353575437476333577676334375434444534643424625515143143125542242122443223 +133313424413242132435143145415624425432323342465636373477633334655443375367675734644754744444543655253644424536623311412512255414122232231332 +211434114433312255415534254152336656453436642362432756543657654776563733355333775375767673446532434442535552454415513542345325123344322124311 +133142123433422412535554325232543335224353432264466563753456566753774666476365366547377555456326365525436265335521131124331451232211413242422 +133214314433124434412522135251352233366535226644666262327435765447754777365657457556356355323555453466525643511233512441244413434314231323211 +211131222343342224242524255124414422543653344232555334263535564453336637333375357332635324436463655644653464321142553551135324123233211333231 +123233234322243443445531354522242541265224366554462456526553424643335377647376533435555454436353464352634531155433531123441143342414414313222 +212113141323343313113124515542132431124454356332452466443364225643625462365425543453223226552266322354562115144321531543511131314334141223112 +213111133114434224222123524231135315522345254635366223346536655223235642534423253642526446463435556343332441224222442155324132241424113121322 +223222321231241122313412543431335251132245645353232542526652653434224222554626366263322655356264633452155451544521214314122334243222421113121 +131232332414122232342441453354552333215545165353623522554352256652334326466453635363245464336465552651524123244224254341331222443242112332222 +221131111141332114113122223223245133232545533634662444236455452656226446632254453235365564664624461353243551312122131213222112114314232311312 +222332312133244424124314434324521342222353512451455246535234536536534363523264534423664452646235525122552333134242253313211122223324122122111 +133131131113412132434133112452142521331241432443442226622632522465465365425423456324662622533125242352152452351342234432331424311212221322321 +312313111312231122441141224234215431542441154131222223554646555362246635324644232464355626533215423242522444345313324324324232433411331212331 +312332312332211141111432433434233421253514444553354253424256255626434565544362656622233332321331524325355155452244211211432413114222112211233 +231123222213331342111342344334333452325414125114135254421553133645344245456665245445445411133352351341555111313142122113422232311312122211223 +121123132323111314324343143134432145441543533444112222153335113243211134331544531545514234523115432244322244522132334211124411122131131311233 +121222233311211131211241414434334412214242211512332245551314511441334422313445555324141134211225113155154522443414123143421422223122133332131 +221122232211322333121343424443211412124113553454354415252111413545523252335341312114233241345455552432344224331121212123341131121321123133322 +122111232133212333114241342211414432432451525532212541215254352135314151345452314123533254542323111254213312342213222422324223233113312333211 +112221312121332223111124124211412322424112443244532125423544253254455521144521222323412142453225133322144423313322213112233113122332123222111 +222222213133232221311123322442113434233223134131232412515351432512434332551315534323552531252322544341242341431223321421331112333333332122112 +112222121122221212222331122123242441213424442111531144421232221124154331442433155341112524322525212214112321333234222132131323112333212111122 diff --git a/2023/tests/day17.txt b/2023/tests/day17.txt index e69de29..f400d6e 100644 --- a/2023/tests/day17.txt +++ b/2023/tests/day17.txt @@ -0,0 +1,13 @@ +2413432311323 +3215453535623 +3255245654254 +3446585845452 +4546657867536 +1438598798454 +4457876987766 +3637877979653 +4654967986887 +4564679986453 +1224686865563 +2546548887735 +4322674655533