Skip to content

Instantly share code, notes, and snippets.

@mcelaney
Created March 2, 2017 03:44
Show Gist options
  • Save mcelaney/26b7b77cfe3fefd211411ec99c700ae5 to your computer and use it in GitHub Desktop.
Save mcelaney/26b7b77cfe3fefd211411ec99c700ae5 to your computer and use it in GitHub Desktop.
PhillyDev Slack #daily_programmer for March 1, 2017
defmodule MagicSquare do
@moduledoc """
Today's daily programmer challenge is to test a magic square. This challenge
is stolen verbatim from r/dailyprogrammer. A 3x3 magic square is a 3x3 grid
of the numbers 1-9 such that each row, column, and major diagonal adds up to
15. Here's an example:
```
8 1 6
3 5 7
4 9 2
```
When given the inputs arrays, you should get:
`[8, 1, 6, 3, 5, 7, 4, 9, 2] => true`
`[2, 7, 6, 9, 5, 1, 4, 3, 8] => true`
`[3, 5, 7, 8, 1, 6, 4, 9, 2] => false`
`[8, 1, 6, 7, 5, 3, 4, 9, 2] => false`
"""
@type axis_values :: list(list(pos_integer))
@type original_list :: list(pos_integer)
@box_width 3
@goal_value 15
@doc """
Determines whether a given order list of integers represents a magic box
This solution is maybe more complex - but it allows us to solve for any size
magic square - simply change the @box_width and @goal_value to the necessary
values. For instance - for a 4x4 square the magic value is 34... set those
values and this should pass:
```
MagicSquare.magic(
[8, 11, 14, 1, 13, 2, 7, 12, 3, 16, 9, 6, 10, 5, 4, 15]
)
```
The horizontal and vertical rows are fairly straightforward - the diagonal
rows took some thinking, however, to determine a pattern.
# Examples
iex> MagicSquare.magic([8, 1, 6, 3, 5, 7, 4, 9, 2])
true
iex> MagicSquare.magic([2, 7, 6, 9, 5, 1, 4, 3, 8])
true
iex> MagicSquare.magic([3, 5, 7, 8, 1, 6, 4, 9, 2])
false
iex> MagicSquare.magic([8, 1, 6, 7, 5, 3, 4, 9, 2])
false
"""
@spec magic(original_list) :: boolean
def magic(arr) do
arr
|> get_combos
|> Enum.all?(fn(list) -> Enum.sum(list) == @goal_value end)
end
@spec get_combos(original_list) :: axis_values
defp get_combos(arr) do
horizontal(arr) ++
vertical(arr) ++
items_at(arr, diagonal_1()) ++
items_at(arr, diagonal_2())
end
@spec horizontal(original_list) :: axis_values
defp horizontal(arr) do
Enum.chunk(arr, @box_width)
end
@spec vertical(original_list) :: axis_values
defp vertical(arr) do
arr
|> Enum.chunk(@box_width)
|> Enum.zip
|> Enum.map(fn(tuple) -> Tuple.to_list(tuple) end)
end
@spec items_at(original_list, list(pos_integer)) :: axis_values
defp items_at(arr, positions) do
[Enum.map(positions, fn(x) -> Enum.at(arr, x) end)]
end
@spec diagonal_1() :: list(pos_integer)
defp diagonal_1, do: diagonal_1([0], 1)
@spec diagonal_1(original_list, pos_integer) :: list(pos_integer)
defp diagonal_1(acc, position) when position == @box_width, do: acc
defp diagonal_1(acc, position) do
diagonal_1(acc ++ [position * (@box_width + 1)], position + 1)
end
@spec diagonal_2() :: list(pos_integer)
defp diagonal_2, do: diagonal_2([@box_width - 1], 1)
@spec diagonal_2(original_list, pos_integer) :: list(pos_integer)
defp diagonal_2(acc, position) when position == @box_width, do: acc
defp diagonal_2(acc, position) do
diagonal_2(acc ++ [@box_width - 1 + List.last(acc)], position + 1)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment