2-dimensionale Strahlverfolgung

9

Die Herausforderung besteht darin, ein zweidimensionales Raytracing-Programm auf Textbasis zu implementieren.

Weiße Lichtquellen sind @Symbole. R, GUnd Bsind Lichtfilter. /und \sind Spiegel mit 80% Reflexionsvermögen. ?ist ein Lichtsensor. >, <, ^Und Vin der entsprechenden Richtung Licht kombinieren ( zum Beispiel , wenn eine rote und eine grüne kam in ein >das Licht würde nach rechts emittiert werden , und es wäre gelb). Andere Nicht-Leerzeichen absorbieren alles Licht. Licht wird von @Symbolen in vier Richtungen emittiert .

Wenn das Programm ausgeführt wird, sollte es die gleiche Ausgabe wie die Eingabe erzeugen, jedoch mit verfolgten Strahlen. Da dies zweidimensional ist und ich garantiere, dass sich in der Eingabe niemals Strahlen kreuzen werden, wird es damit kein Problem geben. Jeder Strahl sollte durch einen Buchstaben dargestellt werden; r = rot, g = grün, b = blau, c = cyan, m = magenta, y = gelb, w = weiß. Es wird niemals ternäre Farben geben. Das Gehäuse ist wichtig, um es vom Eingang zu unterscheiden. Nach dieser Ausgabe sollten die von den Fragezeichen erfassten Lichtwerte (in der Reihenfolge ihres Erscheinungsbilds von links nach rechts von oben nach unten) als Prozentsätze und Farben ausgegeben werden. Zum Beispiel diese Eingabe:

 /                  @
                    -
 \R>                 ?

 @B/

Sollte die Ausgabe geben:

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

#1: 72% Magenta

Ein weiterer wichtiger Punkt: Wenn zwei Farben mit einem "Prisma" (den Pfeilen) kombiniert werden, wird die Stärke des kombinierten Lichts zur durchschnittlichen Stärke der beiden. Die Ausgabe muss genau wie angegeben sein (z. B. #x: [x] [x] x% Farbe ).

Wenn Ihre Sprache nicht aus STDIN lesen und in STDOUT schreiben kann, erstellen Sie eine Funktion (anonym oder Lambda, falls verfügbar), die die Eingabe als Argument akzeptiert und das Ergebnis zurückgibt.

Anweisungen an den Compiler, Strukturen, die für alle oder die meisten in der Sprache erstellten Programme usw. erforderlich oder empfohlen sind, können weggelassen werden. Zum Beispiel #includeund usingRichtlinien (aber nicht #define) in C-Stil Sprachen, entfernt werden #/usr/bin/perl -optionsin Perl, und

 Module Module1
      Sub Main()
      End Sub
 End Module

in VB.NET zum Beispiel. Wenn Sie Namespaces importieren oder Include-Anweisungen hinzufügen, notieren Sie diese bitte in Ihrer Antwort.

Ist das jetzt schwer genug? :) :)

Ry-
quelle
Im Zusammenhang mit Code Golf: Laser bei Stapelüberlauf.
dmckee --- Ex-Moderator Kätzchen
Das Verhalten der Spiegel in Ihrem Beispiel macht keinen Sinn. Sie haben ein \ (Flucht ist gebrochen), das das Licht beeinflusst und direkt daran vorbeigeht. Es erscheint viel sinnvoller, wenn Licht in dieselbe Reihe wie der Spiegel fällt und in derselben Spalte verbleibt oder umgekehrt. In ähnlicher Weise >fängt das Licht ein, das direkt daran vorbeigeht. Und wenn das wvon oben durchgeht R, sollte das auch bvon unten gehen. Schließlich (glaube ich) liegen Sie falsch, wenn sich Strahlen nicht kreuzen. Um ein einzeiliges Beispiel zu geben, wofür wäre die richtige Ausgabe @R> B@?
Peter Taylor
Warum haben Sie ein zufälliges w hinzugefügt und den gesamten Abstand unterbrochen? Und das Licht geht nicht direkt daran vorbei, nicht sicher, was du meinst.
Ry
@minitech, @das unten links Licht in alle vier Richtungen aussendet, nicht wahr ? Insbesondere strahlt es das aus w. Und ich habe keinen Abstand gebrochen, zumindest nicht wie in Chrom gerendert. Wenn ich direkt daran vorbeikomme, kann meine Bearbeitung dies klären.
Peter Taylor
5
minitech: Als Ratschlag für zukünftige Aufgaben: Bitten Sie zuerst in der Sandbox oder im Puzzle Lab um Kommentare, die ausreichen sollten, um Inkonsistenzen und frühe Probleme mit Aufgaben auszubügeln. Auf diese Weise wissen Sie, sobald Sie die Aufgabe hier veröffentlicht haben, dass sie bereits von einigen anderen überprüft (und möglicherweise implementiert) wurde.
Joey

Antworten:

2

Python, 602 559 614 Zeichen

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

Bearbeiten: behoben, so dass keine nachgestellten Leerzeichen benötigt werden.

Keith Randall
quelle
Fast - aber das Testergebnis ist falsch. Siehe: ideone.com/kUTxE . +1 trotzdem, es ist großartig !!!
Ry
@minitech: Ich denke, das hat mit dem Mangel an nachgestellten Leerzeichen zu tun. In meinem Code wird davon ausgegangen, dass jede Zeile dieselbe Länge hat und bei Bedarf mit Leerzeichen aufgefüllt wird. Ist das nicht der Fall? Wenn ja, woher wissen Sie dann, wie weit die obere Lichtquelle nach rechts geht?
Keith Randall
Wenn Sie die Länge der längsten Linie zum Auffüllen verwenden, können Sie das gesamte Raster ermitteln. Selbst wenn es mit Leerzeichen aufgefüllt ist, gibt es Folgendes
Ry
@minitech: In der 4. Zeile fehlt ein Leerzeichen. Ich werde meinen Code so korrigieren, dass keine nachgestellten Leerzeichen erforderlich sind.
Keith Randall
Oh, wow es funktioniert !! Gut gemacht. Aber ja, es wäre gut, wenn es keine Polsterung erfordern würde.
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
quelle
Ungolfed aber trotzdem genial! +1.
Ry