Gdzie idzie laser?


34

Weź dwuwymiarową siatkę i narysuj na niej kilka segmentów linii, które będą reprezentować lustra. Teraz wybierz punkt, aby umieścić teoretyczny laser i kąt, aby zdefiniować kierunek, w który wskazuje. Pytanie brzmi: jeśli podążasz ścieżką wiązki laserowej na określonej odległości, w jakim punkcie współrzędnych jesteś?

Przykład:

przykład lasera

Na tym rysunku Ljest położenie lasera tjest jego kąt (mierzona od dodatniej osi X) M1, M2i M3są regulowane lusterka odcinka linii, i Ejest punktem na drodze wiązki laserowej po D = d1 + d2 + d3 + d4jednostek, począwszy od L.

Cel

Napisz najkrótszy program (w bajtach), które wyjścia Epodane L, t, D, oraz listę luster.
(Użyj http://mothereff.in/byte-counter do liczenia bajtów.)

Format wejściowy

Dane wejściowe będą pochodzić ze standardowego wejścia w formacie:

Lx Ly t D M1x1 M1y1 M1x2 M1y2 M2x1 M2y1 M2x2 M2y2 ...
  • Wszystkie wartości będą punkty odpowiadające tym regex pływających: [-+]?[0-9]*\.?[0-9]+.
  • Zawsze jest dokładnie jedna spacja między każdą liczbą.
  • Wymagane jest stosowanie cudzysłowów wokół danych wejściowych.
  • tjest w stopniach, ale niekoniecznie w [0, 360)zakresie. (Jeśli wolisz, możesz zamiast tego użyć radianów, po prostu powiedz to w swojej odpowiedzi).
  • Dmoże być ujemny, skutecznie obracając laser o 180 stopni. Dmoże również wynosić 0.
  • Może być dowolnie wiele kopii lustrzanych (w tym w ogóle żadnych).
  • Kolejność serwerów lustrzanych nie powinna mieć znaczenia.
  • Możesz założyć, że dane wejściowe będą wielokrotności 4 liczb. na przykład Lx Ly tczy Lx Ly t D M1x1są nieważne i nie będą testowane. Żadne dane wejściowe są również nieprawidłowe.

Powyższy układ można wprowadzić jako:

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

(Zwróć uwagę, że obraz został narysowany odręcznie, a te wartości są jedynie przybliżeniami. Wartości wejściowe Martina Büttnera z

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

spowoduje więcej kolizji, choć nie pasują do szkicu).

Format wyjściowy

Dane wyjściowe powinny przejść na standardowe wyjście w formacie:

Ex Ey

Są to również zmiennoprzecinkowe i mogą mieć postać wykładniczą.

Notatki

  • Lustra mogą się przecinać.
  • Obie strony lusterek są odblaskowe.
  • Wiązka może trafić wiele razy w to samo lustro.
  • Wiązka trwa wiecznie.

Niezdefiniowane przypadki

Możesz założyć, że w przypadkach, w których

  • laser rozpoczyna się na odcinku linii lustrzanej
  • promień lasera uderza w punkt końcowy lustra
  • promień lasera uderza w przecięcie dwóch luster

są niezdefiniowane i nie będą testowane. Twój program może zrobić wszystko, jeśli wystąpią, w tym zgłosić błąd.

Premia

Dla zabawy przyznam 200 punktów premiowych za najwyżej ocenione zgłoszenie, które przedstawia graficzną reprezentację problemu (możesz nawet napisać interaktywny skrypt). Te bonusy nie muszą być rozgrywane w golfa i mogą być pobłażliwe przy przetwarzaniu danych wejściowych i wyjściowych. Różnią się one od rzeczywistych zgłoszeń golfowych, ale oba powinny zostać przesłane w tej samej odpowiedzi .

Uwaga: Tylko przesłanie odpowiedzi premiowej jest w porządku, po prostu nie będziesz akceptowaną odpowiedzią. Aby zostać zaakceptowanym, musisz dokładnie przestrzegać specyfikacji wejścia / wyjścia (np. Wyjście dotyczy tylko Ex Ey, a nie obrazów) i być najkrótszym.


1
Gra w golfa i bez golfa w jednym pytaniu stanie się ogromnym bałaganem. 200 punktów premiowych jest tak atrakcyjnych, że gra w golfa staje się niewielka.
Howard,

1
@PeterTaylor Cytujesz mnie poza kontekstem. Przeczytałem dodatkowe odpowiedzi z sekcji OP, ponieważ dwa zgłoszenia są całkowicie odrębne, ale przynależą do tego samego postu, jeśli spróbuje się obu (co oznaczałoby, że wystarczy odpowiedź popcona). W każdym razie, to są twoje głosy i od ciebie zależy, jak z nich korzystasz, i prawdopodobnie w pewnym momencie dodam wersję golfową. Ale sądzę, że OP mógłby wyjaśnić, czy chciał, aby odpowiedzi tylko popcon były ważne, czy nie.
Martin Ender,

1
@ MartinBüttner, „ bonus ” oznacza „ dodatkowy, dodatkowy ”. To nie jest część głównego wyzwania. Pytanie ma tylko jeden tag - golf-golf .
Peter Taylor,

2
@PeterTaylor MartinBüttner ma rację. Odpowiedź na tylko część bonusową pytania jest w porządku. Powiedziałem, że odpowiedzi bonusowe mogą być pozbawione gry w golfa i pobłażliwe przy wejściu / wyjściu, a wszystkie obecne zgłoszenia bonusowe wyglądają dla mnie dobrze. Najkrótsza uległość, że ma dokładnie śledzić spec będzie akceptowana odpowiedź. Obecnie nie ma żadnych zgłoszeń, ale nie mam nic przeciwko.
Calvin's Hobbies

1
W takim przypadku „ bonus ” jest niewłaściwym słowem, a ty prosisz ludzi o złamanie zasad , co nie jest pomocne.
Peter Taylor

Odpowiedzi:


39

Rubinowy, 327 bajtów

(przewiń w dół)

Mathematica, dodatkowa odpowiedź

wprowadź opis zdjęcia tutaj

Teraz wybieram tylko przesyłanie graficzne. Mogę przenieść to później do Ruby i pograć w golfa, jeśli mi się spodoba.

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

Możesz to tak nazwać

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

To da ci animację w Mathematica, a także wyeksportuje GIF (ten u góry dla tego wejścia). Lekko rozszerzyłem o to przykład OP, aby uczynić go nieco bardziej interesującym.

Więcej przykładów

Rurka o lekko rozbieżnych ścianach, ale z zamkniętym końcem:

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

wprowadź opis zdjęcia tutaj

Trójkąt równoboczny i kierunek początkowy, który jest prawie równoległy do ​​jednego z boków.

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

wprowadź opis zdjęcia tutaj

Jeszcze jeden:

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

wprowadź opis zdjęcia tutaj

Ruby, gra w golfa

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}

