Dreieckige Schlachtschiffe (Ein rechnerisches Geometrieproblem)

18

Sie sind der Kapitän eines Schlachtschiffes. Die Konstruktionsabteilung hat in diesem Jahr bei den Entwürfen Abstriche gemacht, sodass das Schiff, auf dem Sie sich befinden, die Form eines einfachen Dreiecks hat.

Sie gehen auf das Deck und genießen die Meeresbrise ... wenn auch nicht lange. Ein Feind hat auf dich geschossen! - aber wird der Schuss treffen?

Eingang

Sie können entweder eine Funktion oder ein vollständiges Programm für diese Herausforderung schreiben.

Ihr Programm wird 11 ganze Zahlen enthalten, von denen zehn gepaart sind:

  • Die ersten drei Paare von Ganzzahlen (x 1 , y 1 ), (x 2 , y 2 ), (x 3 , y 3 ) geben die Eckpunkte Ihres Schiffes an. Das gebildete Dreieck hat eine Fläche ungleich Null.

  • Das nächste Ganzzahlpaar (e x , e y ) gibt die Position der feindlichen Kanone an. Die feindliche Kanone wird niemals auf oder innerhalb der Grenze Ihres Schiffes liegen. *

  • Das Paar (a x , a y ) danach gibt an, wohin der Feind zielte. Dies unterscheidet sich von (e x , e y ).

  • Die letzte positive ganze Zahl R gibt die Reichweite des gegnerischen Schusses an

* Du wärst ein schrecklicher Kapitän, wenn du das nicht einmal bemerkt hättest!

Ausgabe

Sie müssen einen Wahrheitswert (z. B. true, 1) ausgeben , wenn das Schlachtschiff getroffen wird, andernfalls einen falschen Wert (z. B. false, 0).

Was ist ein Hit?

Der gegnerische Schuss ist ein gerades Liniensegment der Länge R von (e x , e y ) in Richtung von (a x , a y ). Wenn dieses Liniensegment einen Teil des Innenraums Ihres dreieckigen Schlachtschiffs überlappt , gilt dies als Treffer. Sonst ist es kein Treffer.

Schüsse, die entlang des Dreiecks grasen oder nur bis zur Grenze des Dreiecks reichen, zählen nicht als Treffer.

Beispiele

0 0 0 1 1 0
1 1
0 0
2

test1

Treffer: Der Feind ist mitten durch dein Schiff geschossen!


2 0 0 2 4 4
0 0
1 1
1

test2

Kein Treffer: Die Reichweite des Gegners ist zu gering, sodass Sie in Sicherheit sind.


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

test3

Kein Treffer: Der Feind hat die Seite Ihres Schiffes gestreift, dies zählt also nicht als Treffer. Glücklich!


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

test4

Kein Treffer: Der gegnerische Schuss hält nur kurz vor dem Schiff an, sodass Sie in Sicherheit sind. Wenn die feindliche Kanone eine noch etwas bessere Reichweite hätte, wären Sie getroffen worden! Puh!


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

test5

Treffer: Auch wenn der Schuss nicht auf die andere Seite eingedrungen ist, ist dies immer noch ein Treffer.


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

test6

Kein Treffer: Für die Aufzeichnung ist dies ein weiterer enger Fehler.


Zusätzliche Testfälle

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

test7

Kein Treffer: Dies ist eine weitere Weide, aber in einem Winkel.


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

test8

Treffer: Der Schuss wurde über einen Scheitelpunkt des Schiffes abgegeben.

Wertung

Das ist , also gewinnt der kürzeste Code in Bytes. Es gelten Standardlücken .

Sp3000
quelle
Nur um sicherzugehen, dass wir das nicht können: Können wir annehmen, dass das Schiff keinen Boden hat und dass zwischen beiden Seiten eine kleine Lücke besteht, sodass wir es als Fehlschuss betrachten, wenn der Schuss durch seine Ecke ins Schiff eindringt?
John Dvorak
@JanDvorak Wenn ein Schuss das Schiff durchquert, indem er durch einen Scheitelpunkt eintritt, ist dies ein Treffer, da das Liniensegment das Innere des Schiffes überlappt. Wenn also im vierten Beispiel die Reichweite größer als 5 wäre, wäre dies ein Treffer.
Sp3000
Wie viel dürfen wir mit den Argumenten spielen? Dürfen wir sie gruppieren, die Reihenfolge ändern oder verlangen, dass sie schwimmen?
FryAmTheEggman
@FryAmTheEggman Sie können die Argumente nach Bedarf gruppieren oder neu anordnen. Sie können Floats verwenden, aber das Programm muss für kleine Raster (z. B. bis zu 20 x 20) korrekt funktionieren, ohne sich um die Genauigkeit sorgen zu müssen.
Sp3000
Ich denke, den Beispielen fehlt ein wichtiger Fall, der meine beabsichtigte Lösung zum Scheitern bringt: Wenn das Schiff zum Beispiel durch eine Ecke dringt 0 0 -1 3 4 -1 -3 -4 3 4 6.
Nutki

Antworten:

3

Python 3, 252 Bytes

Dies sind mit Sicherheit die meisten Variablen, die ich jemals im Codegolf verwendet habe. : ^ 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 _

Etwas ungolfed, mit Kommentaren:

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 _

Wie es funktioniert:

  • Berechnen Sie den Endpunkt der Aufnahme.
  • Testen Sie viele Punkte entlang der Linie vom Endpunkt bis zur Position der Kanone:
    • Berechnen Sie die Winkel von Scheitelpunkt 1 zu den beiden anderen Scheitelpunkten und zum Testpunkt.
    • Berechnen Sie die Winkel vom Scheitelpunkt 2 zu den beiden anderen Scheitelpunkten und zum Testpunkt.
    • Wenn der Testpunktwinkel in beiden Fällen zwischen den beiden anderen Winkeln liegt, befindet sich der Testpunkt innerhalb des Dreiecks und das Schiff wurde getroffen.

Probeläufe:

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

Berechnet das Kreuzprodukt AB x APzwischen den Ecken A, B und dem Punkt P. Wenn alle drei das gleiche Vorzeichen haben, befindet sich der Punkt innerhalb des Dreiecks.

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

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

C 247 Bytes

Auf jeden Fall noch nicht ganz golfen.

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

Gegenwärtig wird ein Ansatz verwendet, der der DLosc-Lösung ähnelt, dh es werden alle möglichen Koordinaten auf dem Liniensegment durchlaufen, um festzustellen, ob es das Dreieck schneidet. (Wenn der Bereich über 1000 liegt, schlägt dies fehl.) Es wird jedoch die Formel von http://mathworld.wolfram.com/TriangleInterior.html verwendet , um zu bestimmen, ob sich ein Punkt innerhalb des Dreiecks befindet. Dies vermeidet eine Reihe von trigonometrischen Funktionen.


Beispiel überprüfen, sollte drucken 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
quelle
1

JavaScript (ES6) 320 448 522 627

(Könnte noch mehr Golf gespielt werden?)

Schritte:

  1. Finde das tatsächlich getroffene Ziel (Punkt in der Entfernung r auf der Linie vom Feind zum Ziel)
  2. Treffer: Wenn das Segment vom Feind zum Ziel eine der Seiten des Schiffes schneidet, jedoch nicht an den Endpunkten
  3. Treffer auch: Befindet sich das Ziel im Schiff - auch wenn der Schuss auf einen Scheitelpunkt gefallen ist - Testfall 8

Ref:
Segmentschnittpunkt
innerhalb des Dreiecks
Punkt in einem Segment, dem ein Abstand gegeben ist

Testen Sie 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);
}  
edc65
quelle