Penelusuran sinar 2 dimensi

9

Tantangannya adalah untuk mengimplementasikan program penelusuran sinar 2 dimensi, berbasis teks.

Sumber cahaya putih adalah @simbol. R, Gdan Bmerupakan filter cahaya. /dan \merupakan cermin dengan reflektivitas 80%. ?adalah sensor cahaya. >, <, ^Dan Vmenggabungkan cahaya dalam arah yang tepat (misalnya jika satu merah dan satu hijau datang ke >cahaya akan dipancarkan ke arah kanan dan itu akan menjadi kuning). Karakter non-spasi putih menyerap semua cahaya. Cahaya dipancarkan dari @simbol dalam empat arah.

Ketika program dijalankan, ia harus menghasilkan output yang sama dengan input, tetapi dengan sinar yang dilacak. Karena ini adalah 2 dimensi, dan saya jamin dalam input tidak akan ada sinar yang melintas, tidak akan ada masalah dengan itu. Setiap sinar harus diwakili oleh surat; r = merah, g = hijau, b = biru, c = cyan, m = magenta, y = kuning, w = putih. Tidak akan ada warna ternary, selamanya. Casing penting untuk membedakannya dari input. Setelah output itu, nilai-nilai cahaya yang ditangkap oleh tanda tanya (dalam urutan penampilannya, dari kiri ke kanan atas ke bawah) harus ditampilkan sebagai persentase dan warna. Misalnya, input ini:

 /                  @
                    -
 \R>                 ?

 @B/

Harus memberikan output:

 /wwwwwwwwwwwwwwwwww@w
 w                  -
w\R>mmmmmmmmmmmmmmmmm?
 w b
 @B/

#1: 72% Magenta

Poin penting lainnya yang perlu diperhatikan - ketika dua warna digabungkan menggunakan "prisma" (panah), kekuatan cahaya gabungan menjadi kekuatan rata-rata keduanya. Output harus persis seperti yang ditentukan (mis. #X: [x] [x] x% Warna ).

Jika bahasa Anda tidak dapat membaca dari STDIN dan menulis ke STDOUT, buat fungsi (anonim atau lambda bila tersedia) yang menerima input sebagai argumen dan mengembalikan hasilnya.

Arahan ke kompiler, struktur yang diperlukan atau direkomendasikan untuk semua atau sebagian besar program yang dibuat dalam bahasa, dll. Dapat dihilangkan. Misalnya, #includedan usingarahan (tetapi tidak #define) dapat dihapus dalam bahasa gaya-C, #/usr/bin/perl -optionsdi Perl, dan

 Module Module1
      Sub Main()
      End Sub
 End Module

di VB.NET, misalnya. Jika Anda mengimpor ruang nama atau menambahkan sertakan arahan, harap catat dalam jawaban Anda.

Apakah itu cukup sulit, sekarang? :)

Ry-
sumber
Terkait dengan Code Golf: Laser on Stack Overflow.
dmckee --- ex-moderator kitten
Perilaku cermin dalam contoh Anda tidak masuk akal. Anda memiliki \ (melarikan diri rusak) yang mempengaruhi cahaya yang akan langsung melewatinya. Tampaknya jauh lebih masuk akal jika cahaya masuk pada baris yang sama dengan cermin dan pergi pada kolom yang sama, atau sebaliknya. Demikian pula >menangkap cahaya yang akan langsung melewatinya. Dan jika wdari atas melewati itu R, maka harus bdari atas. Akhirnya (saya pikir), Anda salah tentang sinar yang tidak menyeberang. Untuk memberikan contoh satu baris, untuk apa hasil yang benar @R> B@?
Peter Taylor
Mengapa Anda menambahkan w acak dan memecah semua spasi? Dan cahaya tidak langsung melewatinya, tidak yakin apa yang Anda maksud.
Ry-
@minitech, yang @di kiri bawah memancarkan cahaya ke empat arah, bukan? Jadi secara khusus, itu memancarkannya w. Dan saya belum memutuskan spasi apa pun, setidaknya seperti yang diberikan dalam Chromium. Untuk melewatinya, hasil edit saya dapat menghapusnya.
Peter Taylor
5
minitech: Sebagai saran untuk tugas di masa depan: Minta komentar di Sandbox atau Puzzle Lab terlebih dahulu yang seharusnya cukup untuk menyelesaikan inkonsistensi dan masalah awal dengan tugas. Dengan begitu, setelah Anda memposting tugas di sini, Anda akan tahu itu sudah diperiksa (dan mungkin dilaksanakan) oleh beberapa orang lain.
Joey

Jawaban:

2

Python, 602 559 614 karakter

import sys
S=sys.stdin.readlines()
X=max(len(s)for s in S)
I='#'*X+''.join(t[:-1]+' '*(X-len(t))+'\n'for t in S)+'#'*X
L=len(I)
R=range(L)
B=[0]*L
C=[0]*L
for p in R:
 if'@'!=I[p]:continue
 for d in(1,-1,X,-X):
  q=p;c=7;b=100.
  while 1:
   q+=d;a=I[q];B[q]+=b;C[q]|=c
   if a in'\/':d=(ord(a)/30-2)*X/d;b*=.8
   elif a in'RGB':c&=ord(a)/5-12
   elif a in'><^V':d={'>':1,'<':-1,'^':-X,'V':X}[a];b/=2
   elif' '!=a:break
print''.join(I[p]if' '!=I[p]else' bgcrmyw'[C[p]]for p in R[X:-X])
i=0
for p in R:
 if'?'==I[p]:i+=1;print'#%d:'%i,'%.0f%%'%B[p],[0,'Blue','Green','Cyan','Red','Magenta','Yellow','White'][C[p]]

Sunting: diperbaiki sehingga tidak perlu spasi tambahan.

Keith Randall
sumber
Hampir - tetapi hasil test case tidak benar. Lihat: ideone.com/kUTxE . +1 bagaimanapun, ini luar biasa !!!
Ry-
@minitech: Saya pikir ini ada hubungannya dengan kurangnya ruang tambahan. Kode saya mengasumsikan bahwa setiap baris memiliki panjang yang sama, diisi dengan spasi jika perlu. Apakah bukan ini masalahnya? Jika demikian, lalu bagaimana Anda tahu, misalnya, seberapa jauh sumber cahaya atas pergi ke kanan?
Keith Randall
Dengan menggunakan panjang garis terpanjang untuk membalutnya, Anda dapat mengetahui seluruh kisi. Namun, bahkan ketika diisi dengan spasi, ia memberikan ini (input # 4): ideone.com/kUTxE
Ry-
@minitech: Anda kehilangan ruang di baris ke-4. Saya akan memperbaiki kode saya untuk tidak memerlukan spasi tambahan.
Keith Randall
Oh, wow itu berhasil !! Kerja bagus. Tapi ya, akan lebih baik jika tidak membutuhkan bantalan.
Ry-
2

F #

#nowarn "0025"

open System

type MirrorDirection = bool
type LightDirection = bool * bool
type Sq =
  | Air // [ ]
  | Mirror of MirrorDirection // [/] [\]
  | FilterR
  | FilterG
  | FilterB
  | Sensor // [?]
  | Combine of LightDirection // [^] [v] [<] [>]
  | Emitter // [@]
  | Wall of Char // non-whitespace

let [ mL; mR ] : MirrorDirection list = [ true; false ]
(* true T^/
       F</>F
        /vT   false
 *)
let [ dN; dS; dW; dE ] : LightDirection list = [ true, true; false, true; true, false; false, false ]
let bounce (m : MirrorDirection) ((a, b) : LightDirection) =
  m <> a, not b

let dv (a : LightDirection) =
  if a = dN then 0, -1
  elif a = dS then 0, 1
  elif a = dW then -1, 0
  else 1, 0

let fo<'a> : (('a option)[,] -> 'a seq) =
  Seq.cast
  >> Seq.filter Option.isSome
  >> Seq.map Option.get

let input = Console.In.ReadToEnd().Replace("\r\n", "\n")
let sqs =
  input.Split('\n')
  |> Array.map (fun x ->
    x.ToCharArray()
    |> Array.map (
      function
      | ' ' | '\t' | '\v' -> Air
      | '/' -> Mirror mL
      | '\\' -> Mirror mR
      | 'R' -> FilterR
      | 'G' -> FilterG
      | 'B' -> FilterB
      | '?' -> Sensor
      | '^' -> Combine dN
      | 'v' -> Combine dS
      | '<' -> Combine dW
      | '>' -> Combine dE
      | '@' -> Emitter
      | x -> Wall x
    )
  )

let w =
  Array.map Array.length sqs
  |> Set.ofArray
  |> Set.maxElement
