Maxximiliann

Maxximiliann

Learning Elixir - Year One

defmodule ArrangeOpeningCombos do
  def remove_illegal_third_moves_after_e4(potential_line) do
    [one, w1, b1, two, w2, b2, three, w3, b3, four, w4, b4] = potential_line

    illegal_moves_map = %{
      "Bd3" => [
        "a5",
        "b5",
        "c5",
        "d3",
        "d4",
        "d5",
        "e5",
        "f5",
        "g5",
        "h5",
        "Nd2",
        "Ba3",
        "Bb2",
        "Bc2",
        "Be3",
        "Bf4",
        "Bf5",
        "Bg5",
        "Bg6",
        "Bh6"
      ]
    }

    cond do
      w2 == "Bd3" ->
        illegal_moves_list = Map.get(illegal_moves_map, "Bd3")

        if w3 not in illegal_moves_list do
          "(#{one} #{w1} #{b1} #{two} #{w2} #{b2} #{three} #{w3} #{b3} #{four} #{w4} #{b4}) "
        end

      true ->
        "(#{one} #{w1} #{b1} #{two} #{w2} #{b2} #{three} #{w3} #{b3} #{four} #{w4} #{b4}) "
        # potential_line
    end
  end

  def remove_illegal_second_moves_after_e4(possible_line) do
    [one, w1, b1, two, w2, b2, three, w3, b3, four, w4, b4] = possible_line

    list_of_illegal_second_moves_after_e4 = [
      "a5",
      "b5",
      "c5",
      "d5",
      "e5",
      "f5",
      "g5",
      "h5",
      "Nd2",
      "Ba3",
      "Bb2",
      "Bd2",
      "Be3",
      "Bf4",
      "Bg5",
      "Bg2",
      "Bh3",
      "Bh6"
    ]

    if w2 not in list_of_illegal_second_moves_after_e4 do
      [one, w1, b1, two, w2, b2, three, w3, b3, four, w4, b4]
    end
    |> remove_illegal_third_moves_after_e4()
  end

  def remove_illegal_second_moves_after_d4(possible_line) do
    [one, w1, b1, two, w2, b2, three, w3, b3, four, w4, b4] = possible_line

    list_of_illegal_second_moves_after_d4 = [
      "a5",
      "b5",
      "c5",
      "d5",
      "e5",
      "f5",
      "g5",
      "h5",
      "Ne2",
      "Ba3",
      "Ba6",
      "Bb2",
      "Bb5",
      "Bc4",
      "Bd3",
      "Be2",
      "Bg2",
      "Bh3"
    ]

    if w2 not in list_of_illegal_second_moves_after_d4 do
      "(#{one} #{w1} #{b1} #{two} #{w2} #{b2} #{three} #{w3} #{b3} #{four} #{w4} #{b4}) "
    end

    # |> remove_illegal_third_moves_after_d4()
  end

  def scrub_moves_list(moves_list) do
    Enum.map(
      moves_list,
      fn [one, w1, b1, two, w2, b2, three, w3, b3, four, w4, b4] = possible_line ->
        cond do
          w1 == "e4" ->
            remove_illegal_second_moves_after_e4(possible_line)

          w1 == "d4" ->
            remove_illegal_second_moves_after_d4(possible_line)

          true ->
            "(#{one} #{w1} #{b1} #{two} #{w2} #{b2} #{three} #{w3} #{b3} #{four} #{w4} #{b4}) "
        end
      end
    )
  end

  def permute([]), do: [[]]

  def permute(list) do
    for x <- list, y <- permute(list -- [x]), do: [x | y]
  end

  def go(opening_moves_string) do
    [
      one,
      white1,
      black1,
      two,
      white2,
      black2,
      three,
      white3,
      black3,
      four,
      white4,
      black4
    ] = String.split(opening_moves_string)

    move_numbering = [one, two, three, four]
    whites_moves_list = [white1, white2, white3, white4]
    blacks_moves_list = [black1, black2, black3, black4]

    permutations = permute(whites_moves_list)

    combinations_list =
      Enum.map(
        permutations,
        fn moves_list ->
          Enum.zip(move_numbering, moves_list)
          |> Enum.zip(blacks_moves_list)
          |> Enum.flat_map(fn {{number, whites_move}, blacks_move} ->
            [number, whites_move, blacks_move]
          end)
        end
      )

    Enum.map(
      combinations_list,
      fn [
           one,
           w1,
           b1,
           two,
           w2,
           b2,
           three,
           w3,
           b3,
           four,
           w4,
           b4
         ] ->
        illegal_first_moves = [
          "a5",
          "b5",
          "c5",
          "d5",
          "e5",
          "f5",
          "g5",
          "h5",
          "Ne2",
          "Nd2",
          "Ba3",
          "Ba6",
          "Bb2",
          "Bb5",
          "Bc4",
          "Bd3",
          "Be2",
          "Bd2",
          "Be3",
          "Bf4",
          "Bg5",
          "Bg2",
          "Bh3",
          "Bh6"
        ]

        if w1 not in illegal_first_moves do
          [one, w1, b1, two, w2, b2, three, w3, b3, four, w4, b4]
        end
      end
    )
    |> Enum.reject(&is_nil(&1))
    |> scrub_moves_list()
    |> Enum.reject(&is_nil(&1))
    |> IO.puts()
  end
