bjorng

bjorng

Erlang Core Team

Advent of Code 2024 - Day 4

Here is my solution for day 4:

Most Liked

BartOtten

BartOtten

Looking at your code I see which rabbit hole you experienced. I experienced the same and just dropped all code and started from scratch.

You made it, which is the most important :slight_smile:

woojiahao

woojiahao

Today looked more intimidating than it actually was. General strategy is to bruteforce all directions. We can minimize bruteforce area by only searching when “X” (for part 1) or “A” (for part 2):

defmodule AOC.Y2024.Day4 do
  @moduledoc false

  use AOC.Solution

  @dirs [
    {-1, 0},
    {1, 0},
    {0, -1},
    {0, 1},
    {-1, -1},
    {-1, 1},
    {1, -1},
    {1, 1}
  ]

  @impl true
  def load_data() do
    Data.load_day_as_grid(2024, 4)
  end

  @impl true
  def part_one({grid, _, _}) do
    grid
    |> Enum.filter(fn {_, v} -> v == "X" end)
    |> General.map_sum(fn {coord, _} -> count_xmas(grid, coord) end)
  end

  @impl true
  def part_two({grid, _, _}) do
    grid
    |> Enum.filter(fn {_, v} -> v == "A" end)
    |> Enum.count(fn {coord, _} -> has_x_mas(grid, coord) end)
  end

  defp has_x_mas(grid, {r, c}) do
    [tl, tr, bl, br] =
      @dirs
      |> Enum.slice(4..-1//1)
      |> Enum.map(fn {dr, dc} -> {r + dr, c + dc} end)
      |> Enum.map(fn coord -> Map.get(grid, coord, ".") end)

    ([tl, br] == ["M", "S"] or [tl, br] == ["S", "M"]) and
      ([tr, bl] == ["M", "S"] or [tr, bl] == ["S", "M"])
  end

  defp count_xmas(grid, {r, c}) do
    for {dr, dc} <- @dirs do
      0..3
      |> Enum.map(fn j -> {r + dr * j, c + dc * j} end)
      |> Enum.map_join(fn coord -> Map.get(grid, coord, ".") end)
    end
    |> Enum.count(fn v -> v == "XMAS" end)
  end
end
Aetherus

Aetherus

I feel part 2 is actually easier.

defmodule AoC2024.Day04 do
  def part_1(grid) do
    directions =
      for di <- -1..1,
          dj <- -1..1,
          di != 0 or dj != 0,
          do: {di, dj}

    directions
    |> Enum.map(fn {di, dj} ->
      Enum.count(Map.keys(grid), fn coord ->
        coord
        |> Stream.iterate(fn {i, j} -> {i + di, j + dj} end)
        |> Stream.take(4)
        |> Enum.map(&grid[&1])
        |> Kernel.==(~c"XMAS")
      end)
    end)
    |> Enum.sum()
  end

  def part_2(grid) do
    Enum.count(grid, fn
      {{i, j}, ?A} ->
        [grid[{i - 1, j - 1}], grid[{i + 1, j + 1}]] in [~c"MS", ~c"SM"] and
        [grid[{i - 1, j + 1}], grid[{i + 1, j - 1}]] in [~c"MS", ~c"SM"]
        
      _ ->
        false
    end)
  end
end

where grid is built like this:

charlists = puzzle_input |> String.split() |> Enum.map(&String.to_charlist/1)

grid =
  for {row, i} <- Enum.with_index(charlists),
      {char, j} <- Enum.with_index(row),
      into: %{},
      do: {{i, j}, char}
sevenseacat

sevenseacat

Author of Ash Framework

Oh I really like the idea of having helpers in the grid for moving in different directions! Much less magic -1 and 1 everywhere :smiley:

lkuty

lkuty

#!/usr/bin/env elixir

# Aoc 2024. day 4.

defmodule Part1 do
  @spec xmas(map(), non_neg_integer(), non_neg_integer()) :: non_neg_integer()
  def xmas(m, r, c) do
    b2i(unquote(:"xmas↑")(m, r, c)) +
    b2i(unquote(:"xmas↗")(m, r, c)) +
    b2i(unquote(:"xmas→")(m, r, c)) +
    b2i(unquote(:"xmas↘")(m, r, c)) +
    b2i(unquote(:"xmas↓")(m, r, c)) +
    b2i(unquote(:"xmas↙")(m, r, c)) +
    b2i(unquote(:"xmas←")(m, r, c)) +
    b2i(unquote(:"xmas↖")(m, r, c))
  end

  defp unquote(:"xmas↑")(m, r, c),
    do: m[{r,c}] == "X" && m[{r-1,c}] == "M" && m[{r-2,c}] == "A" && m[{r-3,c}] == "S"
  defp unquote(:"xmas↗")(m, r, c),
    do: m[{r,c}] == "X" && m[{r-1,c+1}] == "M" && m[{r-2,c+2}] == "A" && m[{r-3,c+3}] == "S"
  defp unquote(:"xmas→")(m, r, c),
    do: m[{r,c}] == "X" && m[{r,c+1}] == "M" && m[{r,c+2}] == "A" && m[{r,c+3}] == "S"
  defp unquote(:"xmas↘")(m, r, c),
    do: m[{r,c}] == "X" && m[{r+1,c+1}] == "M" && m[{r+2,c+2}] == "A" && m[{r+3,c+3}] == "S"
  defp unquote(:"xmas↓")(m, r, c),
    do: m[{r,c}] == "X" && m[{r+1,c}] == "M" && m[{r+2,c}] == "A" && m[{r+3,c}] == "S"
  defp unquote(:"xmas↙")(m, r, c),
    do: m[{r,c}] == "X" && m[{r+1,c-1}] == "M" && m[{r+2,c-2}] == "A" && m[{r+3,c-3}] == "S"
  defp unquote(:"xmas←")(m, r, c),
    do: m[{r,c}] == "X" && m[{r,c-1}] == "M" && m[{r,c-2}] == "A" && m[{r,c-3}] == "S"
  defp unquote(:"xmas↖")(m, r, c),
    do: m[{r,c}] == "X" && m[{r-1,c-1}] == "M" && m[{r-2,c-2}] == "A" && m[{r-3,c-3}] == "S"

    def b2i(true), do: 1
  def b2i(false), do: 0
end

map = File.stream!("../day04.txt")
  |> Stream.with_index()
  |> Enum.reduce(%{}, fn {line, row}, map ->
    line
    |> String.trim_trailing()
    |> String.codepoints()
    |> Enum.with_index()
    |> Enum.reduce(map, fn {c, col}, map -> Map.put(map, {row, col}, c) end)
  end)

# part 1
Enum.reduce(map, 0, fn {{r,c},_char}, n -> n+Part1.xmas(map, r, c) end)
|> IO.inspect(label: "part 1")

defmodule Part2 do
  @spec xmas(map(), non_neg_integer(), non_neg_integer()) :: boolean()
  def xmas(m, r, c) do
    unquote(:"xmas↗↙")(m, r, c) && unquote(:"xmas↖↘")(m, r, c)
  end
  defp unquote(:"xmas↗↙")(m, r, c),
    do:  m[{r,c}] == "A" && ((m[{r+1,c-1}] == "M" && m[{r-1,c+1}] == "S") || (m[{r+1,c-1}] == "S" && m[{r-1,c+1}] == "M"))
  defp unquote(:"xmas↖↘")(m, r, c),
    do:  m[{r,c}] == "A" && ((m[{r+1,c+1}] == "M" && m[{r-1,c-1}] == "S") || (m[{r+1,c+1}] == "S" && m[{r-1,c-1}] == "M"))
end

# part 2
Enum.reduce(map, 0, fn {{r,c},_char}, n -> n+Part1.b2i(Part2.xmas(map, r, c)) end)
|> IO.inspect(label: "part 2")

Where Next?

Popular in Challenges Top

Qqwy
Note by the Moderators: This topic is to talk about Day 6 of the Advent of Code. For general discussion about the Advent of Code 2018 an...
New
stevensonmt
Trying to get more facility with dynamic programming concepts on Leetcode and having an issue I can’t find a way around. It’s a chutes an...
New
Aetherus
This topic is about the Advent of Code 2021 - Day 4. Thanks to @bjorng , we now have a new Private Leaderboard. The entry code is: 370...
New
Aetherus
Don’t know why the regex ~r/[\W && [^\.]]/x does not work in Elixir. It works pretty well in Ruby. Anyway, here is my solution:
New
New
bjorng
Note: This topic is to talk about Day 23 of the Advent of Code. For general discussion about the Advent of Code 2018 and links to topics...
New
bjorng
This topic is about Day 1 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums): https://adven...
New
cblavier
Hey there :wave: No magic or algorithmic finesse today, I just finished the challenge and I my code is quite slow (1sec for part1, 3se...
New
bjorng
Note: This topic is to talk about Day 18 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
bjorng
Here is my solution for day 1 of Advent of Code: defmodule Day01 do def part1(input) do all = parse(input) {first, second} = E...
New

Other popular topics Top

freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
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
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
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
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement