2-мірне трасування променів


9

Завдання полягає у реалізації двовимірної програми трасування променів на основі тексту.

Джерелами білого світла є @символи. R, Gі Bє світлофільтрами. /і \є дзеркалами з 80% відбиткою. ?є датчиком світла. >, <, ^І Vоб'єднати світло в потрібному напрямку (наприклад , якщо один червоний і один зелений прийшов в >світ випромінюватиметься в напрямку вправо і буде жовтим кольором). Інші символи, що не мають пробілу, поглинають усе світло. Світло випромінюється від @символів у чотирьох напрямках.

Коли програма запущена, вона повинна видавати вихід такий же, як і вхід, але із простеженими променями. Оскільки це двовимірне, і я гарантую, що на вході жодні промені ніколи не перетнуться, з цим не буде проблем. Кожен промінь повинен бути представлений буквою; 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% Колір ).

Якщо ваша мова не може читати з STDIN і писати в STDOUT, створіть функцію (анонімна або лямбда-версія, якщо вона доступна), яка приймає введення як аргумент і повертає результат.

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

 Module Module1
      Sub Main()
      End Sub
 End Module

наприклад, у VB.NET Якщо ви імпортуєте простори імен або додаєте директиви, що включають, врахуйте їх у своїй відповіді.

Зараз це досить важко? :)


Пов’язані з Code Golf: Лазери на переливі стека.
dmckee --- кошеня колишнього модератора

Поведінка дзеркал у вашому прикладі не має сенсу. У вас \ (втеча зламана), що впливає на світло, яке йде прямо повз нього. Здавалося б, набагато розумніше, щоб світло входило в один і той же ряд з дзеркалом і залишалося в тому ж стовпці, або навпаки. Аналогічним чином >це захоплення світла, яке йде прямо повз нього. І якщо wвершина проходить через це R, то так само bі знизу. Нарешті (я думаю) ви помиляєтесь, що промені не перетинаються. Щоб навести однорядковий приклад, яким би був правильний вихід @R> B@?
Пітер Тейлор

Чому ви додали випадковий w і розіб’єте всі проміжки? І світло не йде прямо повз нього, не впевнений, що ти маєш на увазі.
Ри-

@minitech, що @внизу зліва випромінює світло у всіх чотирьох напрямках, чи не так? Так, зокрема, це випромінює w. І я не порушив жодного інтервалу, принаймні, як це було зроблено в Chromium. Щодо того, щоб пройти прямо повз нього, моя редакція може зрозуміти це.
Пітер Тейлор

5
minitech: Як порада щодо майбутніх завдань: Спершу запитайте коментарі в « Пісочниці» або « Логічній головоломці», яких вистачить, щоб виправити невідповідності та ранні проблеми із завданнями. Таким чином, коли ви опублікуєте завдання тут, ви дізнаєтесь, що його вже перевірили (а можливо, реалізували) деякі інші.
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 у будь-якому випадку, проте, це здорово !!!
Ри-

@minitech: Я думаю, це пов'язане з відсутністю пробілів. Мій код передбачає, що кожен рядок має однакову довжину, при необхідності прокладений пробілами. Хіба це не так? Якщо так, то як ви знаєте, наприклад, наскільки далеко верхнє джерело світла йде вправо?
Кіт Рендалл

Використовуючи довжину найдовшої лінії для накладки на неї, ви можете розібрати всю сітку. Тим НЕ менше, навіть тоді , коли доповнюються пробілами, вона дає це (вхід № 4): ideone.com/kUTxE
Рибаков

@minitech: пропущено пробіл у 4-му рядку. Я виправлю код, щоб не вимагати пробілів.
Кіт Рендалл

О, вау, це працює !! Хороша робота. Але так, було б добре, якби це не вимагало прокладки.
Ри-

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.
Ри-
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.