5 added 10 characters in body
source | link

Explanation

It seems easiest to build the layout first using just a single character (· in this case) and then fill the resulting layout with the input characters. The main reasons for this are that using a single character lets me make use of backreferences and character repetition, where laying out the input directly would require expensive balancing groups.

 

Although it doesn't look like much, this first stages removes spaces from the input.

^
$._$*·¶

We start by prepending an additional line which contains M centre dots, where M is the length of the input (after removing spaces).

^·¶
¶

If the input was a single character, we remove that centre dot again. This is an unfortunate special case that isn't covered by the next stage.

((^·|\2·)*)·\1{5}·+
$2·

This computes the required side length N minus 1. Here is how that works: centred hexagonal numbers are of the form 3*N*(N-1) + 1. Since triangular numbers are N*(N-1)/2, that means hexagonal numbers are six times a triangular number plus 1. That's convenient because matching triangular numbers (which are really just 1 + 2 + 3 + ... + N) in a regex is fairly easy with forward references. The (^·|\2·)* matches the largest triangular number it can. As a nice bonus, $2 will then hold the index of this triangular number. To multiply it by 6, we capture it into group 1 and match it another 5 times. We make sure there are at least two more · with the · and the ·+. This way the index of the found triangular number doesn't increase until there's one character more than a centred hexagonal number.

In the end, this match gives us two less than the side-length of the required hexagon in group $2, so we write that back together with one more centre dot to get N-1.

^·*
$.&$* ·$&$&$.&$* 

This turns our string of N-1 centre dots into N-1 spaces, 2N-1 centre dots and another N-1 spaces. Note that this is the maximum indentation, followed by the diameter of the hexagon, followed by the indentation again.

M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$

This is unpleasantly long, but it basically just gives us all overlapping matches, which are either a) 2N-1 characters long and on the first line or b) the second line. This expands the result from the previous stage into the full, but weirdly indented hexagon. E.g. for input 12345678 we'd get:

  ···
 ····
·····
···· 
···  
12345678

This is why we needed to append spaces in the previous stage as well.

+m`^( *·+)· *¶(?=\1)
$& 

This fixes the indentation of the lines after the centre, by repeatedly indenting any line that is shorter than the previous (ignoring trailing spaces), so we get this:

  ···
 ····
·····
 ···· 
  ···  
12345678

Now we just insert some spaces with

·
 ·

Which gives us:

   · · ·
  · · · ·
 · · · · ·
  · · · · 
   · · ·  
12345678

Phew, that's done.

O$`(·)|\S
$1

Time to fill the input string into the centre dots. This is done with the help of a sort stage. We match all centre dots and each character on the last line, and we sort them by the result of the given substitution. That substitution is empty for the characters on the last line and · for the centre dots, so what happens is that the centre dots are simply sorted to the end (since sorting is stable). This moves the input characters into place:

   1 2 3
  4 5 6 7
 8 · · · ·
  · · · · 
   · · ·  
········

Just two things left now:

·
.

This turns centre dots into regular periods.

G-2`

And this discards the last line.

Explanation

It seems easiest to build the layout first using just a single character (· in this case) and then fill the resulting layout with the input characters. The main reasons for this are that using a single character lets me make use of backreferences and character repetition, where laying out the input directly would require expensive balancing groups.

 

Although it doesn't look like much, this first stages removes spaces from the input.

^
$._$*·¶

We start by prepending an additional line which contains M centre dots, where M is the length of the input (after removing spaces).

^·¶
¶

If the input was a single character, we remove that centre dot again. This is an unfortunate special case that isn't covered by the next stage.

((^·|\2·)*)·\1{5}·+
$2·

This computes the required side length N minus 1. Here is how that works: centred hexagonal numbers are of the form 3*N*(N-1) + 1. Since triangular numbers are N*(N-1)/2, that means hexagonal numbers are six times a triangular number plus 1. That's convenient because matching triangular numbers (which are really just 1 + 2 + 3 + ... + N) in a regex is fairly easy with forward references. The (^·|\2·)* matches the largest triangular number it can. As a nice bonus, $2 will then hold the index of this triangular number. To multiply it by 6, we capture it into group 1 and match it another 5 times. We make sure there are at least two more · with the · and the ·+. This way the index of the found triangular number doesn't increase until there's one character more than a centred hexagonal number.

In the end, this match gives us two less than the side-length of the required hexagon in group $2, so we write that back together with one more centre dot to get N-1.

^·*
$.&$* ·$&$&$.&$* 

This turns our string of N-1 centre dots into N-1 spaces, 2N-1 centre dots and another N-1 spaces. Note that this is the maximum indentation, followed by the diameter of the hexagon, followed by the indentation again.

M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$

This is unpleasantly long, but it basically just gives us all overlapping matches, which are either a) 2N-1 characters long and on the first line or b) the second line. This expands the result from the previous stage into the full, but weirdly indented hexagon. E.g. for input 12345678 we'd get:

  ···
 ····
·····
···· 
···  
12345678

This is why we needed to append spaces in the previous stage as well.

+m`^( *·+)· *¶(?=\1)
$& 

This fixes the indentation of the lines after the centre, by repeatedly indenting any line that is shorter than the previous (ignoring trailing spaces), so we get this:

  ···
 ····
·····
 ···· 
  ···  
12345678

Now we just insert some spaces with

·
 ·

Which gives us:

   · · ·
  · · · ·
 · · · · ·
  · · · · 
   · · ·  
12345678

Phew, that's done.

