Laser đi đâu?


34

Lấy lưới 2 chiều và vẽ một số đoạn thẳng trên nó để thể hiện các gương. Bây giờ chọn một điểm để đặt một tia laser lý thuyết và một góc để xác định hướng nó đang chỉ. Câu hỏi là: nếu bạn đi theo đường đi của tia laser trong một khoảng cách xác định, bạn đang ở điểm tọa độ nào?

Thí dụ:

ví dụ laser

Trong hình ảnh này, Llà vị trí của laser, tlà nó của góc (đo từ trục X dương), M1, M2, và M3đều là những gương đoạn thẳng, và Elà điểm trên con đường tia laser sau khi D = d1 + d2 + d3 + d4đơn vị, bắt đầu từ L.

Mục tiêu

Viết chương trình ngắn nhất (tính theo byte) mà kết quả đầu ra Eđược L, t, D, và một danh sách các gương.
(Sử dụng http://otherseff.in/byte-count để đếm byte.)

Định dạng đầu vào

Đầu vào sẽ đến từ stdin ở định dạng:

Lx Ly t D M1x1 M1y1 M1x2 M1y2 M2x1 M2y1 M2x2 M2y2 ...
  • Tất cả các giá trị sẽ là các điểm nổi phù hợp với biểu thức chính quy này : [-+]?[0-9]*\.?[0-9]+.
  • Luôn có chính xác một khoảng trống giữa mỗi số.
  • Yêu cầu báo giá xung quanh đầu vào được cho phép.
  • tlà độ, nhưng không nhất thiết phải trong [0, 360)phạm vi. (Nếu bạn thích bạn có thể sử dụng radian thay vào đó, chỉ cần nói như vậy trong câu trả lời của bạn.)
  • Dcó thể âm tính, hiệu quả xoay laser 180 độ. Dcũng có thể là 0.
  • Có thể có nhiều gương tùy ý (bao gồm cả không có gương).
  • Thứ tự của các gương không quan trọng.
  • Bạn có thể giả sử đầu vào sẽ có bội số của 4 số. ví dụ Lx Ly thoặc Lx Ly t D M1x1không hợp lệ và sẽ không được kiểm tra. Không có đầu vào nào là không hợp lệ.

Bố cục trên có thể là đầu vào như:

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

(Lưu ý rằng hình ảnh được vẽ tự do và các giá trị này chỉ là xấp xỉ. Giá trị đầu vào của Martin Büttner là

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

sẽ cho nhiều va chạm hơn mặc dù chúng không khớp với bản phác thảo.)

Định dạng đầu ra

Đầu ra nên đi đến thiết bị xuất chuẩn ở định dạng:

Ex Ey

Đây cũng là những chiếc phao và có thể ở dạng hàm mũ.

Ghi chú

  • Gương có thể giao nhau.
  • Cả hai mặt của gương đều được phản chiếu.
  • Chùm tia có thể chiếu vào cùng một gương nhiều lần.
  • Chùm sáng cứ kéo dài mãi.

Các trường hợp không xác định

Bạn có thể cho rằng các trường hợp

  • Laser bắt đầu trên một phân khúc dòng gương
  • chùm tia laser chiếu vào điểm cuối của gương
  • chùm tia laser chạm vào giao điểm giữa hai gương

không xác định và sẽ không được kiểm tra. Chương trình của bạn có thể làm bất cứ điều gì nếu những điều này xảy ra, bao gồm cả lỗi.

Tiền thưởng

Để giải trí, tôi sẽ trao 200 điểm thưởng cho bài nộp được bình chọn cao nhất đưa ra biểu diễn đồ họa của vấn đề (thậm chí bạn có thể viết một kịch bản tương tác). Những phần thưởng này không cần phải được đánh gôn và có thể được khoan dung với cách xử lý đầu vào và đầu ra. Chúng khác với các bài nộp golf thực tế nhưng cả hai nên được gửi trong cùng một câu trả lời .

Lưu ý: Chỉ gửi câu trả lời thưởng là tốt, bạn sẽ không được trả lời. Để được chấp nhận, bạn phải tuân thủ chính xác thông số đầu vào / đầu ra (ví dụ: đầu ra chỉ liên quan Ex Ey, không liên quan đến hình ảnh) và là ngắn nhất.


1
Có những bài nộp golf và không có ý thức trong một câu hỏi là imho sẽ trở thành một mớ hỗn độn lớn. 200 điểm thưởng rất hấp dẫn, việc chơi golf trở thành điểm phụ.
Howard

1
@PeterTaylor Bạn đang trích dẫn tôi ra khỏi bối cảnh. Tôi đọc câu trả lời phần thưởng của OP vì hai bài nộp hoàn toàn khác biệt nhưng thuộc cùng một bài nếu cả hai đều cố gắng (điều đó có nghĩa là chỉ câu trả lời popcon cũng sẽ ổn). Dù sao, đó là phiếu bầu của bạn và tùy thuộc vào cách bạn sử dụng chúng, và đôi khi tôi có thể sẽ thêm một phiên bản chơi gôn. Nhưng tôi đoán OP có thể làm rõ liệu anh ta có ý định, câu trả lời chỉ dành cho popcon có hợp lệ hay không.
Martin Ender

1
@ MartinBüttner, " tiền thưởng " có nghĩa là " bổ sung, thêm ". Đây không phải là một phần của thách thức chính. Và câu hỏi chỉ có một thẻ, mã golf .
Peter Taylor

2
@PeterTaylor MartinBüttner nói đúng. Chỉ trả lời phần thưởng của câu hỏi là tốt. Tôi đã nói rằng các câu trả lời tiền thưởng có thể không được đánh golf và khoan dung với i / o, và tất cả các bài nộp thưởng hiện tại đều phù hợp với tôi. Bài nộp ngắn nhất thực hiện chính xác thông số kỹ thuật sẽ là câu trả lời được chấp nhận. Hiện tại không có bài nộp nào, nhưng điều đó ổn với tôi.
Sở thích của Calvin

1
Trong trường hợp đó, " tiền thưởng " là từ sai khi sử dụng và bạn đang yêu cầu mọi người phá vỡ các quy tắc , điều này không hữu ích.
Peter Taylor

Câu trả lời:


39

Ruby, 327 byte

(cuộn xuống phía dưới)

Toán học, phần thưởng

nhập mô tả hình ảnh ở đây

Tôi chỉ đi nộp đồ họa ngay bây giờ. Tôi có thể chuyển nó cho Ruby sau và chơi golf nếu tôi cảm thấy thích nó.

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

Bạn có thể gọi nó như thế

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

Điều đó sẽ cung cấp cho bạn một hình ảnh động trong Mathicala và cũng xuất một GIF (cái ở trên cùng cho đầu vào này). Tôi đã mở rộng một chút ví dụ về OP cho điều này, để làm cho nó thú vị hơn một chút.

Thêm ví dụ

Một ống có các bức tường hơi phân kỳ nhưng một đầu kín:

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

nhập mô tả hình ảnh ở đây

Một tam giác đều và một hướng ban đầu gần như song song với một trong các cạnh.

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

nhập mô tả hình ảnh ở đây

Một lần nữa:

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

nhập mô tả hình ảnh ở đây

Ruby, câu trả lời 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}

