9
\$\begingroup\$

There is a great story to tell about regular hexagons found for example in honeycombs. But this busy bee needs your help in telling him which point is inside or outside his honeypot. So, given a regular hexagon as pictured below, centered at the origin and with edge size l, determine if a set of coordinates (x,y) are inside, exactly on the edge or outside of my regular hexagon.

Hexagon with edge length l, centered at the origin

Input, output and rules

The rules are:

  • Input and output methods follow the default rules.
  • Input consists of three integers: x,y,l.
  • x and y are of any convenient signed integer format. l is positive (never 0).
  • Your program must output/return a 1 if the point (x,y) is inside the regular hexagon, -1 if it's outside or 0 if it's exactly on the edge.
  • This is a code-golf, so shortest code wins. In case of a tie, the earliest post wins.
  • For output to stdout: leading/trailing spaces or newlines in the output are permitted.
  • Standard loopholes apply.

Test cases

Here are some test cases:

0,0,1        --> 1
0,1,1        --> -1
0,-1,1       --> -1
1,0,1        --> 0
-1,0,1       --> 0
-1,-1,1      --> -1
1,1,1        --> -1
-2,-3,4      --> 1
32,45,58     --> 1
99,97,155    --> -1
123,135,201  --> 1
\$\endgroup\$
7
  • \$\begingroup\$ I assume this is a regular hexagon, but you should make that explicit. \$\endgroup\$ Mar 10, 2016 at 21:53
  • \$\begingroup\$ @LevelRiverSt yes. A regular. I'll add that in a moment. \$\endgroup\$
    – agtoever
    Mar 10, 2016 at 22:02
  • 1
    \$\begingroup\$ Can we take x,y as a complex number x+yi? \$\endgroup\$
    – lirtosiast
    Mar 10, 2016 at 22:41
  • \$\begingroup\$ Related. \$\endgroup\$
    – xnor
    Mar 11, 2016 at 1:48
  • \$\begingroup\$ @lirtosiast the question is about a hexagon in the euclidian plane, not in the complex plane. Because of that complex input isn't allowed. \$\endgroup\$
    – agtoever
    Mar 11, 2016 at 8:18

7 Answers 7

3
\$\begingroup\$

JavaScript (ES6) 77 83

(a,b,l,h=Math.sqrt(3)*l,x=a<0?-a:a,y=b<0?-b:b)=>y|x!=l?2*y<h&x/l+y/h<1?1:-1:0

Test

f=(a,b,l,h=Math.sqrt(3)*l,x=a<0?-a:a,y=b<0?-b:b)=>y|x!=l?2*y<h&x/l+y/h<1?1:-1:0

// TEST

function go() {
  C.width=400;C.height=300;
  var l=+I.value, x,y, cols={0:'#ff0',1:'#0f0','-1':'#888'},
  ctx = C.getContext("2d")
  ctx.translate(200,150)
  ctx.strokeStyle='#000'
  ctx.lineWidth=1;
  ctx.beginPath();
  ctx.moveTo(0,-150);ctx.lineTo(0,150);ctx.moveTo(-200,0);ctx.lineTo(200,0);
  ctx.stroke();
  ctx.strokeStyle='#f00'
  ctx.beginPath();
  ctx.moveTo(l*10,0);ctx.lineTo(l*5,l*Math.sqrt(3)*5);ctx.lineTo(-l*5,l*Math.sqrt(3)*5)
  ctx.lineTo(-l*10,0);ctx.lineTo(-l*5,-l*Math.sqrt(3)*5);ctx.lineTo(l*5,-l*Math.sqrt(3)*5)
  ctx.closePath();
  ctx.stroke();

  for(y=-14;y<15;y++)
    for(x=-19;x<20;x++) {
      ctx.beginPath();
      ctx.moveTo(x*10,y*10-3);ctx.lineTo(x*10,y*10+3);
      ctx.moveTo(x*10-3,y*10);ctx.lineTo(x*10+3,y*10);
      ctx.strokeStyle=cols[f(x,y,l)]
      ctx.stroke()
    }
}