Jest to w zasadzie bezpośrednie tłumaczenie rozwiązania Mathematica na Ruby, a także trochę gry w golfa i upewnienie się, że spełnia on kryteria wejścia / wyjścia.


Jak lazer przecina trójkąt lustrzany na końcu pierwszego przykładu?
AJMansfield

1
@AJMansfield W trójkącie znajduje się niewielka dziura, którą widać na początku animacji.
Martin Ender

Byłoby wspaniale, gdybyś mógł napisać akapit wyjaśniający, jak to działa.
JeffSB,

@JeffSB Udokumentowałem teraz kod Mathematica. Wersja Ruby robi dokładnie to samo z niejasnymi nazwami zmiennych i bez kreślenia.
Martin Ender

@ MartinBüttner Dzięki. Ciekawie jest zobaczyć, jak robią to inni ludzie. Czy zdałeś sobie sprawę, zanim zdarzyło się, że musisz wykluczyć ostatnie lustro, z którego się odbiłeś? Nie zrobiłem tego, ale wkrótce to zrozumiałem. Zauważyłem bardzo małą liczbę w twoim kodzie i dlatego poprosiłem o sprawdzenie, jak to działa.
JeffSB,

18

Python 3 (421C 390C, 366C)

Użyj builtin.complexjako wektora 2d. Więc

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

Aby pokonać rozwiązanie Ruby 368C, znalazłem dość kompaktową metodę obliczania odbicia punktu wzdłuż lustra. A także użył złożonej algebry, aby zmniejszyć liczbę znaków. Można je łatwo znaleźć w nierozwiniętym kodzie.

Oto wersja golfowa.

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)

Bez golfa

# 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, dostosowanie i kalkulacja w czasie rzeczywistym

Przeciągasz dowolne punkty końcowe (lub lazer, mirra), a następnie renderowana jest ścieżka. Obsługuje również dwa typy danych wejściowych, ten opisany w pytaniu i ten używany przez @Martin Büttner.

Skalowanie jest również dostosowywane automatycznie.

Na razie nie ma animacji. Może poprawię to później. Jednak przeciągnięcie białych punktów spowoduje wyświetlenie innego rodzaju animacji. Wypróbuj online tutaj , to zabawne!

Cały projekt można znaleźć tutaj

przypadek 1 przypadek 2

Aktualizacja

Tutaj przedstawiam interesujący przypadek:

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

Rezultat to: okrąg


-1 nie spełnia specyfikacji dla danych wejściowych lub wyjściowych.
Peter Taylor,

@Ray Jako dodatkową odpowiedź jest w porządku. Musi dokładnie spełniać specyfikację, aby stać się odpowiedzią na golfa.
Calvin's Hobbies

@PeterTaylor Poznaj specyfikację teraz.
Ray

To naprawdę fajne, jak możesz przenosić lustra! Wasz jest mój pierwszy głos +1.
JeffSB,

