2 차원 레이 트레이싱


9

문제는 텍스트 기반의 2 차원 레이 트레이싱 프로그램을 구현하는 것입니다.

백색광 원은 @기호입니다. R, GB빛 필터입니다. /\80 %의 반사율 거울입니다. ?광 센서입니다. >, <, ^V적절한 방향으로 빛을 결합 (예를 들어, 한 빨간색과 하나 개의 녹색가에 온 경우 >오른쪽으로 방출 될 빛과 노란색으로 될 것이다). 공백이 아닌 다른 문자는 모든 빛을 흡수합니다. 빛은 @기호에서 방향으로 방출됩니다 .

프로그램이 실행되면 입력과 동일하지만 추적 광선을 사용하여 출력을 생성해야합니다. 이것은 2 차원이며, 입력에서 광선이 교차하지 않도록 보장하기 때문에 아무런 문제가 없습니다. 각 광선은 문자로 나타내야합니다. r = 적색, g = 녹색, b = 청색, c = 청록색, m = 자홍색, y = 황색, w = 백색. 삼원색은 전혀 없습니다. 케이싱은 입력과 구별하기 위해 중요합니다. 그 출력 후, 물음표에 의해 포착 된 빛의 값 (모양의 순서, 왼쪽에서 오른쪽으로 위에서 아래로)이 백분율 및 색상으로 출력되어야합니다. 예를 들어이 입력은 다음과 같습니다.

 /                  @
                    -
 \R>                 ?

 @B/

출력을 제공해야합니다 :

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

#1: 72% Magenta

주목해야 할 또 다른 중요한 점- "프리즘"(화살표)을 사용하여 두 가지 색상을 결합하면 결합 된 빛의 강도가 두 가지의 평균 강도가됩니다. 출력은 지정된대로 정확하게 입력 해야 합니다 (예 : #x : [x] [x] x % Color ).

언어가 STDIN에서 읽고 STDOUT에 쓸 수없는 경우 입력을 인수로 승인하고 결과를 리턴하는 함수 (사용 가능한 경우 익명 또는 람다)를 작성하십시오.

컴파일러 지시어, 언어로 작성된 모든 또는 대부분의 프로그램에 필요하거나 권장되는 구조 등은 생략 할 수 있습니다. 예를 들어 #includeusing지침 (그러나 #define), C 스타일의 언어로 제거 될 수 #/usr/bin/perl -options펄, 그리고

 Module Module1
      Sub Main()
      End Sub
 End Module

예를 들어 VB.NET에서. 네임 스페이스를 가져 오거나 포함 지시문을 추가하는 경우 답에 유의하십시오.

지금은 충분하지 않습니까? :)



귀하의 예에서 거울의 동작은 의미가 없습니다. 똑바로 지나가는 빛에 영향을 미치는 \ (이스케이프가 깨짐)가 있습니다. 거울과 같은 줄에 빛을 비추고 같은 기둥에 그대로 두거나 그 반대의 경우가 훨씬 더 현명 해 보일 것입니다. 마찬가지로 >바로 지나가는 빛을 포착합니다. 그리고 w위에서 부터 시작하면 아래에서 시작 R해야합니다 b. 마지막으로 (제 생각에는) 광선이 교차하지 않는 것이 잘못되었습니다. 한 줄짜리 예를 들어, 올바른 출력은 @R> B@무엇입니까?
피터 테일러

왜 임의의 w를 추가하고 모든 간격을 did습니까? 그리고 빛이 그것을 지나치지 않고, 무슨 뜻인지 확실하지 않습니다.
Ry-

@minitech, @왼쪽 아래에서 네 방향 모두에서 빛을 방출합니다. 특히, 그것은 그것을 방출합니다 w. 그리고 적어도 크롬에서 렌더링 된 간격을 끊지 않았습니다. 똑바로지나 가면 편집 내용이 명확해질 수 있습니다.
피터 테일러

5
minitech : 향후 과제에 대한 조언 : Sandbox 또는 Puzzle Lab에 먼저 의견을 요청 하여 불일치와 과제의 초기 문제를 해결하기에 충분합니다. 이런 식으로 작업을 여기에 게시하면 다른 사람이 이미 작업을 확인했거나 구현했을 수 있습니다.
Joey

답변:


2

파이썬, (602 개) 559 (614) 문자

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

편집 : 후행 공백이 필요하지 않도록 수정되었습니다.


거의-그러나 테스트 케이스 결과가 올바르지 않습니다. ideone.com/kUTxE를 참조하십시오 . 어쨌든 +1, 대단합니다 !!!
Ry-

@minitech : 이것은 후행 공백이 부족한 것과 관련이 있다고 생각합니다. 내 코드는 각 행의 길이가 같고 필요한 경우 공백으로 채워져 있다고 가정합니다. 그렇지 않습니까? 그렇다면 어떻게 상부 광원이 오른쪽으로 얼마나 멀리 가는지 어떻게 알 수 있습니까?
Keith Randall

가장 긴 선의 길이를 사용하여 채움으로써 전체 그리드를 파악할 수 있습니다. 그러나, 공백으로 채워하더라도,이 (입력 # 4)를 제공한다 : ideone.com/kUTxE
Ry-

@ minitech : 네 번째 줄에 공백이 없습니다. 후행 공백이 필요하지 않도록 코드를 수정합니다.
Keith Randall

오, 와우 작동합니다 !! 잘 했어. 그러나 패딩이 필요하지 않으면 좋을 것입니다.
Ry-

2

에프#

#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
)

골퍼가 없지만 여전히 굉장합니다! +1.
Ry-
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.