Wohin geht der Laser?

34

Nehmen Sie ein zweidimensionales Gitter und zeichnen Sie eine Reihe von Liniensegmenten darauf, um Spiegel darzustellen. Wählen Sie nun einen Punkt, um einen theoretischen Laser zu platzieren, und einen Winkel, um die Richtung zu definieren, in die er zeigt. Die Frage ist: Wenn Sie dem Laserstrahlverlauf für eine bestimmte Entfernung folgen, an welchem ​​Koordinatenpunkt befinden Sie sich?

Beispiel:

Laser Beispiel

In diesem Bild List der Ort des Lasers, tist es der Winkel (von der positiven X - Achse gemessen) M1, M2und M3sind alle Liniensegment Spiegel und Eist der Punkt , auf dem Laser-Strahlengang nach D = d1 + d2 + d3 + d4Einheiten, ausgehend von L.

Tor

Schreibt das kürzeste Programm (in Bytes) , die Ausgänge Egegeben L, t, D, und eine Liste von Spiegeln.
(Verwenden Sie http://mothereff.in/byte-counter, um Bytes zu zählen.)

Eingabeformat

Die Eingabe erfolgt von stdin im Format:

Lx Ly t D M1x1 M1y1 M1x2 M1y2 M2x1 M2y1 M2x2 M2y2 ...
  • Alle Werte werden Punkte schweben diese Regex übereinstimmt , gefunden [-+]?[0-9]*\.?[0-9]+.
  • Zwischen jeder Zahl steht immer genau ein Leerzeichen.
  • Anführungszeichen für die Eingabe sind zulässig.
  • tist in Grad, aber nicht unbedingt im [0, 360)Bereich. (Wenn Sie es vorziehen, können Sie stattdessen Radianten verwenden, sagen Sie dies einfach in Ihrer Antwort.)
  • Dkann negativ sein und den Laser effektiv um 180 Grad drehen. Dkann auch 0 sein.
  • Es kann beliebig viele Spiegel geben (einschließlich überhaupt keiner).
  • Die Reihenfolge der Spiegel sollte keine Rolle spielen.
  • Sie können davon ausgehen, dass die Eingabe in Vielfachen von 4 Zahlen erfolgt. zB Lx Ly toder Lx Ly t D M1x1sind ungültig und werden nicht getestet. Keine Eingabe ist auch ungültig.

Das obige Layout könnte wie folgt eingegeben werden:

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

(Beachten Sie, dass das Bild freihändig gezeichnet wurde und diese Werte nur Näherungswerte sind. Martin Büttners Eingabewerte von

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

wird mehr Kollisionen geben, obwohl sie nicht mit der Skizze übereinstimmen.)

Ausgabeformat

Die Ausgabe sollte im Format stdout erfolgen:

Ex Ey

Dies sind auch Schwimmer und können exponentiell sein.

Anmerkungen

  • Spiegel können sich überschneiden.
  • Beide Seiten der Spiegel reflektieren.
  • Der Strahl kann mehrmals auf denselben Spiegel treffen.
  • Der Strahl geht für immer weiter.

Undefinierte Fälle

Sie können davon ausgehen, dass die Fälle, in denen

  • Der Laser startet auf einem Spiegelliniensegment
  • Der Laserstrahl trifft auf den Endpunkt eines Spiegels
  • Der Laserstrahl trifft auf den Schnittpunkt zweier Spiegel

sind undefiniert und werden nicht getestet. Ihr Programm kann in solchen Fällen alles Mögliche tun, auch einen Fehler auslösen.

Bonus

Nur zum Spaß werde ich 200 Kopfgeldpunkte für die am höchsten bewertete Einsendung vergeben, die eine grafische Darstellung des Problems ausgibt (Sie können sogar ein interaktives Skript schreiben). Diese Bonus-Einreichung muss nicht gespielt werden und kann im Hinblick auf den Umgang mit Input und Output nachlässig sein. Sie unterscheiden sich von den tatsächlichen Einsendungen, sollten jedoch beide in derselben Antwort eingereicht werden .

Hinweis: Nur die Übermittlung einer Bonusantwort ist in Ordnung. Sie werden einfach nicht als Antwort akzeptiert. Um akzeptiert zu werden, müssen Sie die Eingabe- / Ausgabespezifikation genau befolgen (z. B. Ausgabe bezieht Ex Eynur Bilder ein, nicht Bilder) und die kürzeste sein.

Calvins Hobbys
quelle
1
Es wird ein großes Durcheinander werden, wenn in einer Frage Golf- und Nicht-Golf-Einsendungen eingereicht werden. Die 200 Kopfgeldpunkte sind so attraktiv, dass das Golfen zum Nebensächlichen wird.
Howard
1
@PeterTaylor Du zitierst mich gut aus dem Zusammenhang. Ich habe den Abschnitt Bonusantworten des OP gelesen, da die beiden Beiträge völlig verschieden sind, aber zum selben Beitrag gehören, wenn beide versucht werden (was bedeuten würde, dass auch nur die Popcon-Antwort in Ordnung wäre). Wie auch immer, es sind Ihre Stimmen und es liegt an Ihnen, wie Sie sie verwenden, und ich werde wahrscheinlich sowieso irgendwann eine Golfversion hinzufügen. Aber ich denke, das OP könnte klären, ob er beabsichtigte, Popcon-only-Antworten als gültig zu betrachten oder nicht.
Martin Ender
1
@ MartinBüttner, " Bonus " bedeutet " zusätzlich, extra ". Es ist nicht Teil der Hauptherausforderung. Und die Frage hat nur einen Tag, Code-Golf .
Peter Taylor
2
@PeterTaylor MartinBüttner ist richtig. Die Beantwortung nur des Bonus-Teils der Frage ist in Ordnung. Ich sagte, die Bonusantworten können mit dem I / O nicht golfen und nachsichtig sein, und alle aktuellen Bonusbeiträge sehen für mich gut aus. Die kürzeste Einreichung, die genau der Spezifikation folgt, ist die akzeptierte Antwort. Derzeit gibt es keine Einreichungen, aber das ist okay für mich.
Calvins Hobbys
1
In diesem Fall ist " Bonus " das falsche Wort und Sie bitten die Leute, die Regeln zu brechen , was nicht hilfreich ist.
Peter Taylor

Antworten:

39

Ruby, 327 Bytes

(nach unten scrollen)

Mathematica, Bonusantwort

Bildbeschreibung hier eingeben

Ich werde jetzt nur die grafische Einreichung vornehmen. Ich könnte das später auf Ruby portieren und Golf spielen, wenn ich Lust dazu habe.

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

Du kannst es so nennen

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

Dadurch erhalten Sie eine Animation in Mathematica und können auch ein GIF exportieren (das für diese Eingabe oben stehende). Ich habe das OPs-Beispiel dazu etwas erweitert, um es ein bisschen interessanter zu machen.

Mehr Beispiele

Eine Röhre mit leicht divergierenden Wänden, aber einem geschlossenen Ende:

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

Bildbeschreibung hier eingeben

Ein gleichseitiges Dreieck und eine Anfangsrichtung, die fast parallel zu einer der Seiten verläuft.

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

Bildbeschreibung hier eingeben

Einer noch:

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

Bildbeschreibung hier eingeben

Ruby, Golf Antwort

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}

