Help needed with my Wordle attempt

word = "apple"
tempWord = 
  String.codepoints(word) 
  |> Enum.with_index() 
  |> Enum.map(fn {char, index} -> 
    cond do
      char == String.at(guess, index)  -> {char, index, :correct}
      true                             -> {char, index, :null}
    end
  end)

  # [{"a", 0, :null}, {"p", 1, :null}, {"p", 2, :null}, {"l", 3, :correct}, {"e", 4, :null}]
guess = "hello"
tempGuess = 
  String.codepoints(guess) 
  |> Enum.with_index() 
  |> Enum.map(fn {char, index} -> 
    cond do
      char == String.at(word, index)   -> {char, index, :correct}
      not String.contains?(word, char) -> {char, index, :wrong}
      String.contains?(word, char)     -> {char, index, :maybe}
    end
  end)

  # [{"h", 0, :wrong}, {"e", 1, :maybe}, {"l", 2, :maybe}, {"l", 3, :correct}, {"o", 4, :wrong}]

In the above code, what would be the Elixir way to achieve the following:

  1. Take each element of guessTemp
  2. if the state is maybe, then check the wordTemp list;
  3. find the first occurrence of a corresponding character, if the state is :null, then
  4. in wordTemp, change the state from :null to :done
  5. back to item 2; if there is no such occurrence in wordTemp, change the status of the entry in guessTemp as :incorrect

Thus, for the above example, the final list should be in the form of:
[{"h", 0, :wrong}, {"e", 1, :maybe}, {"l", 2, :maybe}, {"l", 3, :correct}, {"o", 4, :wrong}]

1 Like

Sorry, I do not get why your expected output is tempGuess when you are mentioning the changes in tempWord?

1 Like

Right, it was incomplete. I hope it is okay now. Thanks for the reminder.

Ok, so here is what I came up with:

defmodule Example do
  def sample(word, guess) do
    guess_codepoints = String.codepoints(guess)
    word_codepoints = String.codepoints(word)
    input = sample(word_codepoints, guess_codepoints, word_codepoints, 0)

    Enum.reduce(input, input, fn
      %{guess_char: guess_char, guess_state: :maybe, index: index}, acc ->
        case replace_first_null(acc, guess_char) do
          :not_found -> replace_state(acc, index, :incorrect)
          acc -> acc
        end

      _, acc ->
        acc
    end)
  end

  defp sample([], [], _word, _index), do: []

  defp sample([char | word_tail], [char | guess_tail], word, index) do
    element = %{
      guess_char: char,
      guess_state: :correct,
      index: index,
      word_char: char,
      word_state: :correct
    }

    [element | sample(word_tail, guess_tail, word, index + 1)]
  end

  defp sample([word_char | word_tail], [guess_char | guess_tail], word, index) do
    if guess_char in word do
      element = %{
        guess_char: guess_char,
        guess_state: :maybe,
        index: index,
        word_char: word_char,
        word_state: :null
      }

      [element | sample(word_tail, guess_tail, word, index + 1)]
    else
      element = %{
        guess_char: guess_char,
        guess_state: :wrong,
        index: index,
        word_char: word_char,
        word_state: :null
      }

      [element | sample(word_tail, guess_tail, word, index + 1)]
    end
  end

  defp replace_first_null(input, guess_char, acc \\ [])

  defp replace_first_null([], _guess_char, _acc), do: :not_found

  defp replace_first_null(
         [%{word_char: guess_char, word_state: :null} = head | tail],
         guess_char,
         acc
       ) do
    :lists.reverse(acc) ++ [%{head | word_state: :done} | tail]
  end

  defp replace_first_null([head | tail], word_char, acc) do
    replace_first_null(tail, word_char, [head | acc])
  end

  defp replace_state(list, index, state) do
    put_in(list, [Access.at(index), Access.key(:guess_state)], state)
  end
end

Example.sample("apple", "hello")

Here is what I do:

  1. First of all I changed your code in 2 ways:
    a) I’m using one loop to work with 2 lists at same time
    b) I’m using one list of maps instead of two lists of tuples

  2. The part you are the most interested about is a simple reduce, but over same (at start) data, so accumulated input is updated only when needed (see pattern matching inside anonymous function)

Does it answer your question?

2 Likes

Thank you. At least, what I missed was not something short and trivial. I guess, tasks of this kind are not especially suited for functional paradigm.