Où va le laser?

34

Prenez une grille à 2 dimensions et dessinez dessus un certain nombre de segments de lignes pour représenter les miroirs. Maintenant, choisissez un point pour placer un laser théorique et un angle pour définir la direction vers laquelle il pointe. La question qui se pose est la suivante: si vous suivez le trajet du faisceau laser sur une certaine distance, à quel point de coordonnées vous trouvez-vous?

Exemple:

exemple laser

Dans cette image, Lest l'emplacement du laser, test l' angle de ce (mesuré à partir de l'axe X positif), M1, M2et M3sont tous les miroirs de segment de ligne, et Eest le point situé sur le trajet du faisceau laser après D = d1 + d2 + d3 + d4unités, à partir de L.

Objectif

Ecrire le programme le plus court (en octets) que les résultats Edonnés L, t, Det une liste de miroirs.
(Utilisez http://mothereff.in/byte-counter pour compter les octets.)

Format d'entrée

L'entrée viendra de stdin au format:

Lx Ly t D M1x1 M1y1 M1x2 M1y2 M2x1 M2y1 M2x2 M2y2 ...
  • Toutes les valeurs seront flottants pour cette regex: [-+]?[0-9]*\.?[0-9]+.
  • Il y a toujours exactement un espace entre chaque numéro.
  • Exiger des guillemets autour de l'entrée est autorisé.
  • test en degrés, mais pas nécessairement dans la [0, 360)gamme. (Si vous préférez, vous pouvez utiliser des radians, dites simplement cela dans votre réponse.)
  • Dpeut être négatif, faisant effectivement pivoter le laser à 180 degrés. Dpeut aussi être 0.
  • Il peut y avoir arbitrairement beaucoup de miroirs (dont aucun du tout).
  • L'ordre des miroirs ne devrait pas avoir d'importance.
  • Vous pouvez supposer que l'entrée viendra en multiples de 4 chiffres. par exemple Lx Ly tou Lx Ly t D M1x1sont invalides et ne seront pas testés. Aucune entrée n'est également invalide.

La mise en page ci-dessus peut être entrée en tant que:

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

(Notez que l'image a été dessinée à main levée et que ces valeurs ne sont que des approximations. Les valeurs d'entrée de Martin Büttner:

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

donnera plus de collisions bien qu'elles ne correspondent pas à l'esquisse.)

Format de sortie

La sortie devrait aller à stdout au format:

Ex Ey

Ce sont aussi des flotteurs et peuvent être sous forme exponentielle.

Remarques

  • Les miroirs peuvent se croiser.
  • Les deux côtés des miroirs sont réfléchissants.
  • Le faisceau peut heurter plusieurs fois le même miroir.
  • Le faisceau continue pour toujours.

Cas non définis

Vous pouvez supposer que les cas où

  • le laser démarre sur un segment de droite en miroir
  • le faisceau laser atteint l'extrémité d'un miroir
  • le faisceau laser frappe l'intersection entre deux miroirs

ne sont pas définis et ne seront pas testés. Votre programme peut faire n'importe quoi si cela se produit, y compris une erreur.

Prime

Juste pour le plaisir, je vais attribuer 200 points de prime à la soumission la plus votée qui produit une représentation graphique du problème (vous pouvez même écrire un script interactif). Ces bonus ne doivent pas nécessairement être joués et peuvent être indulgents avec la manière dont les entrées et les sorties sont gérées. Ils sont distincts des soumissions golfées mais les deux doivent être soumis dans la même réponse .

Remarque: soumettre uniquement une réponse en prime suffit, vous ne serez pas la réponse acceptée. Pour être accepté, vous devez suivre exactement les spécifications d'entrée / sortie (par exemple, la sortie implique uniquement Ex Ey, pas les images), et être le plus court.

Les passe-temps de Calvin
la source
1
Avoir des soumissions de golf et de non-golf en une question risque de devenir un énorme gâchis. Les 200 points de prime sont si attrayants que le golf devient le point mineur.
Howard
1
@PeterTaylor Vous me citez bien hors contexte. J'ai lu les réponses aux bonus de la section OP, les deux soumissions étant complètement distinctes mais appartenant au même poste si toutes les deux sont tentées (ce qui signifierait que la réponse popcon conviendrait également). De toute façon, ce sont vos votes et c'est à vous de choisir comment vous les utilisez, et j'ajouterai probablement une version jouée au golf à un moment donné. Mais je suppose que le PO pourrait préciser s’il avait l’intention de valider ou non les réponses réservées à Popcon.
Martin Ender
1
@ MartinBüttner, " bonus " signifie " supplémentaire, supplémentaire ". Cela ne fait pas partie du défi principal. Et la question n'a qu'un tag, code-golf .
Peter Taylor
2
@PeterTaylor MartinBüttner a raison. Répondre à la partie bonus de la question ne pose pas de problème. J'ai dit que les réponses de bonus peuvent être non-golfées et indulgentes avec l'entrée / sortie, et que toutes les soumissions de bonus actuelles me semblent bonnes. La soumission la plus courte qui suit exactement les spécifications sera la réponse acceptée. Actuellement, aucune soumission ne le fait, mais ça me va.
Les passe-temps de Calvin
1
Dans ce cas, le mot « bonus » n'est pas le bon mot et vous demandez aux gens d' enfreindre les règles , ce qui n'est pas utile.
Peter Taylor

Réponses:

39

Ruby, 327 octets

(défiler vers le bas)

Mathematica, réponse bonus

entrez la description de l'image ici

Je vais seulement pour la soumission graphique pour le moment. Je pourrais porter ça à Ruby plus tard et jouer au golf si j'en ai envie.

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

Vous pouvez l'appeler comme

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

Cela vous donnera une animation dans Mathematica et exportera également un fichier GIF (celui du haut pour cette entrée). J'ai légèrement développé l'exemple des PO pour cela, pour le rendre un peu plus intéressant.

Plus d'exemples

Un tube avec des parois légèrement divergentes mais une extrémité fermée:

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

entrez la description de l'image ici

Un triangle équilatéral et une direction initiale presque parallèle à l’un des côtés.

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

entrez la description de l'image ici

Un de plus:

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

entrez la description de l'image ici

Ruby, réponse au 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}

Il s’agit essentiellement d’une traduction directe de la solution Mathematica en Ruby, ainsi que de la pratique du golf et de la vérification du respect des critères d’entrée / sortie.

Martin Ender
la source
Comment faites-vous que le lazer croise le triangle miroir à la fin du premier exemple?
AJMansfield
1
@AJMansfield Il y a un petit trou dans le triangle, que vous pouvez voir au début de l'animation.
Martin Ender
Ce serait bien si vous pouviez écrire un paragraphe expliquant comment cela fonctionne.
JeffSB
@JeffSB J'ai documenté le code Mathematica maintenant. La version Ruby fait à peu près exactement la même chose avec des noms de variables obscures et sans tracé.
Martin Ender
@ MartinBüttner Merci. C'est intéressant de voir comment les autres le font. Saviez-vous qu'avant que cela se produise, vous devez exclure le dernier miroir duquel vous avez sauté? Je ne l'ai pas fait, mais je l'ai compris assez tôt. J'ai remarqué le très petit nombre dans votre code et c'est pourquoi j'ai demandé à voir comment cela fonctionne.
JeffSB
18

Python 3 (421C 390C, 366C)

Utilisez builtin.complexcomme vecteur 2D. Alors

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

Afin de battre la solution 368C Ruby, j'ai trouvé une méthode assez compacte pour calculer la réflexion ponctuelle le long d'un miroir. Et aussi utilisé une algèbre complexe pour réduire plus de caractères. Ceux-ci peuvent être facilement trouvés dans le code non-golfé.

Voici la version golfée.

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, ajustement et calcul en temps réel

C’est-à-dire que vous faites glisser les points de fin (ou les miroirs), puis la piste est rendue. Il prend également en charge deux types d'entrées, celle décrite dans la question et celle utilisée par @Martin Büttner.

La mise à l'échelle est également ajustée automatiquement.

Pour l'instant, il n'y a pas d'animation. Peut-être que je l'améliorerai plus tard. Cependant, en faisant glisser les points blancs, vous pouvez voir un autre type d'animation. Essayez-le en ligne ici vous-même, c'est drôle!

L'ensemble du projet peut être trouvé ici

cas 1 cas 2

Mise à jour

Ici, je fournis un cas intéressant:

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

Et le résultat est: cercle

Rayon
la source
-1 ne correspond pas aux spécifications pour l'entrée ou la sortie.
Peter Taylor
@Ray En réponse bonus, c'est très bien. Il ne faut que répondre exactement à la spécification pour devenir la réponse code-golf.
Les passe-temps de Calvin
@PeterTaylor Rencontrez spec maintenant.
Ray
C'est vraiment cool de pouvoir déplacer les miroirs! Le vôtre est mon premier +1 vote.
JeffSB
17

HTML JavaScript, 10 543, 947 889

J'ai corrigé un bogue et je me suis assuré que la sortie répond à la spécification de la question. La page Web ci-dessous contient la version avec golf et la version avec bonus graphique. J'ai aussi corrigé un bug signalé par @Ray qui enregistrait 58 caractères. (Merci Ray.) Vous pouvez également exécuter le code de jeu dans une console JavaScript. (J'utilise maintenant un laser vert de 2 mW.)

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

Contribution

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

Sortie

14.743305098514739 3.759749038188634


Vous pouvez le tester ici: http://goo.gl/wKgIKD

entrez la description de l'image ici

Explication

Le code de la page Web est commenté. Fondamentalement, je calcule l'intersection du laser avec chaque miroir en supposant que le laser et les miroirs sont infiniment longs. Ensuite, je vérifie si l'intersection est comprise dans la longueur finie du miroir et du laser. Ensuite, je prends l'intersection la plus proche, déplace le laser à ce point et continue jusqu'à ce que le laser manque tous les miroirs.

Projet très amusant. Merci d'avoir posé cette question!

Code lisible

// 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
la source
Plutôt cool, j'adore l'interface web. Une autre entrée amusant: 0 0 0.4 100 1 1 1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1.
Les passe-temps de Calvin
1
Où est le programme actuel?
Peter Taylor
C'est dans la page web ici: goo.gl/wKgIKD
JeffSB
Les réponses sur ce site doivent généralement inclure tout le code nécessaire pour répondre à la question. Dans le cas de cette question, il s’agit d’un programme qui lit à partir de stdin et écrit sur stdout. De plus, puisqu'il s'agit d'un code-golf, vous devez le minimiser autant que possible: supprimez au minimum les commentaires et les espaces inutiles et utilisez autant que possible des identificateurs à un caractère.
Peter Taylor
@JeffSB Cette soumission est valable pour la réponse bonus, mais pas pour la réponse acceptée. (Bien que vous souhaitiez inclure tout votre code.)
Calvin's Hobbies
6

Python - 765

Bon challenge. C'est ma solution qui reçoit les entrées de stdin et les sorties sur stdout. En utilisant l'exemple de @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

Voici le code golfé:

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)

Et voici le code non-golfé avec un chiffre bonus

miroirs

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
la source
-1: ne répond pas aux spécifications. La sortie spécifiée est deux chiffres, pas deux chiffres et une image.
Peter Taylor
@PeterTaylor Vous voulez donc dire stdin / stdout?
Ray
@willem En réponse bonus, c'est très bien. Il ne faut que répondre exactement à la spécification pour devenir la réponse code-golf.
Les passe-temps de Calvin
J'ai mis à jour le code
Willem
Notez que ce sys.argvn'est pas stdin.
Ray
6

Matlab (388)

Terrain

terrain parcelle 2

Concepts

Points de réflexion

Pour calculer les points de réflexion, nous devons fondamentalement intersecter deux lignes droites. L'un avec le point p0 et le vecteur v, l'autre entre les deux points p1, p2. Donc l'équation à résoudre est (s, t sont des paramètres): p0 + t v = s p1 + (1-s) * p2.

Le paramètre s est alors une coordonnée barycentrique du miroir donc si 0

En miroir

La mise en miroir de v est assez simple. Supposons que || v || = || n || = 1 où n est le vecteur normal du miroir actuel. Ensuite, vous pouvez simplement utiliser la formule v: = v-2 ** n où <,> est le produit scalaire.

Validité de l'étape

Lors du calcul du miroir 'valide' le plus proche, nous devons considérer certains critères le rendant valide. Tout d'abord, le point d'interception du miroir doit se situer entre les deux extrémités, il doit donc être 0

Programme

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

Un peu de 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
flawr
la source
Cela me ramène. Ma première expérience avec Matlab a été de modéliser la trajectoire d'un laser à travers un système de miroirs et de lentilles alors que j'étais en recherche au cours de mes études de premier cycle. Vos graphiques en particulier semblent très familiers. :) Quoi qu'il en soit, juste un aparté. Beau travail ici, +1.
Alex A.
Haha merci! Je ne me souvenais même pas que je l'avais fait quand j'ai vu votre commentaire apparaître =)
flawr
Haha alors mon commentaire vous ramène probablement ! (À quand vous avez posté ça.)
Alex A.