Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lovebes/de57c109217ff87745f9153e25ef65a6 to your computer and use it in GitHub Desktop.
Save lovebes/de57c109217ff87745f9153e25ef65a6 to your computer and use it in GitHub Desktop.
2021 Advent of Code Day 15 - with Djikstra Implementation in Elixir, two different min-dist finding solutions
defmodule Advent do
@moduledoc """
How to use it:
1. Uncomment which `@proof` you want to run against - part I or part II
2. Uncomment correct `@last_coord` for target coordinate
3. Decide if you want to expand it or not (check docstring in `run/1), and pipe that result into `Advent.run/1`,
instead of just calling `Advent.run/1`
Metrics:
* Part I: 700ms ~ 1000ms
* Part II: hours (I left it running and slept)
"""
@ascii_offset 48
# @proof [
# '1964778752979887222739789777935919929996793679617497991954953881381939846468999159686925929898196249',
# '8759189989991999115121999113211788158983883981916933973182798769168715496674423979199573198873854989',
# '2916817799179797949192724497956464139512861918986689421481689714471669982489852996119597949888649993',
# '3429492714828168979771398891678818935485694839763399796988678112189583269768679792191755489996819818',
# '7956995159237939293319975584779958986526992814763894821138352743589946198365389719619992176545838879',
# '6894733223797749829183642911499532916859775927195417482554567729418599872969862136349799419994687895',
# '2759239975966982742446721118515649658718899983621767679758422696981391642329892996989459995669798697',
# '4871846878568885679911989919675587272979337779997878941142835988221478131873935963978729297886139169',
# '7999959169281333751174889518989895572896163199221368686388799926519794699799923174194941238928589796',
# '9673899872976699541997289776451458192595451289851921695168998599956996287274974589993895999911968979',
# '2189799897986439761837888853324829341216529742299672852941871997559375864789386122271746495896794791',
# '9938386946518957531699878681584584964918976817297812956462471969177758385919189982296399838923928939',
# '4275189819452758426829188964681958425813889249999799196398898929428939818471881449812315725979925948',
# '7839191997723839711989597539332999799698291856397972249727787849495896789269554411391811373488912969',
# '9991932961553536898969691427116875526215696896938613596999539698956698498155643927131628839711918729',
# '9689481119559879668959932516469834984764657446183977948691462949415366788398622963991783197189499749',
# '7893891719779792989279958991976795699181589597959138897764521861982247998328792718743913147198513966',
# '9229398293621962999951889798191298941966911561894718699997528891728499799769299594844878826379212229',
# '8114755891869969978798891934912769591996819776399989765797896128921791896759288599146617996799938892',
# '5972439691937789543995348496582934955419378443786946743176772134892759981289214999252492789994912519',
# '9999878284296198978291425928754817436271169898969986169389621659848796896718349699944959841976887979',
# '8995172699256977369819859998243969598411799887371899978965729218569995255335578989297761711979697279',
# '5964929613931779238784627758412212181166181675691169528311979637962893954537763982917915779197598919',
# '9329999971496358947166338919899899416981161169866529652865685792919877767118176597496599597196924899',
# '7271975999674778999171971959191993946848163989989997969948924863192929224488787991896699281749827981',
# '7699689989198719837571649869869997239499861716884656858395517769988394779311619459491989383499197268',
# '8138528939235689938575248913721989783488836178269759533138953836592979449873625149573891997212479889',
# '1194186961217198193988498992355788419825494192989525159689798712975319599971827597742959843955911979',
# '9991363768629695336799575699999721989989917969232982174499837972549921599924797598996111985425191495',
# '6224199895942439267375819799214473982598281689668323916911879197967989217151818849734921868839889953',
# '2797891953385665166198975159893818966216198377578712873269784989492119916357987941796591437988938957',
# '9779821394499494593989986959996998692998298791629567995816849947192476378699325431774652164876319399',
# '8279883158919549641581758481988868892673899854755757655799879479727992921235786698877699587719557817',
# '8994986299499779981878149737914546992923788331973355557931912813487414849169586776188929999316996999',
# '3499941923877958919184968172278689699896918797161441492525291995817947177988899429956927999874117615',
# '9518735698739793988999949283669535854518279898395156698682921779151989183918899376431178719847211917',
# '8959569562449992579869539699738394615824477992978595158952999831143599733757959319878895488818466994',
# '4771221597963589993951489919897987919972847979856478899532499699995165221761395991179429879943877199',
# '9358396988965649992967998569567913995272198899939966249136998289878785998148692727934781927566617716',
# '9695897663786123274922829519725449941162197499897582997999837678932164486457189978772888188949785949',
# '2229629789279799955899167476898994839159369899619998578354226667829287299556269181995691312568798773',
# '8986191398899845547849998899881493546398886844887168962985891999929956754259992377999937693899755175',
# '7175873171397179677593515237961898681148199598839711877778496983153317579398682998411683978987881997',
# '1298538869811849383999117371323559984927944988899199699928999419957218968156974484868752469129262989',
# '9731561977991246175968959799134367415782961298978241777998529577773489176786529999298276919819282278',
# '5139485996594989893145888798563499416819996991199156853594539997297977872962913739949965145219399965',
# '8983481898839581176999892935177977798924747335789486142969991819778229398776888781189418591899219928',
# '5991279788897976346748147414862695456951589321587991882347718411894488859198898259759815513119791569',
# '8227945959958298837959782585999896633581225814664916697479824997438358779792895774979843752914453972',
# '4969151368678265681558739735791991537887198957112191497717769345618978852795923288497297897895929999',
# '1519799684949231934618481659618992524489812446699528583999896615941151949985499999993886929639497791',
# '1919828829886491381185477846954179789637515617553898959167959989111765297739994219233999883196929166',
# '9998739993999963799842299399178749796575293889914288923995766963218597719191435991789849824475293761',
# '6292981699893999814978995879985986732598297996514589923713342895999879593853119193717396792997847699',
# '9995978699659218782765696299479144716993781213851813753413888998887766896164659855572836992467197889',
# '9217712236953916773136797729855561896118885989789931276897245797585829719971197298416959471465967299',
# '8749983199162381588749185138547797187291312282935858898773319299528189947814491298821948298698917998',
# '8869537419399779797931123692837166294495799967721996873988844831152966589773118129996872129174245734',
# '6999949888788198499769769893119838727389879594985996264338836189769745171919499431598495487598996829',
# '2643438319977946618997882718739614961945999488979133494999859875958989978289978891995578284983191769',
# '9711343348446947989897455526854391996559785892751279911869879891927989217818761659468859389913918159',
# '4684878627919284791377127887978412232228626693294949676597441174499789199371869999845955919776487925',
# '7336328939319927281586293939269992937969668286319998679381249791842789823962288582686236183876186189',
# '8498199998653599954199689592971461496115597192381769198382378211678161716941995196615974678163278978',
# '2998646498892845989822928114511999895167278839959595169842949959999918557832795946119311714389383991',
# '9984244818187938587919721975981668841119986975695637997888658465896898817589749499729979598979727197',
# '3863786997161389931987278761687458498299597892974921921998696597634973915194429518998588919819992157',
# '3119918226997269698895866728999573113997218692982671793292949996198899525937453993612931127922615284',
# '6379719955643911286241981859839442769958499849676976399619189989256898582949928891859195489926593225',
# '9466177592439495219155699988857895114899757931376891988791861639981429819498859829699953915984557119',
# '5374978935117993911513985778249971394799996215739819192271757989986194819377829685788878384186668579',
# '5996948596188399596276916591738868899415917585689991911459474597973519819254199897579188972591385492',
# '9322616873977891567797785992628933998998689889969238979881992269985738953791967118985999822789973433',
# '9199392396987912599354118198522389299456969987319191279618989499499887993924599797794177285631979167',
# '8855183914541833731439967697497418572879596386674694298959932189933841866512369842921637392699374912',
# '8988284461979263735382265499953696978939495465937911934474937417988811279816877822138921898948376181',
# '5437381949531929775936998169527199196578734594998292179919393485374186397999598711914297897676779586',
# '2396881448499782771276998937887566187891818989841832748994742788697928591911992786489698526461694989',
# '4246912291936687945949431175795992778687971357323979793399128579749866596897749827138281972961559158',
# '8647699922398842951889916948999698459962477299829918411933795388631159575199881299857589572777687999',
# '1311988719839948383899391321597894992589538493239179737996223383196197788192688789936916569885746193',
# '7954826979959947682969468987879991533849653569579969694792898697776848612986289349116791369579963519',
# '6593461421187759143952361892951669186319499192899168818751698499375889498194687225996979991984182191',
# '9335328245597991499897759977969229696591989282589998699129519144748578917798947994995199792918282991',
# '9398849415997381994698899787899727198999987399289971848636857987925948679582957479378858514993191181',
# '4149987331746768149745999853585139629671813691691596384395667999149381812199968928229665789284992374',
# '7427922988292997779559894894993998973997839317916919959899499493579911986477729836979789677259549118',
# '2642977979999586628631219897898878639971585969663183921898877933957895824999784889149738669989189789',
# '9762493593855472517917523499451984451888889977182975177177631967968932881279659956186784398919998985',
# '6432918193479269929129386389769916441769189759989721934769679259263889887181527989833327889848697941',
# '1393231159891181194862992269761747833947956873525561418387821863318689879524877738438288899299953799',
# '3842851494837961961556887296519919879914829134229941988917932644519999778998783391698499139917994989',
# '6459949159299986724199928878695399917976578879189193215239688557999671187992222149868714176139783787',
# '8463293782889894999994957687196286799689666437669478542975956699912379182795887258767997618178425918',
# '1953199634739829152796952926222792594239959671973846419654368418698936881329847929915911741616629164',
# '7845537283597249729992171128489499424199835337125999796988893187159337984973899291199499518477759357',
# '8997952978886957899187992878357919738776489938875699819693849929789872998684248986998859178999693399',
# '1138799983929178584919823996794446498474269912579351992888547384896372519992979599519718742166879882',
# '7298971978993365997919769838756115168896812437881278598927724918958597279567156161156299352891298849',
# '9179979719419789581891969361814895646898199782835916681989892966646599953969992849273537533978176119'
# ]
@proof [
'1163751742',
'1381373672',
'2136511328',
'3694931569',
'7463417111',
'1319128137',
'1359912421',
'3125421639',
'1293138521',
'2311944581'
]
# Set last coordinates here
@last_coord {9, 9}
# @last_coord {49, 49}
# @last_coord {99, 99}
# @last_coord {499, 499}
@doc """
Main function to run.
If you want to run the expansion of increasing the map 5-fold, do this:
ex = Advent.expand_raw_list()
Advent.run(ex)
Explanations:
----------------
This implements a pretty naive understanding of Djikstra.
Due to Elixir(Erlang to be exact)'s linked-list structure of how lists are implemented,
`dist` (distance list) is implemented by `map`, as it is a good middle ground (https://www.theerlangelist.com/article/sequences)
between functionality and ease of reasoning.
Both `visited_nodes` and `dist` are maps with coordinate tuples {x, y} as the key.
However, because it would have a lot of `:inf` items early on, it was attempted to also maintain an `inverted_dist`.
Even then... part II of Day 15 in Advent of Code takes HOURS to run.
I've run out of ideas other than tapping into Nx for something like this. Wonder if it would help.
"""
def run(raw_map \\ @proof) do
mapped = dump_raw_list_to_map(raw_map) |> IO.inspect(label: "mapped")
{dist, visited_nodes} = init(mapped)
# inverted dist so key is integer distance value, and key is coord tuple
inverted_dist = %{0 => [{0, 0}]}
walk(mapped, visited_nodes, dist, nil, inverted_dist)
end
# Last iteration, reaching the final target coordinate. Return the value for the @last_coord in dist
defp walk(_mapped, _visited_nodes, dist, @last_coord, _) do
dist |> Map.get(@last_coord)
end
defp walk(mapped, visited_nodes, dist, _prev_coord, inverted_dist) do
u = get_next_coord(visited_nodes, dist, inverted_dist)
# O(1)
updated_visited_nodes = update_value_at(visited_nodes, u, true)
# O(1)
{updated_dist, updated_inverted_dist} =
update_dist_of_all_unvisited_adjacent_nodes(mapped, dist, visited_nodes, u, inverted_dist)
walk(mapped, updated_visited_nodes, updated_dist, u, updated_inverted_dist)
end
defp check_dists_vals_parity(dist, inverted_dist) do
# check dist types parity
d_vals =
dist
|> Enum.filter(fn
{_coord, val} -> val != :inf
end)
|> Enum.map(fn {_, val} -> val end)
|> MapSet.new()
i_vals = inverted_dist |> Enum.map(fn {val, _coord} -> val end) |> MapSet.new()
if !MapSet.equal?(d_vals, i_vals) do
IO.inspect(d_vals, label: "d_vals")
IO.inspect(i_vals, label: "i_vals")
raise "not equal"
end
end
defp get_next_coord(visited_nodes, dist, inverted_dist) do
# # {next_coord, _} =
# check_dists_vals_parity(dist, inverted_dist)
# coords_by_inverted =
inverted_dist
|> Enum.reduce([], fn {val, coord_list}, acc ->
filtered_coords =
coord_list
|> Stream.filter(fn coord -> !get_value_at(visited_nodes, coord) end)
|> Enum.sort_by(fn {x, y} -> {x, y} end)
case filtered_coords do
[] ->
acc
rest ->
updated_pair = {val, rest}
[updated_pair | acc]
end
end)
|> get_smallest_inv_dist_coord()
end
defp get_smallest_inv_dist_coord(inverted_dist_in_list) do
smallest =
inverted_dist_in_list
|> Stream.map(fn {val, _coord} -> val end)
|> Enum.sort()
|> hd
Enum.find(inverted_dist_in_list, fn {val, _coord} -> val == smallest end)
|> elem(1)
|> hd
end
defp update_dist_of_all_unvisited_adjacent_nodes(
mapped,
dist,
visited_nodes,
coord,
inverted_dist
) do
coord_dist_value = get_value_at(dist, coord)
adjacent_nodes(mapped, coord)
|> adjacent_not_in_visited(visited_nodes)
|> Stream.filter(fn {_, val} -> !is_nil(val) end)
|> Enum.reduce({dist, inverted_dist}, fn {node_coord, wgt}, {prev_dist, prev_inverted_dist} ->
sum = coord_dist_value + wgt
node_dist_value = get_value_at(prev_dist, node_coord)
cond do
node_dist_value == :inf ->
{update_value_at(prev_dist, node_coord, sum),
put_in_inverted_dist(prev_inverted_dist, node_coord, sum)}
sum < node_dist_value ->
{update_value_at(prev_dist, node_coord, sum),
put_in_inverted_dist(prev_inverted_dist, node_coord, sum)}
true ->
{prev_dist, prev_inverted_dist}
end
end)
end
defp put_in_inverted_dist(inverted_dist, coord, sum) do
if inverted_dist[sum] do
Map.put(inverted_dist, sum, [coord | inverted_dist[sum]])
else
Map.put(inverted_dist, sum, [coord])
end
end
defp init(mapped) do
dist = create_dist_from_coord_val_map(mapped)
visited_nodes = %{}
{dist, visited_nodes}
end
# O(1)
defp adjacent_not_in_visited(list_of_adjacent_with_weight, visited_nodes) do
list_of_adjacent_with_weight
|> Enum.filter(fn {coord, _wgt} ->
!get_value_at(visited_nodes, coord)
end)
end
defp adjacent_nodes(mapped, {x, y}) do
[
coord_with_weight(mapped, {x - 1, y}),
coord_with_weight(mapped, {x + 1, y}),
coord_with_weight(mapped, {x, y - 1}),
coord_with_weight(mapped, {x, y + 1})
]
end
defp coord_with_weight(mapped, coord) do
{coord, get_value_at(mapped, coord)}
end
def dump_raw_list_to_map(list_charlist \\ @proof) do
list_charlist
|> Stream.with_index()
|> Stream.flat_map(fn {charlist, row_idx} ->
charlist
|> Stream.with_index()
|> Stream.map(fn {val, col_idx} ->
parsed_val = val - @ascii_offset
{{col_idx, row_idx}, parsed_val}
end)
end)
|> Map.new()
end
def expand_raw_list(list_charlist \\ @proof) do
# expand 5 fold
unit_width = list_charlist |> hd |> length
unit_height = list_charlist |> length
enlarged_rows =
for unit_row <- list_charlist do
1..4
|> Enum.reduce(unit_row, fn _, combined ->
next_set =
combined
|> Enum.reverse()
|> Stream.take(unit_width)
|> Stream.map(&incr_val/1)
|> Enum.reverse()
combined ++ next_set
end)
end
reversed_enlarged_rows =
enlarged_rows
|> Enum.reverse()
1..4
|> Enum.reduce(reversed_enlarged_rows, fn _, combined ->
combined
|> Enum.take(unit_height)
|> Enum.reverse()
|> Enum.reduce(combined, fn row, acc ->
[Enum.map(row, &incr_val/1) | acc]
end)
end)
|> Enum.reverse()
end
defp incr_val(char) do
case char - @ascii_offset do
9 -> @ascii_offset + 1
x -> @ascii_offset + (x + 1)
end
end
defp create_dist_from_coord_val_map(value_map) do
Enum.map(value_map, fn
{{0, 0}, _val} -> {{0, 0}, 0}
{coord, _val} -> {coord, :inf}
end)
|> Map.new()
end
def get_value_at(mapped, {x, y}) do
Map.get(mapped, {x, y})
end
defp update_value_at(mapped, coord, value) do
Map.put(mapped, coord, value)
end
end
defmodule Advent do
@moduledoc """
How to use it:
1. Uncomment which `@proof` you want to run against - part I or part II
2. Uncomment correct `@last_coord` for target coordinate
3. Decide if you want to expand it or not (check docstring in `run/1), and pipe that result into `Advent.run/1`,
instead of just calling `Advent.run/1`
Metrics:
===================
1. With Enum.sort of distance map
* Part I: 700ms ~ 1000ms (answer: 720)
* Part II: hours (I left it running and slept) (answer: 3025)
2. With using Pairing Heap (current solution)
* Part I: 38.558 ms (answer: 720)
* Part II: 1797 ms !!!!!! (answer: 3025)
NOTE: This uses Pairing Heap module for getting the minimal value of distance list,
code used from: https://github.com/ewildgoose/elixir_priority_queue/blob/master/lib/pairing_heap.ex
"""
@ascii_offset 48
@proof [
'1964778752979887222739789777935919929996793679617497991954953881381939846468999159686925929898196249',
'8759189989991999115121999113211788158983883981916933973182798769168715496674423979199573198873854989',
'2916817799179797949192724497956464139512861918986689421481689714471669982489852996119597949888649993',
'3429492714828168979771398891678818935485694839763399796988678112189583269768679792191755489996819818',
'7956995159237939293319975584779958986526992814763894821138352743589946198365389719619992176545838879',
'6894733223797749829183642911499532916859775927195417482554567729418599872969862136349799419994687895',
'2759239975966982742446721118515649658718899983621767679758422696981391642329892996989459995669798697',
'4871846878568885679911989919675587272979337779997878941142835988221478131873935963978729297886139169',
'7999959169281333751174889518989895572896163199221368686388799926519794699799923174194941238928589796',
'9673899872976699541997289776451458192595451289851921695168998599956996287274974589993895999911968979',
'2189799897986439761837888853324829341216529742299672852941871997559375864789386122271746495896794791',
'9938386946518957531699878681584584964918976817297812956462471969177758385919189982296399838923928939',
'4275189819452758426829188964681958425813889249999799196398898929428939818471881449812315725979925948',
'7839191997723839711989597539332999799698291856397972249727787849495896789269554411391811373488912969',
'9991932961553536898969691427116875526215696896938613596999539698956698498155643927131628839711918729',
'9689481119559879668959932516469834984764657446183977948691462949415366788398622963991783197189499749',
'7893891719779792989279958991976795699181589597959138897764521861982247998328792718743913147198513966',
'9229398293621962999951889798191298941966911561894718699997528891728499799769299594844878826379212229',
'8114755891869969978798891934912769591996819776399989765797896128921791896759288599146617996799938892',
'5972439691937789543995348496582934955419378443786946743176772134892759981289214999252492789994912519',
'9999878284296198978291425928754817436271169898969986169389621659848796896718349699944959841976887979',
'8995172699256977369819859998243969598411799887371899978965729218569995255335578989297761711979697279',
'5964929613931779238784627758412212181166181675691169528311979637962893954537763982917915779197598919',
'9329999971496358947166338919899899416981161169866529652865685792919877767118176597496599597196924899',
'7271975999674778999171971959191993946848163989989997969948924863192929224488787991896699281749827981',
'7699689989198719837571649869869997239499861716884656858395517769988394779311619459491989383499197268',
'8138528939235689938575248913721989783488836178269759533138953836592979449873625149573891997212479889',
'1194186961217198193988498992355788419825494192989525159689798712975319599971827597742959843955911979',
'9991363768629695336799575699999721989989917969232982174499837972549921599924797598996111985425191495',
'6224199895942439267375819799214473982598281689668323916911879197967989217151818849734921868839889953',
'2797891953385665166198975159893818966216198377578712873269784989492119916357987941796591437988938957',
'9779821394499494593989986959996998692998298791629567995816849947192476378699325431774652164876319399',
'8279883158919549641581758481988868892673899854755757655799879479727992921235786698877699587719557817',
'8994986299499779981878149737914546992923788331973355557931912813487414849169586776188929999316996999',
'3499941923877958919184968172278689699896918797161441492525291995817947177988899429956927999874117615',
'9518735698739793988999949283669535854518279898395156698682921779151989183918899376431178719847211917',
'8959569562449992579869539699738394615824477992978595158952999831143599733757959319878895488818466994',
'4771221597963589993951489919897987919972847979856478899532499699995165221761395991179429879943877199',
'9358396988965649992967998569567913995272198899939966249136998289878785998148692727934781927566617716',
'9695897663786123274922829519725449941162197499897582997999837678932164486457189978772888188949785949',
'2229629789279799955899167476898994839159369899619998578354226667829287299556269181995691312568798773',
'8986191398899845547849998899881493546398886844887168962985891999929956754259992377999937693899755175',
'7175873171397179677593515237961898681148199598839711877778496983153317579398682998411683978987881997',
'1298538869811849383999117371323559984927944988899199699928999419957218968156974484868752469129262989',
'9731561977991246175968959799134367415782961298978241777998529577773489176786529999298276919819282278',
'5139485996594989893145888798563499416819996991199156853594539997297977872962913739949965145219399965',
'8983481898839581176999892935177977798924747335789486142969991819778229398776888781189418591899219928',
'5991279788897976346748147414862695456951589321587991882347718411894488859198898259759815513119791569',
'8227945959958298837959782585999896633581225814664916697479824997438358779792895774979843752914453972',
'4969151368678265681558739735791991537887198957112191497717769345618978852795923288497297897895929999',
'1519799684949231934618481659618992524489812446699528583999896615941151949985499999993886929639497791',
'1919828829886491381185477846954179789637515617553898959167959989111765297739994219233999883196929166',
'9998739993999963799842299399178749796575293889914288923995766963218597719191435991789849824475293761',
'6292981699893999814978995879985986732598297996514589923713342895999879593853119193717396792997847699',
'9995978699659218782765696299479144716993781213851813753413888998887766896164659855572836992467197889',
'9217712236953916773136797729855561896118885989789931276897245797585829719971197298416959471465967299',
'8749983199162381588749185138547797187291312282935858898773319299528189947814491298821948298698917998',
'8869537419399779797931123692837166294495799967721996873988844831152966589773118129996872129174245734',
'6999949888788198499769769893119838727389879594985996264338836189769745171919499431598495487598996829',
'2643438319977946618997882718739614961945999488979133494999859875958989978289978891995578284983191769',
'9711343348446947989897455526854391996559785892751279911869879891927989217818761659468859389913918159',
'4684878627919284791377127887978412232228626693294949676597441174499789199371869999845955919776487925',
'7336328939319927281586293939269992937969668286319998679381249791842789823962288582686236183876186189',
'8498199998653599954199689592971461496115597192381769198382378211678161716941995196615974678163278978',
'2998646498892845989822928114511999895167278839959595169842949959999918557832795946119311714389383991',
'9984244818187938587919721975981668841119986975695637997888658465896898817589749499729979598979727197',
'3863786997161389931987278761687458498299597892974921921998696597634973915194429518998588919819992157',
'3119918226997269698895866728999573113997218692982671793292949996198899525937453993612931127922615284',
'6379719955643911286241981859839442769958499849676976399619189989256898582949928891859195489926593225',
'9466177592439495219155699988857895114899757931376891988791861639981429819498859829699953915984557119',
'5374978935117993911513985778249971394799996215739819192271757989986194819377829685788878384186668579',
'5996948596188399596276916591738868899415917585689991911459474597973519819254199897579188972591385492',
'9322616873977891567797785992628933998998689889969238979881992269985738953791967118985999822789973433',
'9199392396987912599354118198522389299456969987319191279618989499499887993924599797794177285631979167',
'8855183914541833731439967697497418572879596386674694298959932189933841866512369842921637392699374912',
'8988284461979263735382265499953696978939495465937911934474937417988811279816877822138921898948376181',
'5437381949531929775936998169527199196578734594998292179919393485374186397999598711914297897676779586',
'2396881448499782771276998937887566187891818989841832748994742788697928591911992786489698526461694989',
'4246912291936687945949431175795992778687971357323979793399128579749866596897749827138281972961559158',
'8647699922398842951889916948999698459962477299829918411933795388631159575199881299857589572777687999',
'1311988719839948383899391321597894992589538493239179737996223383196197788192688789936916569885746193',
'7954826979959947682969468987879991533849653569579969694792898697776848612986289349116791369579963519',
'6593461421187759143952361892951669186319499192899168818751698499375889498194687225996979991984182191',
'9335328245597991499897759977969229696591989282589998699129519144748578917798947994995199792918282991',
'9398849415997381994698899787899727198999987399289971848636857987925948679582957479378858514993191181',
'4149987331746768149745999853585139629671813691691596384395667999149381812199968928229665789284992374',
'7427922988292997779559894894993998973997839317916919959899499493579911986477729836979789677259549118',
'2642977979999586628631219897898878639971585969663183921898877933957895824999784889149738669989189789',
'9762493593855472517917523499451984451888889977182975177177631967968932881279659956186784398919998985',
'6432918193479269929129386389769916441769189759989721934769679259263889887181527989833327889848697941',
'1393231159891181194862992269761747833947956873525561418387821863318689879524877738438288899299953799',
'3842851494837961961556887296519919879914829134229941988917932644519999778998783391698499139917994989',
'6459949159299986724199928878695399917976578879189193215239688557999671187992222149868714176139783787',
'8463293782889894999994957687196286799689666437669478542975956699912379182795887258767997618178425918',
'1953199634739829152796952926222792594239959671973846419654368418698936881329847929915911741616629164',
'7845537283597249729992171128489499424199835337125999796988893187159337984973899291199499518477759357',
'8997952978886957899187992878357919738776489938875699819693849929789872998684248986998859178999693399',
'1138799983929178584919823996794446498474269912579351992888547384896372519992979599519718742166879882',
'7298971978993365997919769838756115168896812437881278598927724918958597279567156161156299352891298849',
'9179979719419789581891969361814895646898199782835916681989892966646599953969992849273537533978176119'
]
# @proof [
# '1163751742',
# '1381373672',
# '2136511328',
# '3694931569',
# '7463417111',
# '1319128137',
# '1359912421',
# '3125421639',
# '1293138521',
# '2311944581'
# ]
# Set last coordinates here
# @last_coord {9, 9}
# @last_coord {49, 49}
# @last_coord {99, 99}
@last_coord {499, 499}
@doc """
Main function to run.
If you want to run the expansion of increasing the map 5-fold, do this:
ex = Advent.expand_raw_list()
Advent.run(ex)
Explanations:
----------------
This implements a pretty naive understanding of Djikstra.
Due to Elixir(Erlang to be exact)'s linked-list structure of how lists are implemented,
`dist` (distance list) is implemented by `map`, as it is a good middle ground (https://www.theerlangelist.com/article/sequences)
between functionality and ease of reasoning.
Both `visited_nodes` and `dist` are maps with coordinate tuples {x, y} as the key.
"""
def run(raw_map \\ @proof) do
mapped = dump_raw_list_to_map(raw_map)
{dist, visited_nodes} = init(mapped)
priority_queue = {}
{time, result} = :timer.tc(fn -> walk(mapped, visited_nodes, dist, nil, priority_queue) end)
IO.inspect("Done. It took: #{time / 1_000} msec")
result
end
defp init(mapped) do
dist = create_dist_from_coord_val_map(mapped)
visited_nodes = %{}
{dist, visited_nodes}
end
# Last iteration, reaching the final target coordinate. Return the value for the @last_coord in dist
defp walk(_mapped, _visited_nodes, dist, @last_coord, _) do
dist |> Map.get(@last_coord)
end
defp walk(mapped, visited_nodes, dist, _prev_coord, priority_queue) do
u = get_next_coord(visited_nodes, dist, priority_queue)
# O(1)
updated_visited_nodes = update_value_at(visited_nodes, u, true)
# O(1)
{updated_dist, updated_priority_queue} =
update_dist_of_all_unvisited_adjacent_nodes(
mapped,
dist,
visited_nodes,
u,
priority_queue
)
cleaned_updated_priority_queue =
process_priority_queue_with_visited_nodes(updated_priority_queue, updated_visited_nodes)
walk(
mapped,
updated_visited_nodes,
updated_dist,
u,
cleaned_updated_priority_queue
)
end
defp process_priority_queue_with_visited_nodes({} = queue, _), do: queue
defp process_priority_queue_with_visited_nodes(
{_min_val, min_coord, _} = priority_queue,
visited_nodes
) do
if get_value_at(visited_nodes, min_coord) do
{_popped, updated_queue} = PairingHeap.pop(priority_queue)
updated_queue
else
priority_queue
end
end
defp get_next_coord(
_visited_nodes,
_dist_,
{} = _priority_queue
),
do: {0, 0}
defp get_next_coord(
_visited_nodes,
_dist,
{_curr_min_val, min_coord, _} = _priority_queue
) do
min_coord
end
defp update_dist_of_all_unvisited_adjacent_nodes(
mapped,
dist,
visited_nodes,
coord,
priority_queue
) do
coord_dist_value = get_value_at(dist, coord)
adjacent_nodes(mapped, coord)
|> adjacent_not_in_visited(visited_nodes)
|> Stream.filter(fn {_, val} -> !is_nil(val) end)
|> Enum.reduce({dist, priority_queue}, fn {node_coord, wgt},
{prev_dist, prev_priority_queue} ->
sum = coord_dist_value + wgt
node_dist_value = get_value_at(prev_dist, node_coord)
cond do
sum < node_dist_value || node_dist_value == :inf ->
{update_value_at(prev_dist, node_coord, sum),
maybe_put_to_queue(prev_priority_queue, sum, node_coord)}
true ->
{prev_dist, prev_priority_queue}
end
end)
end
defp maybe_put_to_queue({}, sum, node_coord), do: PairingHeap.new(sum, node_coord)
defp maybe_put_to_queue(
priority_queue,
sum,
node_coord
) do
PairingHeap.put(priority_queue, sum, node_coord)
end
# O(1)
defp adjacent_not_in_visited(list_of_adjacent_with_weight, visited_nodes) do
list_of_adjacent_with_weight
|> Enum.filter(fn {coord, _wgt} ->
!get_value_at(visited_nodes, coord)
end)
end
defp adjacent_nodes(mapped, {x, y}) do
[
coord_with_weight(mapped, {x - 1, y}),
coord_with_weight(mapped, {x + 1, y}),
coord_with_weight(mapped, {x, y - 1}),
coord_with_weight(mapped, {x, y + 1})
]
end
defp coord_with_weight(mapped, coord) do
{coord, get_value_at(mapped, coord)}
end
def dump_raw_list_to_map(list_charlist \\ @proof) do
list_charlist
|> Stream.with_index()
|> Stream.flat_map(fn {charlist, row_idx} ->
charlist
|> Stream.with_index()
|> Stream.map(fn {val, col_idx} ->
parsed_val = val - @ascii_offset
{{col_idx, row_idx}, parsed_val}
end)
end)
|> Map.new()
end
def expand_raw_list(list_charlist \\ @proof) do
# expand 5 fold
unit_width = list_charlist |> hd |> length
unit_height = list_charlist |> length
enlarged_rows =
for unit_row <- list_charlist do
1..4
|> Enum.reduce(unit_row, fn _, combined ->
next_set =
combined
|> Enum.reverse()
|> Stream.take(unit_width)
|> Stream.map(&incr_val/1)
|> Enum.reverse()
combined ++ next_set
end)
end
reversed_enlarged_rows =
enlarged_rows
|> Enum.reverse()
1..4
|> Enum.reduce(reversed_enlarged_rows, fn _, combined ->
combined
|> Enum.take(unit_height)
|> Enum.reverse()
|> Enum.reduce(combined, fn row, acc ->
[Enum.map(row, &incr_val/1) | acc]
end)
end)
|> Enum.reverse()
end
defp incr_val(char) do
case char - @ascii_offset do
9 -> @ascii_offset + 1
x -> @ascii_offset + (x + 1)
end
end
defp create_dist_from_coord_val_map(value_map) do
Enum.map(value_map, fn
{{0, 0}, _val} -> {{0, 0}, 0}
{coord, _val} -> {coord, :inf}
end)
|> Map.new()
end
def get_value_at(mapped, {x, y}) do
Map.get(mapped, {x, y})
end
defp update_value_at(mapped, coord, value) do
Map.put(mapped, coord, value)
end
end
defp get_next_coord(visited_nodes, dist, inverted_dist) do
inverted_dist
|> Enum.reduce([], fn {val, coord_list}, acc ->
filtered_coords =
coord_list
|> Stream.filter(fn coord -> !get_value_at(visited_nodes, coord) end)
|> Enum.sort_by(fn {x, y} -> {x, y} end)
case filtered_coords do
[] ->
acc
rest ->
updated_pair = {val, rest}
[updated_pair | acc]
end
end)
|> get_smallest_inv_dist_coord()
end
defp get_smallest_inv_dist_coord(inverted_dist_in_list) do
smallest =
inverted_dist_in_list
|> Stream.map(fn {val, _coord} -> val end)
|> Enum.sort()
|> hd
Enum.find(inverted_dist_in_list, fn {val, _coord} -> val == smallest end)
|> elem(1)
|> hd
end
defp get_value_at(mapped, {x, y}) do
Map.get(mapped, {x, y})
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment