Optimizing an Elixir puzzle: Rock Paper Scissors

After I had to wait for my BEAM to crash because of infinite recursion (it blocked my laptop totally, even ssh was impossible) I rewrote to use binaries.

The BEAM can optimise appending on them under certain conditions, and I think I have met them.

Also this way you do not need to create that extra list that holds the code points.

A next step was to remove the case/2 from the public function and let this be handled by the helper function.

Then I combined the “draws” into a single pattern match, in your version it would be defp _find_winner([x, x|rest], acc), do: _find_winner(rest, acc).

After that I extracted another function which would determine the winner of a single match.

And the last thing I did, was to remove the underscore prefix, because I do read it as “this isn’t meant to be used”.

defmodule RockPaperScissors do
  def find_winner(lineup), do: find_winner(lineup, "")

  # Nobody won
  defp find_winner("", ""), do: "None"

  # last man standing wins
  defp find_winner(winner = <<_::utf8>>, ""), do: winner

  # Advance to the next round!
  defp find_winner("", acc), do: find_winner(acc, "")

  # Single player remaining (bc of odd number of players)
  defp find_winner(<<a::utf8>>, acc), do: find_winner("", <<acc::binary, a::utf8>>)

  # Both are the same, both loose
  defp find_winner(<<a::utf8, a::utf8, rest::binary>>, acc), do: find_winner(rest, acc)

  # only one hands wins
  defp find_winner(<<a::utf8, b::utf8, rest::binary>>, acc),
    do: find_winner(rest, <<acc::binary, single_game(a, b)::utf8>>)

  defp single_game(?P, ?R), do: ?P
  defp single_game(?P, ?S), do: ?S
  defp single_game(?R, ?P), do: ?P
  defp single_game(?R, ?S), do: ?R
  defp single_game(?S, ?P), do: ?S
  defp single_game(?S, ?R), do: ?R
end

alias RockPaperScissors, as: RPS

IO.puts(~s'"R" is "R" -> #{RPS.find_winner("R")}')
IO.puts(~s'"RR" is "None" -> #{RPS.find_winner("RR")}')
IO.puts(~s'"RRS" is "S" -> #{RPS.find_winner("RRS")}')
IO.puts(~s'"RRRSRSPS" is "S" -> #{RPS.find_winner("RRRSRSPS")}')

I have not benchmark this or one of your codes. But I have not really optimzed for speed, but instead rewritten in a way that I consider easier to read and maintain.