As stated in the question, it is all about finding patterns in the problem which allow for dividing the problem into a set of smaller problems.
Picture size from n
Once we see the parameter n is related to the length of a side of a hexagon,
we can find that the biggest number of red squares in the picture will be
nRedsMax n = 2 * n - 1
.
Now, the green lines in the picture are always 2 pixels longer than the red strip
below (1 pixel to the left and 1 to the right).
This leads to
nGreensMax n = nRedsMax n + 2
From the sample pixture, we see that the longest green line fills in a whole row
of the picture. Hence,
dimX n = nGreensMax n
.
And we can also see, that the picture is a square. So
dimY n = dimX n
.
Putting it all together and simplifying the term, we find, that the picture size
by n is:
dim n = 4 * n - 1
.
Finding the pixel values x,y for n
Basically, the picture is two pictures, vertically interleaved. The green lines being one, the lines with red, being the other. So we can conclude we have 2 cases, we can handle separately.
We also see, that the upper half of the picture and the lower half of the picture is symmetric to its middle axis.
So, for the computation of the pixels in a line y, we always use an
y' = mirrorUp n y
.
This works for lines of both types (red lines and green lines).
The green lines
The picture is scaled by a factor 2 in x direction. So, for a picture showing the hexagon of length n
, the strip of reds and whites has length 2 * n. And the green line above starts 1 pixel to the left of the red strip and is thus 1 pixel longer than the red strip below.
nGreen n y = 2 * n + 1 + 2 * div (mirrorUp n y) 2
The red lines
The red lines are also centered in the picture and for every logical red dot, we use R
- two characters. If
nRed n y = n + div (mirrorUp y) 2
gives the number of red pixels in a row y, thus, the string of reds would thus be 2 * nRed n y
long.
Centering of the green and red strips in a line
The amount of pixels not used by a strip is
dim n - length strip
And for centering the strip in a row, we need half of that on the left and half on the right.
This leads to the following code, which is written such that the steps described above can be easily found.
module Hexagon(picture,color) where
import Data.List.Split(chunksOf)
-- The 3 possible values of the output matrix.
data Color = WHITE | RED | GREEN deriving(Eq,Show)
-- The size of the output matrix.
-- n -> Array[dim n,dim n]
dim n = 4 * n - 1
-- classify the type of line by index.
-- There are Green lines (those with greens)
-- and red lines (those with the reds)
rowType :: Int -> Color
rowType y | mod y 2 == 0 = GREEN
rowType y | mod y 2 == 1 = RED
-- y-symmetry of picture - mirror y of lower half
-- to y of upper half
mirrorUp :: Int -> Int -> Int
mirrorUp n y
| y < div (dim n) 2 = y
| otherwise = dim n - y - 1
-- Number of green elements by n and y
nGreen :: Int -> Int -> Int
nGreen n y = n * 2 + 1 + 2 * div (mirrorUp n y) 2
-- Number of red elements by n and y
nRed :: Int -> Int -> Int
nRed n y = n + div (mirrorUp n y) 2
-- Compute the color of an element by n,x,y
color :: Int -> Int -> Int -> Color
color n x y
| rowType y == GREEN && (x < div (dim n - nGreen n y) 2)
= WHITE
| rowType y == GREEN && x >= (div (dim n - nGreen n y) 2 + nGreen n y)
= WHITE
| rowType y == GREEN = GREEN
| rowType y == RED =
if x < border || x > dim n - border then WHITE
else
case mod (x - border) 2 of
0 -> RED
1 -> WHITE
where
border = div (dim n - redStrip) 2
redStrip = 2 * nRed n y - 1
-- color 2 output character
col2char :: Color -> Char
col2char WHITE = '_'
col2char RED = 'R'
col2char GREEN = 'G'
-- create the ASCII picture of the hexagon for parameter n
picture :: Int -> [Char]
picture n =
(unlines . chunksOf (dim n)) values
where
values =
[col2char (color n x y) | y <- [0,1..dim n - 1], x <- [0,1..dim n - 1] ]
-- Usage Example:
-- putStrLn $ picture 3