go()
#C {
  border: 1px solid #000
}
<b>L</b> <input id=I value=15><button onclick="go()">GO</button><br>
<canvas id=C width=400 height=300></canvas>

\$\endgroup\$
2
\$\begingroup\$

Ruby, 150 145 137 127 125 106 88 76 bytes

76 bytes

->(x,y,l){x,y,t=x.abs,y.abs,3**0.5;d=t*l;z=d-t*x-y;2*y>d ?-1:2*x<l ?1:z<=>0}

Changed triple comparison to a rocket.

88 bytes

->(x,y,l){x,y,t=x.abs,y.abs,3**0.5;d=t*l;z=d-t*x-y;2*y>d ?-1:2*x<l ?1:z==0 ?0:0<z ?1:-1}

Remove the y equal to apothem test for points on the hexagon, because for integers, that can never be true.

106 bytes:

->(x,y,l){x,y,t=x.abs,y.abs,3**0.5;d=t*l;z=d-t*x-y;2*y==d&&2*x<=l ?0:2*y>d ?-1:2*x<l ?1:z==0 ?0:0<z ?1:-1}

Poster suggested not using epsilon, so replaced epsilon with zero and rearranged, removed an abs, etc.

125 bytes:

->(x,y,l){x,y,t,e=x.abs,y.abs,3**0.5,1e-9;d=t*l;z=d-t*x-y;(2*y-d).abs<=e&&2*x<=l ?0:2*y>d ?-1:2*x<l ?1:z.abs<=e ?0:0<z ?1:-1}

Incorporate y into definition of z and remove some parentheses.

127 bytes:

->(x,y,l){x,y,t,e=x.abs,y.abs,3**0.5,1e-9;d=t*l;z=d-t*x;(2*y-d).abs<=e&&2*x<=l ?0:2*y>d ?-1:2*x<l ?1:(z-y).abs<=e ?0:y<z ?1:-1}

Rearranged terms to avoid necessity of to_f cast. Use d (double the apothem) instead of a (the apothem). Combine multiple assignments.

137 bytes:

->(x,y,l){x=x.abs.to_f;y=y.abs.to_f;a=3**0.5*l/2;e=1e-9;z=2*a*(1-x/l);(y-a).abs<=e&&2*x<=l ?0:y>a ?-1:2*x<l ?1:(z-y).abs<=e ?0:y<z ?1:-1}

Inlined 'c'.

150 bytes:

->(x,y,l){c=l/2.0;x=x.abs.to_f;y=y.abs.to_f;a=3**0.5*l/2;e=1e-10;z=2*a*(1-x/l);(y-a).abs<=e&&x<=c ?0:(y>a ?-1:(x<c ?1:((z-y).abs<=e ?0:(y<z ?1:-1))))}

This works for integers or floats! The epsilon test is so that points within round off error of being on the edge are correctly identified.

The absolute values move everything into quadrant one.

The value 'a' is the apothem distance (the y-intercept of the hexagon).

The value 'c' is the x-value of the upper right corner of the hexagon.

The value 'z' is to see if the point is above or below the slant line from the corner to the x-intercept.

Ungolfed:

hex = ->(x,y,l){ 
    c = l/2.0;
    x = x.abs.to_f;
    y = y.abs.to_f;
    a = 3**0.5 * l / 2;
    e = 1e-10;
    z = 2*a*(1 - x/l);
    if (y-a).abs <= e && x <= c then 0
    elsif (y>a) then -1
    elsif (x<c) then 1
    elsif (z-y).abs <= e then 0
    elsif y < z then 1
    else -1
    end
}

Test

hex = ->(x,y,l){x,y,t=x.abs,y.abs,3**0.5;d=t*l;z=d-t*x-y;2*y>d ?-1:2*x<l ?1:z<=>0}

cases = [
    [0,0,1,1],
    [0,1,1,-1],
    [0,-1,1,-1],
    [1,0,1,0],
    [-1,0,1,0],
    [-1,-1,1,-1],
    [1,1,1,-1],
    [-2,-3,4,1],
    [32,45,58,1],
    [99,97,155,-1],
    [123,135,201,1]
]

