Enciende un Roguelike

14

Dado un tablero, escriba el programa o la función más corta para mostrar o devolver qué personajes están a la vista del jugador. Un personaje está a la vista si es posible dibujar una línea entre él y el jugador, sin cruzar ningún personaje que bloquee la visión.

Entrada:

  • @representa la posición del jugador. Solo habrá uno de estos en la entrada.
  • cualquier personaje que coincida con la expresión regular [#A-Z]bloquea la visión.
  • cualquier personaje que coincida [ a-z]permite la visión.
  • no habrá caracteres inválidos
  • tiene garantizada una entrada rectangular

Las líneas se definen de la siguiente manera:

  • definir vector para ser una magnitud y una dirección
  • una dirección es una de N, NE, E, SE, S, SW, W, NW
  • una magnitud es cuántos caracteres a lo largo de esa dirección para contar
  • dejemos que el vector inicial se llame d 1 ; el segundo vector se llamará d 2
  • uno de d 1 o d 2 debe tener una magnitud de 1; el otro puede tener cualquier magnitud
  • La dirección de d 1 debe ser adyacente a la dirección de d 2 (p. ej .: N y NE)

Una línea se define como todos los caracteres a lo largo de la ruta marcada aplicando d 1 , luego d 2 , d 1 , d 2 ...

Línea de muestra (dada por la .s):
d 1 = (magnitud: 4, dirección: E)
d 2 = (magnitud: 1, dirección NE)

               .....
          .....
     .....
@.... 

Salida:

  • cada personaje visible en la posición correcta, .sustituye al espacio.
  • Espacio para cada personaje no visible.

Entrada de muestra:

@         
    K     
 J        

    L   




         o

Salida correspondiente:

@.........
....K.....
.J.....   
..........
.. .L.....
..  . ....
... .. ...
...  .. ..
...   .  .
....  ..  

Entrada de muestra:

 B###M#  by 
 #Q   # Zmpq
 # # aaa    
@  m #      
# ##P#      
# ####      
# ####      
#M ###      
######      

Salida correspondiente:

.B  #M      
.# ..   Z pq
.#.#.aaa..  
@..m.#      
#.##P#      
 .#         
 .#         
 M.         
  #         

Entrada de muestra:

  w                 

     O  l   gg  rT  
   QQL      Ag  #b  
   qqqqq         XqQ
 x     V# f@aa      
   Y        aaa     
   uU  E  l TaKK    
  e  dd  FF d opi   
   e       d        

Salida correspondiente:

   ..........  .....
    ......... ..... 
     O..l...gg..rT  
...QQL......Ag..#b..
...qqqqq.........XqQ
        #.f@aa......
   Y........aaa.....
...uU..E..l.TaKK....
      d..FF.d.op    
     .... .d. ...   
Justin
fuente
3
OK ... los murciélagos, todo tipo de hongos, mohos y serpientes e incluso los espectros bloquean la línea de visión, pero los imitadores gigantes, los orcos de las montañas, los mastodontes, todo tipo de otras criaturas o incluso los vórtices de fuego no lo hacen.
John Dvorak
@ JanDvorak Era flojo y elegí letras mayúsculas para bloquear. Algo así como monstruos ALTOS vs monstruos cortos; cuál podrías ver más allá. Entonces sí.
Justin
1
No sé acerca de la mecánica cuántica, pero el murciélago y la momia gnomo podrían ser un trato fácil. Sin embargo, la mímica podría complicar aún más las cosas. Además, esas tres hormigas podrían ser divertidas, y el gran grupo de monstruos variados en el noreste ya podría saber de ti. Sí ... puede ser desagradable. En cuanto al n. ° 3: ¿dónde está mi pergamino de teleporación? Vaya, eso fue destruir la armadura.
John Dvorak
3
Solo una observación curiosa, pero si entiendo bien su definición de "línea", parece que hay algunos cuadrados que no serán visibles incluso sin ningún obstáculo. Por ejemplo, si el jugador está en (0, 0), ninguna línea puede alcanzar el cuadrado en (5, 12). Podría tener más sentido, por ejemplo, especificar alguna implementación canónica del algoritmo de línea de Bresenham para dibujar una línea entre dos puntos, y definir un cuadrado como oscurecido si la línea entre él y el jugador se cruza con un obstáculo.
Ilmari Karonen
1
@IlmariKaronen Tienes toda la razón. Así es como me gusta. :-).
Justin

