Куда идет лазер?


34

Возьмите 2-мерную сетку и нарисуйте на ней несколько отрезков, чтобы представить зеркала. Теперь выберите точку для размещения теоретического лазера и угол, чтобы определить направление, на которое он указывает. Вопрос в следующем: если вы следуете по пути лазерного луча на определенном расстоянии, в какой точке координат вы находитесь?

Пример:

лазерный пример

На этом изображении, Lявляется расположение лазера, tявляется его угол (измеряется от положительной оси X), M1, M2, и M3все сегменты линии зеркала, и Eявляется точкой на пути лазерного луча после того, как D = d1 + d2 + d3 + d4блоков, начиная с L.

Цель

Написать кратчайшую программу (в байтах) , который выводит Eданные L, t, Dи список зеркал.
(Используйте http://mothereff.in/byte-counter для подсчета байтов.)

Формат ввода

Вход будет поступать со стандартного ввода в формате:

Lx Ly t D M1x1 M1y1 M1x2 M1y2 M2x1 M2y1 M2x2 M2y2 ...
  • Все значения будут плавающей точку , соответствующей это регулярное выражение: [-+]?[0-9]*\.?[0-9]+.
  • Между каждым числом всегда ровно один пробел.
  • Требование кавычек вокруг ввода разрешено.
  • tв градусах, но не обязательно в [0, 360)диапазоне. (Если вы предпочитаете использовать радианы, просто скажите это в своем ответе.)
  • Dможет быть отрицательным, эффективно поворачивая лазер на 180 градусов. Dтакже может быть 0.
  • Там может быть как угодно много зеркал (включая их вообще).
  • Порядок зеркал не должен иметь значения.
  • Вы можете предположить, что ввод будет кратным 4 числам. например, Lx Ly tили Lx Ly t D M1x1являются недействительными и не будут проверены. Нет ввода также является недействительным.

Схема выше может быть введена как:

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

(Обратите внимание, что изображение было нарисовано от руки, и эти значения являются лишь приблизительными. Входные значения Мартина Бюттнера

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

даст больше столкновений, хотя они не соответствуют эскизу.)

Выходной формат

Вывод должен идти в стандартный вывод в формате:

Ex Ey

Они также являются поплавками и могут быть в экспоненциальной форме.

Заметки

  • Зеркала могут пересекаться друг с другом.
  • Обе стороны зеркал являются отражающими.
  • Луч может попасть в одно и то же зеркало много раз.
  • Луч продолжается вечно.

Неопределенные случаи

Вы можете предположить, что случаи, когда

  • лазер запускается на отрезке зеркальной линии
  • лазерный луч достигает конечной точки зеркала
  • лазерный луч попадает на пересечение между двумя зеркалами

не определены и не будут проверены. Ваша программа может сделать что угодно, если это произойдет, в том числе выдать ошибку.

бонус

Просто ради интереса, я буду награждать 200 баллов за наивысшую оценку, которая выводит графическое представление проблемы (вы даже можете написать интерактивный сценарий). Эти бонусные представления не требуют участия в игре и могут быть снисходительны к тому, как обрабатываются ввод и вывод. Они отличаются от фактических представлений о гольфе, но оба должны быть представлены в одном и том же ответе .

Примечание: хорошо подать только бонусный ответ, вы просто не будете принятым ответом. Чтобы быть принятым, вы должны точно следовать спецификации ввода / вывода (например, вывод включает только Ex Eyизображения, а не изображения) и быть самым коротким.


1
Гольф и безголевые представления в одном вопросе imho собирается стать огромным беспорядком. 200 бонусных очков настолько привлекательны, что игра в гольф становится второстепенной.
Говард

1
@PeterTaylor Вы цитируете меня хорошо вне контекста. Я прочитал бонусные ответы в разделе ОП, поскольку эти два представления полностью различны, но принадлежат к одному и тому же посту, если будут предприняты оба действия (что будет означать, что просто ответ на запрос popcon также подойдет). В любом случае, это ваши голоса, и вам решать, как вы их используете, и я, возможно, в какой-то момент добавлю версию для гольфа. Но я полагаю, что ОП мог бы уточнить, намеревался ли он, ответы только для попконов, быть действительными или нет.
Мартин Эндер

1
@ MartinBüttner, « бонус » означает « дополнительный, дополнительный ». Это не часть главной задачи. И вопрос имеет только один тег, код-гольф .
Питер Тейлор

2
@PeterTaylor MartinBüttner прав. Отвечать только на бонусную часть вопроса нормально. Я сказал, что при вводе / выводе бонусные ответы могут быть безболезненными и снисходительными, и все текущие бонусные представления выглядят для меня нормально. Кратчайшее представление , что делает точно следовать спецификациям будет общепринятым ответом. На данный момент никаких заявлений нет, но со мной все в порядке.
Увлечения Кэлвина

1
В этом случае « бонус » - это неправильное слово, и вы просите людей нарушать правила , что бесполезно.
Питер Тейлор

Ответы:


39

Рубин, 327 байт

(прокрутите вниз)

Mathematica, бонусный ответ

введите описание изображения здесь

Я сейчас собираюсь только для графического представления. Я мог бы портировать это на Руби позже и сыграть в гольф, если захочу.

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

Вы можете назвать это как

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

Это даст вам анимацию в Mathematica, а также экспортирует GIF (тот, что вверху для этого ввода). Для этого я немного расширил пример OP, чтобы сделать его немного интереснее.

Больше примеров

Трубка со слегка расходящимися стенками, но закрытым концом:

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

введите описание изображения здесь

Равносторонний треугольник и начальное направление, почти параллельное одной из сторон.

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

введите описание изображения здесь

Еще:

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

введите описание изображения здесь

Рубин, гольф-ответ

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}

