jarlah

jarlah

now all my advent of code doctests completes in 62s, async test mode. Which is a lot .. but i might not bother

0
Post #41
cblavier

cblavier

You will get better performance if you stick with integers.
If you want to switch to strings for number concatenation, there are simple ways to do it with integers :slight_smile:

2
Post #42
jarlah

jarlah

side note btw .. in my mix project

mix setup_day 2024 8

lazy devs …

0
Post #43
jarlah

jarlah

Ah i didnt mean strings for numbers. I meant the operators. Because then its easier to short curcuit all operations. My initial version was &+/2 etc

0
Post #44
mwilsoncoding

mwilsoncoding

A day late and nearly past the next day, but I’m here. My original attempts had a bug in concatenation that had me in the weeds. After looking through this thread, I landed on the following. Still an interesting thought exercise to figure out why my old implementation was so out of whack.

#!/usr/bin/env elixir

defmodule Day7.Part2 do
  defp parse(str) do
    str
    |> String.trim()
    |> String.split([": ", " "], trim: true)
    |> Enum.map(&String.to_integer/1)
  end

  def concat(a, b) when b < 10, do: a * 10 + b
  def concat(a, b), do: concat(a, div(b, 10)) * 10 + rem(b, 10)

  def to_count([target | argv]),
    do: to_count([&Kernel.+/2, &Kernel.*/2, &concat(&1, &2)], target, argv)

  def to_count(_, target, [a]) do
    if target == a, do: target, else: nil
  end

  def to_count(ops, target, [a, b | rest]) do
    Enum.find_value(ops, fn op ->
      to_count(ops, target, [op.(a, b) | rest])
    end)
  end

  def solve() do
    File.stream!("07/input.txt")
    |> Stream.map(&parse/1)
    |> Task.async_stream(&to_count/1)
    |> Stream.map(fn
      {:ok, nil} -> 0
      {:ok, n} -> n
    end)
    |> Enum.sum()
    |> IO.puts()
  end
end

Day7.Part2.solve()
1
Post #45
MikeLindner

MikeLindner

Perhaps I’m just getting more familiar with writing Elixir code, but this was the easiest one yet for me. Didn’t do anything fancy, just tried every combination of calculations.

defmodule Aoc2024.Day7 do
  @moduledoc false

  defp get_input(file) do
    File.read!(file)
    |> String.split("\n", trim: true)
    |> Enum.map(&String.split(&1, ~r/[: ]+/, trim: true))
    |> Enum.map(fn l -> Enum.map(l, &String.to_integer/1) end)
  end

  defp pipepipe(left, right) do
    String.to_integer(Integer.to_string(left) <> Integer.to_string(right))
  end

  defp solvable([target | [first | rest]], operators) do
    solvable = Kernel.in(target,
      Enum.reduce(rest, [first], fn right, lefts ->
        for left <- lefts, op <- operators do
          op.(left, right)
        end
      end))
    if solvable, do: target, else: 0
  end

  def part1(file) do
    operators = [&+/2, &*/2]
    get_input(file)
    |> Enum.map(&solvable(&1, operators))
    |> Enum.sum()
  end

  def part2(file) do
    operators = [&+/2, &*/2, &pipepipe/2]
    get_input(file)
    |> Enum.map(&solvable(&1, operators))
    |> Enum.sum()
  end
end
1
Post #46
al2o3cr

al2o3cr

One of the good days, when the abstraction I picked made part 2 easier for once:

defmodule BridgeFix do
  def read(filename) do
    File.stream!(filename)
    |> Stream.map(&String.trim/1)
    |> Stream.map(&String.split(&1, ~r/[: ]+/))
    |> Stream.map(fn l -> Enum.map(l, &String.to_integer/1) end)
    |> Stream.map(fn [r | rest] -> {r, rest} end)
  end

  def can_work?({desired, values}) do
    values
    |> then(& length(&1) - 1)
    |> operators()
    |> Enum.any?(fn ops ->
      run(values, ops) == desired
    end)
  end

  @operators [:plus, :times, :concat]

  defp operators(0), do: [[]]

  defp operators(n) do
    prev = operators(n-1)

    @operators
    |> Enum.map(fn op -> Stream.map(prev, &[op | &1]) end)
    |> Stream.concat()
  end

  defp do_op(:plus, x, y), do: x + y
  defp do_op(:times, x, y), do: x * y
  defp do_op(:concat, x, y), do: String.to_integer("#{x}#{y}")

  defp run([x], []), do: x
  defp run([x, y | rest], [operator | op_rest]) do
    run([do_op(operator, x, y) | rest], op_rest)
  end
end

BridgeFix.read("input.txt")
|> Task.async_stream(&{BridgeFix.can_work?(&1), &1}, ordered: false)
|> Stream.flat_map(fn
  {:ok, {true, {x, _}}} -> [x]
  _ -> []
end)
|> Enum.sum()
|> IO.inspect()

(part 1 is the same code, just without :concat in @operators)

Task.async_stream helped quite a bit, since this is the textbook definition of “embarrassingly parallel” :stuck_out_tongue:

0
Post #47
adamu

adamu

Came back to this since I skipped it at the time.

I used the new Enum.sum_by/2 from Elixir 1.18

def part2(input) do
  Enum.sum_by(input, fn {answer, values} ->
    if solvable2?(answer, values), do: answer, else: 0
  end)
end

def solvable2?(answer, [result]), do: answer == result

def solvable2?(answer, [a, b | rest]) do
  solvable2?(answer, [a * b | rest]) or solvable2?(answer, [a + b | rest]) or
    solvable2?(answer, [String.to_integer("#{a}#{b}") | rest])
end

https://git.adamu.jp/adam/AdventOfCode/src/branch/main/2024/day7.exs

0
Post #48
stevensonmt

stevensonmt

Naive but quick enough for me. Initially thought about doing some sort of tree structure and then traversing the tree to do the calculation steps but quickly realized that was pointless. I like the recursion, not so much the nested list generation and List.flatten step.

https://github.com/stevensonmt/advent_of_code/blob/02061f6bc06937701d95e274d990801a49eeb93a/2024/day7/lib/day7.ex

0
Post #49
— All posts loaded —