Về cơ bản, đây là bản dịch trực tiếp giải pháp Mathicala sang Ruby, cộng với một số môn đánh gôn và đảm bảo rằng nó đáp ứng các tiêu chí I / O.


Làm thế nào để bạn có lazer vượt qua tam giác gương ở cuối ví dụ đầu tiên?
AJMansfield

1
@AJMansfield Có một lỗ nhỏ trong hình tam giác, bạn có thể nhìn thấy ở phần đầu của hình ảnh động.
Martin Ender

Sẽ thật tuyệt nếu bạn có thể viết một đoạn văn giải thích cách nó hoạt động.
JeffSB

@JeffSB Tôi đã ghi lại mã Mathicala ngay bây giờ. Phiên bản Ruby thực hiện khá chính xác điều tương tự với các tên biến tối nghĩa và không có âm mưu.
Martin Ender

@ MartinBüttner Cảm ơn. Thật thú vị khi xem những người khác làm điều đó như thế nào. Bạn có nhận ra trước khi nó xảy ra rằng bạn phải loại trừ chiếc gương cuối cùng mà bạn bật ra không? Tôi đã không, nhưng tôi đã tìm ra nó đủ sớm. Tôi nhận thấy số lượng rất nhỏ trong mã của bạn và đó là lý do tại sao tôi hỏi để xem nó hoạt động như thế nào.
JeffSB

