Hexagolf: Validagons

Challenge

Given an ASCII art shape, you must find out whether the shape is a regular hexagon or not.

Hexagons

A regular hexagon is defined using two rules:

• It has six sides
• Each side has equal numbers of characters

For example, the following shape is regular but it doesn't have six sides and so is not a regular hexagon:

B a a h
s a i d
y o u r
g a o t


Similarly, the following shape has six sides but has sides of different lengths and so is not a regular hexagon:

  * *
* * *
* * * *
* * *
* *


However, the following shape has six sides and all of the sides have the same number of characters, so it is a regular hexagon:

  T h e
h e x a
g o n s a
r e c o
m i n


Rules

Output should be a truthy or a falsey value (truthy if the shape is a regular hexagon, falsey if not).

The shapes will only ever contain printable ASCII characters.

The border of the shape will never contain spaces. If it does, you can return falsey.

There can be arbitrary amounts of whitespace before and/or after the shape.

All angles in the shape may not be equal for example, the following shape is valid input:

  . . .
. . .
. . . . .
. . . .
. . .


It will return a falsey value.

All shape inputs will be on a space separated grid. Hexagonal input will be on a staggered grid (each line is offset from the next).

Examples

Truthy

The following shapes should return truthy values:

 # _
+ + +
9 :


  5 6 7
8 9 0 1
2 3 4 5 6
7 8 9 0
1 2 3


    t h i s
i       s
a         h
e           x
a         g
o       n
! ! ! !


    5 6 7
8 9 0 1
2 3 4 5 6
7 8 9 0
1 2 3


Falsey

The following should return falsey values

r e c t a
n g l e s


  h e l l o
w o r l d s
t h i s i s b
e t a d e c
a y n o w


  *
* *
* * *


  .....
.......
.........
.......
.....


This shape is not on a space separated grid and is not staggered.

   * * * *
---------
* * * * * *
-------------
* * * * * *
---------
* * * *


5 6 7
8 9 0 1
2 3 4 5 6
7 8 9 0
1 2 3


For single character inputs, your program may output either truthy or falsey:

&


Winning

The shortest program in bytes wins.

• Mathematica one liner incoming. Sep 9, 2016 at 9:48
• I think you might want to add two test cases: One without leading spaces, for example: 5 6 7\n8 9 0 1\n2 3 4 5 6\n7 8 9 0\n1 2 3 and one with an additional space leading for one of the rows: ss5 6 7\nss8 9 0 1\n2 3 4 5 6\ns7 8 9 0\nss1 2 3 (leading spaces are replaced with s to make it a bit more clear in this unformatted form). All 10 of your test cases are validating correctly with my code currently, but these two cases would fail with the approach I used. Sep 9, 2016 at 10:02
• Is the example with the periods necessarily falsey ? The missing period is a space which is one of the valid characters that can make up the hexagon, Sep 9, 2016 at 11:33
• @TonHospel I think the idea is that the outline is an unbroken regular hexagon. Sep 9, 2016 at 11:34
• All angles in the shape may not be equal for example, the following shape is valid input: This phrasing seems misleading. Surely we are detecting regular hexagons? Do you mean to write that the symbols don’t necessarily have angular symmetry?
– Lynn
Sep 9, 2016 at 11:38

R, 184 bytes

Golfed, could probably be golfed by a few bytes

function(m){e=min;f=max;l=length;v=which(m!=" ",T);a=v[,1];n=l(v[a==1,2]);u=(v[a==e(a),2]);all(u==v[a==f(a),2])&all(c(e(d<-v[a==ceiling(f(v[,1])/2),2]),f(d))==c(u[1]-n+1,u[l(u)]+n-1))}


Ungolfed, very messy, more like half way golfed

f=function(m) {
v = which(m!=" ",T)
a = v[,1]
n = length(v[a==1,2])
u=(v[a==min(a),2])
c1 = all(u==v[a==max(a),2])
d = v[a==ceiling(max(v[,1])/2),2]
c2 = all(c(min(d), max(d))==c(u[1]-n+1,u[length(u)]+n-1))
c1 & c2
}


Since the input format is unspecified, Input needs to be specified in an R array format, looking something like this.

         [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] " "  " "  "5"  " "  "6"  " "  "7"  " "  " "
[2,] " "  "8"  " "  "9"  " "  "0"  " "  "1"  " "
[3,] "2"  " "  "3"  " "  "4"  " "  "5"  " "  "6"
[4,] " "  "7"  " "  "8"  " "  "9"  " "  "0"  " "
[5,] " "  " "  "1"  " "  "2"  " "  "3"  " "  " "


Here is a generator function that generates the input. The generator doesn't generate an object that is fed into hexagon check function, but rather the code specifying the array (effectively the same thing). So this should not count as parsing the text. Remember that I'm not inputting text, but rather an array structure.

generate = function(x) {
s = strsplit(strsplit(x, "\n")[[1]], "")
m = max(sapply(s, length))
dput(do.call(rbind, lapply(s, function(x) c(x, rep(" ", m-length(x))))))
}


