Traçado de raios bidimensional

9

O desafio é implementar um programa de rastreamento de raios bidimensional, baseado em texto.

Fontes de luz branca são @símbolos. R, GE Bsão filtros de luz. /e \são espelhos com 80% de refletividade. ?é um sensor de luz. >, <, ^E Vcombinar a luz na direção adequada (por exemplo, se um vermelho e um verde veio em uma >luz seria emitida para a direita e seria amarelo). Outros caracteres sem espaço em branco absorvem toda a luz. A luz é emitida a partir de @símbolos em quatro direções.

Quando o programa é executado, ele deve produzir saída igual à entrada, mas com raios rastreados. Como isso é bidimensional, e eu garanto na entrada que nenhum raio jamais se cruzará, não haverá problema com isso. Cada raio deve ser representado por uma letra; r = vermelho, g = verde, b = azul, c = ciano, m = magenta, y = amarelo, w = branco. Nunca haverá cores ternárias. A caixa é importante para diferenciá-la da entrada. Após essa saída, os valores da luz capturados pelos pontos de interrogação (em ordem de aparência, da esquerda para a direita, de cima para baixo) devem ser exibidos como porcentagens e cores. Por exemplo, esta entrada:

 /                  @
                    -
 \R>                 ?

 @B/

Deve dar a saída:

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

#1: 72% Magenta

Outro ponto importante a ser observado - quando duas cores são combinadas usando um "prisma" (as setas), a força da luz combinada se torna a força média das duas. A saída deve ser exatamente como especificado (por exemplo, #x: [x] [x] x% Cor ).

Se seu idioma não puder ler de STDIN e gravar em STDOUT, crie uma função (anônima ou lambda quando disponível) que aceite a entrada como argumento e retorne o resultado.

Diretivas para o compilador, estruturas necessárias ou recomendadas para todos ou a maioria dos programas criados no idioma etc. podem ser omitidas. Por exemplo, #includee usingdirectivas (mas não #define) pode ser removida em linguagens de estilo C, #/usr/bin/perl -optionsem Perl, e

 Module Module1
      Sub Main()
      End Sub
 End Module

no VB.NET, por exemplo. Se você importar namespaces ou adicionar diretivas de inclusão, observe-os em sua resposta.

Isso é difícil o suficiente agora? :)

Ry-
fonte
Relacionado ao Code Golf: Lasers on Stack Overflow.
dmckee --- ex-moderador gatinho
O comportamento dos espelhos no seu exemplo não faz sentido. Você tem um \ (escapar está quebrado) afetando a luz que está passando diretamente por ela. Parece muito mais sensato que a luz entre na mesma fila que o espelho e saia na mesma coluna, ou vice-versa. Da mesma forma, a >captura de luz passa diretamente por ela. E se o wde cima passa por isso R, o bde baixo deve. Finalmente (eu acho), você está errado sobre os raios não cruzarem. Para dar um exemplo de uma linha, qual seria a saída correta @R> B@?
Peter Taylor
Por que você adicionou w aleatório e quebrou todo o espaçamento? E a luz não está passando diretamente por ela, não tenho certeza do que você quer dizer.
Ry-
@minitech, that @ no canto inferior esquerdo emite luz nas quatro direções, não é? Então, em particular, emite isso w. E eu não quebrei nenhum espaçamento, pelo menos como renderizado no Chromium. Quanto a passar direto, minha edição pode esclarecer isso.
Peter Taylor
5
minitech: Como um conselho para tarefas futuras: solicite comentários na Sandbox primeiro ou no Puzzle Lab, que devem ser suficientes para solucionar inconsistências e problemas iniciais nas tarefas. Dessa forma, depois de postar a tarefa aqui, você saberá que ela já foi verificada (e talvez implementada) por outras pessoas.
Joey

Respostas:

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

Edit: fixo para que não precise de espaços à direita.

Keith Randall
fonte
Quase - mas o resultado do caso de teste está incorreto. Consulte: ideone.com/kUTxE . +1 de qualquer forma, é ótimo !!!
Ry-
@ minitech: Eu acho que isso tem a ver com a falta de espaços à direita. Meu código pressupõe que cada linha tenha o mesmo comprimento, preenchida com espaços, se necessário. Não é esse o caso? Se sim, então como você sabe, por exemplo, até onde a fonte de luz superior vai para a direita?
Keith Randall
Usando o comprimento da linha mais longa para preenchê-lo, você pode descobrir toda a grade. No entanto, mesmo quando preenchido com espaços, ele fornece o seguinte (entrada 4): ideone.com/kUTxE
Ry-
@ minitech: você está perdendo um espaço na quarta linha. Corrigirei meu código para não exigir espaços à direita.
Keith Randall
Oh, uau, funciona !! Bom trabalho. Mas sim, seria bom se não exigisse preenchimento.
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
fonte
Ungolfed mas ainda impressionante! +1.
Ry-