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. xandyare of any convenient signed integer format.lis positive (never 0).- Your program must output/return a
1if the point(x,y)is inside the regular hexagon,-1if it's outside or0if 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
-
\$\begingroup\$ I assume this is a regular hexagon, but you should make that explicit. \$\endgroup\$Level River St– Level River St2016年03月10日 21:53:57 +00:00Commented Mar 10, 2016 at 21:53
-
\$\begingroup\$ @LevelRiverSt yes. A regular. I'll add that in a moment. \$\endgroup\$agtoever– agtoever2016年03月10日 22:02:59 +00:00Commented Mar 10, 2016 at 22:02
-
1\$\begingroup\$ Can we take x,y as a complex number x+yi? \$\endgroup\$lirtosiast– lirtosiast2016年03月10日 22:41:17 +00:00Commented Mar 10, 2016 at 22:41
-
\$\begingroup\$ Related. \$\endgroup\$xnor– xnor2016年03月11日 01:48:37 +00:00Commented 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– agtoever2016年03月11日 08:18:57 +00:00Commented Mar 11, 2016 at 8:18
7 Answers 7
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>
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!"
-
\$\begingroup\$ This could be shorter, you don't need the epsilon for integers \$\endgroup\$edc65– edc652016年03月11日 21:10:56 +00:00Commented 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\$Paul Chernoch– Paul Chernoch2016年03月11日 21:36:27 +00:00Commented 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– edc652016年03月11日 21:45:19 +00:00Commented 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\$Paul Chernoch– Paul Chernoch2016年03月11日 22:14:44 +00:00Commented Mar 11, 2016 at 22:14
-
\$\begingroup\$ Yeah! Just passed the Python solution! \$\endgroup\$Paul Chernoch– Paul Chernoch2016年03月11日 22:55:56 +00:00Commented Mar 11, 2016 at 22:55
-
\$\begingroup\$ Just a tad overdue, but your code is shortest! Well done! ;-) \$\endgroup\$agtoever– agtoever2020年10月31日 17:25:30 +00:00Commented 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– Luis Mendo2020年10月31日 17:38:37 +00:00Commented Oct 31, 2020 at 17:38
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).
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]
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.