woojiahao

woojiahao

Got top 1000 for part 1, part 2 was super long so I took a while to understand it. Pretty fun day overall!

https://github.com/woojiahao/aoc/blob/main/lib/aoc/y2023/day_15.ex

Showing Posts 1 to 10

rugyoga

rugyoga

import AOC

aoc 2023, 15 do
  def p1(input) do
    input |> String.split(",", trim: true) |> Enum.map(&hash/1) |> Enum.sum()
  end

  def hash(string) do
    string
    |> to_charlist()
    |> Enum.reduce(0, fn ascii, hash -> rem((hash + ascii) * 17, 256) end)
  end

  def p2(input) do
    input
    |> String.split(",", trim: true)
    |> Enum.reduce(%{},
      fn command, boxes ->
        if String.contains?(command, "=") do
          [target, n] = String.split(command, "=")
          box = hash(target)
          n = String.to_integer(n)
          stack = Map.get(boxes, box, [])
          index = Enum.find_index(stack, fn {label, _} -> label == target end)
          stack = if is_nil(index) do
            [{target, n} | stack]
          else
            List.replace_at(stack, index, {target, n})
          end
          Map.put(boxes, box, stack)
        else
          target = String.trim(command, "-")
          box = hash(target)
          stack = Map.get(boxes, box, [])|> Enum.reject(fn {label, _} -> label == target end)
          Map.put(boxes, box, stack)
        end
      end)
    |> Enum.flat_map(fn {box, stack} -> stack |> Enum.reverse() |> Enum.with_index(1) |> Enum.map(fn {{_, focal}, slot} -> focal * slot * (box+1) end) end)
    |> Enum.sum()
  end
end
seeplusplus

seeplusplus

Not pretty, but it works

defmodule Day15 do
  def hash_str(s) do
    for ascii <- s |> String.to_charlist(),
        reduce: 0 do
      acc -> rem((acc + ascii) * 17, 256)
    end
  end

  def put_lens_in_box(lens, []) do
    [lens]
  end

  def put_lens_in_box([lens_id, length], lenses) do
    contains_lens? = lenses |> Enum.any?(fn [lens, _] -> lens === lens_id end)

    if contains_lens? do
      lenses
      |> Enum.map(fn [lens, len] ->
        if lens == lens_id do
          [lens, length]
        else
          [lens, len]
        end
      end)
    else
      lenses ++ [[lens_id, length]]
    end
  end

  def handle_instruction({:add, [lens_id, length], dest}, state) do
    lenses = Map.get(state, dest, [])
    lenses = put_lens_in_box([lens_id, length], lenses)
    state |> Map.put(dest, lenses)
  end

  def handle_instruction({:rem, [lens], dest}, state) do
    lenses = Map.get(state, dest, [])
    lenses = lenses |> Enum.filter(fn [bx_lens, _] -> bx_lens !== lens end)
    state |> Map.put(dest, lenses)
  end

  def solve_1() do
    {_, content} = File.read("./input/2023_15.txt")
    for command <- content |> String.trim() |> String.split(","), reduce: 0 do
      acc -> acc + hash_str(command)
    end |> IO.puts()
  end

  def solve_2() do
    {_, content} = File.read("./input/2023_15.txt")
    for {instruction, arg} <-
          [content]
          |> Stream.flat_map(fn s ->
            s
            |> String.trim()
            |> String.split(",")
          end)
          |> Stream.map(fn l ->
            case String.contains?(l, "=") do
              true ->
                [lens, length] = String.split(l, "=")
                {:add, [lens, length |> String.to_integer()]}

              false ->
                {:rem, [String.split(l, "-") |> Enum.at(0)]}
            end
          end),
        reduce: %{} do
      acc ->
        dest = hash_str(arg |> Enum.at(0))
        handle_instruction({instruction, arg, dest}, acc)
    end
    |> Enum.reduce(
      0,
      fn {box_id, lenses}, outer_acc ->
        outer_acc +
          for {[_, fcl_len], idx} <- lenses |> Enum.with_index(), reduce: 0 do
            acc ->
              acc + (box_id + 1) * (idx + 1) * fcl_len
          end
      end
    )
    |> IO.puts()
  end
end
trnasistor

