Acorazados triangulares (un problema de geometría computacional)

18

Eres el capitán de un acorazado. El departamento de ingeniería ha estado cortando esquinas con diseños este año, por lo que el barco en el que estás toma la forma de un triángulo simple.

Sales a la terraza y disfrutas de la brisa marina ... aunque no por mucho tiempo. ¡Un enemigo te ha disparado! - ¿Pero el tiro golpeará?

Entrada

Puede escribir una función o un programa completo para este desafío.

Su programa tomará 11 enteros, diez de los cuales están emparejados:

  • Los primeros tres pares de enteros (x 1 , y 1 ), (x 2 , y 2 ), (x 3 , y 3 ) especificarán los vértices de tu nave. El triángulo formado tendrá un área distinta de cero.

  • El siguiente par de enteros (e x , e y ) especifica la ubicación del cañón del enemigo. El cañón enemigo nunca estará sobre, o dentro de los límites de tu nave. *

  • El par (a x , a y ) después de eso especifica dónde apuntó el enemigo. Esto será distinto de (e x , e y ).

  • El número entero positivo final R especifica el alcance del disparo del enemigo.

* ¡Serías un capitán terrible si ni siquiera te dieras cuenta de que eso está sucediendo!

Salida

Debe imprimir / devolver un Truthy valor (por ejemplo verdadera, 1) si el acorazado se verán afectados, de lo contrario un valor Falsy (por ejemplo falsa, 0).

¿Qué es un hit?

El disparo enemigo es un segmento de línea recta de longitud R desde (e x , e y ) en la dirección de (a x , a y ). Si este segmento de línea se superpone a cualquier parte del interior de su acorazado triangular, entonces esto cuenta como un golpe. De lo contrario, no es un éxito.

Los disparos que pastan o solo alcanzan el límite del triángulo no cuentan como un golpe.

Ejemplos

0 0 0 1 1 0
1 1
0 0
2

prueba1

Golpe: ¡ El enemigo ha disparado a través del centro de tu nave!


2 0 0 2 4 4
0 0
1 1
1

prueba2

Ningún golpe: el alcance del enemigo es demasiado corto, por lo que estás a salvo.


0 0 1 2 3 0
-4 0
0 0
8

prueba3

Sin golpe: el enemigo ha rozado el costado de su nave, por lo que esto no cuenta como un golpe. ¡Suerte!


0 0 -1 3 4 -1
-3 -4
3 4
5

prueba4

Ningún golpe: el disparo enemigo simplemente se detiene cerca de la nave, por lo que estás a salvo. Si el cañón del enemigo tuviera un alcance incluso ligeramente mejor, ¡entonces habrías sido golpeado! ¡Uf!


-2 -3 -3 6 7 -2
-6 2
1 -4
7

prueba5

Golpe: Aunque el disparo no penetró al otro lado, sigue siendo un golpe.


-3 2 2 -4 7 -3
-3 -4
-3 0
10

prueba6

Sin éxito: para el registro, esta es otra falta cercana.


Casos de prueba adicionales

0 0 6 0 6 8
-6 -8
6 8
20

prueba7

Ningún golpe: esta es otra pasta, pero en ángulo.


0 0 -2 -5 5 3
-3 4
0 0
6

prueba8

Impacto: el disparo ingresado a través de un vértice de la nave.

Puntuación

Este es el , por lo que gana el código más corto en bytes. Se aplican lagunas estándar .

Sp3000
fuente
Solo para asegurarnos de que no podemos: ¿podemos suponer que el barco no tiene fondo y que hay una pequeña brecha entre ambos lados, de modo que si el disparo logra ingresar al barco por su esquina, lo consideramos como una falla?
John Dvorak
@ JanDvorak Si un disparo atraviesa la nave al ingresar a través de un vértice, entonces esto sería un éxito porque el segmento de línea se superpone al interior de la nave. Entonces, en el cuarto ejemplo, si el rango fuera mayor que 5, entonces esto sería un éxito.
Sp3000
¿Cuánto se nos permite jugar con los argumentos? ¿Se nos permite agruparlos, cambiar el orden o exigir que sean flotadores?
FryAmTheEggman
@FryAmTheEggman Puede agrupar o reordenar los argumentos según sea necesario. Puede usar flotantes, pero el programa debe funcionar correctamente para cuadrículas pequeñas (por ejemplo, hasta 20x20) sin preocuparse por la precisión.
Sp3000
Creo que a los ejemplos les falta un caso importante que haga que mi solución prevista falle: cuando el barco es atravesado por una esquina, por ejemplo 0 0 -1 3 4 -1 -3 -4 3 4 6.
nutki

Respuestas:

3

Python 3, 252 bytes

Esta es ciertamente la mayoría de las variables que he usado en el golf de código. : ^ P

from math import*;A=atan2
def h(a,b,c,d,e,f,g,h,i,j,R):
 r=R;_=0
 while r>0:Q=A(j-h,i-g);k,l=g+r*cos(Q),h+r*sin(Q);D=A(d-b,c-a);E=A(f-b,e-a);F=A(l-b,k-a);G=A(b-d,a-c);H=A(f-d,e-c);I=A(l-d,k-c);_=_ or(D<F<E or E<F<D)and(G<I<H or H<I<G);r-=.001
 return _

