<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="380553" data-post-id="380553">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="bjorng" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bjorng/120/13187_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  bjorng
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Erlang Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I finally got some time to finish my solution for day 9.</p>
<p>Here is my cleaned up solution stripped of all debugging code. A slightly interesting thing about my solution is that I do ray-casting or walking from one corner to another, but only in the horizontal directions. To check the vertical lines, I ran the same check on the tiles rotated 90 degrees.</p>
<p>The combined runtime for both parts and the examples are 0.4 seconds on my computer.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Day09 do
  def part1(input) do
    tiles = parse(input)
    tiles
    |&gt; Enum.flat_map(fn a -&gt;
      Enum.flat_map(tiles, fn b -&gt;
        if a &lt; b, do: [area(a, b)], else: []
      end)
    end)
    |&gt; Enum.sort(:desc)
    |&gt; Enum.take(1)
    |&gt; hd
  end

  def part2(input) do
    tiles = parse(input)

    transposed = tiles
    |&gt; Enum.map(fn {x, y} -&gt; {y, x} end)
    |&gt; Enum.reverse

    plain = prepare_tiles(tiles)
    transposed = prepare_tiles(transposed)

    {tiles, _, _} = plain

    tiles = Enum.sort(tiles)
    Enum.reduce(tiles, 0, fn a, max_area -&gt;
      Enum.reduce(tiles, max_area, fn b, max_area -&gt;
        if a &gt;= b do
          max_area
        else
          area = area(a, b)
          if area &lt;= max_area do
            max_area
          else
            region1 = [a, b]
            region2 = Enum.map(region1, fn {x, y} -&gt; {y, x} end)
            if solve_one(region1, plain) and solve_one(region2, transposed) do
              area
            else
              max_area
            end
          end
        end
      end)
    end)
  end

  defp prepare_tiles(tiles) do
    edges = Enum.chunk_every(tiles, 2, 1, tiles)
    |&gt; Enum.map(&amp;List.to_tuple/1)

    convex = Enum.chunk_every(edges, 2, 1, edges)
    |&gt; Enum.map(fn [{p1, p2}, {p2, p3}] -&gt;
      convex? = if cross_z(p1, p2, p3) &gt; 0, do: :convex, else: :concave
      {p2, {convex?, p1, p3}}
    end)
    |&gt; Map.new

    {tiles, edges, convex}
  end

  # Given two diagonal corners, check that the two horizontal
  # lines going to the other two corners of the rectangle
  # only have red and green tiles.
  defp solve_one([a, b], {_tiles, edges, convex}) do
    {lx, y1} = a
    {rx, y2} = b
    c = {lx, y2}
    d = {rx, y1}

    cond do
      c === a or d === b -&gt;
        [{b, c, sign(lx - rx)}]
      true -&gt;
        [{b, c, sign(lx - rx)}, {a, d, sign(rx - lx)}]
    end
    |&gt; Enum.all?(fn {from, to, dir} -&gt;
      if from === to do
        false
      else
        walk(from, to, dir, edges, convex)
      end
    end)
  end

  defp sign(int) do
    cond do
      int &lt; 0 -&gt; -1
      int &gt; 0 -&gt; 1
      true -&gt; 0
    end
  end

  defp area({x1, y1}, {x2, y2}) do
    (abs(x1 - x2) + 1) * (abs(y1 - y2) + 1)
  end

  defp walk(from, to, dir, edges, convex) do
    case classify_corner(from, dir, convex) do
      :outside -&gt;
        false
      where -&gt;
        {from_x, from_y} = from
        {to_x, _} = to

        # Only keep the relevant vertical edges.
        edges = edges
        |&gt; Enum.filter(fn {{_,y1}, {_,y2}} -&gt;
          y1 !== y2 and from_y in min(y1, y2)..max(y1, y2)
        end)

        case dir do
          -1 -&gt;
            edges
            |&gt; Enum.reject(fn {{x1,_}, {x1,_}} -&gt;
              from_x &lt; x1 or to_x &gt; x1
            end)
            |&gt; Enum.sort(:desc)
            |&gt; then(fn edges -&gt;
              walk_1(tl(edges), dir, to, convex, where)
            end)
          1 -&gt;
            edges
            |&gt; Enum.reject(fn {{x1,_}, {x1,_}} -&gt;
              x1 &lt; from_x or x1 &gt; to_x
            end)
            |&gt; Enum.sort
            |&gt; then(fn edges -&gt;
              walk_1(tl(edges), dir, to, convex, where)
            end)
        end
    end
  end

  # Classify a corner with respect to the horizontal direction.
  # Returns one of :edge, :inside, or :outside.
  defp classify_corner(from, dir, convex) do
    {from_x, _} = from
    other_corner = case convex do
                     %{^from =&gt; {_, {^from_x, _}, other}} -&gt; other
                     %{^from =&gt; {_, other, {^from_x, _}}} -&gt; other
                   end

    {diff_x, _} = sub(other_corner, from)
    case sign(diff_x) do
      ^dir -&gt;
        # Walking in the given direction is along the edge to
        # the other corner.
        :edge
      _ -&gt;
        # We will not walk along the edge, but we will enter move to
        # to the inside or outside.
        case is_convex(convex, from) do
          :concave -&gt; :inside
          :convex -&gt; :outside
        end
    end
  end

  # Walk from a corner point horizontally in either direction.
  # Returns `true` if the destination point `to` can be reached
  # by only passing red and green tiles.
  defp walk_1(_, _dir, _to, _convex, :outside) do
    false
  end
  defp walk_1([], _dir, _to, _convex, _) do
    true
  end
  defp walk_1([{a, b} | edges], dir, to, convex, where) do
    {to_x, to_y} = to
    {ab_x, a_y} = a
    {_, b_y} = b
    cond do
      dir === -1 and ab_x &lt;= to_x -&gt;
        # Destination is on the edge or at the corner.
        where === :edge or where === :inside
      dir === 1 and to_x &lt;= ab_x -&gt;
        # Destination is on the edge or at the corner.
        where === :edge or where === :inside
      a_y === to_y and corner?(convex, a) -&gt;
        # Destination is beyond this corner.
        where = pass_corner(where, is_convex(convex, a))
        walk_1(edges, dir, to, convex, where)
      b_y === to_y and corner?(convex, b) -&gt;
        # Destination is beyond this corner.
        where = pass_corner(where, is_convex(convex, b))
        walk_1(edges, dir, to, convex, where)
      true -&gt;
        # Destination is beyond this edge.
        false
    end
  end

  defp corner?(convex, point) do
    Map.has_key?(convex, point)
  end

  defp is_convex(convex, corner) do
    {convex?, _, _} = Map.get(convex, corner)
    convex?
  end

  defp pass_corner(:edge, :convex), do: :outside
  defp pass_corner(:edge, :concave), do: :inside
  defp pass_corner(:inside, :concave), do: :edge

  defp cross_z({x1, y1}, {x2, y2}, {x3, y3}) do
    (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2)
  end

  defp sub({x1, y1}, {x2, y2}) do
    {x1 - x2, y1 - y2}
  end

  defp parse(input) do
    input
    |&gt; Enum.map(fn line -&gt;
      line
      |&gt; String.split(",")
      |&gt; Enum.map(&amp;String.to_integer/1)
      |&gt; List.to_tuple
    end)
  end
end

</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="380553" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/advent-of-code-2025-day-9/73594/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-380553" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="380553"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>