LostKobrakai

LostKobrakai

This one was really fun contrary to other “implement something CPU like” puzzles in the past. I used a bunch of Stream API and a module for scoping the CRT logic. I really liked the visual component of part 2.

Solution
defmodule Day10 do
  defmodule CRT do
    defstruct pixels: [], index: 0

    def render_pixel(state, x) do
      pixel = if state.index in (x - 1)..(x + 1), do: "#", else: "."

      %__MODULE__{
        pixels: [pixel | state.pixels],
        index: rem(state.index + 1, 40)
      }
    end

    def render(state) do
      state.pixels
      |> Enum.reverse()
      |> Enum.chunk_every(40)
      |> Enum.join("\n")
    end
  end

  def run(text) do
    text
    |> program()
    |> Stream.filter(fn {_, cycle} -> cycle in [20, 60, 100, 140, 180, 220] end)
    |> Stream.map(fn {x, cycle} -> x * cycle end)
    |> Enum.take(6)
    |> Enum.sum()
  end

  def render_to_crt(text) do
    text
    |> program()
    |> Stream.take_while(fn {_, cycle} -> cycle <= 240 end)
    |> Enum.reduce(%CRT{}, fn {x, _}, crt ->
      CRT.render_pixel(crt, x)
    end)
    |> CRT.render()
  end

  defp program(text) do
    text
    |> String.split("\n", trim: true)
    |> Stream.transform(1, fn
      "noop", x -> {[x], x}
      "addx " <> num, x -> {[x, x], x + String.to_integer(num)}
    end)
    |> Stream.with_index(1)
  end
end

Showing Posts 1 to 10

kwando

kwando

I had a very clunky version that got me the gold stars, but after some coffee I ended up with a super neat version using Stream.transform.

  input
  |> Stream.transform({1, 1}, fn 
    :noop, {cycle, x} ->
      {[{cycle, x}], {cycle + 1, x}}
    {:addx, value}, {cycle, x} ->
      {[{cycle, x}, {cycle + 1, x}], {cycle + 2, x + value}}
  end)

That will give you a stream of {cycle_no, x} tuples that you could use to find the solution to the other parts. input is parsed in to a list of tuples, the small example would look like the [:noop, {:addx, 3}, {:addx, -5}].

Just pipe that stream into this for the second part :slight_smile:

cycle_stream # from above
|> Stream.map(fn 
  {pc, x} when rem(pc-1, 40) in (x-1)..(x+1) -> ?#
  _ -> ?\s
end)
|> Stream.chunk_every(40)
|> Enum.intersperse(?\n)
|> IO.puts

Elixir standard library is so awesomely good.

adamu

adamu

No standard library for me, just a couple of recursive functions.

Both sub-millisecond. Part 1 especially completes in less than 20 microseconds.

You’re convincing me to look into Stream more for these recursive answers though, 5 parameter 6 clause functions are not the height of readability :see_no_evil_monkey:

https://git.adamu.jp/adam/AdventOfCode/src/branch/main/2022/day10.exs

jmkellenberger

jmkellenberger

Newbie programmer here, I really enjoyed today’s puzzle. Here’s my attempt.

Day 10

pistelak

pistelak

I don’t know Elixir that well, but IMHO quite nice, nothing special though: AdventOfCode22/10/aoc/lib/aoc.ex at main · pistelak/AdventOfCode22 · GitHub

stevensonmt

stevensonmt

In the end I’m reasonably pleased with my solution but I had a lot of frustration getting there. Had an off by one error in my initial attempt at part 1 that only showed up with the real input and not the sample data. Part 2 was actually much easier but when I pasted the sample data output into vim for testing one of my plugins appended a # to each line after the first thinking they were meant to be comments. I didn’t realize it and couldn’t understand why my solution was not passing the test. Classic bad input is going to give bad output.

def execute(cmds, cycles) do
    cmds
    |> Enum.reduce_while({%{0 => 1, 1 => 1}, 1}, fn cmd, {register, cycle} ->
      if cycle > cycles do
        {:halt, {register, cycle}}
      else
        {:cont, exec(cmd, register, cycle)}
      end
    end)
    |> elem(0)
  end

  defp exec(cmd, register, cycle) do
    case cmd do
      "noop" ->
        register = Map.put(register, cycle + 1, register[cycle])
        {register, cycle + 1}

      <<"addx ", val::binary>> ->
        last_val = register[cycle]

        register =
          register
          |> Map.put(cycle + 1, last_val)
          |> Map.put(cycle + 2, last_val + String.to_integer(val))

        {register, cycle + 2}
    end
  end

  defp signal_strengths(signal_map, cycles) do
    cycles |> Enum.map(&signal_strength(&1, signal_map))
  end

  defp signal_strength(cycle, signal_map) do
    signal_map[cycle] * cycle
  end

  def part1(input) do
    input
    |> execute(220)
    |> signal_strengths([20, 60, 100, 140, 180, 220])
    |> Enum.sum()
  end

  def part2(input) do
    input
    |> execute(:to_completion)
    |> render()
  end

  defp render(map) do
    0..(map_size(map) - 2)
    |> Enum.reduce("", fn cycle, render ->
      sprite_pos = map[cycle + 1]
      cycle = rem(cycle, 40)

      case {cycle == 0, abs(cycle - sprite_pos) < 2} do
        {true, true} -> render <> "\n" <> "#"
        {true, false} -> render <> "\n" <> "."
        {false, true} -> render <> "#"
        {false, false} -> render <> "."
      end
    end)
    |> String.split("\n", trim: true)
  end
kwando

kwando

It is probably faster with handrolled recursive functions, but it can be quite dense to read :sweat_smile:

There are a lot of gems hidden in the standard library:)

stevensonmt

stevensonmt

Just curious if this really works:
if Enum.member?(i.x..i.x+2, rem(i.cycle, 40))
because I would have thought it needs to be (i.x - 1)..(i.x + 1) since the sprite position is given by it’s middle pixel position.

pistelak

pistelak

You are right, there is something wrong. :sweat_smile: I tried to someone’s else solution and the result is slightly different: 404 File Not Found - Jumpshare … Will take a look.

pistelak

pistelak

Error by one :sweat: - cycles are indexed from 1 but CRT starts drawing at 0. So
Enum.member?(i.x-1..i.x+1, rem(i.cycle - 1, 40)) is correct.

deadbeef

deadbeef

Nothing special/similar to others. Reminded me of 2021-13 which also had you print out letters with .'s and #'s. So had some fun and added some IO.ANSI to “pretty” print, e.g.

https://github.com/ed-flanagan/advent-of-code-solutions-elixir/blob/main/lib/advent/y2022/d10.ex

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