bjorng

bjorng

Erlang Core Team

Advent of Code 2023 - Day 14

My solution finishes both parts in 5 seconds on my computer. That time should be possible to reduce by optimizing my rather naive tilt/2 function, but I decided to instead optimize the use of my time and leave as is.

My solution:

https://github.com/bjorng/advent-of-code-2023/blob/main/day14/lib/day14.ex

Most Liked

trnasistor

trnasistor

My beginner’s solution, Day 14 part 1. Parabolic Reflector Dish

defmodule Day14 do
def part1(input), do:
  input
  |> String.split("\n")
  |> Enum.map(&String.graphemes/1)
  |> List.zip
  |> Enum.reduce(0, fn column, acc -> column
       |> Tuple.to_list
       |> Enum.chunk_by(&(&1=="#"))
       |> Enum.map(&Enum.sort(&1, :desc))
       |> List.flatten
       |> Enum.reverse
       |> Enum.with_index(1)
       |> then(&(for {"O", n} <- &1, reduce: 0 do acc -> acc + n end))
       |> Kernel.+(acc)
     end)
end
exists

exists

Not proud of this solution, although runs under 3 seconds for part two.
A few comments:

  • This is probably the worst bit: I did not specifically look for when the repeated cycle begins, I just tried a few numbers for the length of the initial run. The point is that as long as it’s enough to get into the cycles, it will be good enough to find the cycle length. The upside is that I only need two grids at any given time.
  • As others, just one direction of tilting (for me the easiest seemed “to the left”), and use grid transformations to get the other directions. This could definitely be optimised.
defmodule Main do
  def run() do
    get_input()
    |> Enum.map(&String.to_charlist/1)
    # |> solve1()
    |> solve2()
	end

  def get_input() do
    # "testinput14"
    "input14"
    |> File.read!()
    |> String.trim()
    |> String.split("\n")
  end

  def transpose(ls) do
    ls |> List.zip() |> Enum.map(&Tuple.to_list/1)
  end

  def flip_lr(ls) do
    ls |> Enum.map(&Enum.reverse/1)
  end

  def flip_ud(ls) do
    ls |> Enum.reverse()
  end

  def tilt_row_left(l) do
    (l ++ ~c"#")
    |> Enum.reduce({~c"", ~c"", ~c""}, fn c, {lsf, os, ds} ->
          # state: { line_so_far, accumulated_O_s, accumulated_dots }
          case c do
            ?. -> {lsf, os, ds ++ ~c"."}
            ?O -> {lsf, os ++ ~c"O", ds}
            ?# -> {lsf ++ os ++ ds ++ ~c"#", ~c"", ~c""}
          end
       end)
    |> elem(0)
    |> Enum.drop(-1)
  end

  def count_os(l) do
    l |> Enum.filter(fn c -> c == ?O end) |> Enum.count()
  end

  def value(ls) do
    ls
    |> Enum.map(&count_os/1)
    |> Enum.reverse()
    |> Enum.with_index(1)
    |> Enum.map(fn {n,i} -> n*i end)
    |> Enum.sum()
  end
  
  def solve1(ls) do
    ls
    # |> IO.inspect(width: 20)
    |> transpose()
    |> Enum.map(&tilt_row_left/1)
    |> transpose()
    # |> IO.inspect(width: 20)
    |> value()
  end

  def cycle(ls) do
    ls |> transpose() |> Enum.map(&tilt_row_left/1)
    |> transpose() |> Enum.map(&tilt_row_left/1) # after N,W, oriented orig
    |> transpose() |> flip_lr() |> Enum.map(&tilt_row_left/1)
    |> transpose() |> flip_lr() |> Enum.map(&tilt_row_left/1)
    |> flip_ud() |> flip_lr()
  end

  def run_cycles(ls, n) do
    1..n |> Enum.reduce(ls, fn _, gg -> cycle(gg) end)
  end

  def solve2(ls) do
    initial = 150
    ee = run_cycles(ls,initial)
    period = 1 .. 1_000
              |> Enum.reduce_while(ee, fn n, gg ->
                  if (ng = cycle(gg)) == ee do {:halt, n} else {:cont, ng} end
                end)
    run_cycles(ee, rem(1_000_000_000 - initial,period))
    |> value()
  end
  
end

:timer.tc(&Main.run/0)
|> IO.inspect()
lud

lud

My solution completes part 2 in less than a second but I have the same feeling that the tilt could be improved.

I lost so much time with wrong scores until I decided to print all scores in the loop and see that 64 was never coming. This was because my scoring function works with northbound rows but each cycle leaves the platform in eastbound rows.

So I had to add that final rotate() before scoring and it was fine:

    rows_loop_start
    |> apply_cycles(cycles_left)
    |> rotate()
    |> score()

But before I lost like 30 minutes to re-learn the concepts of division, multiplication, remainders and all… :smiley: My 2nd grade teacher would be proud.

https://github.com/lud/adventofcode/blob/main/lib/solutions/2023/day14.ex

Where Next?

Popular in Challenges Top

bjorng
Note: This topic is to talk about Day 12 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
bjorng
This topic is about Day 17 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/l...
New
bjorng
This topic is about Day 16 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums): https://adve...
New
rugyoga
part 1 https://github.com/rugyoga/aoc2021/blob/main/day7.exs part 2 https://github.com/rugyoga/aoc2021/blob/main/day7b.exs
New
Aetherus
This topic is about Day 15 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/l...
New
bjorng
Note: This topic is to talk about Day 4 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can join...
New
adamu
Probably not the most efficient implementation, because part 1 took &gt;1 ms and part 2 &gt;4ms, but the code was simple enough. def p...
New
seeplusplus
This one was much easier for me than yesterday’s. Part 1 runs in 22ms and part 2 runs in ~3s. defmodule BridgeRepair do def parse_inpu...
New
Aetherus
This topic is about Day 16 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/l...
New
rugyoga
Fairly straightforward Dijkstra’s algorithm import AOC aoc 2023, 17 do def compute(input, candidates) do {{max_row, max_col}, ite...
New

Other popular topics Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New

We're in Beta

About us Mission Statement