let h = sqs.Length

let ib x y = -1 < x && x < w && -1 < y && y < h

let arr = Array2D.init w h (fun x y ->
  if x < sqs.[y].Length then
    sqs.[y].[x]
  else
    Air
)

let board =
  Array2D.map (
    function
    | _ -> 0.0, 0.0, 0.0
  ) arr

let mutable rays =
  Array2D.mapi (fun x y a ->
    match a with
    | Emitter -> Some(x, y)
    | _ -> None
  ) arr
  |> fo
  |> Seq.map (fun (x, y) ->
    [|
      dN, x, y, 1., 1., 1.
      dS, x, y, 1., 1., 1.
      dW, x, y, 1., 1., 1.
      dE, x, y, 1., 1., 1.
    |]
  )
  |> Seq.reduce Array.append

for i = 0 to w * h * 2 do
  rays <-
    rays
    |> Array.map (
      (fun (dir, x, y, r, g, b) ->
        let dx, dy = dv dir
        dir, x + dx, y + dy, r, g, b
      )
      >> (fun (dir, x, y, r, g, b) ->
        if ib x y then
          match arr.[x, y] with
          | Wall _ -> Array.empty
          | Sensor -> [| dir, x, y, r, g, b |]
          | FilterR -> [| dir, x, y, r, 0., 0. |]
          | FilterG -> [| dir, x, y, 0., g, 0. |]
          | FilterB -> [| dir, x, y, 0., 0., b |]
          | Mirror d -> [| bounce d dir, x, y, r * 0.8, g * 0.8, b * 0.8 |]
          | _ -> [| dir, x, y, r, g, b |]
        else
          Array.empty
      ))
    |> Array.concat
  Array2D.mapi (fun x y a ->
    match a with
    | Combine d -> Some(x, y, d)
    | _ -> None
  ) arr
  |> fo
  |> Seq.iter (fun (x, y, d) ->
    for i = 0 to rays.Length - 1 do
      let (d', x', y', r, g, b) = rays.[i]
      if x' = x && y' = y then
        rays.[i] <- (d, x, y, r, g, b)
  )
  for d, x, y, r, g, b in rays do
    if ib x y then
      match board.[x, y] with
      | r', g', b' -> board.[x, y] <- r + r', g + g', b + b'

printfn "%s" (
  let mutable s = ""
  for y = 0 to h - 1 do
    for x = 0 to w - 1 do
      s <- s + (match arr.[x, y] with
                | Air ->
                  match board.[x, y] with
                  | r, g, b ->
                    if r + g + b = 0.0 then ' '
                    else
                      if g = 0.0 && b = 0.0 then 'r'
                      elif r = 0.0 && b = 0.0 then 'g'
                      elif r = 0.0 && g = 0.0 then 'b'
                      elif r = 0.0 then 'c'
                      elif g = 0.0 then 'm'
                      elif b = 0.0 then 'y'
                      else 'w'
                | Wall z -> z
                | Mirror z -> if z = mL then '/' else '\\'
                | FilterR -> 'R'
                | FilterG -> 'G'
                | FilterB -> 'B'
                | Sensor -> '?'
                | Combine z -> if z = dN then '^' elif z = dS then 'v' elif z = dW then '<' else '>'
                | Emitter -> '@'
                |> sprintf "%c")
    s <- s + "\n"
  s
)

Array2D.mapi (fun x y a ->
  match a with
  | Sensor -> Some(x, y)
  | _ -> None
) arr
|> fo
|> Seq.iteri (fun i (x, y) ->
  let (r, g, b) = board.[x, y]
  let desc =
    if r + g + b = 0.0 then "None"
    elif g = 0.0 && b = 0.0 then "Red"
    elif r = 0.0 && b = 0.0 then "Green"
    elif r = 0.0 && g = 0.0 then "Blue"
    elif r = 0.0 then "Cyan"
    elif g = 0.0 then "Magenta"
    elif b = 0.0 then "Yellow"
    else "White"
  let avg = int((r + g + b) * 100.0 / (match desc with
                                       | "White" | "None" -> 3.0
                                       | "Red" | "Green" | "Blue" -> 1.0
                                       | _ -> 2.0))
  printfn "#%d: %d%% %s" (i + 1) avg desc
)
Ming-Tang
sumber
Tidak diserang tetapi masih mengagumkan! +1.
Ry-