For example, the generated code would be: structure(c(" ", " ", "2", " ", " ", " ", "8", " ", "7", " ", "5", " ", "3", " ", "1", " ", "9", " ", "8", " ", "6", " ", "4", " ", "2", " ", "0", " ", "9", " ", "7", " ", "5", " ", "3", " ", "1", " ", "0", " ", " ", " ", "6", " ", " "), .Dim = c(5L, 9L )) which is identical to array(c(" ", " ", "2", " ", " ", " ", "8", " ", "7", " ", "5", " ", "3", " ", "1", " ", "9", " ", "8", " ", "6", " ", "4", " ", "2", " ", "0", " ", "9", " ", "7", " ", "5", " ", "3", " ", "1", " ", "0", " ", " ", " ", "6", " ", " "), dim = c(5, 9))

Hopefully this input method is in compliance with the rules.

Here are the test cases

x1 =
"  5 6 7
8 9 0 1
2 3 4 5 6
7 8 9 0
1 2 3"

x2 =
" # _
+ + +
9 :"

x3 =
"    t h i s
i       s
a         h
e           x
a         g
o       n
! ! ! !"

x4 ="    5 6 7
8 9 0 1
2 3 4 5 6
7 8 9 0
1 2 3"

x5 = "r e c t a
n g l e s"

x6 = "  h e l l o
w o r l d s
t h i s i s b
e t a d e c
a y n o w"

x7 ="  *
* *
* * *"

x8 ="   .....
.......
.........
.......
....."


Generate input arrays

sapply(mget(paste("x", 1:8, sep = "")), generate)


Test for hexagon

sapply(.Last.value , f)

x1    x2    x3    x4    x5    x6    x7    x8
TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE

• @DLosc You didn't have the generate function defined. Try this link: goo.gl/9MtCLg You can also check with a manual input like f(array(c(" ", " ", "2", " ", " ", " ", "8", " ", "7", " ", "5", " ", "3", " ", "1", " ", "9", " ", "8", " ", "6", " ", "4", " ", "2", " ", "0", " ", "9", " ", "7", " ", "5", " ", "3", " ", "1", " ", "0", " ", " ", " ", "6", " ", " "), dim = c(5, 9)))
– Vlo
Sep 10, 2016 at 6:19
• Ah, I missed that part. Thanks. Sep 10, 2016 at 19:22

JavaScript (ES6), 214 bytes

(s,a=s.split\n,n=a[l=a.length>>1].match(r=/(?=\S).*\S/),i=n.index)=>!/\S(  )*\S/.test(s)&&!a.some((s,j)=>(m=r.exec(s))&&(k=m.index)<i+(j<l?j=l-j:j-=l)|k+(m=m[0].length)+j>i+n[0].length|k+i+j&1|j%l<1&m+j+j!=l*4+1)


Where \n represents a literal newline character. Ungolfed:

function validhex(s) {
if (/S(  )*/S/.test(s)) return false;
var a = s.split("\n");
var l = Math.floor(a.length / 2);
var n = a[l].match(/(?=\S).*\S/);
for (var j = -l; j <= l; j++) {
var m = a[j+l].match(/(?=\S).*\S/);
if (!m) continue;
if (m.index < n.index + Math.abs(j)) return false;
if (m.index + m[0].length + Math.abs(j) > n.index + n[0].length) return false;
if ((m.index + n.index + j) % 2) return false;
if (j % l) continue;
if (m[0].length != l * 4 + 1 - 2 * Math.abs(j)) return false;
}
return true;
}

• I found a bug: input " x\n g g\ng g g\n g g" should give false, but gives true. Sep 10, 2016 at 2:31
• @DLosc I take it that's two spaces before the x?
– Neil
Sep 10, 2016 at 10:26
• @DLosc I think I have it fixed now, cost me 30 bytes though...
– Neil
Sep 10, 2016 at 11:58

SnakeEx, 200 bytes

The right language for the job... sort of.

m:{v<>}{r<RF>2P}{r<R>2P}{h<RF>1P}{w<>}{l<RF>2P}{l<R>2P}{h<.>1}
w:{u<>P}{v<>}
v:{e<L>}{u<R>1}
u:.*{e<>}
e:.$r:[^ ]+ h:([^ ] )+ l:({c<.>}[^ ])+{c<.>} c:{b<B>}(. )+{x<>LP}{s<>} b:.{s<>} s:[^\!-\~]*$
x:.


SnakeEx is a language from the 2-D Pattern Matching challenge. It ought to be really good at this task, but unfortunately all the corner cases really bloated the code. I also turned up a couple of interpreter bugs. Still, it was a fun challenge.

m is the main snake that calls all the others to do the actual work. It matches starting at the top right corner of the hexagon and going clockwise. Numbered groups are used to verify that the diagonal side lengths are all equal and that the horizontal side length matches the height of the whole figure. I could write a more detailed explanation, but I've spent the last two days dealing with corner cases, so just try it out for yourself here. :^)

Perl, 127125124 121 bytes

Includes +4 for -0p

Give input on STDIN

#!/usr/bin/perl -0p
/ *(.*\S)/;$a=join'\S * \1',map$"x(abs).'\S '.(/$n/?'\S ':'. ')x(2*$n-1-abs),-($n=$1=~y/ //)..$n;$_=/^$( *)$a\S\s*\$/
`