seeplusplus

seeplusplus

Advent of Code 2024 - Day 7

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_input(input) do
     input
        |> String.trim()
        |> String.split("\n")
        |> Stream.map(fn l -> 
          [acc, nums] = String.split(l, ":")
          nums = nums |> String.trim() |> String.split(" ") |> Enum.map(&String.to_integer/1)
          {acc |> String.to_integer(), nums}
        end)
  end
  def combine_ints(acc, [], _), do: acc
  def combine_ints(acc, [i | rest], ops) do
    ops
      |> Enum.flat_map(fn op -> 
        acc |> Enum.map(fn j ->
          case op do
            :plus -> j + i
            :mul -> j * i
            :cat -> "#{j}#{i}" |> String.to_integer()
          end
        end)
      end)
    |> combine_ints(rest, ops)
  end

  def part_one_ops() do
    [:plus, :mul]
  end

  def part_two_ops() do
    [:cat | part_one_ops()]
  end
  
  def solve(input, part) do
    input 
     |> BridgeRepair.parse_input()
     |> Stream.filter(fn {sum, [i | rest]} -> 
        BridgeRepair.combine_ints(
          [i],
          rest, 
          (if part == :part1, do: part_one_ops(), else: part_two_ops())
        ) |> Enum.any?(&(&1 == sum)) 
      end)
    |> Stream.map(fn {i, _} -> i end)
    |> Enum.sum
  end
end

Edit small optimization on the concat operation:

:cat -> j*10**(i |> Integer.digits() |> Enum.count) + i

reduces part 2 runtime to 800ms.

Most Liked

sevenseacat

sevenseacat

Author of Ash Framework

Much easier than yesterday! I still went through a few iterations before landing on this one:

This takes about 250ms to run on my machine for part 2.

My aim for this year is to have each solution run in less than a second - we’ll see how far I get lol

rugyoga

rugyoga

billylanchantin

billylanchantin

LOC: 11

defmodule Aoc2024.Day07 do
  def part1(file), do: main(file, [&+/2, &*/2])
  def part2(file), do: main(file, [&+/2, &*/2, &concat/2])
  def main(file, fs), do: file |> f2ls(&p/1) |> Enum.reduce(0, &(&2 + value(&1, fs)))
  def f2ls(f, fun), do: f |> File.read!() |> String.trim() |> String.split("\n") |> Enum.map(fun)
  def p(l), do: l |> String.replace(":", "") |> String.split() |> Enum.map(&String.to_integer/1)
  def value([out, in1 | rest], fs), do: if(search?(out, in1, rest, fs), do: out, else: 0)
  def search?(out, acc, [], _), do: out == acc
  def search?(out, acc, [h | t], fs), do: Enum.any?(fs, &search?(out, &1.(acc, h), t, fs))
  def concat(a, b), do: Integer.undigits(Integer.digits(a) ++ Integer.digits(b))
end

In my actual solution, f2ls and p are better named via an import:

import Aoc2024.Util
#...
def main(file, fs), do: file |> file_to_lines(&parse/1) |> Enum.reduce(0, &(&2 + value(&1, fs)))
def parse(line), do: line |> String.replace(":", "") |> line_to_list_of_ints

But that bumps up the line count.

lkuty

lkuty

I like recursion :slight_smile: Back in 1996-98, I did a lot of Scheme (MIT Scheme) and fell in love with that language, FP in general and tail recursion (and TCO of course). We had a teacher in Belgium who went to the USAs and was exposed to all of it. I had a really good time.

Excluding I/O, when I call C for the produced? function using a NIF, the code executes ± 3 times faster.

#!/usr/bin/env elixir

# AoC 2024. day 7.

###########################################################
# Part 1

defmodule Part1 do
  def produced?(test_value, numbers), do: produced?(test_value, numbers, nil)
  def produced?(test_value, [], value), do: value == test_value
  def produced?(test_value, [x | numbers], nil), do: produced?(test_value, numbers, x)
  def produced?(test_value, _numbers, value) when value > test_value, do: false
  def produced?(test_value, [x | numbers], value) do
    produced?(test_value, numbers, value*x) || produced?(test_value, numbers, value+x)
  end
end

