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
asked Mar 10, 2016 at 21:40
\$\endgroup\$
7
  • \$\begingroup\$ I assume this is a regular hexagon, but you should make that explicit. \$\endgroup\$ Commented Mar 10, 2016 at 21:53
  • \$\begingroup\$ @LevelRiverSt yes. A regular. I'll add that in a moment. \$\endgroup\$ Commented Mar 10, 2016 at 22:02
  • 1
    \$\begingroup\$ Can we take x,y as a complex number x+yi? \$\endgroup\$ Commented Mar 10, 2016 at 22:41
  • \$\begingroup\$ Related. \$\endgroup\$ Commented 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\$ Commented 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>

answered Mar 11, 2016 at 17:13
\$\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!"
answered Mar 11, 2016 at 18:21
\$\endgroup\$
6
  • \$\begingroup\$ This could be shorter, you don't need the epsilon for integers \$\endgroup\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented Mar 11, 2016 at 22:14
  • \$\begingroup\$ Yeah! Just passed the Python solution! \$\endgroup\$ Commented 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!

answered Mar 11, 2016 at 0:09
\$\endgroup\$
2
  • \$\begingroup\$ Just a tad overdue, but your code is shortest! Well done! ;-) \$\endgroup\$ Commented Oct 31, 2020 at 17:25
  • \$\begingroup\$ @agtoever Hey, thanks! People don't tend to accept answers these days... (I usually do) :-) \$\endgroup\$ Commented 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).

answered Mar 10, 2016 at 22:42
\$\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]
answered Mar 11, 2016 at 15:17
\$\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.

answered Mar 12, 2016 at 18:45
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.