Lazer nereye gidiyor?


34

2 boyutlu bir ızgara alın ve aynaları temsil etmek için üzerine birkaç çizgi parçası çizin. Şimdi teorik bir lazer yerleştirmek için bir nokta ve işaret ettiği yönü tanımlamak için bir açı seçin. Soru şu: eğer belirli bir mesafe için lazer ışını yolunu izlerseniz, hangi koordinat noktasındasınız?

Örnek:

lazer örneği

Bu resimde, Llazer konumudur t(pozitif X ekseninden ölçülen) 's açısıdır M1, M2ve M3tüm çizgi parçası aynası ve Esonra lazer ışını yolu ile nokta D = d1 + d2 + d3 + d4başlayarak birimleri L.

Hedef

(Bayt) kısa programı çıktıları Yazın Everilen L, t, Daynalar ve bir liste.
( Baytları saymak için http://mothereff.in/byte-counter kullanın .)

Giriş biçimi

Girdi biçiminde stdin'den gelecek:

Lx Ly t D M1x1 M1y1 M1x2 M1y2 M2x1 M2y1 M2x2 M2y2 ...
  • Tüm değerler bu regex eşleşen noktaları yüzen olacaktır: [-+]?[0-9]*\.?[0-9]+.
  • Her sayı arasında her zaman tam olarak bir boşluk vardır.
  • Girişin çevresinde tırnak istenmesine izin verilir.
  • tderece cinsindendir, ancak [0, 360)aralıkta olması gerekmez . (İsterseniz radyan kullanabilirsiniz, cevabınıza sadece söyleyin.)
  • Dlazeri 180 derece döndürerek negatif olabilir. Dayrıca 0 olabilir.
  • Keyfi olarak birçok ayna olabilir (hiç yok dahil).
  • Aynaların sırası önemli olmamalıdır.
  • Girişin 4 sayının katları olarak geleceğini varsayabilirsiniz. örneğin Lx Ly tveya Lx Ly t D M1x1geçersiz ve test edilmeyecek. Hiçbir giriş de geçersiz.

Yukarıdaki düzen şu şekilde girilebilir:

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

(Görüntünün serbest çizilmiş olduğunu ve bu değerlerin sadece yaklaşık olduğunu unutmayın. Martin Büttner'ın giriş değerleri.

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

Eskizle eşleşmemesine rağmen daha fazla çarpışma yapacak.)

Çıkış biçimi

Çıktı şu şekilde stdout'a gitmeli:

Ex Ey

Bunlar ayrıca yüzer ve üstel formda olabilir.

notlar

  • Aynalar birbiriyle kesişebilir.
  • Aynaların her iki tarafı da yansıtıcıdır.
  • Işın birçok kez aynı aynaya çarpabilir.
  • Işın sonsuza dek sürüyor.

Tanımsız Vakalar

Durumlarda olduğu varsayılabilir.

  • lazer, ayna çizgisinde başlar
  • lazer ışını aynanın bitiş noktasına çarpıyor
  • lazer ışını iki ayna arasındaki kesişime çarpar

tanımsız ve test edilmeyecek. Hata oluşursa, programınız bu gerçekleşirse her şeyi yapabilir.

Bonus

Sadece eğlence için, sorunun grafiksel sunumunu çıkaran en yüksek oy alan gönderiye 200 ödül puanı vereceğim (etkileşimli bir senaryo bile yazabilirsiniz). Bu bonus teslimi golf oynamak zorunda değildir ve girdi ve çıktıların nasıl ele alındığına dair esnek olabilir. Bunlar gerçek golf oynamalarından farklıdır, ancak her ikisi de aynı cevapta sunulmalıdır .

Not: Sadece bir bonus cevap göndermek iyidir, sadece kabul edilen cevap siz de olmaz. Kabul edilmek için, girdi / çıktı spesifikasyonunu tam olarak takip etmelisiniz (örneğin, çıktı yalnızca Ex Eygörüntüleri içermelidir ) ve en kısa olmalıdır.


1
Tek bir soruda golf oynayan ve dallanmamış gönderiler çok büyük bir karışıklık olacak. 200 ödül puanı o kadar çekici ki, golf sporu en küçük nokta haline geliyor.
Howard,

1
@PeterTaylor Beni bağlam dışında çok iyi alıntı yapıyorsun. OP'nin bölüm bonusu cevaplarını, iki gönderimin tamamen farklı olduğu ancak ikisinin de denenmesi durumunda aynı gönderiye ait olması nedeniyle okudum (bu sadece popcon cevabının da iyi olacağı anlamına gelir). Her neyse, onlar senin oyların ve onları nasıl kullandığın sana kalmış ve muhtemelen bir noktada bir golf oyunu ekleyeceğim. Ancak, OP'nin sadece popcon-sadece cevapların geçerli olup olmadığını amaçladığını netleştirebileceğini düşünüyorum.
Martin Ender

1
@ MartinBüttner, " bonus " " ek, fazla " anlamına gelir . Bu ana mücadelenin bir parçası değil. Ve sorunun sadece bir etiketi var, kod-golf .
Peter Taylor

2
@ PeterTaylor MartinBüttner haklı. Sorunun sadece bonus kısmını cevaplamak iyi. Bonus cevaplarının golf oynayamayacağını ve giriş / çıkış ile hafifletilebileceğini söyledim ve mevcut tüm bonus başvuruları bana iyi geldi. En kısa teslim etmez tam teknik özelliğinin izlenmesinin kabul cevap olacaktır. Şu anda hiçbir gönderi yok, ama bu benimle tamam.
Calvin'in Hobileri

1
Bu durumda " bonus " kullanılacak yanlış kelimedir ve sizden insanlardan kuralları çiğnemelerini istersiniz , bu yararlı değildir.
Peter Taylor

Yanıtlar:


39

Ruby, 327 bayt

(aşağıya doğru kaydırın)

Mathematica, bonus cevabı

görüntü tanımını buraya girin

Şimdilik sadece grafik gönderim için gidiyorum. Bunu daha sonra Ruby'ye taşıyabilir ve istersem golf oynayabilirim.

(* 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
   ];

Gibi arayabilirsin

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

Bu size Mathematica'da bir animasyon verecek ve aynı zamanda bir GIF (bu girdi için en üstte olanı) verecektir. Bunun için biraz daha ilginç hale getirmek için OP'lerin örneğini biraz uzattım.

Daha fazla örnek

Hafifçe değişen duvarları olan ancak kapalı uçlu bir tüp:

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

görüntü tanımını buraya girin

Bir eşkenar üçgen ve neredeyse bir tarafa paralel olan bir başlangıç ​​yönü.

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

görüntü tanımını buraya girin

Bir tane daha:

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

görüntü tanımını buraya girin

Yakut, golf cevap

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}

Bu, temel olarak, Mathematica çözümünün Ruby'ye doğrudan çevirisidir, ayrıca golf oynamak ve G / Ç kriterlerini karşıladığından emin olmaktır.


İlk örneğin sonunda lazerin ayna üçgenini nasıl geçtiniz?
AJMansfield

1
@AJMansfield Üçgenin içinde, animasyonun başında görebileceğiniz küçük bir delik vardır.
Martin Ender

Nasıl çalıştığını açıklayan bir paragraf yazarsanız çok iyi olurdu.
JeffSB

@JeffSB Şimdi Mathematica kodunu belgeledim. Ruby sürümü hemen hemen aynı şeyi karanlık değişken isimleriyle ve çizim yapmadan yapıyor.
Martin Ender

@ MartinBüttner Teşekkürler. Başkalarının nasıl yaptığını görmek ilginç. O olmadan önce, sıçradığınız son aynayı dışlamak zorunda olduğunuzu fark ettiniz mi? Yapmadım, ama yakında anladım. Kodunuzdaki çok küçük sayıyı fark ettim ve bu yüzden nasıl çalıştığını görmek istedim.
JeffSB

18

Piton 3 (421C 390C, 366C)

builtin.complex2d vektör olarak kullanın . Yani

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

368C Ruby çözümünü yenmek için, ayna boyunca nokta yansımasını hesaplamak için oldukça kompakt bir yöntem buldum. Ayrıca daha fazla karakteri azaltmak için bazı karmaşık cebirler kullandı. Bunlar ungolfed kodunda kolayca bulunabilir.

İşte golf versiyonu.

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)

Ungolfed

# 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, Gerçek Zamanlı Ayarlama ve Hesaplama

Bu, herhangi bir bitiş noktasını (veya lazer, mirros) sürüklemeniz, ardından parça oluşturulmuştur. Ayrıca, soruda açıklanan ve @ Martin Büttner tarafından kullanılan iki tür girişi de destekler.

Ölçeklendirme de otomatik olarak yapılır.

Şimdilik animasyonu yok. Belki daha sonra geliştiririm. Ancak, beyaz noktaları sürükleyerek başka bir animasyon türü görebilirsiniz. Çevrimiçi olarak kendiniz deneyin , komik!

Tüm proje burada bulunabilir

dava 1 durum 2

Güncelleştirme

İşte ilginç bir durum sunuyorum:

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

Ve sonuç: daire


-1, giriş veya çıkış için spec ile uyuşmuyor.
Peter Taylor

@Ray Bonus cevabı olarak bu iyi. Sadece kod-golf cevabı olabilmek için teknik özelliklere uygun olmalıdır.
Calvin'in Hobileri

@PeterTaylor Şimdi spec ile tanışın.
Ray

Aynaları nasıl hareket ettirebileceğiniz gerçekten harika! Seninki benim ilk + 1 oyum.
JeffSB

