Ray tracing bidimensionale


9

La sfida è implementare un programma di ray tracing bidimensionale, basato su testo.

Le fonti di luce bianca sono @simboli. R, GE Bsono la luce filtra. /e \sono specchi con una riflettività dell'80%. ?è un sensore di luce. >, <, ^E Vcombinare la luce nella direzione appropriata (ad esempio, se uno rosso e uno verde entrato in una >luce sarebbe emessa verso destra e sarebbe giallo). Altri caratteri non bianchi assorbono tutta la luce. La luce viene emessa dai @simboli in quattro direzioni.

Quando il programma viene eseguito, dovrebbe produrre un output uguale all'input, ma con raggi tracciati. Poiché si tratta di una dimensione bidimensionale e garantisco nell'input che i raggi non attraverseranno mai, non vi sarà alcun problema. Ogni raggio dovrebbe essere rappresentato da una lettera; r = rosso, g = verde, b = blu, c = ciano, m = magenta, y = giallo, w = bianco. Non ci saranno mai colori ternari, mai. L'involucro è importante per differenziarlo dall'input. Dopo tale uscita, i valori della luce catturati dai punti interrogativi (in ordine di aspetto, da sinistra a destra dall'alto verso il basso) dovrebbero essere emessi come percentuali e colori. Ad esempio, questo input:

 /                  @
                    -
 \R>                 ?

 @B/

Dovrebbe dare l'output:

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

#1: 72% Magenta

Un altro punto importante da notare: quando due colori vengono combinati usando un "prisma" (le frecce) la forza della luce combinata diventa la forza media dei due. L'output deve essere esattamente come specificato (ad es. #X: [x] [x] x% Color ).

Se la tua lingua non è in grado di leggere da STDIN e scrivere su STDOUT, crea una funzione (anonimo o lambda quando disponibile) che accetta l'input come argomento e restituisce il risultato.

Le direttive per il compilatore, le strutture richieste o consigliate per tutti o la maggior parte dei programmi creati nella lingua, ecc. Possono essere omesse. Ad esempio, #includee le usingdirettive (ma non #define) possono essere rimosse in linguaggi di tipo C, #/usr/bin/perl -optionsin Perl e

 Module Module1
      Sub Main()
      End Sub
 End Module

in VB.NET, ad esempio. Se importi spazi dei nomi o aggiungi direttive di inclusione, annotale nella tua risposta.

È abbastanza difficile, adesso? :)


Correlati a Code Golf: Laser su Stack Overflow.
— dmckee --- ex gattino moderatore,

Il comportamento dei mirror nel tuo esempio non ha senso. Hai un \ (scappare è rotto) che influenza la luce che sta andando dritto oltre. Sembrerebbe molto più sensato far entrare la luce sulla stessa fila dello specchio e lasciarla sulla stessa colonna, o viceversa. Allo stesso modo, la >luce sta catturando e la sta superando. E se la wparte superiore la attraversa R, anche la bparte inferiore. Infine (penso), ti sbagli sul fatto che i raggi non si incrociano. Per fare un esempio di una riga, quale sarebbe l'output corretto @R> B@?
— Peter Taylor,

Perché hai aggiunto una W casuale e hai interrotto tutta la spaziatura? E la luce non sta andando oltre, non è sicuro di cosa tu voglia dire.
— Ry,

@minitech, che @in basso a sinistra emette luce in tutte e quattro le direzioni, no? Quindi, in particolare, lo emette w. E non ho rotto nessuna spaziatura, almeno come reso in Chromium. Per quanto riguarda il superarlo, la mia modifica potrebbe chiarirlo.
— Peter Taylor,

5
minitech: come consiglio per compiti futuri: prima chiedi commenti nel Sandbox o nel Puzzle Lab che dovrebbero essere sufficienti per appianare incoerenze e problemi iniziali con i compiti. In questo modo, una volta pubblicata l'attività qui, saprai che è stata verificata (e forse implementata) già da altri.
— Joey,

Risposte:


2

Python, 602 559 614 caratteri

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

Modifica: risolto in modo da non aver bisogno di spazi finali.


Quasi - ma il risultato del test case non è corretto. Vedi: ideone.com/kUTxE . +1 comunque, è fantastico !!!
— Ry,

@minitech: penso che ciò abbia a che fare con la mancanza di spazi finali. Il mio codice presuppone che ogni riga abbia la stessa lunghezza, riempita con spazi se necessario. Non è così? In tal caso, come fai a sapere, ad esempio, fino a che punto la sorgente luminosa superiore va verso destra?
— Keith Randall,

Usando la lunghezza della linea più lunga per riempirla, puoi capire l'intera griglia. Tuttavia, anche se imbottito di spazi, dà questo (input # 4): ideone.com/kUTxE
— Ry

@minitech: ti manca uno spazio sulla 4a linea. Risolverò il mio codice per non richiedere spazi finali.
— Keith Randall,

Oh, wow, funziona !! Buon lavoro. Ma sì, sarebbe bello se non richiedesse imbottitura.
— 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
)

Ungolfed ma comunque fantastico! +1.
— Ry,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.