Respuestas:

5

GolfScript, 171 caracteres

.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*

La entrada debe proporcionarse en STDIN.

El resultado de los ejemplos dados anteriormente es ligeramente diferente. Verifiqué las respuestas a mano y creo que son correctas.

Ejemplo 1:

@.........
....K.....
.J.....   
..........
.. .L.....
..  . ....
... .. ...
...  .. ..
...   .  .
....  ..  

Ejemplo 2

.B  #M      
.# ..   Z pq
.#.#.aaa..  
@..m.#      
#.##P#      
 .#         
 .#         
 M.         
  #         

Ejemplo 3

   ..........  .....
    ......... ..... 
     O..l...gg..rT  
...QQL......Ag..#b..
...qqqqq.........XqQ
        #.f@aa......
   Y........aaa.....
...uU..E..l.TaKK....
      d..FF.d.op    
     .... .d. ...   
Howard
fuente
Esto no parece funcionar para entradas de una sola línea (que son rectángulos válidos ...)
Justin
@Quincunx El código asume que finaliza su entrada con una nueva línea. Alternativamente anteponer n+el código.
Howard
4

Ruby - 510 caracteres

Todo un mamut; pero es mi primer intento en un 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

La entrada es por archivo especificado como argumento; Supongo que el archivo de entrada consta de un bloque rectangular de caracteres (incluidos los espacios finales incluidos) y que tiene una nueva línea final.

Esta versión hace un amplio uso de catch-throw para salir de bucles profundos; Posiblemente pueda mejorar las cosas con los bucles de verificación de límites.

Código no ofuscado:

# 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

Editar

Ilmari Karonen señala en los comentarios de la pregunta que el algoritmo de visión dado no ve todos los cuadrados, incluso cuando no hay obstáculo. Aquí hay una demostración de eso, a (40,40) de distancia del jugador.

@.......................................
........................................
........................................
........................................
........................................
............ ...........................
..............  ........................
............ ...   ..... ...............
.............. ...    .....  ...........
...............  ...     .....   .......
.................  ...      .....    ...
..................   ...       .....
..... . ............   ...        .....
.....................    ...         ...
...... . ..............    ...
...... .. ..............     ...
....... . ................     ...
....... .. ............. ..      ...
.......  .  .................      ...
........ .. ............... ..       ...
........  .  ............... ...       .
........  ..  ................ ..
.........  .  ................. ...
.........  ..  .................  ..
....... .   .   . ................ ...
..........  ..  ...................  ..
..........   .   ...................  ..
........ .   ..   . ..................
........ ..   .   .. ..................
...........   ..   .....................
......... .    .    . ..................
......... ..   ..   .. .................
......... ..    .    .. ................
.......... .    ..    . ................
.......... ..    .    .. ...............
.......... ..    ..    .. ..............
..........  .     .     .  .............
........... ..    ..    .. .............
........... ..     .     .. ............
...........  .     ..     .  ...........
Chowlett
fuente
Hmm Esto falla la prueba 3. Necesita depuración.
Chowlett
¿Estás seguro? Podría haber cometido un error ...
Justin
Bastante seguro: ¡puedo ver la V detrás de la pared! Creo que no estoy detectando la nueva línea después de la línea anterior.
Chowlett
Definitivamente no debería ver eso ...
Justin
Ah, fue un problema con mi entrada; Tuve un espacio extra después XqQ. Dicho esto, su respuesta dada para 3 no coincide en absoluto con el caso de prueba, al menos tiene una línea adicional en la parte superior y solo un espacio entre el Oy l.
Chowlett