Trazado de rayos bidimensional

9

El desafío es implementar un programa de trazado de rayos bidimensional, basado en texto.

Las fuentes de luz blanca son @símbolos. R, Gy Bson filtros de luz. /y \son espejos con 80% de reflectividad. ?Es un sensor de luz. >, <, ^Y Vcombinar la luz en la dirección apropiada (por ejemplo, si uno rojo y otro verde entró en una >la luz se emite hacia la derecha y sería amarillo). Otros personajes que no son espacios en blanco absorben toda la luz. Los @símbolos emiten luz en cuatro direcciones.

Cuando se ejecuta el programa, debe producir una salida igual que la entrada, pero con rayos trazados. Debido a que esto es bidimensional, y garantizo que en la entrada no se cruzarán nunca los rayos, no habrá ningún problema con eso. Cada rayo debe estar representado por una letra; r = rojo, g = verde, b = azul, c = cian, m = magenta, y = amarillo, w = blanco. No habrá colores ternarios, nunca. La carcasa es importante para diferenciarla de la entrada. Después de esa salida, los valores de luz capturados por los signos de interrogación (en orden de su aparición, de izquierda a derecha de arriba a abajo) se deben generar como porcentajes y colores. Por ejemplo, esta entrada:

 /                  @
                    -
 \R>                 ?

 @B/

Debería dar la salida:

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

#1: 72% Magenta

Otro punto importante a tener en cuenta: cuando se combinan dos colores usando un "prisma" (las flechas), la fuerza de la luz combinada se convierte en la fuerza promedio de los dos. La salida debe ser exactamente como se especifica (p. Ej. #X: [x] [x] x% Color ).

Si su idioma no puede leer desde STDIN y escribir en STDOUT, cree una función (anónima o lambda cuando esté disponible) que acepte la entrada como argumento y devuelva el resultado.

Se pueden omitir las directivas para el compilador, las estructuras requeridas o recomendadas para todos o la mayoría de los programas creados en el lenguaje, etc. Por ejemplo, #includey las usingdirectivas (pero no #define) pueden eliminarse en lenguajes de estilo C, #/usr/bin/perl -optionsen Perl y

 Module Module1
      Sub Main()
      End Sub
 End Module

en VB.NET, por ejemplo. Si importa espacios de nombres o agrega directivas de inclusión, anótelas en su respuesta.

¿Ya es bastante difícil? :)

Ry-
fuente
Relacionado con Code Golf: Láseres en desbordamiento de pila.
dmckee --- ex-gatito moderador
El comportamiento de los espejos en su ejemplo no tiene sentido. Tiene un \ (el escape está roto) que afecta a la luz que pasa directamente. Parecería mucho más sensato que la luz entrara en la misma fila que el espejo y se fuera en la misma columna, o viceversa. Del mismo modo, >está capturando la luz que pasa directamente. Y si la wde arriba pasa por eso R, también la bde abajo. Finalmente (creo), te equivocas con los rayos que no se cruzan. Para dar un ejemplo de una línea, ¿para qué sería la salida correcta @R> B@?
Peter Taylor
¿Por qué agregaste una w aleatoria y rompiste todo el espacio? Y la luz no pasa directamente, no estoy seguro de lo que quieres decir.
Ry-
@minitech, que @en la parte inferior izquierda emite luz en las cuatro direcciones, ¿no? Entonces, en particular, emite eso w. Y no he roto ningún espacio, al menos como se muestra en Chromium. En cuanto a pasar directamente, mi edición puede aclarar eso.
Peter Taylor
55
minitech: como consejo para futuras tareas: primero solicite comentarios en el Sandbox o Puzzle Lab, lo que debería ser suficiente para resolver inconsistencias y problemas iniciales con las tareas. De esa manera, una vez que publique la tarea aquí, sabrá que ya ha sido verificada (y tal vez implementada) por otros.
Joey

Respuestas:

2

Python, 602 559 614 caracteres

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

Editar: corregido para que no necesite espacios finales.

Keith Randall
fuente
Casi, pero el resultado del caso de prueba es incorrecto. Ver: ideone.com/kUTxE . De todos modos, ¡+1, es genial!
Ry:
@minitech: Creo que esto tiene que ver con la falta de espacios finales. Mi código asume que cada fila tiene la misma longitud, rellenada con espacios si es necesario. No es ese el caso? Si es así, ¿cómo sabe, por ejemplo, qué tan lejos va la fuente de luz superior a la derecha?
Keith Randall
Usando la longitud de la línea más larga para rellenarla, puede descubrir toda la cuadrícula. Sin embargo, incluso cuando se rellena con espacios, da esto (entrada # 4): ideone.com/kUTxE
Ry-
@minitech: te falta un espacio en la cuarta línea. Arreglaré mi código para que no requiera espacios finales.
Keith Randall
¡Oh, wow, funciona! Buen trabajo. Pero sí, sería bueno si no requiriera relleno.
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
fuente
¡Sin golf, pero aún así increíble! +1.
Ry: