blackened

blackened

I am a complete Elixir beginner. I am trying to understand certain concepts with the following Mastermind sub-problem.

Suppose I have two lists code = [0, 3, 2 ,3, 4, 5] and guess = [0, 4, 2, 4, 3, 3].

My approach is, we compare the two lists and assign two values, one for both correct guess and position (CP), and the other for correct guess but for wrong position (CG). In the above example: CP is 2 (the first and third guess), and the CG is 3 (two 3s and one 4).

I guess there are numerous ways to approach. What I wanted to achieve was this: We traverse the two lists to determine correct positional guesses and produce two new lists. For the above example, that would be:

[:true, 3, :true, 3, 4, 5] and [:true, 4, :true, 4, 3, 3]

Then, we traverse the two lists for the second time and produce a third list, such that we take each item from the new guess list and check if the code list contains it. If so, the new list returns, say, :guess for those guesses. So the third list will be:

[:true, :guess, :true, :guess, :guess: 5] and [:true, :ok, :true, 4, :ok, :ok]

And now we count the :true, and :guess.

  1. What is the Elixir way to produce the second list and the third list.
  2. How else can I approach it anyway (again, in an idiomatic Elixir way)?

Showing Posts 1 to 4

Sebb

Sebb

Nice problem to learn some Enum functions.

The ‘CP’ problem can easily be solved with zip

{cps, other} = Enum.zip(code, guess) |> Enum.split_with(fn {c,g} -> c == g end)
cp = length(cps)

‘CG’ is a little more involved, there are several functions in Enum one could use. There is most likely some better approach, but for example this seems to work:

{code, guess} = Enum.unzip(other)
code_freqs = Enum.frequencies(code)
guess_freqs = Enum.frequencies(guess)
cg = Enum.reduce(guess_freqs, 0, fn {guess, guess_freq}, score ->
  code_freq = Map.get(code_freqs, guess, 0)
  score + Enum.min([code_freq, guess_freq])
end)

Have a look at the other functions in Enum. Try to solve 'CG" without the help of frequencies.

This may help: Elixir Enum Cheatsheet

alfredfriedrich

alfredfriedrich

My surely not so Elixir way approach:

firstcodes = [0, 3, 2 ,3, 4, 5]
firstguesses = [0, 4, 2, 4, 3, 3]

defmodule Try do
  

  def correct_positional_guess({same, same}), do: {:true, :true}
  def correct_positional_guess({code, guess}), do: {code, guess}

  def correct_guess(codes, :true), do: {codes, :true}
  def correct_guess(codes, guess) do

    Enum.map_reduce(codes, guess, fn elem, acc ->  
      cond do
        elem == :true -> {elem, acc}
        acc == :guess -> {elem, acc}
        elem == acc -> {:guess, :ok}
        true -> {elem, acc}
      end
    end)
  end

end


IO.inspect(firstcodes, label: "1st codes")
IO.inspect(firstguesses, label: "1st guesses")

second_lists = 
  Enum.zip(firstcodes, firstguesses)
  |> Enum.map(&Try.correct_positional_guess/1)
  
{codes2, guesses2} = second_lists |> Enum.unzip()

IO.inspect(codes2, label: "2nd codes")
IO.inspect(guesses2, label: "2nd guesses")

{third_lists, finalacc} = 
  second_lists
  |> Enum.map_reduce(codes2, fn {code, guess}, codes -> 
    cond do
      code == guess == :true -> {{code, guess}, codes}
      true -> 
        {newcodes, newguess} = Try.correct_guess(codes, guess)
        {{code, newguess}, newcodes}
    end
    
end)

thirdcodes = finalacc

thirdguesses = 
  third_lists
  |> Enum.unzip()
  |> Kernel.elem(1)

IO.inspect(thirdcodes, label: "3rd codes")
IO.inspect(thirdguesses, label: "3rd guesses")

Will produce something like:

1st codes: [0, 3, 2, 3, 4, 5]
1st guesses: [0, 4, 2, 4, 3, 3]
2nd codes: [true, 3, true, 3, 4, 5]
2nd guesses: [true, 4, true, 4, 3, 3]
3rd codes: [true, :guess, true, :guess, :guess, 5]
3rd guesses: [true, :ok, true, 4, :ok, :ok]

Thanks for the opportunity to read up again about Enum and map_reduce :slight_smile:

matt-savvy

matt-savvy

I love these games. I wrote a mastermind game & solver a few years ago in JS and the evaluation fn
relied heavily on mutating arrays. (also recently wrote a wordle knockoff & solver in Elm that used an approach similar to the Enum.frequencies approach above).

Here’s my crack at the evaluation in Elixir, but I’m also still new to the language.

defmodule Mastermind do
  def evaluate(guess, code) do
    { correct_position, remaining } = get_correct_positions(guess, code)

    { remaining_guess, remaining_code } = Enum.unzip(remaining)
    correct_guesses = get_correct_guesses(remaining_guess, remaining_code)

    # return the results
    correct_position ++ correct_guesses
  end

  # right number in the right spot
  defp get_correct_positions(guess, code) do
    { correct_position, remaining } = Enum.zip(guess, code)
      |> Enum.split_with(fn { guess_digit, code_digit } ->
        code_digit == guess_digit
      end)

    correct_position = Enum.map(correct_position, fn _ -> :correct_position end)
    { correct_position, remaining }
  end

  # recursion finished
  defp get_correct_guesses(remaining_guess, remaining_code, results \\ [])
  defp get_correct_guesses([], _code, results), do: results
  defp get_correct_guesses([ guess_digit | remaining_guess], remaining_code, results) do
    index = Enum.find_index(remaining_code, &(&1 == guess_digit))
    # found this digit in the guess
    if index do
      get_correct_guesses(
        remaining_guess,
        # remove this found digit from the code so we don't count it again
        List.delete_at(remaining_code, index),
        # push a correct_guess onto the results
        [:correct_guess | results]
      )
    else
      # no hits on this digit, continue
      get_correct_guesses(remaining_guess, remaining_code, results)
    end
  end
end

I really like Sebb’s approach with Enum.split_with, otherwise I would have just done one round of mapping the correct positions to an atom, another round of reducing to come up with atoms for the correct guesses, and then rejected everything from the list that wasn’t an atom (just using Kernel.is_atom/1)

matt-savvy

matt-savvy

@blackened

I just had a thought and I’m pretty sure you can do this to get the remaining correct guesses (as in, right number, wrong place)

  defp get_correct_guesses(remaining_guess, remaining_code) do
    (remaining_guess -- remaining_code) |> Enum.map(fn _ -> :correct_guess end)
  end
— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews