Created
March 2, 2017 03:44
-
-
Save mcelaney/26b7b77cfe3fefd211411ec99c700ae5 to your computer and use it in GitHub Desktop.
PhillyDev Slack #daily_programmer for March 1, 2017
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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