17

HTML JavaScript, 10543, 947 889

Bir hatayı düzelttim ve çıktının soru spesifikasyonunu karşıladığından emin oldum. Aşağıdaki web sayfası golf versiyonuna ve grafiksel bonus versiyonuna sahiptir. Ayrıca @Ray'ın belirttiği, 58 karakter kaydeden bir hatayı düzelttim. (Thanks Ray.) Golf kodunu bir JavaScript konsolunda da çalıştırabilirsiniz. (Şimdi 2mW yeşil lazer kullanıyorum.)

Golf Kodu

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

Giriş

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

Çıktı

14.743305098514739 3.759749038188634


Burada test edebilirsiniz: http://goo.gl/wKgIKD

görüntü tanımını buraya girin

açıklama

Web sayfasındaki kod yorumlanır. Temel olarak, lazerin ve aynaların sonsuz uzun olduğunu farz ederek her aynayla lazerin kesişimini hesaplarım. Sonra kesişimin aynanın ve lazerin sonlu uzunluğu içinde olup olmadığını kontrol ederim. Sonra en yakın kavşağı alıyorum, lazeri o noktaya getiriyorum ve lazer tüm aynaları kaçırana kadar devam ediyorum.

Çok eğlenceli bir proje. Bu soruyu sorduğun için teşekkürler!

Okunabilir kod

// 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);
}

Oldukça havalı, web arayüzünü seviyorum. Başka eğlenceli girişi: 0 0 0.4 100 1 1 1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1.
Calvin'in Hobileri

1
Asıl program nerede?
Peter Taylor

Buradaki web sayfasında: goo.gl/wKgIKD
JeffSB

Bu sitedeki cevaplar genellikle soruyu cevaplamak için gereken tüm kodları içermelidir. Bu soruda, stdin'den okuyan ve stdout'a yazan bir program bu. Buna ek olarak, bir kod golf sorusu olduğundan, kodu olabildiğince simge durumuna küçültmelisiniz: en azından yorumları ve gereksiz boşlukları kaldırmak ve mümkünse tek karakterli tanımlayıcıları kullanmak.
Peter Taylor

@JeffSB Bu başvuru bonus cevap için geçerlidir, kabul edilen cevap için geçerli değildir. (Tüm kodunuzu eklemek isteyebilirsiniz.)
Calvin'in Hobileri

6

Python - 765

İyi bir meydan okuma. Bu benim stdin'den girdi alan ve stdout'a çıktıran benim çözümüm. @Martin Büttner örneğini kullanmak:

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

İşte golf kodu:

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)

Ve işte ikramiye rakamlı ungolfed kod

aynalar

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: spec ile uyuşmuyor. Belirtilen çıktı iki sayı değil, iki sayıdır ve bir görüntüdür.
Peter Taylor

@PeterTaylor Yani, stdin / stdout?
Ray,

@willem Bonus cevap olarak bu iyi. Sadece kod-golf cevabı olabilmek için teknik özelliklere uygun olmalıdır.
Calvin'in Hobileri

Kodu güncelledik
Willem

Bunun sys.argvstdin olmadığını unutmayın .
Ray,

6

Matlab (388)

Arsa

arsa plot2

Kavramlar

Yansıtma Noktaları

Yansıma noktalarını hesaplamak için temel olarak iki düz çizgiyi kesmek zorundayız. Biri p0 ve vektör v ile, diğeri iki p1, p2 noktaları arasındadır. Yani çözülecek denklem (s, t parametrelerdir): p0 + t v = s p1 + (1-s) * p2.

S parametresi daha sonra aynanın barysantrik koordinatıdır, yani 0

Yansıtma

V yansıması oldukça basittir. Bunu varsayalım. || v || = || n || = 1, burada n, geçerli aynanın normal vektörüdür. Sonra sadece v: = v-2 ** n formülünü kullanabilirsiniz. Burada <,> nokta üründür.

Adımın geçerliliği

En yakın 'geçerli' aynayı hesaplarken, onu geçerli kılan bazı kriterleri göz önünde bulundurmalıyız. İlk önce aynanın kesişme noktası iki uç nokta arasında olmalıdır, bu nedenle 0 olmalıdır

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,:))

Hafif golf (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

Bu beni geri alır. Matlab ile ilk deneyimim, lisans eğitimim sırasında araştırma pozisyonunda iken bir lazer yolunu ayna ve mercek sistemi aracılığıyla modellemekti. Özellikle grafikleriniz çok tanıdık geliyor. :) Neyse, sadece bir kenara. Burada iyi iş çıkardın, +1.
Alex A.

Haha teşekkürler! Sadece yorumunuzu açtığımda bunu yaptığımı bile hatırlamadım =)
flawr

Haha sonra benim comment muhtemelen alır seni geri! (Bunu gönderdiğiniz zamana kadar.)
Alex A.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.