Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions livebooks/elixir_ASCII_easteregg.livemd
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Elixir goes ASCII Art ¯\_(ツ)_/¯: The easter egg

## It's (nerdy) easter time!

The aim is to create an egg-shaped pattern in your terminal that is made up of characters from the given string Elixir.

The following code achieves that by using a combination of mathematical equations and string manipulation techniques.

You'll find a step-by-step breakdown after the code. Let's go!

```elixir
defmodule EasterEgg do
@egg String.graphemes("Elixir ")

def character_at(x, y) do
index = rem(x - y, length(@egg))
char = Enum.at(@egg, index)

case math_power(x, y) do
true -> char
false -> " "
end
end

def math_power(x, y) do
case y > 0 do
true -> :math.pow(x / 2.5, 2) + :math.pow(y / 2, 2) <= 100
false -> :math.pow(x / 2.5, 2) + :math.pow(y / 1, 2) <= 100
end
end

def patch_together() do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def paint() do
def draw() do

Copy link
Collaborator

@resterle resterle Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also maybe just let the paint or draw function have all its inputs as parameters.

draw("Java", &inside_egg?/2)

draw("Elixir", &inside_heart?/2)

draw("golang", &inside_bunny?/2)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what you mean.

Copy link
Collaborator

@resterle resterle Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how i would have done it. Just as a "Denksanstoß".

defmodule AsciiArt do
  @columns 60
  @rows 36
  @row_offset -13

  def draw_shape(word, shape_fun) do
    make_cords()
    |> offset_every_x(-div(@columns, 2))
    |> offset_every_y(@row_offset)
    |> append_visibility(shape_fun)
    |> to_characters("#{word} ")
    |> create_rows()
    |> Enum.join("\n")
  end

  defp make_cords() do
    0..(@columns*@rows)
    |> Enum.map(fn a -> {rem(a, @columns), div(a, @columns)} end)
    |> Enum.reverse()
  end

  defp offset_every_x(cords, offset) do
    Enum.map(cords, &offsetX(&1, offset))
  end

  defp offsetX({x, y}, offset) do
    {x+offset, y}
  end

  defp offset_every_y(cords, offset) do
    Enum.map(cords, &offsetY(&1, offset))
  end

  defp offsetY({x, y}, offset) do
    {x, y+offset}
  end

  defp append_visibility(cords, shape_fun) do
    Enum.map(cords, fn {x, y} = c -> {x, y, shape_fun.(c)} end)
  end

  defp to_characters(cords, word) do
    Enum.map(cords, &character_at(&1, word))
  end

  defp character_at({_x, _y, false}, _word), do: " "
  defp character_at({x, y, true}, word) do
    index = rem(x - y, String.length(word))
    String.at(word, index)
  end

  defp create_rows(characters) do
    characters
    |> Enum.chunk_every(@columns)
    |> Enum.map(&Enum.join/1)
  end
end

And then you can do:

inside_egg? = fn 
  {x, y} when y > 0 -> :math.pow(x / 2.5, 2) + :math.pow(y / 2, 2) <= 100
  {x, y} -> :math.pow(x / 2.5, 2) + :math.pow(y / 1, 2) <= 100
end

AsciiArt.draw_shape("Java", inside_egg?)
|> Kino.Markdown.new()

or:

inside_heart? = fn {x, y} ->
    :math.pow(:math.pow(x * 0.05, 2) + :math.pow(y * 0.1, 2) - 1, 3) -
    :math.pow(x * 0.05, 2) * :math.pow(y * 0.1, 3) <= 0
end

AsciiArt.draw_shape("Elixir", inside_heart?)
|> Kino.Markdown.new()

-13..23
|> Enum.map(fn y ->
-30..30
|> Enum.map(fn x -> EasterEgg.character_at(x, y) end)
|> Enum.join("")
end)
|> Enum.reverse()
|> Enum.join("\n")
end
end

IO.puts(EasterEgg.patch_together())
```
11 changes: 7 additions & 4 deletions livebooks/elixir_ASCII_heart.livemd
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Elixir goes ASCII Art ¯\_(ツ)_/¯
# Elixir goes ASCII Art ¯\_(ツ)_/¯: The Heart.

## We💜Elixir, right?

Expand Down Expand Up @@ -55,11 +55,14 @@ The code is split into 5 parts:
3. The function `math_power` takes two arguments, x and y, and returns a boolean, indicating whether the point (x, y) is inside the heart shape or not.

* This is the basic mathematical formula for our heart: ((x)² + (y)^2 - 1)³ - (x)² * (y)³ <= 0

* The ((x)² + (y)² - 1)³ part describes a circle, the (x)² * (y)³ part descibes a cube.

* Subtract the (x)² * (y)³ part from the ((x)² + (y)² - 1)³ part and less-than-or-equal it to 0 to get the shape of a heart.

* To play around with this math part, visit: <https://www.wolframalpha.com/input?i=++%28%28x+*+0.05%29%5E2+%2B+%28y+*+0.1%29%5E2+-+1%29%5E3+-+%28x+*+0.05%29%5E2+*+%28y+*+0.1%29%5E3+%3C%3D+0>

1. The function `patch_together` is the main function that creates the heart shape. It does so by:
4. The function `patch_together` is the main function that creates the heart shape. It does so by:

* maps over a range of y values, and for each y value, again maps over a range of x values in order to create a row of characters for that specific y value.

Expand All @@ -69,8 +72,8 @@ The code is split into 5 parts:

* joins all the rows with a newline character to form the complete heart shape.

1. The last section of the code calls the `patch_together` function and prints the result to the terminal.
5. The last section of the code calls the `patch_together` function and prints the result to the terminal.

<!-- livebook:{"break_markdown":true} -->

![](images/elixir_ascii_heart.png)
![](images/ASCII_heart.png)