Navi da battaglia triangolari (un problema di geometria computazionale)


18

Sei il capitano di una nave da guerra. Il dipartimento di ingegneria ha tagliato gli angoli con i disegni quest'anno, quindi la nave che stai prendendo prende la forma di un semplice triangolo.

Esci sul ponte e goditi la brezza marina ... anche se non per molto. Un nemico ti ha sparato! - ma il colpo colpirà?

Ingresso

Puoi scrivere una funzione o un programma completo per questa sfida.

Il tuo programma prenderà 11 numeri interi, dieci dei quali sono accoppiati:

  • Le prime tre coppie di numeri interi (x 1 , y 1 ), (x 2 , y 2 ), (x 3 , y 3 ) specificheranno i vertici della tua nave. Il triangolo formato avrà un'area diversa da zero.

  • La prossima coppia di numeri interi (e x , e y ) specifica la posizione del cannone del nemico. Il cannone nemico non mentirà mai, né all'interno del confine della tua nave. *

  • La coppia (a x , a y ) in seguito specifica dove punta il nemico. Questo sarà distinto da (e x , e y ).

  • L'intero positivo finale R specifica il raggio di tiro del nemico

* Saresti un terribile capitano se non ti accorgessi nemmeno che sta succedendo!

Produzione

Devi stampare / restituire un valore di verità (es. Vero, 1) se la corazzata verrà colpita, altrimenti un valore di falsa (es. Falso, 0).

Che cos'è un successo?

Il tiro nemico è un segmento di linea retta di lunghezza R da (e x , e y ) nella direzione di (a x , a y ). Se questo segmento di linea si sovrappone a qualsiasi parte dell'interno della tua nave da battaglia triangolare, allora conta come un colpo. Altrimenti non è un successo.

I colpi che sfiorano o raggiungono solo il limite del triangolo non contano come un colpo.

Esempi

0 0 0 1 1 0
1 1
0 0
2

test1

Hit: il nemico ha sparato proprio attraverso il centro della tua nave!


2 0 0 2 4 4
0 0
1 1
1

test2

Nessun colpo: la distanza del nemico è troppo breve, quindi sei al sicuro.


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

test3

Nessun colpo: il nemico ha sfiorato il lato della tua nave, quindi questo non conta come un colpo. Fortunato!


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

test4

Nessun colpo: il colpo nemico si ferma a breve distanza dalla nave, quindi sei al sicuro. Se il cannone del nemico avesse un raggio persino migliore, allora saresti stato colpito! Accidenti!


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

test5

Hit: anche se il tiro non è penetrato dall'altra parte, questo è ancora un successo.


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

test6

Nessun risultato: per la cronaca, questa è un'altra sfortuna.


Casi di prova aggiuntivi

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

test7

Nessun colpo: questa è un'altra mania, ma ad angolo.


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

test8

Hit: il colpo è entrato attraverso un vertice della nave.

punteggio

Questo è , quindi vince il codice più breve in byte. Si applicano scappatoie standard .


Giusto per essere sicuri che non possiamo: possiamo presumere che la nave non abbia il fondo e che ci sia un piccolo spazio tra le due parti, in modo che se il tiro riesce ad entrare nella nave attraverso il suo angolo, lo consideriamo un miss?
John Dvorak,

@JanDvorak Se un colpo attraversa la nave entrando attraverso un vertice, allora questo sarebbe un colpo perché il segmento di linea si sovrappone all'interno della nave. Quindi, nel 4 ° esempio, se l'intervallo fosse maggiore di 5, questo sarebbe un successo.
Sp3000,

Quanto ci è permesso giocare con gli argomenti? Ci è permesso raggrupparli, cambiare l'ordine o richiedere che siano float?
FryAmTheEggman,

@FryAmTheEggman È possibile raggruppare o riordinare gli argomenti, se necessario. Puoi usare i float, ma il programma deve funzionare correttamente per griglie di piccole dimensioni (diciamo fino a 20x20) senza preoccuparti della precisione.
Sp3000,

Penso che negli esempi manchi un caso importante che fa fallire la mia soluzione prevista: quando la nave viene penetrata in un angolo, per esempio 0 0 -1 3 4 -1 -3 -4 3 4 6.
Nutki,

Risposte:


3

Python 3, 252 byte

Questa è sicuramente la maggior parte delle variabili che io abbia mai usato nel golf del codice. : ^ 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 _

Leggermente ungolfed, con commenti:

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 _

Come funziona:

  • Calcola il punto finale del tiro.
  • Prova molti punti lungo la linea dall'endpoint alla posizione del cannone:
    • Calcola gli angoli dal vertice 1 agli altri due vertici e al punto di prova;
    • Calcola gli angoli dal vertice 2 agli altri due vertici e al punto di prova;
    • Se l'angolo del punto di prova si trova tra gli altri due angoli, in entrambi i casi, il punto di prova si trova all'interno del triangolo e la nave è stata colpita.

Esecuzioni campione:

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

2

Python 2.7, 235 byte

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

Calcola il prodotto incrociato AB x APtra gli angoli A, B e il punto P. Se tutti e tre hanno lo stesso segno, il punto si trova all'interno del triangolo.

Ungolfed:

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

test:

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

1

C, 247 byte

Sicuramente non ancora del tutto golfato.

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

Attualmente utilizza un approccio simile alla soluzione di DLosc, ovvero scorre attraverso tutte le coordinate possibili sul segmento di linea per determinare se si interseca con il triangolo. (Quindi fallirà se l'intervallo è superiore a 1000) Tuttavia, utilizza la formula di http://mathworld.wolfram.com/TriangleInterior.html per determinare se un punto si trova all'interno del triangolo. Questo evita un mucchio di funzioni trigonometriche.


Esempio di controllo, dovrebbe stampare 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));
    }
}

1

JavaScript (ES6) 320 448 522 627

(Si potrebbe ancora giocare a golf di più?)

passi:

  1. Trova il bersaglio effettivo (punta alla distanza r sulla linea dal nemico per mirare)
  2. Hit: se il segmento dal nemico al bersaglio interseca uno dei lati della nave, ma non ai punti finali
  3. Colpisci anche: se il bersaglio è all'interno della nave - anche se il tiro è entrato in un vertice - test case 8

Rif: Punto di
intersezione del segmento
all'interno del triangolo
Punto in un segmento a una distanza

Test in 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>

Ungolfed

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);
}  
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.