18

Con trăn 3 (421C 390C, 366C)

Sử dụng builtin.complexnhư vector 2d. Vì thế

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

Để đánh bại giải pháp Ruby 365C, tôi đã tìm một phương pháp khá nhỏ gọn để tính toán phản xạ điểm dọc theo gương. Và cũng sử dụng một số đại số phức tạp để giảm nhiều ký tự hơn. Chúng có thể dễ dàng tìm thấy trong mã không mã hóa.

Đây là phiên bản chơi gôn.

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)

Bị đánh cắp

# 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)

Phần thưởng: HTML, Coffeescript, Điều chỉnh & Tính toán thời gian thực

Đây là, bạn kéo bất kỳ điểm cuối nào (hoặc lazer, mirros), sau đó bản nhạc được hiển thị. Nó cũng hỗ trợ hai loại đầu vào, một loại được mô tả trong câu hỏi và loại được sử dụng bởi @Martin Büttner.

Tỷ lệ cũng được điều chỉnh tự động.

Bây giờ nó không có hoạt hình. Có lẽ tôi sẽ cải thiện nó sau. Tuy nhiên, kéo các điểm trắng và bạn có thể thấy một loại hình ảnh động khác. Hãy thử trực tuyến tại đây , thật buồn cười!

Toàn bộ dự án có thể được tìm thấy ở đây

trường hợp 1 trường hợp 2

Cập nhật

Ở đây tôi cung cấp một trường hợp thú vị:

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

Và kết quả là: vòng tròn


-1 không đáp ứng thông số kỹ thuật cho đầu vào hoặc đầu ra.
Peter Taylor

@Ray Như một câu trả lời tiền thưởng này là tốt. Nó chỉ phải đáp ứng chính xác thông số kỹ thuật để trở thành câu trả lời mã golf.
Sở thích của Calvin

@PeterTaylor Gặp spec bây giờ.
Ray

Điều đó thật tuyệt vời khi bạn có thể di chuyển những chiếc gương xung quanh! Của bạn là phiếu bầu +1 đầu tiên của tôi.
JeffSB

17

JavaScript HTML, 10,543, 947 889

Tôi đã sửa một lỗi và đảm bảo đầu ra đáp ứng được thông số kỹ thuật. Trang web dưới đây có phiên bản chơi gôn và cả phiên bản phần thưởng đồ họa. Tôi cũng đã sửa một lỗi được chỉ ra bởi @Ray đã lưu 58 ký tự. (Cảm ơn Ray.) Bạn cũng có thể chạy mã được đánh gôn trong bảng điều khiển JavaScript. (Bây giờ tôi đang sử dụng laser xanh 2mW.)

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

Đầu vào

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

Đầu ra

14.743305098514739 3.759749038188634


Bạn có thể kiểm tra nó ở đây: http://goo.gl/wKgIKD

nhập mô tả hình ảnh ở đây

Giải trình

Mã trong trang web được bình luận. Về cơ bản, tôi tính toán giao điểm của laser với mọi gương giả sử laser và gương dài vô hạn. Sau đó, tôi kiểm tra xem giao điểm có nằm trong chiều dài hữu hạn của gương và laser không. Sau đó, tôi đi đến giao lộ gần nhất, di chuyển tia laser đến điểm đó và tiếp tục cho đến khi tia laser bỏ qua tất cả các gương.