По сути, это прямой перевод решения Mathematica на Ruby, плюс некоторая игра в гольф и проверка его соответствия критериям ввода / вывода.


Как у вас лазер пересекает зеркальный треугольник в конце первого примера?
AJMansfield

1
@AJMansfield В треугольнике есть небольшое отверстие, которое вы можете увидеть в начале анимации.
Мартин Эндер

Было бы здорово, если бы вы могли написать параграф, объясняющий, как это работает.
JeffSB

@JeffSB Я задокументировал код Mathematica сейчас. Версия Ruby делает почти то же самое с непонятными именами переменных и без прорисовки.
Мартин Эндер

@ MartinBüttner Спасибо. Интересно посмотреть, как это делают другие люди. До того, как это произошло, вы поняли, что должны исключить последнее зеркало, от которого вы отскочили? Я не сделал, но я понял это достаточно скоро. Я заметил очень небольшое число в вашем коде, и поэтому я спросил, как это работает.
JeffSB

18

Python 3 (421C 390C, 366C)

Использовать builtin.complexкак 2d вектор. Так

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

Чтобы обойти решение Ruby 368C, я нашел довольно компактный метод для расчета точечного отражения вдоль зеркала. А также использовал некоторую сложную алгебру, чтобы уменьшить количество символов. Их можно легко найти в некоголенном коде.

Вот версия для гольфа.

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)

Бонус: HTML, Coffeescript, настройка и расчет в реальном времени

То есть вы перетаскиваете любые конечные точки (или lazer, mirros), затем дорожка визуализируется. Он также поддерживает два типа ввода: тот, который описан в вопросе, и тот, который используется @Martin Büttner.

Масштабирование также регулируется автоматически.

Пока нет анимации. Возможно я улучшу это позже. Однако, перетаскивая белые точки, вы можете увидеть другой тип анимации. Попробуйте сами онлайн здесь , это смешно!

Весь проект можно найти здесь.

Случай 1 случай 2

Обновить

Здесь я приведу интересный случай:

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

И результат: круг


-1 не соответствует спецификации для ввода или вывода.
Питер Тейлор

@Ray В качестве бонуса ответ это хорошо. Это только должно точно соответствовать спецификации, чтобы стать ответом гольф-кода.
Увлечения Кэлвина

@PeterTaylor Познакомьтесь со спецификацией сейчас.
Рэй

Это действительно круто, как вы можете перемещать зеркала вокруг! Ваш мой первый +1 голос.
JeffSB

17

HTML JavaScript, 10543, +947 +889

Я исправил ошибку и убедился, что вывод соответствует спецификации вопроса. Веб-страница ниже имеет версию для игры в гольф, а также графическую бонусную версию. Я также исправил ошибку, указанную @Ray, которая спасла 58 символов. (Спасибо, Рэй.) Вы также можете запустить гольф-код в консоли JavaScript. (Сейчас я использую зеленый лазер мощностью 2 мВт.)

Гольф-код

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

вход

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

Выход

14.743305098514739 3.759749038188634


Вы можете проверить это здесь: http://goo.gl/wKgIKD

введите описание изображения здесь

объяснение

Код на веб-странице прокомментирован. По сути, я рассчитываю пересечение лазера с каждым зеркалом, предполагая, что лазер и зеркала бесконечно длинные. Затем я проверяю, находится ли пересечение в пределах конечной длины зеркала и лазера. Затем я беру ближайший перекресток, перемещаю лазер в эту точку и продолжаю, пока лазер не пропустит все зеркала.

Очень веселый проект. Спасибо, что задали этот вопрос!

Читаемый код

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

Довольно круто, я люблю веб-интерфейс. Другой забавный вход: 0 0 0.4 100 1 1 1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1.
Увлечения Кэлвина

1
Где актуальная программа?
Питер Тейлор

Это на веб-странице здесь: goo.gl/wKgIKD
JeffSB

Ответы на этом сайте, как правило, должны включать весь код, необходимый для ответа на вопрос. В случае этого вопроса это программа, которая читает из стандартного ввода и пишет в стандартный вывод. Кроме того, поскольку речь идет о коде-гольфе, вы должны как можно меньше сворачивать код: по крайней мере, удаляя комментарии и ненужные пробелы и по возможности используя односимвольные идентификаторы.
Питер Тейлор

@JeffSB Это представление действительно для бонусного ответа, но не для принятого ответа. (Хотя вы, возможно, захотите включить весь свой код.)
Увлечения Кэлвина

6

Питон - 765

Хороший вызов. Это моё решение, которое получает вход от stdin и выводит на stdout. Используя пример @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

Вот код для игры в гольф:

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)

А вот и код ungolfed с бонусной фигурой

зеркала

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: не соответствует спецификации Указанный вывод представляет собой два числа, а не два числа и изображение.
Питер Тейлор

@PeterTaylor Так ты имеешь в виду стандартный ввод / вывод?
Рэй

@willem В качестве бонуса ответ это хорошо. Это только должно точно соответствовать спецификации, чтобы стать ответом гольф-кода.
Увлечения Кэлвина

Я обновил код
Виллем

Обратите внимание, что sys.argvэто не stdin.
Рэй

6

Матлаб (388)

участок

сюжет Plot2

Концепции

Точки отражения

Для расчета точек отражения нам нужно пересечь две прямые линии. Один с точкой p0 и вектором v, другой между двумя точками p1, p2. Таким образом, уравнение для решения (s, t - параметры): p0 + t v = s p1 + (1-s) * p2.

Параметр s является тогда барицентрической координатой зеркала, так что если 0

Зеркальное

Зеркальное отображение v довольно просто. Предположим, что || v || = || n || = 1 где n - нормальный вектор текущего зеркала. Тогда вы можете просто использовать формулу v: = v-2 ** n, где <,> - скалярное произведение.

Срок действия шага

При вычислении ближайшего «действительного» зеркала мы должны учитывать некоторые критерии, которые делают его действительным. Сначала точка пересечения зеркала должна находиться между двумя конечными точками, поэтому она должна быть 0

программа

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

Слегка поиграл в гольф (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

Это забирает меня обратно. Мой первый опыт работы с Matlab - это моделирование лазерного пути через систему зеркал и линз, когда я занимался исследовательской работой во время обучения в университете. Ваша графика в частности выглядит очень знакомой. :) Во всяком случае, просто в сторону. Хорошая работа здесь, +1.
Алекс А.

Хаха спасибо! Я просто даже не помню, что сделал это, когда увидел твой комментарий всплывающим =)
flawr

Ха-ха, тогда мой комментарий, вероятно , вернет тебя ! (Чтобы, когда вы это опубликовали.)
Алекс А.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.