Light up a Roguelike
GolfScript, 171 characters
.n?):L)[n*.]*1/:I'@'?{\+[{1$+}*]..{I=26,{65+}%"#
"+\?)}??)<}+{[L~.).)1L)L.(-1L~]>2<`{[.[~\]]{1/~2$*+L*.-1%}%\;~}+L,%~}8,%%{|}*`{1$?)I@=.n=@|\.' '={;'.'}*' 'if}+I,,%''*n%n*
The input must be provided on STDIN.
The output for the examples given above is slightly different. I verified the responses by hand and think they are correct.
Example 1:
@.........
....K.....
.J.....
..........
.. .L.....
.. . ....
... .. ...
... .. ..
... . .
.... ..
Example 2:
.B #M
.# .. Z pq
.#.#.aaa..
@..m.#
#.##P#
.#
.#
M.
#
Example 3:
.......... .....
......... .....
O..l...gg..rT
...QQL......Ag..#b..
...qqqqq.........XqQ
#.f@aa......
Y........aaa.....
...uU..E..l.TaKK....
d..FF.d.op
.... .d. ...
Ruby - 510 chars
Quite a mammoth; but it is my first ever attempt at a golf.
m=$<.read;w,s,o,p=m.index(?\n)+1,m.size,m.dup.gsub(/[^@\n]/,' '),m.index(?@);d=[-w,1-w,1,w+1,w,w-1,-1,-1-w];0.upto(7){|i|x=d[i];[i-1,i+1].each{|j|y=d[j%8];[1,nil].each{|l|t=0;catch(:a){loop{c,f,r=p,1,nil;catch(:b){loop{(l||r)&&(1.upto(t){|u|c+=x;n=!(0...s).include?(c)||m[c]==?\n;n&&throw(f ?:a: :b);o[c]=m[c]==" "??.: m[c];a=m[c]=~/[#A-Z]/;a&&throw(f ?:a: :b)};f=nil);r=1;c+=y;n=!(0...s).include?(c)||m[c]==?\n;n&&throw(f ?:a: :b);o[c]=m[c]==" "??.: m[c];a=m[c]=~/[#A-Z]/;a&&throw(f ?:a: :b)}};t+=1}}}}};$><<o
Input is by file specified as argument; I assume the input file to consist of a rectangular block of characters (so, trailing spaces included), and to have a trailing newline.
This version makes extensive use of catch-throw
to exit deep loops; I can possibly improve matters with bounds-checked loops instead.
Unobfuscated code:
# Read the map in
map = $<.read
# Determine its width and size
width = map.index("\n")+1
size = map.size
# Create a blank copy of the map to fill in with visible stuff
output = map.dup.gsub /[^@\n]/,' '
# Locate the player
player = map.index('@')
dirs = [
-width, # N
1-width, # NE
1, # E
width+1, # SE
width, # S
width-1, # SW
-1, # W
-1-width # NW
]
0.upto(7) do |i1|
d1 = dirs[i1]
[i1-1, i1+1].each do |i2|
d2 = dirs[i2%8]
# Stepping by 0,1,2... in d1, work along the line.
# Write the cell value into the duplicate map, then break if it's
# a "solid" character.
#
# Extensive use of catch-throw lets us exit deep loops.
# For convenience of notation, instead of having either d1 or d2
# be magnitude 1, and always doing d1,d2,d1... - I have d2 always
# being magnitude 1, and doing either d1,d2,d1 or d2,d1,d2...
# Loop twice - first with d1,d2,d1... second with d2,d1,d2...
[true,false].each do |long_first|
step = 0
catch(:all_done) do
# This loop increments step each iteration, being the magnitude of d1
loop do
cell = player
first = true # True until we've done the first d1
later = false # True once we've done the first d2
catch(:done) do
# This loop repeatedly applies d1 and d2
loop do
if long_first || later # Don't apply d1 first if starting with d2
1.upto(step) do |dd1|
cell += d1 # Move one cell in d1
invalid = !(0...size).include?(cell) || map[cell]=="\n" # Out of range
throw :all_done if first && invalid # No point trying a longer step if the
# first application of d1 is out of range
throw :done if invalid # No point continuing with this step length
output[cell]=map[cell] == " " ? '.' : map[cell] # Transfer visble character
wall = map[cell]=~/[#A-Z]/ # Hit a wall?
throw :all_done if first && wall # Drop out as before
throw :done if wall
end
first = false
end
later=true
# Now repeat above for the single d2 step
cell += d2
invalid = !(0...size).include?(cell) || map[cell]=="\n"
throw :all_done if first && invalid
throw :done if invalid
output[cell]=map[cell] == " " ? '.' : map[cell]
wall = map[cell]=~/[#A-Z]/
throw :all_done if first && wall
throw :done if wall
end
end
step += 1
end
end
end
end
end
puts output
Edit
Ilmari Karonen notes in the question comments that the given vision algorithm doesn't see all squares, even when there's no obstacle. Here's a demonstration of that, out to (40,40) away from the player.
@.......................................
........................................
........................................
........................................
........................................
............ ...........................
.............. ........................
............ ... ..... ...............
.............. ... ..... ...........
............... ... ..... .......
................. ... ..... ...
.................. ... .....
..... . ............ ... .....
..................... ... ...
...... . .............. ...
...... .. .............. ...
....... . ................ ...
....... .. ............. .. ...
....... . ................. ...
........ .. ............... .. ...
........ . ............... ... .
........ .. ................ ..
......... . ................. ...
......... .. ................. ..
....... . . . ................ ...
.......... .. ................... ..
.......... . ................... ..
........ . .. . ..................
........ .. . .. ..................
........... .. .....................
......... . . . ..................
......... .. .. .. .................
......... .. . .. ................
.......... . .. . ................
.......... .. . .. ...............
.......... .. .. .. ..............
.......... . . . .............
........... .. .. .. .............
........... .. . .. ............
........... . .. . ...........