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