trnasistor

My beginner’s solution, Day 15 part 1. Lens Library

defmodule Day15 do

  def part1(input) do
    parse(input)
    |> Enum.map(&hash/1)
    |> Enum.sum
  end

  def hash(string) do
    for <<char::8 <- string>>, reduce: 0 do
      acc -> rem(17 * (acc + char), 256)
    end
  end

  def parse(raw_sequence) do
    raw_sequence
    |> remove("\n") 
    |> String.split(",")
  end
  defp remove(s, p), do: String.replace(s, p, "")

end
code-shoily

code-shoily

This was quite fun. Although I was lazy and used Aja.OrdMap

defmodule AdventOfCode.Y2023.Day15 do
  alias AdventOfCode.Helpers.InputReader
  alias Aja.OrdMap

  def input, do: InputReader.read_from_file(2023, 15)

  def run(input \\ input()) do
    input = parse_1(input)
    {run_1(input), run_2(input)}
  end

  defp run_1(input), do: Enum.reduce(input, 0, &(&2 + hash(&1)))

  defp run_2(input) do
    input
    |> parse_2()
    |> Enum.reduce(%{}, fn
      {op, b, len}, acc ->
        hash = hash(b)

        case op do
          :add -> Map.update(acc, hash, OrdMap.new(%{b => len}), &OrdMap.put(&1, b, len))
          :remove -> Map.update(acc, hash, OrdMap.new(%{}), &OrdMap.drop(&1, [b]))
        end
    end)
    |> total_focus()
  end

  defp total_focus(map) do
    Enum.reduce(map, 0, fn {box, boxes}, acc ->
      acc +
        (boxes
         |> OrdMap.to_list()
         |> Enum.with_index(1)
         |> Enum.reduce(0, &(&2 + row_focus(&1, box))))
    end)
  end

  defp row_focus({{_, len}, slot}, box), do: (box + 1) * slot * len

  def parse_1(input \\ input()), do: String.split(input, ",", trim: true)

  def parse_2(commands) do
    Enum.map(commands, fn cmd ->
      case Regex.run(~r{([a-zA-Z]+)(=|-)([0-9]*)}, cmd) do
        [_, label, "=", len] -> {:add, label, String.to_integer(len)}
        [_, label, "-", ""] -> {:remove, label, 0}
      end
    end)
  end

  def hash(lst) do
    for ch <- String.to_charlist(lst), reduce: 0 do
      acc -> rem((acc + ch) * 17, 256)
    end
  end
end
exists

exists

This one seemed rather straightforward to me, no fancy algorithms or optimisations. My solution for part 2 does feel a bit clumsy in terms of list handling, but it works.

defmodule Main do
  def hash(s), do: s |> String.to_charlist() |> Enum.reduce(0, fn c, h -> rem(17*(c+h),256) end)

  def solve1(ls), do: ls |> Enum.map(&hash/1) |> Enum.sum()

  def parse(i) do
    [l,a,n] = Regex.run(~r/^([a-z]+)([-=])(\d*)$/,i) |> tl()
    {l,hash(l),a,if n == "" do 0 else String.to_integer(n) end}
  end

  def solve2(ls) do
    ls |> Enum.map(&parse/1)
    |> Enum.reduce(%{}, fn {l,h,a,n}, d ->
          case a do
            "-" -> Map.update(d,h,[],&List.keydelete(&1,l,0))
            "=" -> Map.update(d,h,[{l,n}], &( if List.keymember?(&1,l,0) do
                          List.keyreplace(&1,l,0,{l,n})
                       else &1 ++ [{l,n}] end))
          end
        end)
    |> Enum.into([])
    |> Enum.map(fn {h,bxl} ->
          bxl |> Enum.with_index(1) |> Enum.map(fn {{_,p},i} -> (h+1)*i*p end) end)
    |> Enum.concat() |> Enum.sum()
  end

  def run() do
    get_input()
    # |> solve1()
    |> solve2()
	end

  def get_input() do
    # "testinput15"
    "input15"
    |> File.read!() |> String.trim() |> String.split(",")
  end
  
end

:timer.tc(&Main.run/0)
|> IO.inspect(charlists: :as_lists)
nico.t

nico.t

