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 && 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