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.