Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 17

d17.livemd

Day 17

Section

defmodule Coords do
  defstruct x: 0, y: 0

  @type t() :: %__MODULE__{
          x: integer(),
          y: integer()
        }

  def up(coords = %__MODULE__{y: y}), do: %{coords | y: y - 1}
  def down(coords = %__MODULE__{y: y}), do: %{coords | y: y + 1}
  def left(coords = %__MODULE__{x: x}), do: %{coords | x: x - 1}
  def right(coords = %__MODULE__{x: x}), do: %{coords | x: x + 1}
end
{:module, Coords, <<70, 79, 82, 49, 0, 0, 12, ...>>, {:right, 1}}
defmodule Rock do
  @moduledoc """
  `anchor` is the leftmost bottom point on an imaginary square encasing the rock formation.
  """
  defstruct anchor: %Coords{}, shape: :hline

  @shapes [:hline, :plus, :close_quote, :vline, :square]
  @type rock_shapes() :: :hline | :plus | :close_quote | :vline | :square

  @type t() :: %__MODULE__{
          anchor: Coords.t(),
          shape: rock_shapes()
        }

  def new(anchor, shape) do
    if shape not in @shapes do
      raise("unknown shape #{inspect(shape)}.")
    end

    %__MODULE__{anchor: anchor, shape: shape}
  end

  def down(rock = %__MODULE__{anchor: anchor}), do: %{rock | anchor: Coords.down(anchor)}
  def left(rock = %__MODULE__{anchor: anchor}), do: %{rock | anchor: Coords.left(anchor)}
  def right(rock = %__MODULE__{anchor: anchor}), do: %{rock | anchor: Coords.right(anchor)}

  def next_shape(shape) do
    case shape do
      :hline -> :plus
      :plus -> :close_quote
      :close_quote -> :vline
      :vline -> :square
      :square -> :hline
    end
  end

  def all_coords(%__MODULE__{anchor: anchor, shape: :hline}) do
    c2 = Coords.right(anchor)
    c3 = Coords.right(c2)
    c4 = Coords.right(c3)

    MapSet.new([anchor, c2, c3, c4])
  end

  def all_coords(%__MODULE__{anchor: anchor, shape: :plus}) do
    h1 = Coords.up(anchor)
    h2 = Coords.right(h1)
    h3 = Coords.right(h2)

    v1 = Coords.right(anchor)
    v3 = Coords.up(h2)

    MapSet.new([h1, h2, h3, v1, v3])
  end

  def all_coords(%__MODULE__{anchor: anchor, shape: :close_quote}) do
    h2 = Coords.right(anchor)
    h3 = Coords.right(h2)

    v2 = Coords.up(h3)
    v3 = Coords.up(v2)

    MapSet.new([anchor, h2, h3, v2, v3])
  end

  def all_coords(%__MODULE__{anchor: anchor, shape: :vline}) do
    c2 = Coords.up(anchor)
    c3 = Coords.up(c2)
    c4 = Coords.up(c3)

    MapSet.new([anchor, c2, c3, c4])
  end

  def all_coords(%__MODULE__{anchor: anchor, shape: :square}) do
    c2 = Coords.up(anchor)
    c3 = Coords.right(anchor)
    c4 = Coords.up(c3)

    MapSet.new([anchor, c2, c3, c4])
  end
end
{:module, Rock, <<70, 79, 82, 49, 0, 0, 22, ...>>, {:all_coords, 1}}
defmodule World do
  defstruct active_rock: nil,
            stationary_formation: MapSet.new(),
            highest_y: 0,
            floor_level: 0,
            next_shape: :hline,
            jets: [],
            jet_idx: 0,
            next_step: :add_rock,
            stopped_rocks: 0

  @type t() :: %__MODULE__{
          active_rock: Rock.t() | nil,
          stationary_formation: MapSet.t(),
          highest_y: integer() | nil,
          floor_level: non_neg_integer(),
          next_shape: Rock.rock_shapes(),
          jets: [String.t()],
          jet_idx: non_neg_integer(),
          next_step: atom(),
          stopped_rocks: 0
        }

  @width 7

  def new(jets) when is_list(jets), do: %World{jets: jets}

  defp increment_jet_idx(world = %__MODULE__{jets: jets, jet_idx: jet_idx}) do
    max_jet_idx = length(jets)
    %{world | jet_idx: rem(jet_idx + 1, max_jet_idx), next_step: :fall}
  end

  def advance(world = %__MODULE__{next_step: next_step}) do
    case next_step do
      :add_rock -> add_rock(world)
      :push -> push(world)
      :fall -> fall(world)
    end
  end

  def advance_till_next_falling(world = %__MODULE__{}) do
    Stream.unfold(world, fn world ->
      new_world = advance(world)
      {new_world, new_world}
    end)
    |> Enum.find(fn world -> world.next_step == :add_rock end)
  end

  def advance_till_stopped(world = %__MODULE__{}, n_stopped) when is_integer(n_stopped) do
    advance_till_stopped(world, n_stopped, 0)
  end

  defp advance_till_stopped(world, n_stopped, n_stopped), do: world

  defp advance_till_stopped(world, n_stopped, n) do
    world = advance(world)
    n = if world.stopped_rocks > n, do: n + 1, else: n
    advance_till_stopped(world, n_stopped, n)
  end

  defp add_rock(world = %__MODULE__{highest_y: highest_y, next_shape: next_shape}) do
    anchor = %Coords{x: 2, y: highest_y - 4}
    rock = Rock.new(anchor, next_shape)

    %{world | active_rock: rock, next_shape: Rock.next_shape(next_shape), next_step: :push}
  end

  defp push(
         world = %__MODULE__{
           active_rock: rock = %Rock{},
           stationary_formation: stationary_formation,
           jets: jets,
           jet_idx: jet_idx
         }
       ) do
    new_rock =
      case Enum.at(jets, jet_idx) do
        "<" -> Rock.left(rock)
        ">" -> Rock.right(rock)
      end

    all_new_coords = Rock.all_coords(new_rock)

    cond do
      # hit the wall, don't move
      Enum.any?(all_new_coords, fn %Coords{x: x} -> x < 0 || x >= @width end) ->
        increment_jet_idx(world)

      # hit stationary rocks, don't move
      MapSet.intersection(all_new_coords, stationary_formation) |> MapSet.size() != 0 ->
        increment_jet_idx(world)

      # no obstacles, move
      true ->
        %{world | active_rock: new_rock} |> increment_jet_idx()
    end
  end

  defp fall(
         world = %__MODULE__{
           active_rock: rock = %Rock{},
           stationary_formation: stationary_formation
         }
       ) do
    new_rock = Rock.down(rock)

    if new_rock.anchor.y == world.floor_level do
      # rock fallen to the floor, becomes stationary
      mark_active_as_stationary(world)
    else
      all_new_coords = Rock.all_coords(new_rock)
      collisions = MapSet.intersection(all_new_coords, stationary_formation)

      if MapSet.size(collisions) != 0 do
        # collision with stationary rocks detected, becomes stationary
        mark_active_as_stationary(world)
      else
        # no collisions detected, proceeds to fall
        %{world | active_rock: new_rock, next_step: :push}
      end
    end
  end

  defp mark_active_as_stationary(
         world = %__MODULE__{
           active_rock: rock = %Rock{},
           highest_y: highest_y,
           stationary_formation: stationary_formation,
           stopped_rocks: stopped_rocks
         }
       ) do
    coords = Rock.all_coords(rock)

    coords_highest_y = Enum.map(coords, fn coord -> coord.y end) |> Enum.min()
    highest_y = min(highest_y, coords_highest_y)

    stationary_formation = MapSet.union(stationary_formation, coords)

    %{
      world
      | active_rock: nil,
        highest_y: highest_y,
        stationary_formation: stationary_formation,
        next_step: :add_rock,
        stopped_rocks: stopped_rocks + 1
    }
  end

  def draw(world = %__MODULE__{}) do
    active_rock_coords = (world.active_rock &amp;&amp; Rock.all_coords(world.active_rock)) || MapSet.new()

    for y <- (world.highest_y - 7)..world.floor_level do
      if y == world.floor_level do
        IO.puts("+-------+")
      else
        line =
          Enum.reduce(-1..@width, [], fn x, line ->
            coords = %Coords{x: x, y: y}

            cond do
              x == -1 || x == @width -> ["|" | line]
              coords in active_rock_coords -> ["@" | line]
              coords in world.stationary_formation -> ["#" | line]
              true -> ["." | line]
            end
          end)

        line |> Enum.reverse() |> IO.puts()
      end
    end

    :ok
  end

  # for p2 

  def top_boundary_normalized(world = %__MODULE__{}) do
    world.stationary_formation
    |> Enum.group_by(fn %Coords{x: x} -> x end)
    |> Enum.reduce([], fn {_x, coords}, acc ->
      highest = Enum.min_by(coords, fn %Coords{y: y} -> y end)
      highest = %{highest | y: highest.y - world.highest_y}
      [highest | acc]
    end)
  end

  def find_cycle(world = %__MODULE__{}) do
    find_cycle(world, Map.new())
  end

  def find_cycle(world, memo) do
    new_world = advance_till_next_falling(world)

    case top_boundary_normalized(new_world) do
      [] ->
        find_cycle(new_world, memo)

      boundary ->
        key = {new_world.jet_idx, new_world.next_shape, boundary}

        if Map.has_key?(memo, key) do
          {prev_n_stopped, prev_highest_y} = memo[key]

          %{
            curr_n_stopped: new_world.stopped_rocks,
            curr_highest_y: new_world.highest_y,
            prev_n_stopped: prev_n_stopped,
            prev_highest_y: prev_highest_y,
            key: key
          }
        else
          memo = Map.put(memo, key, {new_world.stopped_rocks, new_world.highest_y})
          find_cycle(new_world, memo)
        end
    end
  end
