Bulls and Cows


Task

Create a four digit random number from the digits   1   to   9,   without duplication.

The program should:

   ask for guesses to this number
   reject guesses that are malformed
   print the score for the guess


The score is computed as:

 The player wins if the guess is the same as the randomly chosen number, and the program ends.
 A score of one bull is accumulated for each digit in the guess that equals the corresponding digit in the randomly chosen initial number.
 A score of one cow is accumulated for each digit in the guess that also appears in the randomly chosen number, but in the wrong position.


Related tasks

   Bulls and cows/Player
   Guess the number
   Guess the number/With Feedback
   Mastermind


defmodule Bulls_and_cows do
  def play(size \\ 4) do
    secret = Enum.take_random(1..9, size) |> Enum.map(&to_string/1)
    play(size, secret)
  end
 
  defp play(size, secret) do
    guess = input(size)
    if guess == secret do
      IO.puts "You win!"
    else
      {bulls, cows} = count(guess, secret)
      IO.puts "  Bulls: #{bulls}; Cows: #{cows}"
      play(size, secret)
    end
  end
 
  defp input(size) do
    guess = IO.gets("Enter your #{size}-digit guess: ") |> String.strip
    cond do
      guess == "" ->
        IO.puts "Give up"
        exit(:normal)
      String.length(guess)==size and String.match?(guess, ~r/^[1-9]+$/) ->
        String.codepoints(guess)
      true -> input(size)
    end
  end
 
  defp count(guess, secret) do
    Enum.zip(guess, secret) |>
    Enum.reduce({0,0}, fn {g,s},{bulls,cows} ->
      cond do
        g == s      -> {bulls + 1, cows}
        g in secret -> {bulls, cows + 1}
        true        -> {bulls, cows}
      end
    end)
  end
end
 
Bulls_and_cows.play