cases.each { |test| 
  expected = test[3]
  actual = hex.call(test[0],test[1],test[2])
  status = expected == actual ? "PASS" : "FAIL";
  p "#{status}. #(x,y) L = (#{test[0]},#{test[1]}) #{test[2]} Expects #{expected}. Actual #{actual}"
}
"Done!"
\$\endgroup\$
6
  • \$\begingroup\$ This could be shorter, you don't need the epsilon for integers \$\endgroup\$
    – edc65
    Mar 11, 2016 at 21:10
  • \$\begingroup\$ By introducing the square root of three, I am forced to use floating point. I could round the numbers before comparison, or use epsilon calculations. Since epsilon allows the code to be more general and work for floats, I left in the epsilon. I have only been programming Ruby a short while, so am not sure how it deals with rounding errors. \$\endgroup\$ Mar 11, 2016 at 21:36
  • \$\begingroup\$ The slope of the left and right side is not rational. The apotheme is not rational. The are only 2 points with integer coordinates lying on the perimeter: [l,0] and [-l,0] \$\endgroup\$
    – edc65
    Mar 11, 2016 at 21:45
  • \$\begingroup\$ You are probably right that for integer inputs there are no other possible integer points that are "on" the hexagon. Proving that to myself was harder than making the code not care, using epsilon. \$\endgroup\$ Mar 11, 2016 at 22:14
  • \$\begingroup\$ Yeah! Just passed the Python solution! \$\endgroup\$ Mar 11, 2016 at 22:55
1
\$\begingroup\$

MATL, 29 25 bytes

3X^/Eh|-IG2G-IX^*1G-hZSX<

Inputs are y, x, l in that order.

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ Just a tad overdue, but your code is shortest! Well done! ;-) \$\endgroup\$
    – agtoever
    Oct 31, 2020 at 17:25
  • \$\begingroup\$ @agtoever Hey, thanks! People don't tend to accept answers these days... (I usually do) :-) \$\endgroup\$
    – Luis Mendo
    Oct 31, 2020 at 17:38
0
\$\begingroup\$

Julia, 65 58 bytes

f(x,l)=(t=maxabs(x/l*[[0 1 1];[2 1 -1]/3^.5]);(t<1)-(t>1))

x is a row vector [x y]. Call like this: f([0 0],1).

\$\endgroup\$
0
\$\begingroup\$

Python 2, 89 bytes

almost the same solution as Julia answer but we can use operation on vector without numpy

lambda x,y,L,K=3**0.5/2.:cmp(K*L,max([abs(x*i+y*j)for i,j in[[K,1/2.],[0,1],[-K,1/2.]]]))

Results

>>> f(0,0,1)
1
>>> f(32,45,58)
1
>>> f(99,97,155)
-1
>>> f(-1,0,1)
0
>>> [f(0,0,1)== 1,f(0,1,1)== -1,f(0,-1,1)== -1,f(1,0,1)== 0,f(-1,0,1)== 0,f(-1,-1,1)== -1,f(1,1,1)== -1,f(-2,-3,4)== 1,f(32,45,58)== 1,f(99,97,155)== -1,f(123,135,201)== 1,f(0,0,1)== 1,f(0,1,1)== -1,f(0,-1,1)== -1,f(1,0,1)== 0,f(-1,0,1)== 0,f(-1,-1,1)== -1,f(1,1,1)== -1,f(-2,-3,4)== 1,f(32,45,58)== 1,f(99,97,155)== -1,f(123,135,201)== 1]
[True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]
\$\endgroup\$
0
\$\begingroup\$

Pyth, 41 bytes

?<=d.5K+?>ZJ-c.ahQeQdZJ.acchtQeQ^3d_1s>dK

Test it here

\$\endgroup\$
0
\$\begingroup\$

JavaScript (ES6), 67 bytes

with(Math)(a,b,l)=>sign(min(l*l*3-b*b*4,(l-abs(a))*sqrt(3)-abs(b)))

Note: To assign this to a variable so that you can call it, put the f= after the with(Math).

I used l*l and b*b in the first parameter to min to avoid calls to abs and sqrt but I couldn't work out whether I could do a similar trick with the second parameter.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.