Cuirassés triangulaires (Un problème de géométrie informatique)

18

Vous êtes le capitaine d'un cuirassé. Le département d'ingénierie a pris les devants avec des designs cette année, donc le navire sur lequel vous êtes prend la forme d'un simple triangle.

Vous sortez sur le pont et profitez de la brise marine ... mais pas pour longtemps. Un ennemi vous a tiré dessus! - mais le coup va-t-il frapper?

Contribution

Vous pouvez écrire une fonction ou un programme complet pour ce défi.

Votre programme comprendra 11 entiers, dont dix sont jumelés:

  • Les trois premières paires d'entiers (x 1 , y 1 ), (x 2 , y 2 ), (x 3 , y 3 ) spécifieront les sommets de votre vaisseau. Le triangle formé aura une aire non nulle.

  • La prochaine paire d'entiers (e x , e y ) spécifie l'emplacement du canon ennemi. Le canon ennemi ne reposera jamais sur ou à l'intérieur des limites de votre navire. *

  • La paire (a x , a y ) après cela spécifie où l'ennemi visait. Ce sera différent de (e x , e y ).

  • Le dernier entier positif R spécifie la portée du tir de l'ennemi

* Vous seriez un terrible capitaine si vous ne l'aviez même pas remarqué!

Production

Vous devez imprimer / retourner une valeur véridique (par exemple true, 1) si le cuirassé sera touché, sinon une valeur falsifiée (par exemple false, 0).

Qu'est-ce qu'un hit?

Le tir ennemi est un segment de ligne droite de longueur R de (e x , e y ) dans la direction de (a x , a y ). Si ce segment de ligne chevauche une partie de l' intérieur de votre cuirassé triangulaire, cela compte comme un coup. Sinon, ce n'est pas un succès.

Les coups qui frôlent le long du triangle ou atteignent seulement la limite du triangle ne comptent pas comme un coup.

Exemples

0 0 0 1 1 0
1 1
0 0
2

test1

Hit: l'ennemi a tiré à travers le centre de votre vaisseau!


2 0 0 2 4 4
0 0
1 1
1

test2

Aucun coup sûr : la portée de l'ennemi est trop courte, vous êtes donc en sécurité.


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

test3

Aucun coup sûr: l'ennemi a effleuré le côté de votre vaisseau, donc cela ne compte pas comme un coup sûr. Chanceux!


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

test4

Aucun coup sûr : le tir ennemi s'arrête juste devant le navire, vous êtes donc en sécurité. Si le canon ennemi avait une portée encore légèrement meilleure, alors vous auriez été touché! Phew!


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

test5

Coup: Même si le tir n'a pas pénétré de l'autre côté, c'est toujours un coup.


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

test6

Pas de succès: Pour mémoire, il s'agit d'un autre coup sûr.


Cas de test supplémentaires

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

test7

Pas de coup: c'est un autre pâturage, mais en biais.


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

test8

Coup: Le tir est entré via un sommet du navire.

Notation

Il s'agit de , donc le code le plus court en octets l'emporte. Des échappatoires standard s'appliquent.

Sp3000
la source
Juste pour nous assurer que nous ne pouvons pas: pouvons-nous supposer que le navire n'a pas de fond et qu'il y a un petit espace entre chaque deux côtés, de sorte que si le tir parvient à entrer dans le navire par son coin, nous le considérons comme un échec?
John Dvorak du
@JanDvorak Si un tir traverse le vaisseau en entrant par un sommet, alors ce serait un coup parce que le segment de ligne chevauche l'intérieur du vaisseau. Donc, dans le 4ème exemple, si la plage était supérieure à 5, ce serait un succès.
Sp3000
Combien sommes-nous autorisés à jouer avec les arguments? Sommes-nous autorisés à les regrouper, à modifier l'ordre ou à exiger qu'ils soient flottants?
FryAmTheEggman
@FryAmTheEggman Vous pouvez grouper ou réorganiser les arguments si nécessaire. Vous pouvez utiliser des flottants, mais le programme doit fonctionner correctement pour les petites grilles (disons, jusqu'à 20x20) sans se soucier de la précision.
Sp3000
Je pense que les exemples manquent un cas important qui fait échouer ma solution prévue: lorsque le navire est pénétré dans un coin, par exemple 0 0 -1 3 4 -1 -3 -4 3 4 6.
nutki

Réponses:

3

Python 3, 252 octets

C'est certainement la plupart des variables que j'ai jamais utilisées dans le golf de code. : ^ 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 _

Légèrement non golfé, avec commentaires:

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 _

Comment ça fonctionne:

  • Calculez le point final de la prise de vue.
  • Testez de nombreux points le long de la ligne depuis le point final jusqu'à l'emplacement du canon:
    • Calculer les angles du sommet 1 aux deux autres sommets et au point de test;
    • Calculer les angles du sommet 2 aux deux autres sommets et au point de test;
    • Si l'angle du point de test se situe entre les deux autres angles, dans les deux cas, le point de test se trouve dans le triangle et le navire a été touché.

Exemples de cycles:

>>> 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
la source
2

Python 2.7, 235 octets

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

Calcule le produit croisé AB x APentre les coins A, B et le point P. Si les trois ont le même signe, alors le point est à l'intérieur du triangle.

Non 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])

Tests:

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
la source
1

C, 247 octets

Certainement pas encore tout à fait au golf.

#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;}

Actuellement, cela utilise une approche similaire à la solution de DLosc, c'est-à-dire qu'il parcourt toutes les coordonnées possibles sur le segment de ligne pour déterminer s'il intersecte avec le triangle. (Il échouera donc si la plage est supérieure à 1000). Cependant, il utilise la formule de http://mathworld.wolfram.com/TriangleInterior.html pour déterminer si un point se trouve à l'intérieur du triangle. Cela évite un tas de fonctions trigonométriques.


Exemple de vérification, doit s'imprimer 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
la source
1

JavaScript (ES6) 320 448 522 627

(Pourrait-on encore jouer au golf?)

Pas:

  1. Trouver la cible réelle touchée (pointez à la distance r sur la ligne de l'ennemi à viser)
  2. Toucher: si le segment de l'ennemi à la cible intersecte l'un des côtés du navire, mais pas aux points d'extrémité
  3. Frappez aussi: si la cible est à l'intérieur du navire - même si le tir est entré à un sommet - cas de test 8

Réf:
Intersection de segments
Point à l'intérieur du triangle
Point dans un segment étant donné une distance

Tester dans 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>

Non 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
la source