end
{:module, World, <<70, 79, 82, 49, 0, 0, 50, ...>>, {:find_cycle, 2}}
defmodule D17 do
  def p1(jets) do
    world = World.new(jets) |> World.advance_till_stopped(2022)
    abs(world.highest_y)
  end

  @p2_rocks 1_000_000_000_000

  @doc """
  The idea is: 

  - find a cycle in world states that have the same top-rocks outline 
  (highest y for each x, normalized by the actual highest y for the state), 
  same jet index, and same next shape.
  - use the length of that cycle + the difference in height that the whole cycle
  contributes to quickly calculate the height.

  Note, that the height computation needs to account for: 

  - height increase before the first cycle starts (prev_highest_y)
  - height increase via cycles `((@p2_rocks - prev_n_stopped) div cycle_length) * height_increase`, 
  where `prev_n_stopped` is the offset before the first cycle starts
  - remaining steps before `@p2_rocks` is reached: `((@p2_rocks - prev_n_stopped) mod cycle_length)`.
  We can compute that height increase by going the remaining steps after the end of the first found cycle.
  """
  def p2(jets) do
    world = World.new(jets)

    %{
      curr_n_stopped: curr_n_stopped,
      curr_highest_y: curr_highest_y,
      prev_n_stopped: prev_n_stopped,
      prev_highest_y: prev_highest_y
    } = World.find_cycle(world)

    height_increase = abs(curr_highest_y - prev_highest_y)
    cycle_length = curr_n_stopped - prev_n_stopped

    cyclic_height_increase = div(@p2_rocks - prev_n_stopped, cycle_length) * height_increase

    # account for the additional cycles
    additional_steps_after_cycles = rem(@p2_rocks - prev_n_stopped, cycle_length)
    world = World.new(jets) |> World.advance_till_stopped(curr_n_stopped)
    before_additional_cycles_y = world.highest_y

    world =
      World.new(jets)
      |> World.advance_till_stopped(curr_n_stopped + additional_steps_after_cycles)

    additional_height_increase = abs(world.highest_y - before_additional_cycles_y)

    abs(prev_highest_y) + cyclic_height_increase + additional_height_increase
  end
end
{:module, D17, <<70, 79, 82, 49, 0, 0, 13, ...>>, {:p2, 1}}

Tests

ExUnit.start(autorun: false)

defmodule D17Tests do
  use ExUnit.Case, async: true

  setup_all _context do
    %{
      input: File.read!("inputs/d17") |> String.trim_trailing() |> String.graphemes(),
      test_input: ">>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>" |> String.graphemes()
    }
  end

  test "p1", %{input: input, test_input: test_input} do
    assert D17.p1(test_input) == 3068
    assert D17.p1(input) == 3179
  end

  test "p2", %{input: input, test_input: test_input} do
    assert D17.p2(test_input) == 1_514_285_714_288
    assert D17.p2(input) == 1_567_723_342_929
  end
end

ExUnit.run()
..
Finished in 1.4 seconds (1.4s async, 0.00s sync)
2 tests, 0 failures

Randomized with seed 160117
%{excluded: 0, failures: 0, skipped: 0, total: 2}

Test Example

jets = ">>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>" |> String.graphemes()
world = World.new(jets)
World.draw(world)
|.......|
|.......|
|.......|
|.......|
|.......|
|.......|
|.......|
+-------+
:ok
world = World.advance(world)
World.draw(world)
|.......|
|.......|
|.......|
|..@@@@.|
|.......|
|.......|
|.......|
+-------+
:ok
world = World.advance(world)
World.draw(world)
|.......|
|.......|
|.......|
|...@@@@|
|.......|
|.......|
|.......|
+-------+
:ok
world = World.advance(world)
World.draw(world)
|.......|
|.......|
|.......|
|.......|
|...@@@@|
|.......|
|.......|
+-------+
:ok
world = World.advance_till_next_falling(world) |> World.advance()
World.draw(world)
|.......|
|...@...|
|..@@@..|
|...@...|
|.......|
|.......|
|.......|
|..####.|
+-------+
:ok
world = World.advance_till_next_falling(world) |> World.advance()
World.draw(world)
|.......|
|....@..|
|....@..|
|..@@@..|
|.......|
|.......|
|.......|
|...#...|
|..###..|
|...#...|
|..####.|
+-------+
:ok
world = World.advance_till_next_falling(world) |> World.advance()
World.draw(world)
|..@....|
|..@....|
|..@....|
|..@....|
|.......|
|.......|
|.......|
|..#....|
|..#....|
|####...|
|..###..|
|...#...|
|..####.|
+-------+
:ok
world = World.new(jets) |> World.advance_till_stopped(2022)
World.draw(world)
...
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|..###..|
|..###..|
|..####.|
|....###|
|.....#.|
|.#####.|
|.#..#..|
|.#..#..|
|.####.#|
|.####.#|
|###.###|
|.#####.|
|.###...|
|.###...|
|.#.#...|
|.#.#.#.|
|.######|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|....#..|
|...###.|
|#...#..|
|#####..|
|#.#....|
|#.#....|
|####...|
|..#####|
|...#.##|
|..####.|
|.##....|
|.##...#|
|..#...#|
|..#.###|
|..#..#.|
|..#.###|
|.#####.|
|....#..|
|....#..|
|....#..|
|....#..|
|.##.#..|
|.##.#..|
|..###..|
|...#...|
|..###..|
|...#...|
|..####.|
|.....##|
|.....##|
|......#|
|......#|
|...####|
|..###..|
|...#...|
|#..####|
|#...#..|
|#...#..|
|#...##.|
|##..##.|
|######.|
|.###...|
|..#....|
|.####..|
|....##.|
|....##.|
|....#..|
|..#.#..|
|..#.#..|
|#####..|
|..###..|
|...#...|
|..####.|
+-------+
:ok
abs(world.highest_y)
3068