Triangular battleship (Masalah komputasi geometri)

18

Anda adalah kapten kapal perang. Departemen teknik telah mengambil jalan pintas dengan desain tahun ini, jadi kapal yang Anda tumpangi berbentuk segitiga sederhana.

Anda berjalan ke geladak dan menikmati angin laut ... meskipun tidak lama. Musuh telah menembaki Anda! - tetapi apakah tembakannya akan mengenai?

Memasukkan

Anda dapat menulis fungsi atau program lengkap untuk tantangan ini.

Program Anda akan mencakup 11 bilangan bulat, sepuluh di antaranya dipasangkan:

  • Tiga pasang bilangan bulat pertama (x 1 , y 1 ), (x 2 , y 2 ), (x 3 , y 3 ) akan menentukan simpul kapal Anda. Segitiga yang terbentuk akan memiliki area bukan nol.

  • Sepasang bilangan bulat berikutnya (e x , e y ) menentukan lokasi meriam musuh. Meriam musuh tidak akan pernah berbaring, atau dalam batas kapal Anda. *

  • Pasangan (a x , a y ) setelah itu menentukan ke mana tujuan musuh. Ini akan berbeda dari (e x , e y ).

  • Integer positif akhir R menentukan kisaran tembakan musuh

* Anda akan menjadi kapten yang mengerikan jika Anda bahkan tidak melihat itu terjadi!

Keluaran

Anda harus mencetak / mengembalikan truthy nilai (misalnya benar, 1) jika kapal perang akan terkena, jika nilai falsy (misalnya palsu, 0).

Apa itu hit?

Tembakan musuh adalah segmen garis lurus dengan panjang R dari (e x , e y ) ke arah (a x , a y ). Jika segmen garis ini tumpang tindih dengan bagian mana pun dari interior kapal perang segitiga Anda, maka ini dianggap sebagai hit. Kalau tidak, itu bukan hit.

Tembakan yang merumput di sepanjang atau hanya mencapai hingga batas segitiga tidak dihitung sebagai hit.

Contohnya

0 0 0 1 1 0
1 1
0 0
2

test1

Hit: Musuh telah menembak tepat melalui pusat kapalku!


2 0 0 2 4 4
0 0
1 1
1

test2

Tidak ada pukulan: Rentang musuh terlalu pendek, jadi Anda aman.


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

test3

Tanpa pukulan: Musuh telah menyerempet sisi kapal Anda, jadi ini tidak dihitung sebagai pukulan. Beruntung!


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

test4

Tanpa pukulan: Tembakan musuh hanya berhenti di dekat kapal, jadi Anda aman. Jika meriam musuh memiliki jangkauan yang sedikit lebih baik, maka Anda akan terkena! Fiuh!


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

test5

Hit: Meskipun tembakan tidak menembus ke sisi lain, ini masih menjadi hit.


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

test6

Tanpa hit: Sebagai catatan, ini adalah miss close lainnya.


Kasus uji tambahan

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

test7

Tidak ada pukulan: Ini adalah satu lagi goresan, tetapi pada satu sudut.


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

test8

Hit: Tembakan masuk melalui simpul kapal.

Mencetak gol

Ini adalah , jadi kode terpendek dalam byte menang. Celah standar berlaku.

Sp3000
sumber
Hanya untuk memastikan kita tidak bisa: bisakah kita mengasumsikan kapal tidak memiliki dasar dan bahwa ada celah kecil antara kedua belah pihak, sehingga jika tembakan berhasil memasuki kapal melalui sudutnya, kita menganggapnya sebagai kehilangan?
John Dvorak
@JanDvorak Jika tembakan menembus kapal dengan memasukkan melalui titik, maka ini akan menjadi hit karena segmen garis tumpang tindih dengan interior kapal. Jadi pada contoh ke-4, jika rentang lebih besar dari 5, maka ini akan menjadi hit.
Sp3000
Berapa banyak kita diizinkan untuk bermain dengan argumen? Apakah kita boleh mengelompokkan mereka, mengubah urutan, atau mengharuskan mereka mengapung?
FryAmTheEggman
@FryAmTheEggman Anda dapat mengelompokkan atau menyusun ulang argumen yang diperlukan. Anda dapat menggunakan pelampung, tetapi program harus bekerja dengan benar untuk kisi-kisi kecil (katakanlah, hingga 20x20) tanpa khawatir tentang presisi.
Sp3000
Saya pikir contoh-contohnya hilang satu kasus penting yang membuat solusi yang saya maksud gagal: ketika kapal ditembus melalui sudut, misalnya 0 0 -1 3 4 -1 -3 -4 3 4 6.
nutki

Jawaban:

3

Python 3, 252 byte

Ini tentu saja variabel terbanyak yang pernah saya gunakan dalam kode golf. : ^ 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 _

Sedikit ungolfed, dengan komentar:

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 _

Bagaimana itu bekerja:

  • Hitung titik akhir tembakan.
  • Tes banyak poin di sepanjang garis dari titik akhir ke lokasi meriam:
    • Hitung sudut dari titik 1 ke dua titik lainnya dan untuk menguji titik;
    • Hitung sudut dari titik 2 ke dua titik lainnya dan untuk menguji titik;
    • Jika sudut titik uji berada di antara dua sudut lainnya, dalam kedua kasus, maka titik uji berada di dalam segitiga dan kapal telah mengenai.

Sampel berjalan:

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

Menghitung produk silang AB x APantara sudut A, B dan titik P. Jika ketiganya memiliki tanda yang sama, maka titik tersebut berada di dalam segitiga.

Tidak Disatukan:

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

Tes:

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

C, 247 byte

Jelas belum bermain 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;}

Saat ini ini menggunakan pendekatan yang mirip dengan solusi DLosc, yaitu iterate melalui semua koordinat yang mungkin pada segmen garis untuk menentukan apakah itu bersinggungan dengan segitiga. (Jadi itu akan gagal jika rentangnya lebih dari 1000) Namun, ia menggunakan rumus dari http://mathworld.wolfram.com/TriangleInterior.html untuk menentukan apakah suatu titik berada di dalam segitiga. Ini menghindari banyak fungsi trigonometri.


Contoh cek, harus dicetak 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
sumber
1

JavaScript (ES6) 320 448 522 627

(Masih bisa bermain golf lagi?)

Langkah:

  1. Temukan target sasaran yang sebenarnya (arahkan jarak r pada garis dari musuh ke sasaran)
  2. Hit: jika segmen dari musuh ke target memotong salah satu sisi kapal, tetapi tidak di titik akhir
  3. Pukul juga: jika target ada di dalam kapal - walaupun tembakan masuk pada titik uji 8

Ref:
Segment intersection
Point dalam segitiga
Point dalam suatu segmen diberi jarak

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

Tidak disatukan

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
sumber