Ligeramente descamados, con comentarios:

from math import*
# Parameters:
#  (a,b) (c,d) (e,f) - vertices of the triangle
#  (g,h) - location of cannon
#  (i,j) - aim of cannon
#  R - range of cannon
# Variables within function:
#  _ - was this shot a hit?
#  r - distance 0 < r <= R that we're testing
#  Q - angle between cannon source and destination
#  (k,l) - point that we're testing
#  D,E,F - angles between point 1 and 2,3,test
#  G,H,I - angles between point 2 and 1,3,test
def h(a,b,c,d,e,f,g,h,i,j,R):
    r=R;_=0
    while r>0:
        Q=atan2(j-h,i-g)
        k,l=g+r*cos(Q),h+r*sin(Q)
        D=atan2(d-b,c-a)
        E=atan2(f-b,e-a)
        F=atan2(l-b,k-a)
        G=atan2(b-d,a-c)
        H=atan2(f-d,e-c)
        I=atan2(l-d,k-c)
        _=_ or(D<F<E or E<F<D)and(G<I<H or H<I<G)
        r-=.001
    return _

Cómo funciona:

  • Calcule el punto final del disparo.
  • Pruebe muchos puntos a lo largo de la línea desde el punto final hasta la ubicación del cañón:
    • Calcule ángulos desde el vértice 1 a otros dos vértices y al punto de prueba;
    • Calcular ángulos desde el vértice 2 a otros dos vértices y al punto de prueba;
    • Si el ángulo del punto de prueba está entre los otros dos ángulos, en ambos casos, entonces el punto de prueba está dentro del triángulo y el barco ha sido golpeado.

Ejecuciones de muestra:

>>> h(0,0,0,1,1,0,1,1,0,0,2)
True
>>> h(0,0,1,2,3,0,-4,0,0,0,8)
False
>>> h(0,0,-1,3,4,-1,-3,-4,3,4,5)
False
>>> h(-2,-3,-3,6,7,-2,-6,2,1,-4,7)
True
DLosc
fuente
2

Python 2.7, 235 bytes

from numpy import*
X=cross
h=lambda q,w,a,s,y,x,c,v,d,f,r:max([(X([a-q,s-w],[c+k*(d-c)-q,v+k*(f-v)-w])>0)==(X([y-a,x-s],[c+k*(d-c)-a,v+k*(f-v)-s])>0)==(X([q-y,w-x],[c+k*(d-c)-y,v+k*(f-v)-x])>0)for k in arange(0,r/hypot(d-c,f-v),1e-4)])

Calcula el producto cruzado AB x APentre las esquinas A, B y el punto P. Si los tres tienen el mismo signo, entonces el punto está dentro del triángulo.

Sin golf:

from numpy import *
def i(q,w,a,s,y,x,e,r): # helper-function, checks whether ER is inside the triangle QW-AS-YX
  t=cross([a-q,s-w],[e-q,r-w])>0
  g=cross([y-a,x-s],[e-a,r-s])>0
  b=cross([q-y,w-x],[e-y,r-x])>0
  return t==g==b

def h(q,w,a,s,y,x,c,v,d,f,r):
  R=arange(0,r/hypot(d-c,f-v),1e-3)
  return max([i(q,w,a,s,y,x,c+k*(d-c),v+k*(f-v)) for k in R])

Pruebas:

In : h(0,0,0,1,1,0,1,1,0,0,2)
Out: True

In : h(-3,2,2,-4,7,-3,-3,-4,-3,0,10)
Out: False

In : h(0,0,1,2,3,0,-4,0,0,0,8)
Out: True
     Grazes may count as hits...
In : h(1,2,0,0,3,0,-4,0,0,0,8)
Out: False
     ...or not, depending on the order of edges
DenDenDo
fuente
1

C, 247 bytes

Definitivamente no del todo golfizado todavía.

#include<math.h>
int z(float*k){float s=1e-3,t=s,p=k[8]-k[6],q=k[9]-k[7],r=k[10]/hypot(p,q);int w=0;for(;t<1;t+=s){float x=k[6]-k[0]+p*r*t,y=k[7]-k[1]+q*r*t,b=k[2]*k[5]-k[3]*k[4],d=(x*k[5]-y*k[4])/b,e=(x*k[3]-y*k[2])/b;w|=d>0&e<0&d-e<1;}return w;}

Actualmente, esto utiliza un enfoque similar a la solución de DLosc, es decir, recorre todas las coordenadas posibles en el segmento de línea para determinar si se cruza con el triángulo. (Por lo tanto, fallará si el rango es superior a 1000) Sin embargo, utiliza la fórmula de http://mathworld.wolfram.com/TriangleInterior.html para determinar si un punto está dentro del triángulo. Esto evita un montón de funciones trigonométricas.


Ejemplo de verificación, debe imprimir 1 0 0 0 1 0.

#include <stdio.h>
int main() {
    {
        float arr[] = {0,0,0,1,1,0,1,1,0,0,2};
        printf("%d\n", z(arr));
    }

    {
        float arr[] = {2,0,0,2,4,4,0,0,1,1,1};
        printf("%d\n", z(arr));
    }

    {
        float arr[] = {0,0,1,2,3,0,-4,0,0,0,8};
        printf("%d\n", z(arr));
    }

    {
        float arr[] = {0,0,-1,3,4,-1,-3,-4,3,4,5};
        printf("%d\n", z(arr));
    }

    {
        float arr[] = {-2,-3,-3,6,7,-2,-6,2,1,-4,7};
        printf("%d\n", z(arr));
    }

    {
        float arr[] = {-3,2,2,-4,7,-3,-3,-4,-3,0,10};
        printf("%d\n", z(arr));
    }
}
kennytm
fuente
1

JavaScript (ES6) 320 448 522 627

(¿Todavía se puede jugar más al golf?)

Pasos:

  1. Encuentre el objetivo real alcanzado (apunte a la distancia r en la línea del enemigo para apuntar)
  2. Impacto: si el segmento del enemigo al objetivo se cruza con cualquiera de los lados de la nave, pero no en los puntos finales
  3. Golpe también: si el objetivo está dentro de la nave, incluso si el disparo ingresó en un vértice, caso de prueba 8

Ref:
intersección del segmento
Punto dentro del triángulo
Punto en un segmento dada una distancia

Prueba en Firefox

C=(i,j,k,l,m,n,g,h,a,b,r,
  d=a-g,e=b-h,f=r/Math.sqrt(d*d+e*e),
  p=g+f*d,q=h+f*e,
  z=j*(m-k)+i*(l-n)+k*n-l*m,
  s=(j*m-i*n+(n-j)*p+(i-m)*q)/z,
  t=(i*l-j*k+(j-l)*p+(k-i)*q)/z,
  S=(i,j,k,l,
     a=k-i,b=l-j,c=p-g,d=q-h,e=i-g,f=j-h,
     s=a*f-b*e,t=c*f-d*e,m=a*d-c*b)=>
     m&&((s/=m)>0&s<1&(t/=m)>0&t<1)
)=>s>0&t>0&s+t<1|S(i,j,k,l)|S(i,j,m,n)|S(m,n,k,l)

// Test
MyOutput.innerHTML = ['Test1', C(0,0, 0,1, 1,0, 1,1, 0,0, 2),
'<br>Test2', C(2,0, 0,2, 4,4, 0,0, 1,1, 1),
'<br>Test3', C(0,0, 1,2, 3,0, -4,0, 0,0, 8),
'<br>Test4', C(0,0, -1,3, 4,-1, -3,-4, 3,4, 5),
'<br>Test5', C(-2,-3, -3,6, 7,-2, -6,2, 1,-4, 7),
'<br>Test6', C(-3,2, 2,-4, 7,-3, -3,-4, -3,0 ,10),
'<br>Test7', C(0,0, 6,0, 6,8, -6,-8, 6,8, 20),
'<br>Test8', C(0,0,-2,-5, 5,3, -3,4, 0,0, 6)];
<div id="MyOutput"></div>

Sin golf

function check(p0x, p0y, p1x, p1y, p2x, p2y, ex, ey, ax, xy, r)
{
  var sec = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y)
  {
      var s10x = p1x - p0x, s10y = p1y - p0y, 
          s32x = p3x - p2x, s32y = p3y - p2y,
          s02x = p0x - p2x, s02y = p0y - p2y,
          s = s10x * s02y - s10y * s02x, t = s32x * s02y - s32y * s02x,
          d = s10x * s32y - s32x * s10y;
      return d && (s/=d) > 0 && s<1 && (t/=d) > 0 && t < 1 && [p0x + (t * s10x), p0y + (t * s10y)];
  }
  var pr = function(p0x, p0y, p1x, p1y, r)
  {
      var dx = (p1x-p0x), dy = (p1y-p0y), f = r/Math.sqrt(dx*dx+dy*dy);
      return [p0x + f*dx, p0y+f*dy];
  }
  var inside = function(p0x, p0y, p1x, p1y, p2x, p2y, px, py)
  {
      var area2 = (-p1y*p2x + p0y*(-p1x + p2x) + p0x*(p1y - p2y) + p1x*p2y),
          s = (p0y*p2x - p0x*p2y + (p2y - p0y)*px + (p0x - p2x)*py)/area2,
          t = (p0x*p1y - p0y*p1x + (p0y - p1y)*px + (p1x - p0x)*py)/area2;
      return s > 0 && t > 0 && s+t < 1;
  }
  var tx, xy;
  [tx, ty] = pr(ex, ey, ax, ay, r);

  return inside(p0x, p0y, p1x, p1y, p2x, p2y, tx,ty)
  || sec(p0x, p0y, p1x, p1y, ex, ey, tx, ty)
  || sec(p0x, p0y, p2x, p2y, ex, ey, tx, ty)
  || sec(p2x, p2y, p1x, p1y, ex, ey, tx, ty);
}  
edc65
fuente