O$`(·)|\S
$1

Time to fill the input string into the centre dots. This is done with the help of a sort stage. We match all centre dots and each character on the last line, and we sort them by the result of the given substitution. That substitution is empty for the characters on the last line and · for the centre dots, so what happens is that the centre dots are simply sorted to the end (since sorting is stable). This moves the input characters into place:

   1 2 3
  4 5 6 7
 8 · · · ·
  · · · · 
   · · ·  
········

Just two things left now:

·
.

This turns centre dots into regular periods.

G-2`

And this discards the last line.

4 added 10 characters in body
source | link

Retina, 161 bytes

Thanks to FryAmTheEggman for saving 2 bytes.

This answer is non-competing. Retina has seen a few updates since this challenge, and I'm pretty sure I'm using some of the newer features (although I haven't checked).

Byte count assumes ISO 8859-1 encoding. The first and third empty lines are supposed to containline contains a single space. Note that most of the · are actually centre dots (0xB7).

 

^
$._$*·¶

 
^·¶
¶
((^·|\2·)*)·\1{5}·+
$2·
^·*
$.&$*&$* ·$&$&$·$&$&$.&$*&$* 
M!&m`&m`(?<=(?= *(·)+)^.*)(?<-1>1>.)+(?(1)!)|^.+$
+m`^( *·+)· *¶(?=\1)
$&$& 
·
 ·
O$`(·)|\S
$1
·
.
G-2`

Try it online!Try it online!

Well...

I'll add an explanation in a bit, at least for the general algorithm and what the individual stages do at a higher level.

Retina, 161 bytes

Thanks to FryAmTheEggman for saving 2 bytes.

This answer is non-competing. Retina has seen a few updates since this challenge, and I'm pretty sure I'm using some of the newer features (although I haven't checked).

Byte count assumes ISO 8859-1 encoding. The first and third empty lines are supposed to contain a single space. Note that most of the · are actually centre dots (0xB7).

^
$._$*·¶

 
^·¶
¶
((^·|\2·)*)·\1{5}·+
$2·
^·*
$.&$* ·$&$&$.&$* 
M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$
+m`^( *·+)· *¶(?=\1)
$& 
·
 ·
O$`(·)|\S
$1
·
.
G-2`

Try it online!

Well...

I'll add an explanation in a bit, at least for the general algorithm and what the individual stages do at a higher level.

Retina, 161 bytes

Thanks to FryAmTheEggman for saving 2 bytes.

This answer is non-competing. Retina has seen a few updates since this challenge, and I'm pretty sure I'm using some of the newer features (although I haven't checked).

Byte count assumes ISO 8859-1 encoding. The first line contains a single space. Note that most of the · are actually centre dots (0xB7).

 

^
$._$*·¶
^·¶
¶
((^·|\2·)*)·\1{5}·+
$2·
^·*
$.&$* ·$&$&$.&$* 
M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$
+m`^( *·+)· *¶(?=\1)
$& 
·
 ·
O$`(·)|\S
$1
·
.
G-2`

Try it online!

Well...

    Post Undeleted by Martin Ender
3 added 10 characters in body
source | link

Retina, 160 158161 bytes

Thanks to FryAmTheEggman for saving 2 bytes.

This answer is non-competing. Retina has seen a few updates since this challenge, and I'm pretty sure I'm using some of the newer features (although I haven't checked).

Byte count assumes ISO 8859-1 encoding. The first and third empty lines are supposed to contain a single space. Note that most of the · are actually centre dots (0xB7).

^
$._$*·¶


(^·¶
¶
((^·|\3·^·|\2·)*)·)\2·\1{5}·+|··+
$1$2·
^·*
$.&$* ·$&$&$.&$* 
M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$
+m`^( *·+)· *¶(?=\1)
$& 
·
 ·
O$`(·)|\S
$1
·
.
G-2`

Try it online!Try it online!

Well...

I'll add an explanation in a bit, at least for the general algorithm and what the individual stages do at a higher level.

Retina, 160 158 bytes

Thanks to FryAmTheEggman for saving 2 bytes.

This answer is non-competing. Retina has seen a few updates since this challenge, and I'm pretty sure I'm using some of the newer features (although I haven't checked).

Byte count assumes ISO 8859-1 encoding. The first and third empty lines are supposed to contain a single space. Note that most of the · are actually centre dots (0xB7).

^
$._$*·¶


(((^·|\3·)*)·)\2{5}·+|·
$1
^·*
$.&$* ·$&$&$.&$* 
M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$
+m`^( *·+)· *¶(?=\1)
$& 
·
 ·
O$`(·)|\S
$1
·
.
G-2`

Try it online!

Well...

I'll add an explanation in a bit, at least for the general algorithm and what the individual stages do at a higher level.

Retina, 161 bytes

Thanks to FryAmTheEggman for saving 2 bytes.

This answer is non-competing. Retina has seen a few updates since this challenge, and I'm pretty sure I'm using some of the newer features (although I haven't checked).

Byte count assumes ISO 8859-1 encoding. The first and third empty lines are supposed to contain a single space. Note that most of the · are actually centre dots (0xB7).

^
$._$*·¶


^·¶
¶
((^·|\2·)*)·\1{5}·+
$2·
^·*
$.&$* ·$&$&$.&$* 
M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$
+m`^( *·+)· *¶(?=\1)
$& 
·
 ·
O$`(·)|\S
$1
·
.
G-2`

Try it online!

Well...

I'll add an explanation in a bit, at least for the general algorithm and what the individual stages do at a higher level.

    Post Deleted by Martin Ender
2 added 219 characters in body
source | link
1
source | link