Dự án rất vui. Cảm ơn đã hỏi câu hỏi này!

Mã dễ đọc

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

Khá tuyệt, tôi thích giao diện web. Một đầu vào thú vị khác : 0 0 0.4 100 1 1 1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1.
Sở thích của Calvin

1
Chương trình thực tế ở đâu?
Peter Taylor

Có trong trang web ở đây: goo.gl/wKgIKD
JeffSB

Câu trả lời trên trang web này thường bao gồm tất cả các mã cần thiết để trả lời câu hỏi. Trong trường hợp của câu hỏi này, đó là một chương trình đọc từ stdin và viết lên thiết bị xuất chuẩn. Ngoài ra, vì đó là một câu hỏi về golf-code, bạn nên tối thiểu hóa mã càng nhiều càng tốt: ít nhất, loại bỏ các bình luận và khoảng trắng không cần thiết và sử dụng mã định danh một ký tự nếu có thể.
Peter Taylor

@JeffSB Bài nộp này có giá trị cho câu trả lời thưởng, chỉ là câu trả lời không được chấp nhận. (Mặc dù bạn có thể muốn bao gồm tất cả mã của mình.)
Sở thích của Calvin

6

Con trăn - 765

Thử thách tốt. Đây là giải pháp của tôi nhận đầu vào từ stdin và đầu ra cho thiết bị xuất chuẩn. Sử dụng ví dụ của @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

Đây là mã đánh gôn:

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)

Và đây là mã không được mã hóa với một con số tiền thưởng

gương

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: không đáp ứng thông số kỹ thuật. Đầu ra được chỉ định là hai số, không phải hai số và một hình ảnh.
Peter Taylor

@PeterTaylor Vậy ý bạn là stdin / stdout?
Ray

@willem Là một câu trả lời tiền thưởng, điều này là tốt. Nó chỉ phải đáp ứng chính xác thông số kỹ thuật để trở thành câu trả lời mã golf.
Sở thích của Calvin

Tôi đã cập nhật mã
Willem

Lưu ý rằng đó sys.argvkhông phải là stdin.
Ray

6

Matlab (388)

Âm mưu

âm mưu lô2

Các khái niệm

Điểm phản chiếu

Để tính toán các điểm phản xạ, về cơ bản chúng ta phải xen kẽ hai đường thẳng. Một điểm có p0 và vectơ v, điểm kia nằm giữa hai điểm p1, p2. Vậy phương trình cần giải là (s, t là tham số): p0 + t v = s p1 + (1-s) * p2.

Tham số s sau đó là tọa độ nhị phân của gương nên nếu 0

Phản chiếu

Việc phản chiếu của v khá đơn giản. Chúng ta hãy giả sử rằng | | v | | = | | n | | = 1 trong đó n là vectơ bình thường của gương hiện tại. Sau đó, bạn chỉ có thể sử dụng công thức v: = v-2 ** n trong đó <,> là sản phẩm chấm.

Hiệu lực của bước

Trong khi tính toán gương 'hợp lệ' gần nhất, chúng ta phải xem xét một số tiêu chí làm cho nó hợp lệ. Đầu tiên, điểm đánh chặn của gương phải nằm giữa hai điểm cuối, vì vậy nó phải là 0

Chương trình

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

Hơi bị đánh 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

Điều này đưa tôi trở lại. Trải nghiệm đầu tiên của tôi với Matlab là mô hình hóa đường đi của tia laser thông qua hệ thống gương và thấu kính trong khi ở vị trí nghiên cứu trong thời gian học đại học. Đồ họa của bạn đặc biệt trông rất quen thuộc. :) Dù sao, chỉ là một bên. Làm việc tốt ở đây, +1.
Alex A.

Cảm ơn! Tôi thậm chí còn không nhớ mình đã làm điều này khi thấy bình luận của bạn bật lên =)
flawr

Haha sau đó nhận xét của tôi có thể đưa bạn trở lại! (Tới khi bạn đăng bài này.)
Alex A.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.