Dwuwymiarowe śledzenie promieni

9

Wyzwanie polega na wdrożeniu dwuwymiarowego programu śledzenia promieni opartego na tekście.

Źródła białego światła są @symbolami. R, GI Bsą filtry świetlne. /i \są zwierciadłami o współczynniku odbicia 80%. ?to czujnik światła. >, <, ^I Vłączy światło w odpowiednim kierunku (na przykład, jeżeli jeden czerwony, zielony i jeden był w >świetle będą emitowane w kierunku na prawo i że to żółty). Inne znaki niebiałe zajmują całe światło. Światło jest emitowane z @symboli w czterech kierunkach.

Po uruchomieniu program powinien generować dane wyjściowe takie same jak dane wejściowe, ale ze śledzonymi promieniami. Ponieważ jest to dwuwymiarowe i gwarantuję na wejściu, że żadne promienie nigdy się nie skrzyżują, nie będzie z tym problemu. Każdy promień powinien być reprezentowany przez literę; r = czerwony, g = zielony, b = niebieski, c = cyjan, m = magenta, y = żółty, w = biały. Nigdy nie będzie żadnych trójskładnikowych kolorów. Obudowa jest ważna, aby odróżnić ją od wejścia. Po tym wyjściu wartości światła przechwycone przez znaki zapytania (w kolejności ich wyglądu, od lewej do prawej od góry do dołu) powinny być wyprowadzane jako wartości procentowe i kolory. Na przykład to wejście:

 /                  @
                    -
 \R>                 ?

 @B/

Powinien dać wynik:

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

#1: 72% Magenta

Kolejny ważny punkt, na który należy zwrócić uwagę - gdy dwa kolory są łączone za pomocą „pryzmatu” (strzałki), siła połączonego światła staje się średnią siłą dwóch. Dane wyjściowe muszą być dokładnie takie, jak określono (np. #X: [x] [x] x% Kolor ).

Jeśli twój język nie może czytać ze STDIN i pisać do STDOUT, utwórz funkcję (anonimową lub lambda, jeśli jest dostępna), która przyjmuje dane wejściowe jako argument i zwraca wynik.

Dyrektywy dotyczące kompilatora, struktury wymagane lub zalecane dla wszystkich lub większości programów utworzonych w języku itp. Można pominąć. Na przykład, #includei usingdyrektyw (ale nie #define) może być usunięta w językach stylu C, #/usr/bin/perl -optionsPerl, i

 Module Module1
      Sub Main()
      End Sub
 End Module

na przykład w VB.NET. Jeśli importujesz przestrzenie nazw lub dodajesz dyrektywy dołączające, zapisz je w swojej odpowiedzi.

Czy to już wystarczająco trudne? :)

Ry-
źródło
Związane z Code Golf: Lasers on Stack Overflow.
dmckee --- były moderator kociak
Zachowanie lusterek w twoim przykładzie nie ma sensu. Masz \ (ucieczka jest zepsuta) wpływające na światło, które przechodzi prosto obok niego. Wydaje się o wiele rozsądniejsze, aby światło wpadało do tego samego rzędu co lustro i pozostawiało tę samą kolumnę lub odwrotnie. Podobnie >wychwytuje światło, które przechodzi prosto obok niego. A jeśli wprzechodzi od góry R, to samo powinno być bod dołu. Wreszcie (myślę), mylisz się, że promienie się nie krzyżują. Na przykład w jednym wierszu, dla czego byłoby poprawne wyjście @R> B@?
Peter Taylor
Dlaczego dodałeś losowe w i złamałeś wszystkie odstępy? I światło nie przechodzi prosto obok niego, nie jestem pewien, co masz na myśli.
Ry
@minitech, który @w lewym dolnym rogu emituje światło we wszystkich czterech kierunkach, prawda? W szczególności to emituje w. I nie złamałem żadnych odstępów, przynajmniej tak jak w Chromium. Jeśli chodzi o przejście obok, moja edycja może to wyjaśnić.
Peter Taylor
5
minitech: Jako rada dla przyszłych zadań: najpierw poproś o komentarze w Sandbox lub Puzzle Lab, które powinny wystarczyć, aby wyeliminować niespójności i wczesne problemy z zadaniami. W ten sposób, kiedy opublikujesz tutaj zadanie, będziesz wiedział, że zostało ono sprawdzone (a może zaimplementowane) już przez inne osoby.
Joey,

Odpowiedzi:

2

Python, 602 559 614 znaków

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

Edycja: naprawiono, aby nie wymagało spacji końcowych.

Keith Randall
źródło
Prawie - ale wynik przypadku testowego jest niepoprawny. Zobacz: ideone.com/kUTxE . W każdym razie +1, to świetnie !!!
Ry-
@minitech: Myślę, że ma to związek z brakiem spacji końcowych. Mój kod zakłada, że ​​każdy wiersz ma tę samą długość, w razie potrzeby uzupełniony spacjami. Czy tak nie jest? Jeśli tak, to skąd wiesz, np. Jak daleko górne źródło światła idzie w prawo?
Keith Randall
Używając długości najdłuższej linii do jej wypełnienia, możesz obliczyć całą siatkę. Jednak nawet po wypełnieniu
Ry-
@minitech: brakuje ci spacji w 4. linii. Naprawię mój kod, aby nie wymagał spacji końcowych.
Keith Randall
Och, wow, to działa !! Dobra robota. Ale tak, byłoby dobrze, gdyby nie wymagało wyściełania.
Ry-
2

FA#

#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
źródło
Nie golfowany, ale wciąż niesamowity! +1.
Ry-