This day was easy.

I used regex too, especially named captures:

%{"label" => label, "op" => op, "length" => length} =
      Regex.named_captures(~r/(?<label>[a-z]*)(?<op>(=|-))(?<length>[0-9]*)/, str)

imo it’s prettier than using String.contains? and String.split

           case String.contains?(str, "=") do
              true ->
                 ... = String.split(str, "=")
               ...

              false ->
                ... = String.split(str, "-")
                ...
            end

Question: I tend to always use Enum.reduce/3 as I find it convenient to introduce it into a pipeline but I see a lot of people using:

for x <- enum, reduce: 0
 acc -> ...
end

Can you tell me why do you prefer this syntax? No judgement, I’m just curious… :wink:

stevensonmt

stevensonmt

The problem was really straightforward on both parts for this day. Makes me sad that I got so far behind on days 12-14. I didn’t do anything clever in my solution. This thread showed me some things I had not heard of. Namely, @code-shoily introducing Aja and @exists highlighting List.keydelete and List.keymember?. I wanted the theme this year to be binary pattern matching and manipulation as much as possible, but I didn’t want to overcomplicate this one and just used the String functions mostly.

defmodule Part1 do
    def solve(input) do
      input
      |> parse()
      |> Enum.sum()
    end

    defp parse(input) do
      hasher(input, 0, [])
    end

    def hasher(<<>>, current_val, acc), do: [current_val | acc]
    def hasher(<<",", rest::binary>>, current_val, acc), do: hasher(rest, 0, [current_val | acc])
    def hasher(<<"\n", rest::binary>>, current_val, acc), do: hasher(rest, current_val, acc)

    def hasher(<<next::8, rest::binary>>, current_val, acc),
      do: hasher(rest, hash_fun(next, current_val), acc)

    def hash_fun(next, current_val), do: rem((next + current_val) * 17, 256)
  end

  defmodule Part2 do
    def solve(input) do
      input
      |> parse()
      |> Enum.map(&calc_focus_pwr/1)
      |> total_focus_pwr()
    end

    defp parse(input) do
      input
      |> String.replace("\n", "")
      |> String.split(",")
      |> Enum.map(&parse_label/1)
      |> Enum.reduce(%{}, fn ops_label, map ->
        hash_mapper(ops_label, map)
      end)
    end

    defp parse_label(label) do
      String.split(label, ~r{-|=}, include_captures: true)
    end

    defp hash_mapper([label, op, len], map) do
      [box] = Part1.hasher(label, 0, [])

      case op do
        "-" ->
          Map.update(map, box, [], fn curr ->
            Enum.reject(curr, fn lens -> match?([^label, _], lens) end)
          end)

        "=" ->
          Map.update(map, box, [[label, len]], fn curr ->
            with nil <- Enum.find_index(curr, fn [a, _b] -> a == label end) do
              [[label, len] | curr]
            else
              ndx ->
                List.replace_at(curr, ndx, [label, len])
            end
          end)
      end
    end

    defp calc_focus_pwr({box, lenses}) do
      lenses
      |> Enum.reverse()
      |> Enum.with_index(1)
      |> Enum.map(fn {[_label, lens], ndx} -> String.to_integer(lens) * ndx * (box + 1) end)
      |> Enum.sum()
    end

    defp total_focus_pwr(enum), do: Enum.sum(enum)
  end
stevensonmt

stevensonmt

Like you I mostly use Enum.reduce. When I do reach for the for comprehension syntax it’s usually to take advantage of how easy it is to combine multiple enumerables into one logic pipeline.

a = [1, 2, 3]
b = [?a, ?b, ?c]
for i <- a, c <- b, i + c == 100, reduce: 0 do 
  acc -> acc + i + c
end
# 300

versus

a
|> Enum.reduce(0, fn i, acc -> 
        b 
        |> Enum.filter(fn c -> c + i == 100 end) 
        |> Enum.reduce(acc, fn c, accacc -> 
               accacc + c + i end)
     end)
# 300

I suspect it also may seem more familiar to people more comfortable with imperative programming approaches. Finally, there might be some cases where the for comprehension does provide some performance improvements as per this discussion.

Where Next? Top

Trending in Challenges Top

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews