Kemana laser pergi?


34

Ambil kisi 2 dimensi dan gambar sejumlah segmen garis di atasnya untuk mewakili cermin. Sekarang pilih titik untuk meletakkan laser teoretis dan sudut untuk menentukan arah yang ditunjuknya. Pertanyaannya adalah: jika Anda mengikuti jalur sinar laser untuk jarak tertentu, di titik koordinat manakah Anda?

Contoh:

contoh laser

Dalam gambar ini, Ladalah lokasi laser, tadalah sudut itu (diukur dari sumbu X positif), M1, M2, dan M3semua cermin segmen garis, dan Eadalah titik pada jalur sinar laser setelah D = d1 + d2 + d3 + d4unit, mulai dari L.

Tujuan

Menulis program terpendek (dalam byte) yang output Eyang diberikan L, t, D, dan daftar cermin.
(Gunakan http://mothereff.in/byte-counter untuk menghitung byte.)

Masukkan format

Masukan akan datang dari stdin dalam format:

Lx Ly t D M1x1 M1y1 M1x2 M1y2 M2x1 M2y1 M2x2 M2y2 ...
  • Semua nilai-nilai akan mengambang poin pencocokan regex ini: [-+]?[0-9]*\.?[0-9]+.
  • Selalu ada satu ruang di antara setiap angka.
  • Membutuhkan kutipan di sekitar input diperbolehkan.
  • tdalam derajat, tetapi tidak harus dalam [0, 360)kisaran. (Jika Anda lebih suka menggunakan radian saja, katakan saja dalam jawaban Anda.)
  • Dmungkin negatif, secara efektif memutar laser 180 derajat. Dmungkin juga 0.
  • Mungkin ada banyak mirror yang berubah-ubah (termasuk tidak sama sekali).
  • Urutan cermin seharusnya tidak masalah.
  • Anda dapat berasumsi bahwa input akan datang dalam kelipatan 4 angka. misalnya Lx Ly tatau Lx Ly t D M1x1tidak valid dan tidak akan diuji. Tidak ada input sama sekali juga tidak valid.

Tata letak di atas mungkin dimasukkan sebagai:

1 1 430 17 4.8 6.3 6.2 5.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3

(Perhatikan bahwa gambar digambar secara bebas dan nilai-nilai ini hanya perkiraan. Nilai input Martin Büttner dari

1 1 430 17 4.8 5.3 6.2 4.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3

akan memberikan lebih banyak tabrakan meskipun tidak cocok dengan sketsa.)

Format output

Output harus masuk ke stdout dalam format:

Ex Ey

Ini juga mengapung dan mungkin dalam bentuk eksponensial.

Catatan

  • Cermin dapat saling berpotongan.
  • Kedua sisi cermin bersifat reflektif.
  • Sinar mungkin mengenai cermin yang sama beberapa kali.
  • Sinar itu berlangsung selamanya.

Case yang tidak terdefinisi

Anda dapat mengasumsikan bahwa kasus-kasus itu ada di mana

  • laser dimulai pada segmen garis cermin
  • sinar laser menyentuh titik akhir cermin
  • sinar laser menyentuh persimpangan antara dua cermin

tidak terdefinisi dan tidak akan diuji. Program Anda dapat melakukan apa saja jika ini terjadi, termasuk melempar kesalahan.

Bonus

Hanya untuk bersenang-senang, saya akan memberikan 200 poin hadiah untuk pengajuan dengan suara terbanyak yang menghasilkan representasi grafis dari masalah (Anda bahkan bisa menulis skrip interaktif). Pengajuan bonus ini tidak perlu di-golf dan bisa ringan dengan bagaimana input dan output ditangani. Mereka berbeda dari kiriman golf yang sebenarnya tetapi keduanya harus diajukan dalam jawaban yang sama .

Catatan: Hanya mengirimkan jawaban bonus baik-baik saja, Anda tidak akan menjadi jawaban yang diterima. Untuk dapat diterima, Anda harus benar-benar mengikuti spesifikasi input / output (mis. Output hanya melibatkan Ex Ey, bukan gambar), dan menjadi yang terpendek.


1
Memiliki pengiriman golf dan ungolfed dalam satu pertanyaan adalah imho akan menjadi kekacauan besar. 200 poin karunia sangat menarik sehingga golf menjadi poin minor.
Howard

1
@PeterTaylor Anda mengutip saya dengan baik di luar konteks. Saya membaca jawaban bonus bagian OP karena dua kiriman sama sekali berbeda tetapi termasuk dalam kiriman yang sama jika keduanya dicoba (yang berarti hanya jawaban popcon juga akan baik-baik saja). Bagaimanapun, itu suara Anda dan terserah pada Anda bagaimana Anda menggunakannya, dan saya mungkin akan menambahkan versi golf pula di beberapa titik. Tapi saya kira OP bisa mengklarifikasi apakah yang dia maksud, hanya jawaban popcon yang valid atau tidak.
Martin Ender

1
@ MartinBüttner, " bonus " berarti " tambahan, ekstra ". Itu bukan bagian dari tantangan utama. Dan pertanyaannya hanya memiliki satu tag, kode-golf .
Peter Taylor

2
@PeterTaylor MartinBüttner benar. Hanya menjawab bagian bonus dari pertanyaan itu baik-baik saja. Saya mengatakan bahwa jawaban bonus bisa tidak golf dan ringan dengan i / o, dan semua pengajuan bonus saat ini terlihat baik bagi saya. Pengajuan terpendek yang benar- benar mengikuti spesifikasi akan menjadi jawaban yang diterima. Saat ini tidak ada pengiriman yang dilakukan, tetapi itu tidak masalah bagi saya.
Calvin Hobbies

1
Dalam hal itu " bonus " adalah kata yang salah untuk digunakan dan Anda meminta orang untuk melanggar aturan , yang tidak membantu.
Peter Taylor

Jawaban:


39

Ruby, 327 byte

(gulir ke bawah)

Mathematica, jawaban bonus

masukkan deskripsi gambar di sini

Saya hanya akan mengajukan pengajuan grafis sekarang. Saya mungkin port ini ke Ruby nanti dan golf jika saya suka.

(* This function tests for an intersection between the laser beam
   and a mirror. r contains the end-points of the laser, s contains
   the end-points of the mirror. *)
intersect[r_, s_] := Module[
   {lr, dr, nr, ds, ns, \[Lambda]},
   (* Get a unit vector in the direction of the beam *)
   dr = r[[2]] - r[[1]];
   lr = Norm@dr;
   dr /= lr;
   (* Get a normal to that vector *)
   nr = {dr[[2]], -dr[[1]]};

   (* The sign of dot product in here depends on whether that end-point
      of the mirror is to the left or to the right of the array. Return 
      infinity if both ends of s are on the same side of the beam. *)
   If[Apply[Times, (s - {r[[1]], r[[1]]}).nr] > 0, 
    Return[\[Infinity]]];

   (* Get a unit vector along the mirror. *)
   ds = s[[2]] - s[[1]];
   ds /= Norm@ds;
   (* And a normal to that. *)
   ns = {ds[[2]], -ds[[1]]};
   (* We can write the beam as p + λ*dr and mirror as q + μ*ds,
      where λ and μ are real parameters. If we set those equal and
      solve for λ we get the following equation. Since dr is a unit 
      vector, λ is also the distance to the intersection. *)
   \[Lambda] = ns.(r[[1]] - s[[1]])/nr.ds;
   (* Make sure that the intersection is before the end of the beam.
      This check could actually be slightly simpler (see Ruby version). *)
   If[\[Lambda] != 0 && lr/\[Lambda] < 1, Infinity, \[Lambda]]
   ];

(* This function actually does the simulation and generates the plot. *)
plotLaser[L_, t_, distance_, M_] := Module[
   {coords, plotRange, points, e, lastSegment, dLeft, \[Lambda], m, p,
     d, md, mn, segments, frames, durations},

   (* This will contain all the intersections along the way, as well
      as the starting point. *)
   points = {L};
   (* The tentative end point. *)
   e = L + distance {Cos@t, Sin@t};
   (* This will always be the currently last segment for which we need
      to check for intersections. *)
   lastSegment = {L, e};
   (* Keep track of the remaining beam length. *)
   dLeft = distance;

   While[True,
    (* Use the above function to find intersections with all mirrors
       and pick the first one (we add a small tolerance to avoid
       intersections with the most recent mirror). *)
    {\[Lambda], m} = 
     DeleteCases[
       SortBy[{intersect[lastSegment, #], #} & /@ M, #[[1]] &], 
       i_ /; i[[1]] < 1*^-10][[1]];
    (* If no intersection was found, we're done. *)
    If[\[Lambda] == \[Infinity], Break[]];
    (* Reduce remaining beam length. *)
    dLeft -= \[Lambda];
    (* The following lines reflect the beam at the mirror and add
       the intersection to our list of points. We also update the
       end-point and the last segment. *)
    p = lastSegment[[1]];
    d = -Subtract @@ lastSegment;
    d /= Norm@d;
    md = -Subtract @@ m;
    md /= Norm@md;
    mn = {md[[2]], -md[[1]]};
    AppendTo[points, p + \[Lambda]*d];
    d = -d + 2*(d - d.mn*mn);
    e = Last@points + dLeft*d;
    lastSegment = {Last@points, e};
    ];
   (* Get a list of all points in the set up so we can determine
      the plot range. *)
   coords = Transpose@Join[Flatten[M, 1], {L, e}];
   (* Turn the list of points into a list of segments. *)
   segments = Partition[points, 2, 1];
   (* For each prefix of that list, generate a frame. *)
   frames = Map[
     Graphics[
       {Line /@ M,
        Red,
        Point@L,
        Line /@ segments[[1 ;; #]]},
       PlotRange -> {
         {Min@coords[[1]] - 1, Max@coords[[1]] + 1},
         {Min@coords[[2]] - 1, Max@coords[[2]] + 1}
         }
       ] &,
     Range@Length@segments];
   (* Generate the initial frame, without any segments. *)
   PrependTo[frames,
    Graphics[
     {Line /@ M,
      Red,
      Point@L},
     PlotRange -> {
       {Min@coords[[1]] - 1, Max@coords[[1]] + 1},
       {Min@coords[[2]] - 1, Max@coords[[2]] + 1}
       }
     ]
    ];
   (* Generate the final frame including lastSegment. *)
   AppendTo[frames,
    Graphics[
     {Line /@ M,
      Red,
      Point@L,
      Line /@ segments,
      Line[lastSegment],
      Point@e},
     PlotRange -> {
       {Min@coords[[1]] - 1, Max@coords[[1]] + 1},
       {Min@coords[[2]] - 1, Max@coords[[2]] + 1}
       }
     ]];

   (*Uncomment to only view the final state *)
   (*Last@frames*)

   (* Export the frames as a GIF. *)
   durations = ConstantArray[0.1, Length@frames];
   durations[[-1]] = 1;
   Export["hardcoded/path/to/laser.gif", frames, 
    "GIF", {"DisplayDurations" -> durations, ImageSize -> 600}];

   (* Generate a Mathematica animation form the frame. *)
   ListAnimate@frames
   ];

Anda bisa menyebutnya seperti

plotLaser[{1, 1}, 7.50492, 95, {
  {{4.8, 5.3}, {6.2, 4.3}}, {{1.5, 4.8}, {3.5, 6}}, {{6.3, 1.8}, {7.1, 3}}, 
  {{5, 1}, {4, 3}}, {{7, 6}, {5, 6.1}}, {{8.5, 2.965}, {8.4, 2}}, 
  {{8.5, 3.035}, {8.6, 4}}, {{8.4, 2}, {10.5, 3}}, {{8.6, 4}, {10.5, 3}}
}]

Itu akan memberi Anda animasi di Mathematica dan juga mengekspor GIF (yang ada di atas untuk input ini). Saya telah sedikit memperluas contoh OP untuk ini, untuk membuatnya sedikit lebih menarik.

Lebih banyak contoh

Sebuah tabung dengan dinding yang agak menyimpang tetapi ujungnya tertutup:

plotLaser[{0, 0}, 1.51, 200, {
  {{0, 1}, {20, 1.1}},
  {{0, -1}, {20, -1.1}},
  {{20, 1.1}, {20, -1.1}}
}]

masukkan deskripsi gambar di sini

Segitiga sama sisi dan arah awal yang hampir sejajar dengan salah satu sisi.

plotLaser[{-1, 0}, Pi/3 + .01, 200, {
  {{-2.5, 5 Sqrt[3]/6}, {2.5, 5 Sqrt[3]/6}},
  {{0, -5 Sqrt[3]/3}, {-2.5, 5 Sqrt[3]/6}},
  {{0, -5 Sqrt[3]/3}, {2.5, 5 Sqrt[3]/6}}
}]

masukkan deskripsi gambar di sini

Satu lagi:

plotLaser[
 {0, 10}, -Pi/2, 145,
 {
   {{-1, 1}, {1, -1}}, {{4.5, -1}, {7.5, Sqrt[3] - 1}},
   {{11, 10}, {13, 10}}, {{16.5, Sqrt[3] - 1}, {19.5, -1}},
   {{23, -1}, {25, 1}}, {{23, 6}, {25, 4}}, {{18, 6}, {20, 4}}, {{18, 9}, {20, 11}},
   {{31, 9}, {31.01, 11}}, {{24.5, 10.01}, {25.52, 11.01}}, {{31, 4}, {31, 6}}, {{25, 4.6}, {26, 5.6}}, {{24.5, 0.5}, {25.5, -0.5}}, 
   {{31, -1}, {33, 1}}, {{31, 9}, {33, 11}}, {{38, 10.5}, {38.45, 9}}
 }
]

masukkan deskripsi gambar di sini

Ruby, jawaban golf

x,y,t,p,*m=gets.split.map &:to_f
u=q=Math.cos t
v=r=Math.sin t
loop{k=i=p
u=x+q*p
v=y+r*p
m.each_slice(4){|a,b,c,d|((a-u)*r-(b-v)*q)*((c-u)*r-(d-v)*q)>0?next: g=c-a
h=d-b
l=(h*(x-a)-g*(y-b))/(r*g-q*h)
f=(g*g+h*h)**0.5
t,k,i=g/f,h/f,l if l.abs>1e-9&&l/i<1}
i==p ?abort([u,v]*' '): p-=i
x+=q*i
y+=r*i
n=q*k-r*t
q-=2*n*k
r+=2*n*t}

Ini pada dasarnya adalah terjemahan langsung dari solusi Mathematica ke Ruby, ditambah beberapa bermain golf dan memastikan itu memenuhi kriteria I / O.


Bagaimana Anda membuat lazer melewati segitiga cermin di akhir contoh pertama?
AJMansfield

1
@AJMansfield Ada lubang kecil di segitiga, yang bisa Anda lihat di awal animasi.
Martin Ender

Alangkah baiknya jika Anda bisa menulis paragraf yang menjelaskan cara kerjanya.
JeffSB

@ JeffSB Saya sudah mendokumentasikan kode Mathematica sekarang. Versi Ruby melakukan cukup banyak hal yang persis sama dengan nama variabel yang tidak jelas dan tanpa merencanakan.
Martin Ender

@ MartinBüttner Terima kasih. Sangat menarik untuk melihat bagaimana orang lain melakukannya. Apakah Anda menyadari sebelum hal itu terjadi bahwa Anda harus mengecualikan cermin terakhir yang Anda pantulkan? Saya tidak melakukannya, tetapi saya segera mengetahuinya. Saya perhatikan jumlah yang sangat kecil dalam kode Anda dan itulah mengapa saya bertanya untuk melihat cara kerjanya.
JeffSB

18

Python 3 (421C 390C, 366C)

Gunakan builtin.complexsebagai vektor 2d. Begitu

dot = lambda a, b: (a.conjugate() * b).real
cross = lambda a, b: (a.conjugate() * b).imag

Untuk mengalahkan solusi Ruby 368C, saya telah menemukan metode yang cukup ringkas untuk menghitung titik refleksi di sepanjang cermin. Dan juga menggunakan beberapa aljabar kompleks untuk mengurangi lebih banyak karakter. Ini dapat dengan mudah ditemukan dalam kode yang tidak diserang.

Ini versi golfnya.

C=lambda a,b:(abs(a)**2/a*b).imag
J=1j
x,y,r,d,*a=map(float,input().split())
p=x+y*J
q=p+d*2.718281828459045**(r*J)
M=[]
while a:x,y,z,w,*a=a;M+=[(x+y*J,z-x+w*J-y*J)]
def T(m):x,y=m;d=C(y,r)+1e-9;t=C(y,x-p)/d;s=C(r,x-p)/d;return[1,t][(1e-6<t<1)*(0<s<1)]
while 1:
 r=q-p;m=f,g=min(M,key=T)
 if T(m)==1:break
 p+=r*T(m);q=(q/g-f/g).conjugate()*g+f
print(q.real,q.imag)

Tidak disatukan

# cross product of two vector
# abs(a)**2 / a == a.conjugate()
cross = lambda a, b: (abs(a)**2 / a * b).imag
# Parse input
x, y, angle, distance, *rest = map(float, input().split())
start = x + y * 1j
# e = 2.718281828459045
# Using formula: e**(r*j) == cos(r) + sin(r) * j
end = start + distance * 2.718281828459045 ** (angle * 1j)
mirrors = []
while rest:
    x1, y1, x2, y2, *rest = rest
    # Store end point and direction vector for this mirror
    mirrors.append((x1 + y1 * 1j, (x2 - x1) + (y2 - y1) * 1j))

def find_cross(mirror):
    # a: one end of mirror
    # s: direction vector of mirror
    a, s = mirror
    # Solve (t, r) for equation: start + t * end == a + r * s
    d = cross(s, end - start) + 1e-9 # offset hack to "avoid" dividing by zero
    t = cross(s, a - start) / d
    r = cross(end - start, a - start) / d
    return t if 1e-6 < t < 1 and 0 < r < 1 else 1

def reflect(p, mirror):
    a, s = mirror
    # Calculate reflection point:
    #  1. Project r = p - a onto a coordinate system that use s as x axis, as r1.
    #  2. Take r1's conjugate as r2.
    #  3. Recover r2 to original coordinate system as r3
    #  4. r3 + a is the final result
    #
    # So we got conjugate((p - a) * conjugate(s)) / conjugate(s) + a
    # which can be reduced to conjugate((p - a) / s) * s + a
    return ((p - a) / s).conjugate() * s + a

while 1:
    mirror = min(mirrors, key=find_cross)
    if find_cross(mirror) == 1:
        break
    start += (end - start) * find_cross(mirror)
    end = reflect(end, mirror)
print(end.real, end.imag)

Bonus: HTML, Coffeescript, Penyesuaian & Perhitungan Realtime

Ini, Anda menyeret titik akhir (atau lazer, mirros), lalu trek ditampilkan. Ini juga mendukung dua jenis input, yang dijelaskan dalam pertanyaan dan yang digunakan oleh @Martin Büttner.

Penskalaan juga disesuaikan secara otomatis.

Untuk saat ini tidak memiliki animasi. Mungkin saya akan memperbaikinya nanti. Namun, menyeret titik putih dan Anda dapat melihat jenis animasi lain. Cobalah sendiri di sini secara online , ini lucu!

Seluruh proyek dapat ditemukan di Sini

kasus 1 kasus 2

Memperbarui

Di sini saya memberikan kasus yang menarik:

0 0.6 -0.0002 500.0 0.980785280403 -0.195090322016 1.0 0.0 1.0 0.0 0.980785280403 0.195090322016 0.980785280403 0.195090322016 0.923879532511 0.382683432365 0.923879532511 0.382683432365 0.831469612303 0.55557023302 0.831469612303 0.55557023302 0.707106781187 0.707106781187 0.707106781187 0.707106781187 0.55557023302 0.831469612303 0.55557023302 0.831469612303 0.382683432365 0.923879532511 0.382683432365 0.923879532511 0.195090322016 0.980785280403 0.195090322016 0.980785280403 6.12323399574e-17 1.0 6.12323399574e-17 1.0 -0.195090322016 0.980785280403 -0.195090322016 0.980785280403 -0.382683432365 0.923879532511 -0.382683432365 0.923879532511 -0.55557023302 0.831469612303 -0.55557023302 0.831469612303 -0.707106781187 0.707106781187 -0.707106781187 0.707106781187 -0.831469612303 0.55557023302 -0.831469612303 0.55557023302 -0.923879532511 0.382683432365 -0.923879532511 0.382683432365 -0.980785280403 0.195090322016 -0.980785280403 0.195090322016 -1.0 1.22464679915e-16 -1.0 1.22464679915e-16 -0.980785280403 -0.195090322016 -0.980785280403 -0.195090322016 -0.923879532511 -0.382683432365 -0.923879532511 -0.382683432365 -0.831469612303 -0.55557023302 -0.831469612303 -0.55557023302 -0.707106781187 -0.707106781187 -0.707106781187 -0.707106781187 -0.55557023302 -0.831469612303 -0.55557023302 -0.831469612303 -0.382683432365 -0.923879532511 -0.382683432365 -0.923879532511 -0.195090322016 -0.980785280403 -0.195090322016 -0.980785280403 -1.83697019872e-16 -1.0 -1.83697019872e-16 -1.0 0.195090322016 -0.980785280403 0.195090322016 -0.980785280403 0.382683432365 -0.923879532511 0.382683432365 -0.923879532511 0.55557023302 -0.831469612303 0.55557023302 -0.831469612303 0.707106781187 -0.707106781187 0.707106781187 -0.707106781187 0.831469612303 -0.55557023302 0.831469612303 -0.55557023302 0.923879532511 -0.382683432365 0.923879532511 -0.382683432365 0.980785280403 -0.195090322016

Dan hasilnya adalah: lingkaran


-1 tidak memenuhi spesifikasi untuk input atau output.
Peter Taylor

@ Ray Sebagai jawaban bonus, ini tidak masalah. Itu hanya harus memenuhi spesifikasi untuk menjadi jawaban kode-golf.
Hobi Calvin

@PeterTaylor Meet spec sekarang.
Ray

Itu sangat keren bagaimana Anda bisa memindahkan cermin di sekitar! Milik Anda adalah suara +1 pertama saya.
JeffSB

17

JavaScript HTML, 10.543, 947 889

Saya memperbaiki bug dan memastikan hasilnya memenuhi spesifikasi pertanyaan. Halaman web di bawah ini memiliki versi golf dan juga versi bonus grafis. Saya juga memperbaiki bug yang ditunjukkan oleh @ Ray yang menyimpan 58 karakter. (Terima kasih Ray.) Anda juga dapat menjalankan kode golf di konsol JavaScript. (Sekarang saya menggunakan laser hijau 2mW.)

Kode Golf

a=prompt().split(" ").map(Number);M=Math,Mc=M.cos,Ms=M.sin,P=M.PI,T=2*P,t=true;l=new S(a[0],a[1],a[0]+a[3]*Mc(a[2]),a[1]+a[3]*Ms(a[2]));m=[];for(i=4;i<a.length;)m.push(new S(a[i++],a[i++],a[i++],a[i++]));f=-1;for(;;){var h=!t,d,x,y,n,r={};for(i=0;i<m.length;i++)if(i!=f)if(I(l,m[i],r))if(!h||r.d<d){h=t;d=r.d;x=r.x;y=r.y;n=i}if(h){l.a=x;l.b=y;l.e-=d;l.f=2*(m[f=n].f+P/2)-(l.f+P);l.c=l.a+l.e*Mc(l.f);l.d=l.b+l.e*Ms(l.f);}else break;}alert(l.c+" "+l.d);function S(a,b,c,d){this.a=a;this.b=b;this.c=c;this.d=d;this.e=D(a,b,c,d);this.f=M.atan2(d-b,c-a)}function D(a,b,c,d){return M.sqrt((a-c)*(a-c)+(b-d)*(b-d))}function I(l,m,r){A=l.a-l.c,B=l.b-l.d,C=m.a-m.c,L=m.b-m.d,E=l.a*l.d-l.b*l.c,F=m.a*m.d-m.b*m.c,G=A*L-B*C;if(!G)return!t;r.x=(E*C-A*F)/G;r.y=(E*L-B*F)/G;H=r.d=D(l.a,l.b,r.x,r.y),O=D(l.c,l.d,r.x,r.y),J=D(m.a,m.b,r.x,r.y),K=D(m.c,m.d,r.x,r.y);return(H<l.e)&&(O<l.e)&&(J<m.e)&&(K<m.e);} 

Memasukkan

1 1 7.50492 17 4.8 6.3 6.2 5.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3

Keluaran

14.743305098514739 3.759749038188634


Anda dapat mengujinya di sini: http://goo.gl/wKgIKD

masukkan deskripsi gambar di sini

Penjelasan

Kode di halaman web dikomentari. Pada dasarnya saya menghitung persimpangan laser dengan setiap cermin dengan asumsi laser dan cermin panjangnya tak terhingga. Lalu saya memeriksa apakah persimpangan berada dalam jarak terbatas cermin dan laser. Lalu saya mengambil persimpangan terdekat, memindahkan laser ke titik itu, dan melanjutkan sampai laser melewatkan semua cermin.

Proyek yang sangat menyenangkan. Terima kasih telah mengajukan pertanyaan ini!

Kode yang bisa dibaca

// a = input array
// M = Math, Mc = M.cos, Ms = M.sin, P=M.PI, T=2*P, t=true
// l = laser segment
// m = array of mirror segments
// i = loop variable
// S = segment class (this.a=x1,b=y1,c=x2,d=y2,e=len,f=theta)
// D = distance function
// I = intersect function
// f = last mirror bounced from
// h = hits a mirror
// n = next intersecing mirror
// d = distance to mirror
// x = intersection point x
// y = intersection point y
// r = mirror intersection result (d,x,y)
// b = number of bounces (FOR DEBUGGING)
// A,B,C,E,F,G,H,J,K,L,O temp variables
// s = laser segment array

// get input array
var a = prompt().split(" ").map(Number);

// some constants
var M = Math, Mc = M.cos, Ms = M.sin, P = M.PI, T = 2 * P, t = true;

// laser segment
var l = new S(a[0], a[1], a[0] + a[3] * Mc(a[2]), a[1] + a[3] * Ms(a[2])), s = [];

// mirror segments
var m = []; for (var i = 4; i < a.length;) m.push(new S(a[i++], a[i++], a[i++], a[i++]));

// bounce until miss
var f = -1, b = 0; for (; ;) {

    // best mirror found
    var h = !t, d, x, y, n, r = {};

    // loop through mirrors, skipping last one bounced from
    for (var i = 0; i < m.length; i++)
        if (i != f)
            if (I(l, m[i], r))
                if (!h || r.d < d) { h = t; d = r.d; x = r.x; y = r.y; n = i }

    // a mirror is hit
    if (h) {

        // add to draw list, inc bounces
        s.push(new S(l.a, l.b, x, y)); b++;

        // move and shorten mirror
        l.a = x; l.b = y; l.e -= d;

        // calculate next angle
        l.f = 2 * (m[f = n].f + P / 2) - (l.f + P);

        // laser end point
        l.c = l.a + l.e * Mc(l.f); l.d = l.b + l.e * Ms(l.f);

    } else {

        // add to draw list, break
        s.push(new S(l.a, l.b, l.c, l.d));
        break;
    }
}
// done, print result
alert("X = " + l.c.toFixed(6) + ",  Y = " + l.d.toFixed(6) + ",  bounces = " + b);
PlotResult();

// segment class
function S(a, b, c, d) { this.a = a; this.b = b; this.c = c; this.d = d; this.e = D(a, b, c, d); this.f = M.atan2(d - b, c - a) }

// distance function
function D(a, b, c, d) { return M.sqrt((a - c) * (a - c) + (b - d) * (b - d)) }

// intersect function
function I(l, m, r) {

    // some values
    var A = l.a - l.c, B = l.b - l.d, C = m.a - m.c, L = m.b - m.d, E = l.a * l.d - l.b * l.c, F = m.a * m.d - m.b * m.c, G = A * L - B * C;

    // test if parallel
    if (!G) return !t;

    // intersection
    r.x = (E * C - A * F) / G; r.y = (E * L - B * F) / G;

    // distances
    var H = r.d = D(l.a, l.b, r.x, r.y), O = D(l.c, l.d, r.x, r.y), J = D(m.a, m.b, r.x, r.y), K = D(m.c, m.d, r.x, r.y);

    // return true if intersection is with both segments
    return (H < l.e) && (O < l.e) && (J < m.e) && (K < m.e);
}

Cukup keren, saya suka antarmuka web. Menyenangkan masukan lain: 0 0 0.4 100 1 1 1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1.
Hobi Calvin

1
Di mana program yang sebenarnya?
Peter Taylor

Ada di halaman web di sini: goo.gl/wKgIKD
JeffSB

Jawaban di situs ini umumnya harus mencakup semua kode yang diperlukan untuk menjawab pertanyaan. Dalam kasus pertanyaan ini, itu adalah program yang membaca dari stdin dan menulis ke stdout. Selain itu, karena itu a pertanyaan kode-golf , Anda harus meminimalkan kode sebanyak mungkin: paling tidak, menghapus komentar dan spasi kosong yang tidak perlu dan menggunakan pengidentifikasi satu karakter jika memungkinkan.
Peter Taylor

@JeffSB Pengajuan ini berlaku untuk jawaban bonus, hanya saja bukan jawaban yang diterima. (Meskipun Anda mungkin ingin memasukkan semua kode Anda.)
Calvin Hobbies

6

Python - 765

Tantangan bagus. Ini adalah solusi saya yang mendapat input dari stdin dan output ke stdout. Menggunakan contoh @Martin Büttner:

python mirrors.py 1 1 70.00024158332184 95 4.8 5.3 6.2 4.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3     5 1 4 3 7 6 5 6.1 8.5 2.965 8.4 2 8.5 3.035 8.6 4 8.4 2 10.5 3 8.6 4 10.5 3

7.7094468894 3.84896396639

Ini kode golfnya:

import sys;from cmath import*
l=[float(d) for d in sys.argv[1:]];c=180/pi;p=phase;q=exp;u=len;v=range
def o(l):
 L=l[0]+1j*l[1];t=l[2]/c;D=l[3];S=[L,L+D*q(1j*t)];N=[[l[i]+1j*l[i+1],l[i+2]+1j*l[i+3]] for i in v(4,u(l),4)];a=[];b=[]
 for M in N:
  z=S[1].real-S[0].real;y=M[0].real-M[1].real;x=S[1].imag-S[0].imag;w=M[0].imag-M[1].imag;d=M[0].real-S[0].real;f=M[0].imag-S[0].imag;g=z*w-x*y;h=w/g;j=-y/g;m=-x/g;n=z/g;a.append(h*d+j*f);b.append(m*d+n*f)
 i=1;e=-1
 for k in v(u(N)):
  if 1>b[k]>0:
   if i>a[k]>1e-14:
    i=a[k];e=k
 if e>-1:
  L=S[0]+i*(S[1]-S[0]);M=N[e];l[0]=L.real;l[1]=L.imag;l[2]=c*(p(M[1]-M[0])+p(q(1j*p(M[1]-M[0]))*q(1j*-t)));l[3]=D*(1-i)
  return l
 J=S[0]+i*(S[1]-S[0]) 
 print J.real, J.imag   
 return J.real, J.imag   
while u(l)>2:
 l=o(l)

Dan di sini adalah kode tanpa tanda dengan angka bonus

cermin

import sys
from cmath import*
import matplotlib
import matplotlib.pyplot as plt
l=[float(d) for d in sys.argv[1:]]
def nextpos(l):
    L=l[0]+1j*l[1]
    t=l[2]/180*pi
    D=l[3]
    S=[L,L + D * exp(1j * t)]
    MM=[[l[i]+1j*l[i+1],l[i+2]+1j*l[i+3]] for i in range(4,len(l), 4)]    
    a=[]
    b=[]
    for M in MM:
        #determine intersections
        a11 = S[1].real-S[0].real 
        a12 = M[0].real-M[1].real
        a21 = S[1].imag-S[0].imag
        a22 = M[0].imag-M[1].imag
        b1  = M[0].real-S[0].real
        b2  = M[0].imag-S[0].imag
        deta = a11*a22-a21*a12
        ai11 = a22/deta
        ai12 = -a12/deta
        ai21 = -a21/deta
        ai22 = a11/deta        
        a.append(ai11*b1+ai12*b2)
        b.append(ai21*b1+ai22*b2)
    #determine best intersection    
    mina = 1
    bestk = -1
    for k in range(len(MM)):
        if 1>b[k]>0:
            if mina>a[k]>1e-14:
                mina=a[k]
                bestk=k
    if bestk>-1:
        #determine new input set
        L=S[0]+mina*(S[1]-S[0])
        M=MM[bestk]
        l[0]=L.real
        l[1]=L.imag
        angr=phase(exp(1j*phase(M[1]-M[0]))*exp(1j *-t))
        l[2]=180/pi*(phase(M[1]-M[0])+angr)
        l[3]=D*(1-mina)
        return l
    J= S[0]+mina*(S[1]-S[0]) 
    print J.real, J.imag   
    return J.real, J.imag   
#plotting
xL = [l[0]]
yL = [l[1]]
fig = plt.figure()
ax = fig.add_subplot(111,aspect='equal')
for i in range(4,len(l), 4):
    plt.plot([l[i],l[i+2]],[l[i+1],l[i+3]], color='b')
while len(l)>2:
    #loop until out of lasers reach
    l = nextpos(l)
    xL.append(l[0])
    yL.append(l[1])
plt.plot(xL,yL, color='r')
plt.show()

-1: tidak memenuhi spesifikasi. Output yang ditentukan adalah dua angka, bukan dua angka dan gambar.
Peter Taylor

@PeterTaylor Jadi maksud Anda stdin / stdout?
Ray

@willem Sebagai jawaban bonus ini tidak masalah. Hanya saja harus memenuhi spesifikasi untuk menjadi jawaban kode-golf.
Calvin Hobbies

Saya telah memperbarui kodenya
Willem

Catat itu sys.argvbukan stdin.
Ray

6

Matlab (388)

Merencanakan

merencanakan plot2

Konsep

Poin Refleksi

Untuk menghitung titik refleksi kita pada dasarnya harus memotong dua garis lurus. Satu dengan titik p0 dan vektor v, yang lainnya di antara dua titik p1, p2. Jadi persamaan yang harus dipecahkan adalah (s, t adalah parameter): p0 + t v = s p1 + (1-s) * p2.

Parameter s kemudian merupakan koordinat barycentric dari cermin jadi jika 0

Mirroring

Mirroring dari v cukup sederhana. Mari kita asumsikan bahwa || v || = || n || = 1 di mana n adalah vektor normal dari cermin saat ini. Maka Anda bisa menggunakan rumus v: = v-2 ** n di mana <,> adalah produk titik.

Validitas langkah

Saat menghitung cermin 'valid' terdekat, kami harus mempertimbangkan beberapa kriteria yang membuatnya valid. Pertama, titik intersepsi cermin harus terletak di antara dua titik akhir, jadi itu harus 0

Program

p = [1 1 430 17 4.8 5.3 6.2 4.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3];
hold on
grid on
for i=2:length(p)/4
    i = i*4+1-4
    p2=p(i+2:i+3)';
    p1=p(i:i+1)'
    plot([p1(1),p2(1)],[p1(2),p2(2)],'r-')
    text(p1(1),p1(2),['m' num2str((i+3)/4-1)])
end
%hold off

history = p(1:2)';


currentPosition = p(1:2)';%current
currentDirection=[cos(p(3)*pi/180);sin(p(3)*pi/180)];
while p(4)>0%as long as we do not have finished our distance
   distanceBuffer = Inf%distance next point buffer
   intersectionBuffer = NaN %next point buffer
   for i=2:length(p)/4%number of mirrors
       i = i*4+1-4 %i is now the index of the firs coordinate of the mirror
       %calculate all crosspoints
       p2=p(i+2:i+3)';
       mirrorVector = p2-p(i:i+1)';
       % idea: p0+s*currentDirection = s*p1+(1-s)*p2 solving for s,t
       r=[currentDirection,mirrorVector]\[p2-currentPosition];
       if r(1)<distanceBuffer && 0.001< r(1) && r(1)<p(4) &&0<=r(2) && r(2)<=1 %search for the nearest intersection
           distanceBuffer=r(1);
           intersectionBuffer=r(1)*currentDirection+currentPosition;
           mirrorBuffer = mirrorVector
       end
   end
   if distanceBuffer == Inf %no reachable mirror found
       endpoint = currentPosition+p(4)*currentDirection;
       counter = counter+1
       history = [history,endpoint];
       break
   else %mirroring takes place
       counter = counter+1
       history = [history,intersectionBuffer];
       currentPosition=intersectionBuffer;
       normal = [0,-1;1,0]*mirrorBuffer;%normal vector of mirror
       normal = normal/norm(normal)
       disp('arccos')
       currentDirection = currentDirection-2*(currentDirection'*normal)*normal;
       %v = v/norm(v)
       p(4)=p(4)-distanceBuffer
   end
end
history
plot(history(1,:),history(2,:))

Golf sedikit (388)

p=[1 1 430 17 4.8 5.3 6.2 4.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3];
c=p(1:2)'
b=pi/180
v=[cos(p(3)*b);sin(p(3)*b)]
f=p(4)
while f>0
q=Inf
for i=2:length(p)/4
b=p(i+2:i+3)'
u=b-p(i:i+1)'
r=[v,u]\[b-c]
s=r(1)
t=r(2)
if s<q&&0.001<s&&s<f&&0<=t&&t<=1 
q=s
n=s*v+c
m=u
end
end
if q==Inf
disp(c+f*v)
break
else 
c=n
g=[0,-1;1,0]*m
g=g/norm(g)
v=v-2*(v'*g)*g
f=f-q
end
end

Ini membawaku kembali. Pengalaman pertama saya dengan Matlab adalah memodelkan jalur laser melalui sistem cermin dan lensa saat dalam posisi penelitian selama studi sarjana saya. Grafik Anda khususnya terlihat sangat akrab. :) Pokoknya, tunggu sebentar. Kerja bagus di sini, +1.
Alex A.

Haha terima kasih! Saya bahkan tidak ingat pernah melakukan ini ketika saya melihat komentar Anda muncul =)
flawr

Haha maka komentar saya mungkin perlu Anda kembali! (Kepada saat Anda memposting ini.)
Alex A.
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.