Hexplosive ASCII-art challenge
JavaScript (ES6), 118 117 bytes
n=>[...Array(m=n+--n)].map((_,i,a)=>a.map((_,j)=>(k=j-(i>n?i-n:n-i))<0?``:k&&++j<m?i/2%n?6:4:3+!i%n).join` `).join`\n`
Where \n
represents a literal newline character. Explanation: Suppose n=4
. We start with the following space-separated digit square:
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
The first |n-i|
0
s are deleted, but the spaces remain:
0 0 0 0
0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0
0 0 0 0
Instant hexagon! It then suffices to calculate the appropriate value in place of each 0
by checking whether we're on the first or last row and/or column. Edit: Saved 1 byte thanks to @Arnauld.
MATL, 39 37 bytes
4*3-:!G:+o~YRtP*!tPw4LY)vtI5&lZ+47+*c
Try it online! Or verify all test cases.
Explanation
I get to use convolution again!
Consider input n = 3
. The code first builds a matrix of size 4*n-3
×n
by adding the column vector [1; 2; ...; 9]
to the row vector [1, 2, 3]
with broadcast. This means computing a 2D array array of all pairwise additions:
2 3 4
3 4 5
4 5 6
5 6 7
6 7 8
7 8 9
8 9 10
9 10 11
10 11 12
Replacing even numbers by 1
and odd numbers by 0
gives the checkerboard pattern
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1
This will be used for generating (part of) the hexagonal grid. Ones will represent points in the grid, and zeros will represent spaces.
The upper-right corner is removed by zeroing out all entries above the main "diagonal" of the matrix:
1 0 0
0 1 0
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1
Element-wise multiplying this matrix by a vertically flipped version of itself removes the lower-right corner as well. Transposing then gives
1 0 1 0 1 0 1 0 1
0 1 0 1 0 1 0 1 0
0 0 1 0 1 0 1 0 0
This begins to look like a hexagon. Using symmetry, the grid is extended to produce the upper half:
0 0 1 0 1 0 1 0 0
0 1 0 1 0 1 0 1 0
1 0 1 0 1 0 1 0 1
0 1 0 1 0 1 0 1 0
0 0 1 0 1 0 1 0 0
Now we need to replace each entry equal to one by the number of neighbours. For this we use convolution with a 3×5 neighbourhood (that is, the kernel is a 3×5 matrix of ones). The result,
2 3 4 5 5 5 4 3 2
4 5 7 7 8 7 7 5 4
4 6 7 8 7 8 7 6 4
4 5 7 7 8 7 7 5 4
2 3 4 5 5 5 4 3 2
has two issues (which will be solved later):
- The values have been computed for all positions, whereas we only need them at the positions of the ones in the zero-one grid.
- For each of those positions, the neighbour count includes the point itself, so it is off by
1
.
The code now adds 47
to each computed value. This corresponds to subtracting 1
to solve issue (2) and adding 48
(ASCII for '0'
), which converts each number to the code point of its corresponding char.
The resulting matrix is then multiplied by a copy of the zero-one grid. This solves issue (1) above, making the points that are not part of the hexagonal grid equal to zero again:
0 0 51 0 52 0 51 0 0
0 52 0 54 0 54 0 52 0
51 0 54 0 54 0 54 0 51
0 52 0 54 0 54 0 52 0
0 0 51 0 52 0 51 0 0
Finally, this array of numbers is cast to a char array. Zero chars are displayed as space, which gives the final result:
3 4 3
4 6 6 4
3 6 6 6 3
4 6 6 4
3 4 3
Python 2, 125 123 bytes
def h(n):m=n-1;t=[' '*(m-r)+' '.join(('46'[r>0]*(r+m-1)).join('34'[r%m>0]*2))for r in range(n)];print'\n'.join(t+t[-2::-1])
Tests are on ideone
Runs through the top to the middle rows, for r in range(n)
, constructing strings:
- making two corners or two edges, '34'[r%m>0]*2
;
- filling by joining them with repeated '6'
or '4'
, '46'[r>0]*(r+m-1)
;
- joining the corners and edges with ' '
;
- prepending with spaces, ' '*(m-r)
;
Then prints this and it's reflection in the middle row joined by new lines, print'\n'.join(t+t[-2::-1])