File.stream!("../day07.txt")
|> Stream.map(fn line -> Regex.scan(~r/\d+/, line) |> Enum.map(fn [x] -> String.to_integer(x) end) end)
|> Enum.reduce(0, fn [test_value | numbers], sum ->
  if Part1.produced?(test_value, numbers), do: sum+test_value, else: sum
end)
|> IO.inspect(label: "Part 1")

###########################################################
# Part 2

defmodule Part2 do
  def produced?(test_value, numbers), do: produced?(test_value, numbers, nil)
  def produced?(test_value, [], value), do: value == test_value
  def produced?(test_value, [x | numbers], nil), do: produced?(test_value, numbers, x)
  def produced?(test_value, _numbers, value) when value > test_value, do: false
  def produced?(test_value, [x | numbers], value) do
    produced?(test_value, numbers, value*x) ||
    produced?(test_value, numbers, value+x) ||
    produced?(test_value, numbers, value*10**ndigits(x)+x)
  end
  @spec ndigits(pos_integer()) :: pos_integer()
  defp ndigits(x), do: trunc(:math.floor(:math.log(x)/:math.log(10))+1)
end

File.stream!("../day07.txt")
|> Stream.map(fn line -> Regex.scan(~r/\d+/, line) |> Enum.map(fn [x] -> String.to_integer(x) end) end)
|> Enum.reduce(0, fn [test_value | numbers], sum ->
  if Part2.produced?(test_value, numbers), do: sum+test_value, else: sum
end)
|> IO.inspect(label: "Part 2")
Aetherus

Aetherus

I rewrote my solution.

defmodule AoC2024.Day07 do
  def part_1(input) do
    input
    |> Enum.filter(fn {result, rev_operands} ->
      solvable_1?(result, rev_operands)
    end)
    |> Enum.map(&elem(&1, 0))
    |> Enum.sum()
  end

  def part_2(input) do
    input
    |> Enum.filter(fn {result, rev_operands} ->
      solvable_2?(result, rev_operands)
    end)
    |> Enum.map(&elem(&1, 0))
    |> Enum.sum()
  end

  defp solvable_1?(target, []) do
    target == 0
  end

  defp solvable_1?(target, [h | t]) do
    solvable_1?(target - h, t) or
    (rem(target, h) == 0 and solvable_1?(div(target, h), t))
  end

  defp solvable_2?(target, []) do
    target == 0
  end
  
  defp solvable_2?(target, [h | t]) do
    solvable_2?(target - h, t) or
    (rem(target, h) == 0 and solvable_2?(div(target, h), t)) or
    ((truncated = truncate(target, h)) && solvable_2?(truncated, t))
  end

  defp truncate(a, b) when a < b, do: false

  defp truncate(a, 0), do: a

  defp truncate(a, b) when rem(a, 10) != rem(b, 10), do: false

  defp truncate(a, b) do
    truncate(div(a, 10), div(b, 10))
  end
end

Where Next?

Popular in Challenges Top

LostKobrakai
This topic is about Day 9 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
antoine-duchenet
Everything went smoothly today. Nothing to change to solve part 2 because I already used memoization for part 1 (it looked like an AoC e...
New
LostKobrakai
This one has been quite the ride. Struggled at first to find a good data format to suite the problem. I really like how that turned out b...
New
adamu
I said I was on a break, but I took a sneak peak and it looked fun so… Part 1 completes in half a millisecond with a single pass of the ...
New
bjorng
Note: This topic is to talk about Day 13 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
christhekeele
Setting this down for the night, as after a quick naive solve for quick part 1 I realize that part 2 is by design computationally expensi...
New
code-shoily
Just did part 1. Part 2 seems to be demanding too much of my reading time so will get to that after I am done with some chores. Oh here ...
New
stevensonmt
Reasonably pleased with my solution. The bitstring packet problems are so well suited to Erlang/Elixir it’s almost not fair. defmodule D...
New
bjorng
This topic is about Day 15 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums): https://adve...
New
code-shoily
Here’s my day 3 code This was quite easy. I was afraid Part 2 would be “un-regex-able” and was preparing for hand crafting automata bu...
New

Other popular topics Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

We're in Beta

About us Mission Statement