2次元光線追跡


9

課題は、テキストベースの2次元レイトレーシングプログラムを実装することです。

白色光源は@シンボルです。RGおよびB光フィルターです。/そして\80%の反射率のミラーです。?光センサーです。><^およびV適切な方向に光結合する(例えば、1つは赤と一つの緑が入ってきた場合に>右に向けて出射される光と、それは黄色であろう)。その他の非空白文字はすべての光を吸収します。@シンボルから4方向に光が放射されます。

プログラムを実行すると、入力と同じ出力が生成されますが、光線はトレースされます。これは2次元であり、入力では光線が交差しないことを保証しているため、問題はありません。各光線は文字で表す必要があります。r =赤、g =緑、b =青、c =シアン、m =マゼンタ、y =黄色、w =白。三元色はありません。ケーシングは、入力と区別するために重要です。その出力後、疑問符でキャプチャされた光の値(出現順に左から右、上から下へ)がパーセンテージと色として出力されます。たとえば、次の入力:

 /                  @
                    -
 \R>                 ?

 @B/

出力を与える必要があります:

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

#1: 72% Magenta

注意すべきもう1つの重要な点-「プリズム」(矢印)を使用して2つの色を組み合わせると、組み合わされた光の強度が2つの色の平均強度になります。出力は指定されたとおりでなければなりません(例:#x:[x] [x] x%Color)。

言語がSTDINから読み取ってSTDOUTに書き込めない場合は、入力を引数として受け入れ、結果を返す関数(使用可能な場合は匿名またはラムダ)を作成します。

コンパイラへのディレクティブ、言語で作成されたすべてまたはほとんどのプログラムに必要または推奨される構造などは省略できます。例えば、#include及びusing(ただしディレクティブ#define)は、Cスタイルの言語で除去することができる#/usr/bin/perl -optionsPerlで、かつ

 Module Module1
      Sub Main()
      End Sub
 End Module

たとえば、VB.NETで。名前空間をインポートするか、includeディレクティブを追加する場合は、回答にそれらを記録してください。

これで十分ですか?:)



この例のミラーの動作は意味がありません。\(エスケープが壊れている)に影響を与え、直進しているライトに影響を与えています。ミラーと同じ行に光が入り、同じ列に光を残す、またはその逆の方がはるかに理にかなっているようです。同様に、>直進している光を捉えています。そしてw、上からがそれを通過するR場合b、下からもそうです。最後に(私は思う)、あなたは光線が交差しないことについて間違っています。1行の例を示すと、正しい出力は何になります@R> B@か?
Peter Taylor、

ランダムなwを追加してすべての間隔を壊したのはなぜですか?そして、光はそれを通り過ぎて行きません、あなたが何を意味するのかわかりません。
Ry-

@minitech、@左下が四方すべてに光を放っていますね。したがって、特に、それはそれを放出しwます。そして、少なくともChromiumでレンダリングしたときのように、間隔を空けていません。直進すると、私の編集はそれをクリアするかもしれません。
Peter Taylor、

5
minitech:将来のタスクのためのアドバイスとしては:にコメントを求めるサンドボックスまたはラボパズル矛盾やタスクを早期に問題を鉄に十分べき最初。そうすれば、ここにタスクを投稿すると、すでに他のユーザーによってチェックされている(そしておそらく実装されている)ことがわかります。
Joey、

回答:


2

Python、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:これは末尾のスペースがないことに関係していると思います。私のコードでは、各行が同じ長さであり、必要に応じてスペースが埋め込まれていると想定しています。そうではありませんか?もしそうなら、どのようにして、例えば、上部の光源がどれだけ右に行くかを知っていますか?
キースランダル、

最長の線の長さを使用してパディングすると、グリッド全体を把握できます。ただし、スペースを埋め込ん
Ry-

@minitech:4行目のスペースが不足しています。末尾のスペースを必要としないようにコードを修正します。
Keith Randall、

ああ、すごい!よくやった。しかし、はい、パディングを必要としないならそれは良いでしょう。
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
)

ゴルフをしていませんが、それでも素晴らしいです!+1。
Ry-
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.