end

Output:

Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)
Elixir is ready
iex(1)> ArrangeOpeningCombos.go("1. e4 e6 2. d4 Ke7 3. Bd3 f6 4. Ne2 Kf7")
(1. e4 e6 2. d4 Ke7 3. Bd3 f6 4. Ne2 Kf7) (1. e4 e6 2. d4 Ke7 3. Ne2 f6 4. Bd3 Kf7) (1. e4 e6 2. Bd3 Ke7 3. Ne2 f6 4. d4 Kf7) (1. e4 e6 2. Ne2 Ke7 3. d4 f6 4. Bd3 Kf7) (1. e4 e6 2. Ne2 Ke7 3. Bd3 f6 4. d4 Kf7) (1. d4 e6 2. e4 Ke7 3. Bd3 f6 4. Ne2 Kf7) (1. d4 e6 2. e4 Ke7 3. Ne2 f6 4. Bd3 Kf7) 
:ok

Ok, so, I’ve been learning Elixir for about a year now despite having zero programming experience - thanks so much to everyone on here who’s so kindly and generously helped me along the way :slight_smile: :slight_smile: :wink: - and I’d like to get a sense of where I’m at, what I’m doing correctly, what bad habits I’ve picked up along the way, if any, and how I can write code that is more efficient, readable and idiomatic while conforming to best practices. To that end I’ve put together this toy project.

Any and all tips, suggestions, recommendations (and encouragements :slight_smile: ) are warmly welcomed. Thanks again guys! :slight_smile:

Most Liked

Eiji

Eiji

With Elixir formatter this is better:

defmodule Example do
  @illegal_moves %{
    "Bd3" => ~w[a5 b5 c5 d3 d4 d5 e5 f5 g5 h5 Nd2 Ba3 Bb2 Bc2 Be3 Bf4 Bf5 Bg5 Bg6 Bh6]
  }
end

as that your list would not be expanded in so many lines …

Shorter:

"(" <> Enum.join(possible_line, " ") <> ") "

Optionally:

illegal_moves_map["Bd3"]

You may use pattern matching for this like:

    Enum.map(moves_list, fn
      [_one, w1 | _rest] = possible_line when w1 in ~w[e4 d4] ->
        remove_illegal_second_moves_after(possible_line, w1)

      possible_line ->
        "(" <> Enum.join(possible_line, " ") <> ") "
    end)

Much better:

        fn moves_list ->
          [move_numbering, moves_list, blacks_moves_list]
          |> Enum.zip()
          |> Enum.flat_map(&Tuple.to_list/1)
        end

scrub_moves_list/1 always returns list, so second Enum.reject/2 is not needed.

Also you can write &is_nil/1.

Finally having "(" <> Enum.join(possible_line, " ") <> ") " multiple times we may want to create a helper function for that.

Where Next?

Popular in Questions Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
Fl4m3Ph03n1x
Background Let’s assume I have a typical GenServer that receives messages as requests, does some operation in a DB and returns responses....
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
AstonJ
You’re a programmer, so you don’t need spoon feeding with the conventional drivel about “this is an integer.” No. You need to know what’s...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New

We're in Beta

About us Mission Statement