Hexagon-In or Hexagon-Out?

8

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

agtoever

Posted 2016-03-10T21:40:33.290

Reputation: 2 661

I assume this is a regular hexagon, but you should make that explicit. – Level River St – 2016-03-10T21:53:57.317

@LevelRiverSt yes. A regular. I'll add that in a moment. – agtoever – 2016-03-10T22:02:59.880

1Can we take x,y as a complex number x+yi? – lirtosiast – 2016-03-10T22:41:17.923

Related. – xnor – 2016-03-11T01:48:37.780

@lirtosiast the question is about a hexagon in the euclidian plane, not in the complex plane. Because of that complex input isn't allowed. – agtoever – 2016-03-11T08:18:57.427

@agtoever There is usually no need to specify a certain value range that we have to support. Going with something like the integer limit of the chosen language is enough most of the time. – Denker – 2016-03-11T15:37:32.227

The input range should be what can reasonably handled with the language of choice - see this meta post http://meta.codegolf.stackexchange.com/q/8471/21348 (ok already done. Thank you)

– edc65 – 2016-03-11T15:41:20.037

Answers

2

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>

edc65

Posted 2016-03-10T21:40:33.290

Reputation: 31 086

2

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!"

Paul Chernoch

Posted 2016-03-10T21:40:33.290

Reputation: 249

This could be shorter, you don't need the epsilon for integers – edc65 – 2016-03-11T21:10:56.903

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. – Paul Chernoch – 2016-03-11T21:36:27.483

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] – edc65 – 2016-03-11T21:45:19.023

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. – Paul Chernoch – 2016-03-11T22:14:44.013

Yeah! Just passed the Python solution! – Paul Chernoch – 2016-03-11T22:55:56.523

Ouch. Passed me too. +1 – edc65 – 2016-03-12T01:04:50.950

1

MATL, 29 25 bytes

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

Inputs are y, x, l in that order.

Try it online!

Luis Mendo

Posted 2016-03-10T21:40:33.290

Reputation: 87 464

0

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).

Rainer P.

Posted 2016-03-10T21:40:33.290

Reputation: 2 457

0

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]

Erwan

Posted 2016-03-10T21:40:33.290

Reputation: 691

0

Pyth, 41 bytes

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

Test it here

Cameron McCluskie

Posted 2016-03-10T21:40:33.290

Reputation: 346

0

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.

Neil

Posted 2016-03-10T21:40:33.290

Reputation: 95 035