Elixir Weekly Challenge: Number Game

The second of this week’s PWC 268 tasks is called Number Game.

Task 2: Number Game

You are given an array of integers, @ints, with even number of elements.

Write a script to create a new array made up of elements of the given array. Pick the two smallest integers and add it to new array in decreasing order i.e. high to low. Keep doing until the given array is empty.

Example 1

Input: @ints = (2, 5, 3, 4)
Output: (3, 2, 5, 4)

Round 1: we picked (2, 3) and push it to the new array (3, 2)
Round 2: we picked the remaining (4, 5) and push it to the new array (5, 4)

Example 2

Input: @ints = (9, 4, 1, 3, 6, 4, 6, 1)
Output: (1, 1, 4, 3, 6, 4, 9, 6)

Example 3

Input: @ints = (1, 2, 2, 3)
Output: (2, 1, 3, 2)

Especially after how I wound up doing this week’s task 1, I immediately thought about pattern matching in function definitions. One function to handle the end case when the list is exhausted, and another to handle pulling off the first two elements from the list, and a wrapper function to sort the list and then call the recursive function that does the real work:

  defp numberGame([], results) do
    results
  end

  defp numberGame([x | y | rest], results) do
    results = Enum.concat(results, [y, x])
    numberGame(rest, results)
  end

  def numberGame(ints) do
    sortedInts = Enum.sort(ints)
    numberGame(sortedInts, [])
  end

$ elixir/ch-2.exs
    error: misplaced operator |/2

    The | operator is typically used between brackets as the
    cons operator:

        [head | tail]

    where head is a sequence of elements separated by commas
    and the tail is the remaining of a list.

    It is also used to update maps and structs, via the 
    %{map | key: value} notation, and in typespecs, such as
    @type and @spec, to express the union of two types

  8defp numberGame([x | y | rest], results) do
    │                          ^

    └─ elixir/ch-2.exs:8:23: PWC.grabTwo/2

Oops! Ok, I know how to fix this, and it will be easy, because instead of defining just one numberGame/1 function and two numberGame/2 functions, I can define one numberGame/1 function, two numberGame/2 functions and one numberGame/3 function:

  # list is empty, return results
  defp numberGame([], results) do
    results
  end

  # grab the first elem off list, recurse
  defp numberGame([x | rest], results) do
    numberGame(x, rest, results)
  end

  # grab second elem off list, swap them, recurse
  defp numberGame(x, [y | rest], results) do
    results = Enum.concat(results, [y, x])
    numberGame(rest, results)
  end

  # sort the list and then recursively swap element pairs
  def numberGame(ints) do
    sortedInts = Enum.sort(ints)
    numberGame(sortedInts, [])
  end

Which brings me to my final implementation:

#!/usr/bin/env elixir

defmodule PWC do
  # list is empty, return results
  defp numberGame([], results) do
    results
  end

  # grab the first elem off list, recurse
  defp numberGame([x | rest], results) do
    numberGame(x, rest, results)
  end

  # grab second elem off list, swap them, recurse
  defp numberGame(x, [y | rest], results) do
    results = Enum.concat(results, [y, x])
    numberGame(rest, results)
  end

  # sort the list and then recursively swap element pairs
  def numberGame(ints) do
    sortedInts = Enum.sort(ints)
    numberGame(sortedInts, [])
  end

  def solution(ints) do
    IO.puts("Input: @ints = (" <> Enum.join(ints, ", ") <> ")")
    result = PWC.numberGame(ints)
    IO.puts("Output: (" <> Enum.join(result, ", ") <> ")")
  end
end

IO.puts("Example 1:")
PWC.solution([2, 5, 3, 4])

IO.puts("\nExample 2:")
PWC.solution([9, 4, 1, 3, 6, 4, 6, 1])

IO.puts("\nExample 3:")
PWC.solution([1, 2, 2, 3])

$ elixir/ch-2.exs
Example 1:
Input: @ints = (2, 5, 3, 4)
Output: (3, 2, 5, 4)

Example 2:
Input: @ints = (9, 4, 1, 3, 6, 4, 6, 1)
Output: (1, 1, 4, 3, 6, 4, 9, 6)

Example 3:
Input: @ints = (1, 2, 2, 3)
Output: (2, 1, 3, 2)

The code can be found on GitHub at https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-268/packy-anderson/elixir