Dies ist im Grunde eine direkte Übersetzung der Mathematica-Lösung nach Ruby, plus ein wenig Golfspielen und sicherstellen, dass sie die I / O-Kriterien erfüllt.

Martin Ender
quelle
Wie soll der Lazer das Spiegeldreieck am Ende des ersten Beispiels überqueren?
AJMansfield
1
@AJMansfield Im Dreieck befindet sich ein kleines Loch, das Sie am Anfang der Animation sehen können.
Martin Ender
Es wäre großartig, wenn Sie einen Absatz schreiben könnten, der erklärt, wie es funktioniert.
JeffSB
@ JeffSB Ich habe den Mathematica-Code jetzt dokumentiert. Die Ruby-Version macht genau dasselbe mit undurchsichtigen Variablennamen und ohne Plotten.
Martin Ender
@ MartinBüttner Danke. Es ist interessant zu sehen, wie andere es machen. Wussten Sie, bevor es passiert ist, dass Sie den letzten Spiegel, von dem Sie abprallten, ausschließen müssen? Ich habe es nicht getan, aber ich habe es früh genug herausgefunden. Ich habe die sehr kleine Zahl in Ihrem Code bemerkt und darum habe ich gefragt, wie es funktioniert.
JeffSB
18

Python 3 (421C 390C366C)

