2-мерная трассировка лучей


9

Задача состоит в том, чтобы реализовать двухмерную программу трассировки лучей на основе текста.

Источники белого света являются @символами. R, GИ Bявляются светофильтры. /и \зеркала с отражающей способностью 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, создайте функцию (анонимную или лямбда, если она доступна), которая принимает входные данные в качестве аргумента и возвращает результат.

Директивы для компилятора, структуры, требуемые или рекомендуемые для всех или большинства программ, созданных на языке, и т. Д. Могут быть опущены. Например, #includeи usingдирективы (но не #define) могут быть удалены в языках стиля C, #/usr/bin/perl -optionsв Perl, и

 Module Module1
      Sub Main()
      End Sub
 End Module

в VB.NET, например. Если вы импортируете пространства имен или добавляете директивы include, запишите их в своем ответе.

Это достаточно сложно, сейчас? :)


Связано с Code Golf: Лазеры на переполнении стека.
dmckee --- котенок экс-модератора

Поведение зеркал в вашем примере не имеет смысла. У вас есть \ (побег разбит), влияющий на свет, который проходит мимо него. Казалось бы, гораздо разумнее, чтобы свет входил в один ряд с зеркалом и уходил в один столбец, или наоборот. Точно так же >захватывает свет, который проходит прямо мимо него. И если wсверху проходит через это R, то bи снизу. Наконец (я думаю), вы ошибаетесь из-за того, что лучи не пересекаются. Чтобы привести пример в одну строку, для чего нужен правильный вывод @R> B@?
Питер Тейлор

Почему вы добавили случайное w и разбили все пробелы? И свет не идет прямо мимо него, не уверен, что вы имеете в виду.
Ry-

@minitech, что @внизу слева излучает свет во всех четырех направлениях, не так ли? Так, в частности, это излучает это w. И я не нарушил интервалы, по крайней мере, как показано в Chromium. Что касается прохождения прямо мимо этого, мое редактирование может прояснить это.
Питер Тейлор

5
minitech: Как совет для будущих задач: сначала попросите комментарии в « Песочнице» или « Лаборатории головоломок», которых должно быть достаточно для устранения несоответствий и ранних проблем с задачами. Таким образом, разместив задачу здесь, вы узнаете, что она уже была проверена (и, возможно, реализована) некоторыми другими.
Джои

Ответы:


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: я думаю, что это связано с отсутствием пробелов. В моем коде предполагается, что каждая строка имеет одинаковую длину и дополняется пробелами при необходимости. Разве это не так? Если так, то как вы узнаете, например, как далеко верхний источник света идет вправо?
Кит Рэндалл

Используя длину самой длинной линии, вы можете вычислить всю сетку. Тем не менее, даже при заполнении пробелами, это дает (вход № 4): ideone.com/kUTxE
Ry-

@minitech: вам не хватает места на 4-й строке. Я исправлю свой код, чтобы он не требовал завершающих пробелов.
Кит Рэндалл

О, это работает !! Отличная работа. Но да, было бы хорошо, если бы он не требовал заполнения.
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-
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.