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

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
bismark
Took me a minute to remember my binary math :smile: :grimacing:… import Bitwise __DIR__ |&gt; Path.join("puzzle.txt") |&gt; File.stream...
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
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
New
stevensonmt
Anyone else think the prompt for this challenge is contradictory? The rules for comparing packets include If both values are lists, c...
New
jkwchui
Monkeys fitted squarely as GenServers in my head. My initial problem was using cast instead of call; I imagine impolite monkeys slinging...
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
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
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

Other popular topics Top

Tee
can someone please explain to me how Enum.reduce works with maps
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
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
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
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
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New

We're in Beta

About us Mission Statement