Verwenden Sie builtin.complexals 2D-Vektor. So

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

Um die 368C Ruby-Lösung zu übertreffen, habe ich eine recht kompakte Methode zur Berechnung der Punktreflexion entlang eines Spiegels gefunden. Und auch einige komplexe Algebra verwendet, um mehr Zeichen zu reduzieren. Diese sind leicht im Code zu finden.

Hier ist die Golfversion.

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, Echtzeitanpassung & Berechnung

Das heißt, Sie ziehen beliebige Endpunkte (oder Lazer, Mirros), dann wird die Spur gerendert. Es werden auch zwei Arten von Eingaben unterstützt, die in der Frage beschriebene und die von @Martin Büttner verwendete.

Die Skalierung wird ebenfalls automatisch angepasst.

Im Moment hat es keine Animation. Vielleicht verbessere ich es später. Wenn Sie jedoch die weißen Punkte ziehen, wird eine andere Art von Animation angezeigt. Probieren Sie es online hier selbst, es ist lustig!

Das gesamte Projekt finden Sie hier

Fall 1 Fall 2

Aktualisieren

Hier stelle ich einen interessanten Fall vor:

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

Und das Ergebnis ist: Kreis

Strahl
quelle
-1 entspricht nicht der Spezifikation für Eingabe oder Ausgabe.
Peter Taylor
@Ray Als Bonusantwort ist dies in Ordnung. Es muss nur genau der Spezifikation entsprechen, um die Code-Golf-Antwort zu werden.
Calvins Hobbys
@PeterTaylor Jetzt Spezifikation erfüllen.
Ray
Das ist echt cool, wie man die Spiegel bewegen kann! Ihre ist meine erste +1 Stimme.
JeffSB
17

HTML JavaScript, 10,543, 947 889

Ich habe einen Fehler behoben und sichergestellt, dass die Ausgabe der Fragenspezifikation entspricht. Die Webseite unten hat die Golfversion und auch die grafische Bonusversion. Ich habe auch einen Fehler behoben, auf den @Ray hingewiesen hat und der 58 Zeichen sparte. (Danke Ray.) Sie können den Code auch in einer JavaScript-Konsole ausführen. (Jetzt benutze ich einen 2mW grünen Laser.)

Golf Code

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

Eingang

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

Ausgabe

14.743305098514739 3.759749038188634


Sie können es hier testen: http://goo.gl/wKgIKD

Bildbeschreibung hier eingeben

Erläuterung

Der Code auf der Webseite ist kommentiert. Grundsätzlich berechne ich den Schnittpunkt des Lasers mit jedem Spiegel unter der Annahme, dass der Laser und die Spiegel unendlich lang sind. Dann überprüfe ich, ob der Schnittpunkt innerhalb der endlichen Länge des Spiegels und des Lasers liegt. Dann nehme ich die nächstgelegene Kreuzung, bewege den Laser zu diesem Punkt und fahre fort, bis der Laser alle Spiegel verfehlt.

Sehr lustiges Projekt. Danke, dass Sie diese Frage gestellt haben!

Lesbarer Code

// 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);
}
JeffSB
quelle
Ziemlich cool, ich liebe das Webinterface. Ein weiterer Spaß - Eingang: 0 0 0.4 100 1 1 1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1.
Calvins Hobbys
1
Wo ist das aktuelle Programm?
Peter Taylor
Es ist in der Webseite hier: goo.gl/wKgIKD
JeffSB
Die Antworten auf dieser Website sollten im Allgemeinen den gesamten Code enthalten, der zur Beantwortung der Frage erforderlich ist. Bei dieser Frage handelt es sich um ein Programm, das von stdin liest und in stdout schreibt. Da es sich um eine Code-Golf- Frage handelt, sollten Sie den Code außerdem so weit wie möglich minimieren: Entfernen Sie zumindest Kommentare und unnötige Leerzeichen, und verwenden Sie nach Möglichkeit Ein-Zeichen-Bezeichner.
Peter Taylor
@JeffSB Diese Einsendung gilt für die Bonusantwort, nur nicht für die akzeptierte Antwort. (Obwohl Sie vielleicht Ihren gesamten Code einfügen möchten.)
Calvins Hobbys
6

Python - 765

Gute Herausforderung. Dies ist meine Lösung, die Eingaben von stdin und Ausgaben von stdout erhält. Am Beispiel von @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

Hier ist der Golfcode:

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)

Und hier ist der ungolfed Code mit einer Bonuszahl

Spiegel

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()
Willem
quelle
-1: entspricht nicht der Spezifikation Die angegebene Ausgabe besteht aus zwei Zahlen, nicht zwei Zahlen und einem Bild.
Peter Taylor
@ PeterTaylor Also meinst du stdin / stdout?
Ray
@willem Als Bonusantwort ist dies in Ordnung. Es muss nur genau der Spezifikation entsprechen, um die Code-Golf-Antwort zu werden.
Calvins Hobbys
Ich habe den Code aktualisiert
Willem
Beachten Sie, dass sys.argvnicht stdin ist.
Ray
6

Matlab (388)

Handlung

Handlung plot2

Konzepte

Reflexionspunkte

Für die Berechnung der Reflexionspunkte müssen grundsätzlich zwei Geraden geschnitten werden. Einer mit dem Punkt p0 und dem Vektor v, der andere zwischen den beiden Punkten p1, p2. Die zu lösende Gleichung lautet also (s, t sind Parameter): p0 + t v = s p1 + (1-s) * p2.

Der Parameter s ist dann eine Schwerpunktskoordinate des Spiegels also wenn 0

Spiegeln

Das Spiegeln von v ist ziemlich einfach. Nehmen wir an, dass || v || = || n || = 1 wobei n der Normalenvektor des Stromspiegels ist. Dann können Sie einfach die Formel v: = v-2 ** n verwenden, wobei <,> das Skalarprodukt ist.

Gültigkeit des Schritts

Bei der Berechnung des nächsten "gültigen" Spiegels müssen einige Kriterien berücksichtigt werden, die ihn gültig machen. Zuerst muss der Abfangpunkt des Spiegels zwischen den beiden Endpunkten liegen, also muss er 0 sein

Programm

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

Leicht golfen (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
fehlerhaft
quelle
Das bringt mich zurück. Meine erste Erfahrung mit Matlab bestand darin, den Weg eines Lasers durch ein System von Spiegeln und Linsen zu modellieren, während ich während meines Studiums in einer Forschungsposition war. Vor allem Ihre Grafiken kommen mir bekannt vor. :) Wie auch immer, nur zur Seite. Gute Arbeit hier, +1.
Alex A.
Haha danke! Ich erinnere mich einfach nicht einmal, dass ich das getan habe, als ich Ihren Kommentar auftauchen sah =)
flawr
Haha, dann bringt dich mein Kommentar wahrscheinlich zurück! (Bis wann Sie dies gepostet haben.)
Alex A.