17

HTML JavaScript, 10,543, 947 889

Naprawiłem błąd i upewniłem się, że wyjście spełnia specyfikację pytania. Poniższa strona ma wersję golfową, a także graficzną wersję bonusową. Naprawiłem również błąd wskazany przez @Ray, który zapisał 58 znaków. (Dzięki Ray.) Możesz także uruchomić golfowy kod w konsoli JavaScript. (Teraz używam zielonego lasera 2 mW).

Kod golfowy

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

Wkład

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

Wydajność

14.743305098514739 3.759749038188634


Możesz to przetestować tutaj: http://goo.gl/wKgIKD

wprowadź opis zdjęcia tutaj

Wyjaśnienie

Kod na stronie jest komentowany. Zasadniczo obliczam przecięcie lasera z każdym lustrem, zakładając, że laser i lustra są nieskończenie długie. Następnie sprawdzam, czy przecięcie znajduje się w skończonej długości lustra i lasera. Następnie biorę najbliższe skrzyżowanie, przesuwam laser do tego punktu i kontynuuję, dopóki laser nie ominie wszystkich lusterek.

Bardzo fajny projekt. Dzięki, że zadałeś to pytanie!

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

Całkiem fajnie, uwielbiam interfejs internetowy. Innym wejście zabawa: 0 0 0.4 100 1 1 1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1.
Calvin's Hobbies

1
Gdzie jest rzeczywisty program?
Peter Taylor

Znajduje się na stronie: goo.gl/wKgIKD
JeffSB

Odpowiedzi na tej stronie powinny zasadniczo zawierać cały kod wymagany do udzielenia odpowiedzi na pytanie. W przypadku tego pytania jest to program, który czyta ze standardowego wejścia i zapisuje na standardowe wyjście. Ponadto, ponieważ jest to pytanie związane z golfem kodowym , należy zminimalizować kod tak bardzo, jak to możliwe: przynajmniej usuwając komentarze i niepotrzebne białe znaki oraz, w miarę możliwości, posługując się jednoznakowymi identyfikatorami.
Peter Taylor,

@JeffSB To zgłoszenie dotyczy odpowiedzi dodatkowej, ale nie jest to odpowiedź zaakceptowana. (Chociaż możesz dołączyć cały swój kod.)
Hobby Calvina

6

Python - 765

Dobre wyzwanie. To jest moje rozwiązanie, które pobiera dane wejściowe ze standardowego wejścia i wyjścia na standardowe wyjście. Korzystając z przykładu @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

Oto kod do gry w golfa:

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)

A oto niepoznany kod z liczbą bonusową

lustra

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: nie spełnia specyfikacji. Podane dane wyjściowe to dwie liczby, a nie dwie liczby i obraz.
Peter Taylor,

@PeterTaylor Więc masz na myśli stdin / stdout?
Ray

@willem Jako odpowiedź bonusową jest w porządku. Musi dokładnie spełniać specyfikację, aby stać się odpowiedzią na golfa.
Calvin's Hobbies

Zaktualizowałem kod
Willem,

Zauważ, że sys.argvto nie jest standardowe.
Ray

6

Matlab (388)

Wątek

wątek działka 2

Pojęcia

Punkty refleksji

Aby obliczyć punkty odbicia, musimy po prostu przeciąć dwie proste linie. Jeden z punktem p0 i wektorem v, drugi między dwoma punktami p1, p2. Zatem równanie do rozwiązania to (s, t są parametrami): p0 + t v = s p1 + (1-s) * p2.

Parametr s jest wówczas współrzędną barrocentryczną lustra, więc jeśli 0

Mirroring

Odbicie lustrzane v jest dość proste. Załóżmy, że || v || = || n || = 1 gdzie n jest normalnym wektorem bieżącego lustra. Następnie możesz po prostu użyć wzoru v: = v-2 ** n gdzie <,> jest iloczynem kropkowym.

Ważność kroku

Przy obliczaniu najbliższego „prawidłowego” lustra musimy wziąć pod uwagę niektóre kryteria, które czynią go ważnym. Najpierw punkt przecięcia lustra musi znajdować się między dwoma punktami końcowymi, więc musi wynosić 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,:))

Lekko golfowy (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

To zabiera mnie z powrotem. Moje pierwsze doświadczenie z Matlabem polegało na modelowaniu ścieżki lasera przez system luster i soczewek, gdy byłem w pozycji badawczej podczas moich studiów licencjackich. Twoja grafika w szczególności wygląda bardzo znajomo. :) W każdym razie, tylko na bok. Dobra robota tutaj, +1.
Alex A.

Haha dzięki! Po prostu nawet nie pamiętam, że to zrobiłem, kiedy zobaczyłem twój komentarz wyskakujący =)
flawr

Haha, więc mój komentarz prawdopodobnie zabierze cię z powrotem! (Do kiedy to opublikowałeś.)
Alex A.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.