เลเซอร์ไปไหน?


34

ใช้กริด 2 มิติและวาดส่วนของเส้นตรงจำนวนหนึ่งเพื่อแสดงมิเรอร์ ตอนนี้เลือกจุดที่จะวางเลเซอร์ทฤษฎีและมุมเพื่อกำหนดทิศทางที่ชี้ไป คำถามคือ: หากคุณติดตามเส้นทางลำแสงเลเซอร์ในระยะทางที่กำหนดไว้คุณจะทำจุดประสานงานอะไร

ตัวอย่าง:

ตัวอย่างเลเซอร์

ในภาพนี้Lเป็นที่ตั้งของเลเซอร์tคือมุมของมัน (วัดจากแกน X ในเชิงบวก) M1, M2และM3มีทั้งหมดกระจกส่วนของเส้นและEเป็นจุดบนเส้นทางลำแสงเลเซอร์หลังจากที่หน่วยงานที่เริ่มต้นจากD = d1 + d2 + d3 + d4L

เป้าหมาย

เขียนโปรแกรมที่สั้นที่สุด (ไบต์) ที่เอาท์พุทEให้L, t, Dและรายการของกระจก
(ใช้http://mothereff.in/byte-counterเพื่อนับจำนวนไบต์)

รูปแบบอินพุต

อินพุตจะมาจาก stdin ในรูปแบบ:

Lx Ly t D M1x1 M1y1 M1x2 M1y2 M2x1 M2y1 M2x2 M2y2 ...
  • ค่าทั้งหมดจะถูกจุดที่ตรงกับ regex [-+]?[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

(โปรดทราบว่าภาพที่วาดด้วยมือเปล่าและค่าเหล่านี้เป็นเพียงการประมาณค่าเข้าของ 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

จะให้การชนมากขึ้นแม้ว่าจะไม่ตรงกับภาพร่าง)

รูปแบบผลลัพธ์

เอาต์พุตควรไปที่ stdout ในรูปแบบ:

Ex Ey

สิ่งเหล่านี้ยังลอยอยู่และอาจอยู่ในรูปแบบเลขชี้กำลัง

หมายเหตุ

  • กระจกอาจตัดกัน
  • กระจกทั้งสองด้านสะท้อนแสง
  • ลำแสงอาจชนกระจกเดียวกันหลายครั้ง
  • ลำแสงยังคงอยู่ตลอดไป

กรณีที่ไม่ได้กำหนด

คุณอาจคิดว่ากรณีที่

  • เลเซอร์จะเริ่มบนส่วนของเส้นกระจก
  • ลำแสงเลเซอร์ชนกับจุดสิ้นสุดของกระจก
  • ลำแสงเลเซอร์ชนกับรอยแยกระหว่างกระจกสองบาน

ไม่ได้ถูกกำหนดและจะไม่ถูกทดสอบ โปรแกรมของคุณอาจทำอะไรก็ได้หากสิ่งเหล่านี้เกิดขึ้นรวมถึงการโยนข้อผิดพลาด

โบนัส

เพียงเพื่อความสนุกฉันจะมอบรางวัล 200 คะแนนให้กับการโหวตที่ได้รับการโหวตสูงสุดซึ่งแสดงผลลัพธ์แบบกราฟิกของปัญหา (คุณสามารถเขียนสคริปต์แบบโต้ตอบได้) การส่งโบนัสเหล่านี้ไม่จำเป็นต้องเล่นกอล์ฟและสามารถผ่อนปรนกับวิธีการจัดการอินพุตและเอาต์พุต พวกเขามีความแตกต่างไปจากการส่งแข็งแรงเล่นกอล์ฟที่เกิดขึ้นจริง แต่ทั้งสองควรจะส่งในคำตอบเดียวกัน

หมายเหตุ:เฉพาะการส่งคำตอบโบนัสเป็นเรื่องปกติคุณก็จะไม่ได้รับคำตอบที่ยอมรับ หากต้องการได้รับการยอมรับคุณจะต้องปฏิบัติตามข้อมูลจำเพาะอินพุต / เอาต์พุต (เช่นเอาต์พุตจะเกี่ยวข้องเท่านั้นEx Eyไม่ใช่ภาพ) และสั้นที่สุด


1
การมีคำถามที่ส่งกอล์ฟและอวดดีในคำถามเดียวคือ imho จะกลายเป็นระเบียบมาก แต้ม 200 ค่าหัวนั้นดึงดูดนักกอล์ฟมากน้อย
โฮเวิร์ด

1
@PeterTaylor คุณกำลังพูดถึงฉันอย่างดีนอกบริบท ฉันอ่านคำตอบโบนัสส่วนของ OP เนื่องจากการส่งสองครั้งนั้นแตกต่างอย่างสิ้นเชิง แต่เป็นของโพสต์เดียวกันหากทั้งคู่พยายาม (ซึ่งหมายความว่าแค่คำตอบ popcon ก็ดีเช่นกัน) อย่างไรก็ตามมันเป็นคะแนนของคุณและมันก็ขึ้นอยู่กับคุณว่าคุณจะใช้มันอย่างไรและฉันอาจจะเพิ่มเวอร์ชัน golfed ต่อไปในบางจุด แต่ฉันเดาว่า OP สามารถให้ความกระจ่างได้ว่าเขาต้องการคำตอบสำหรับ popcon เท่านั้นหรือไม่
Martin Ender

1
@ MartinBüttner " โบนัส " หมายถึง " เพิ่มเติมพิเศษ " มันไม่ได้เป็นส่วนหนึ่งของความท้าทายหลัก และคำถามที่มีแท็กเพียงหนึ่งรหัสกอล์ฟ
Peter Taylor

2
@PeterTaylor MartinBüttnerถูกต้อง การตอบเฉพาะส่วนโบนัสของคำถามนั้นใช้ได้ ฉันบอกว่าคำตอบโบนัสสามารถยกเลิกการเล่นกอล์ฟและผ่อนปรนกับ i / o และการส่งโบนัสปัจจุบันทั้งหมดดูดีสำหรับฉัน การส่งสั้นที่สุดที่ไม่เป็นไปตามข้อกำหนดจะเป็นคำตอบที่ยอมรับได้ ขณะนี้ไม่มีการส่งข้อมูล แต่ไม่เป็นไรสำหรับฉัน
งานอดิเรกของ Calvin

1
ในกรณีนี้ " โบนัส " เป็นคำที่ไม่ถูกต้องในการใช้งานและคุณกำลังขอให้ผู้ใช้ฝ่าฝืนกฎซึ่งไม่เป็นประโยชน์
Peter Taylor

คำตอบ:


39

Ruby, 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 (ภาพที่อยู่ด้านบนสุดสำหรับข้อมูลนี้) ฉันได้ขยายตัวอย่าง OPs เล็กน้อยเพื่อให้น่าสนใจขึ้นอีกเล็กน้อย

ตัวอย่างเพิ่มเติม

หลอดที่มีผนังเบี่ยงเบนไปเล็กน้อย แต่ปลายปิด:

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 รวมถึงการตีกอล์ฟและทำให้แน่ใจว่าเป็นไปตามเกณฑ์ I / O


คุณจะให้เลเซอร์ส่องผ่านสามเหลี่ยมกระจกในตอนท้ายของตัวอย่างแรกได้อย่างไร
AJMansfield

1
@AJMansfield มีรูเล็ก ๆ ในรูปสามเหลี่ยมซึ่งคุณสามารถเห็นได้ที่จุดเริ่มต้นของภาพเคลื่อนไหว
Martin Ender

มันจะดีถ้าคุณสามารถเขียนย่อหน้าที่อธิบายวิธีการทำงาน
JeffSB

@JeffSB ฉันได้บันทึกรหัส Mathematica แล้ว รุ่น Ruby ทำสิ่งเดียวกันกับชื่อตัวแปรที่ไม่ชัดเจนและไม่มีการพล็อต
Martin Ender

@ MartinBüttnerขอบคุณ เป็นที่น่าสนใจที่จะเห็นว่าคนอื่นทำมันอย่างไร คุณเคยรู้มาก่อนหรือเปล่าว่าคุณต้องแยกกระจกสุดท้ายที่คุณเด้งออก ฉันไม่ได้ แต่ฉันคิดออกเร็วพอ ฉันสังเกตเห็นจำนวนน้อยมากในรหัสของคุณและนั่นเป็นสาเหตุที่ฉันขอให้ดูว่ามันทำงานอย่างไร
JeffSB

18

Python 3 (421C 390C, 366C)

ใช้builtin.complexเป็นเวกเตอร์ 2 มิติ ดังนั้น

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

เพื่อเอาชนะ 368C Ruby solution ฉันพบวิธีการที่ค่อนข้างกะทัดรัดในการคำนวณการสะท้อนจุดตามกระจก และยังใช้พีชคณิตที่ซับซ้อนเพื่อลดจำนวนตัวอักษร เหล่านี้สามารถพบได้ง่ายในรหัส ungolfed

นี่คือเวอร์ชั่นของกอล์ฟ

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 ไม่ตรงตามข้อกำหนดสำหรับอินพุตหรือเอาต์พุต
Peter Taylor

@Ray คำตอบโบนัสนี้ใช้ได้ มันจะต้องตรงตามข้อกำหนดเท่านั้นที่จะเป็นคำตอบรหัสกอล์ฟ
งานอดิเรกของ Calvin

@PeterTaylor พบกับ spec ตอนนี้
Ray

มันยอดเยี่ยมจริงๆที่คุณสามารถขยับกระจกไปมาได้! ของคุณคือ +1 คะแนนแรกของฉัน
JeffSB

17

จาวาสคริปต์ HTML, 10543, 947 889

ฉันแก้ไขข้อผิดพลาดและทำให้แน่ใจว่าผลลัพธ์ตรงตามข้อกำหนดของคำถาม หน้าเว็บด้านล่างมีเวอร์ชั่น golfed และเวอร์ชั่นโบนัสกราฟิก ฉันยังแก้ไขข้อบกพร่องที่ชี้ให้เห็นโดย @Ray ซึ่งบันทึกได้ 58 ตัวอักษร (ขอบคุณ Ray.) คุณยังสามารถเรียกใช้รหัส golfed ในคอนโซล JavaScript (ตอนนี้ฉันกำลังใช้เลเซอร์สีเขียว 2mW)

รหัสกอล์ฟ

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อีกสนุกป้อนข้อมูล:
งานอดิเรกของ Calvin

1
โปรแกรมจริงอยู่ที่ไหน
Peter Taylor

อยู่ในหน้าเว็บที่นี่: goo.gl/wKgIKD
JeffSB

คำตอบในเว็บไซต์นี้ควรรวมถึงรหัสทั้งหมดที่จำเป็นในการตอบคำถาม ในกรณีของคำถามนี้คือโปรแกรมที่อ่านจาก stdin และเขียนไปยัง stdout นอกจากนี้เนื่องจากเป็นคำถามเกี่ยวกับการเล่นกอล์ฟคุณควรลดรหัสให้มากที่สุด: อย่างน้อยที่สุดให้ลบความคิดเห็นและช่องว่างที่ไม่จำเป็นออกไป
Peter Taylor

@JeffSB การส่งนี้ใช้ได้สำหรับคำตอบโบนัสไม่ใช่คำตอบที่ยอมรับ (แม้ว่าคุณอาจต้องการรวมรหัสทั้งหมดของคุณ)
งานอดิเรกของ Calvin

6

Python - 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

นี่คือรหัส golfed:

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)

และนี่คือรหัสที่ไม่ได้แต่งแต้มด้วยรูปโบนัส

กระจก

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: ไม่ตรงตามข้อกำหนด เอาต์พุตที่ระบุคือสองตัวเลขไม่ใช่สองตัวเลขและรูปภาพ
Peter Taylor

@PeterTaylor ดังนั้นคุณหมายถึง stdin / stdout หรือไม่
เรย์

@willem ในฐานะที่เป็นคำตอบโบนัสนี้เป็นเรื่องปกติ มันจะต้องตรงตามข้อกำหนดเท่านั้นที่จะกลายเป็นคำตอบรหัสกอล์ฟ
งานอดิเรกของ Calvin

ฉันได้อัปเดตรหัสแล้ว
วิลเล็ม

โปรดทราบว่าsys.argvไม่ใช่ stdin
Ray

6

Matlab (388)

พล็อต

พล็อต plot2

แนวคิด

คะแนนการสะท้อนกลับ

สำหรับการคำนวณจุดสะท้อนนั้นเราต้องทำการตัดเส้นสองเส้นตรง หนึ่งจุด p0 และเวกเตอร์ v, อีกจุดระหว่างจุดสองจุด p1, p2 ดังนั้นสมการที่ต้องแก้คือ (s, t คือพารามิเตอร์): p0 + t v = s p1 + (1-s) * p2

พารามิเตอร์ s นั้นเป็นพิกัด barycentric ของมิเรอร์ดังนั้นถ้า 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
Alex A.

ฮ่าฮ่าขอบคุณ! ฉันเพียงแค่ไม่ได้จำได้ว่าผมทำอย่างนี้เมื่อฉันเห็นความคิดเห็นของคุณปรากฏขึ้น =)
flawr

ฮ่าฮ่าความคิดเห็นของฉันอาจนำคุณกลับมา! (ถึงเมื่อคุณโพสต์สิ่งนี้)
อเล็กซ์ A.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.