Renderice la vista de arriba hacia abajo de un techo de cadera en ASCII

23

Primero, alguna terminología ( fuente ):

  • Un techo de cadera es (citando Wikipedia) "un tipo de techo donde todos los lados se inclinan hacia las paredes, generalmente con una pendiente bastante suave"
  • Una pendiente es una superficie plana que forma parte del techo.
  • Una cresta es un borde donde se encuentran dos pendientes opuestas del techo
  • Una cadera es un borde convexo donde se encuentran dos pendientes que pertenecen a paredes perpendiculares
  • Un valle es un borde cóncavo donde se encuentran dos pendientes que pertenecen a paredes perpendiculares
  • Las caderas y los valles se denominarán colectivamente bordes diagonales.

Posible entrada:

 ** * ***
******** 
 ** *  **

Salida correspondiente:

    +-------+   +---+   +-----------+
    |\     /|   |\ /|   |\         /|
    | \   / |   | V |   | \   ^---< |
    |  \ /  |   | | |   |  \ / \   \|
+---+   V   +---+ | +---+   X   +---+
|\   \  |  /     \|/     \ / \  |
| >---> | <-------X-------V   > |
|/   /  |  \     /|\         /| |
+---+   ^   +---+ | +-------+ | +---+
    |  / \  |   | | |       | |/   /|
    | /   \ |   | ^ |       | /---< |
    |/     \|   |/ \|       |/     \|
    +-------+   +---+       +-------+

Un par de casos de prueba más:

** ***   *    *   * *
*       ***   *****  
    ** *****  *****  
* *  *  ***  *** *** 
* ****   *     * *   

Salidas correspondientes:

+-------+   +-----------+           +---+               +---+           +---+   +---+
|\     /|   |\         /|           |\ /|               |\ /|           |\ /|   |\ /|
| \---< |   | >-------< |           | V |               | V |           | V |   | X |
| |\   \|   |/         \|           | | |               | | |           | | |   |/ \|
| | +---+   +-----------+       +---+ | +---+           | | +-----------+ | |   +---+
| | |                           |\   \|/   /|           | |/             \| |
| ^ |                           | \   V   / |           | <               > |
|/ \|                           |  \     /  |           |  \             /  |
+---+           +-------+   +---+   \   /   +---+       |   \-----------/   |
                |\     /|   |\   \   \ /   /   /|       |   |\         /|   |
                | >---/ |   | >--->   X   <---< |       |   | \       / |   |
                |/   /| |   |/   /   / \   \   \|       |   |  \     /  |   |
+---+   +---+   +---+ | |   +---+   /   \   +---+   +---+   ^   +---+   ^   +---+
|\ /|   |\ /|       | | |       |  /     \  |       |\   \ / \  |   |  / \ /   /|
| V |   | V |       | | |       | /   ^   \ |       | >---V   > |   | <   V---< |
| | |   | | |       | | |       |/   /|\   \|       |/       /| |   | |\       \|
| | |   | | +-------+ | |       +---+ | +---+       +-------+ | |   | | +-------+
| | |   | |/         \| |           | | |                   | | |   | | |
| ^ |   | /-----------\ |           | ^ |                   | ^ |   | ^ |
|/ \|   |/             \|           |/ \|                   |/ \|   |/ \|
+---+   +---------------+           +---+                   +---+   +---+

Su entrada será un mapa de bits, una matriz 2D de píxeles cuadrados, del área que debe estar cubierta por el techo. Puede suponer que el límite de esta área será una curva de Jordania, es decir, continua y sin intersección propia, es decir, el área cubierta será continua, sin agujeros y nunca habrá cuatro paredes que se unan en un solo punto. Los formatos de entrada válidos incluyen una sola cadena con separadores de nueva línea, una lista de cadenas y una matriz 2D de caracteres o booleanos.

Las reglas para construir el techo son:

  • Cada segmento recto del área cubierta (de ahora en adelante denominado muro) debe tener exactamente una pendiente contigua. La pendiente se levantará lejos de la pared. Cada pendiente debe tener al menos un muro contiguo, y todos los muros adyacentes a una pendiente deben ser colineales.
  • Todas las pendientes deben tener el mismo ángulo (distinto de cero) contra la superficie horizontal. Es decir, deben tener el mismo tono.
  • Las pendientes formarán una superficie cuyo límite es el límite del área cubierta. Es decir, no se pueden utilizar otras superficies que no sean las pendientes.
  • Cualquier escenario en el que esta especificación permita más de una solución (hasta escala vertical) se considera un error en la especificación. Cualquier corrección se aplica retroactivamente.

De manera equivalente, el techo puede definirse por la regla de que cada punto del techo se coloca lo más alto posible sin exceder la pendiente máxima para ese techo, medido utilizando la distancia de Chebyshev en una vista de arriba hacia abajo.

Su salida será una representación artística ASCII del techo, ya sea una sola cadena que contenga caracteres de nueva línea o una matriz de cadenas, cada una de las cuales denota una sola línea de la salida. El techo se debe representar en una vista de arriba hacia abajo a una escala de 4x, es decir, cada cuadrado del plano de planta debe afectar un área de 5x5 de la salida de modo que las esquinas de esta área de 5x5 se compartan con los cuadrados vecinos (de modo que cada el carácter de esquina se ve afectado por cuatro cuadrados de entrada diferentes), como lo indica la salida de ejemplo. Se permite un espacio en blanco adicional siempre que se conserve la forma de salida. Los caracteres en la salida serán:

  • se utilizará un marcador de nueva línea definido por el entorno (generalmente U + 000A, U + 000D o un par de ambos) si la salida tiene la forma de una sola cadena
  • (Espacio U + 0020) representa un punto fuera de un área cubierta o un punto interior a una pendiente
  • + (U + 002B signo más) representa un punto con dos paredes perpendiculares adyacentes
  • - (U + 002D guión-menos) representa un muro o una cresta orientada horizontalmente (este-oeste)
  • / (U + 002F solidus) representa una cadera o un valle orientado de nordeste a sureste, o un punto contiguo a dos de esos
  • < (U + 003C menor que el signo) representa un punto con dos bordes diagonales unidos al este
  • > (U + 003E mayor que el signo) representa un punto con dos bordes diagonales unidos al oeste
  • \ (U + 005C solidus inverso) representa una cadera o un valle orientado del noroeste al sureste, o un punto contiguo a dos de esos
  • ^ (U + 005E acento circunflejo) representa un punto con dos bordes diagonales unidos al sur
  • V (U + 0056 letra mayúscula latina v) representa un punto con dos bordes diagonales unidos al norte
  • X (U + 0058 letra mayúscula latina x) representa un punto con bordes diagonales unidos a él en los cuatro lados
  • | (U + 007C barra vertical) representa una pared o una cresta orientada verticalmente (norte-sur)

Tenga en cuenta que no es posible que un número impar de aristas diagonales termine en el mismo punto (excepto en paredes). Podemos visualizar eso dividiendo la vecindad de cada punto en la pendiente norte + pendiente sur y en la pendiente este + pendiente oeste. El límite entre ambas particiones tiene que estar compuesto de bordes diagonales.

Si su entorno utiliza una codificación de caracteres incompatible con ASCII, puede utilizar los caracteres equivalentes (el mismo glifo o el más cercano disponible) en la codificación de caracteres que utiliza su entorno.

La siguiente implementación de referencia (fea) en Ruby es normativa con respecto a la salida sin espacios en blanco. Tenga en cuenta particularmente el rendermétodo:

def pad ary
  row = ary.first.map{-1}
  ([row] + ary + [row]).map{|r| [-1] + r + [-1]}
end

def parse str
  str.split("\n").map{|r| r.chars.map(&{" " => -1, "*" => Float::INFINITY})}
end

def squares ary, size
  ary.each_cons(size).map do |rows|
    rows.map{|row| row.each_cons(size).to_a}.transpose
  end
end

def consmap2d ary, size
  squares(ary, size).map{|sqrow| sqrow.map{|sq| yield sq}}
end

def relax ary
  loop do
    new = consmap2d(pad(ary), 3){|sq| sq[1][1] == -1 ? -1 : sq.flatten.min + 1}
    return new if new == ary
    ary = new
  end
end

def semidouble ary, op
  ary.zip(ary.each_cons(2).map{|r1,r2|r1.zip(r2).map(&op)}).flatten(1).compact.transpose
end

def heightmap str
  relax(semidouble(semidouble(semidouble(semidouble(pad(parse str),:max),:max),:min),:min))
end

def render heightmap
  puts consmap2d(heightmap, 3){|sq|
    next " " if sq[1][1] == -1
    hwall = sq[0][1] == -1 || sq[2][1] == -1
    vwall = sq[1][0] == -1 || sq[1][2] == -1
    next "+" if hwall && vwall
    next "-" if hwall
    next "|" if vwall
    next "+" if sq.flatten.min == -1

    nws = sq[0][1] == sq[1][0]
    nes = sq[0][1] == sq[1][2]
    sws = sq[2][1] == sq[1][0]
    ses = sq[2][1] == sq[1][2]

    next "X"  if nws && nes && sws && ses
    next "V"  if nws && nes
    next "^"  if sws && ses
    next ">"  if nws && sws
    next "<"  if nes && ses
    next "/"  if nes && sws
    next "\\" if nws && ses
    next " "  if sq[0][1] != sq[2][1] || sq[1][0] != sq[1][2]
    next "|"  if sq[0][1] == sq[1][1]
    next "-"  if sq[1][0] == sq[1][1]
    ??
  }.map(&:join)
end

render heightmap $<.read if __FILE__ == $0 
John Dvorak
fuente
1
Debería agregar más casos de prueba.
mbomb007
@ mbomb007 Añadido. Dado el espacio que ocupan, ¿debo agregar más?
John Dvorak
@ JanDvorak Quizás agregue el caso de prueba *. De lo contrario, probablemente sea suficiente.
mbomb007
¿Es [[0,1,1],[1,0,1],[1,1,1]]una entrada válida? (La entrada no tiene "agujeros", pero hay una esquina molesta cerca de la auto-intersección.)
Lynn
@ Lynn No necesita preocuparse por ese caso, no es una entrada válida. La esquina que menciona sí cuenta como un límite de auto intersección (o más bien, un límite que no es una curva).
John Dvorak

Respuestas:

11

Python 2, 500 bytes

z=input()
W=4*len(z[0])+1
H=4*len(z)+1
R=range
s=[-~W*[0]for _ in R(-~H)]
for y in R(H/4):
 for x in R(W/4):
        for h in R(25):s[y*4+h%5][x*4+h/5]|=z[y][x]
F=[(x/3-1,x%3-1)for x in[1,7,3,5,0,6,8,2]]
exec'for y in R(H):\n for x in R(W):s[y][x]+=0<s[y][x]<=min(s[y+d][x+e]for(e,d)in F)\n'*H
for y in R(H):
 l=''
 for x in R(W):h=s[y][x];a=[s[y+d][x+e]for(e,d)in F[:4]];l+=r' XabcVde^f ||g>h\\+//+<<jk<l//+\\+>>m --^^oVVqrX'[h and int(''.join(`int(n==h)`for n in a),2)*3+((h==1)*2or max(a)==h)+1]
 print l

Cansado de jugar golf, y llegué a un buen puntaje redondo, así que aquí está.

La sangría de ocho espacios es una pestaña.

Pase una matriz binaria sobre STDIN, así:

python2.7 roof.py <<<"[[1,1,0,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0], [1,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,0], [0,0,0,0,1,1,0,1,1,1,1,1,0,0,1,1,1,1,1,0], [1,0,1,0,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,1], [1,0,1,1,1,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0]]"
Lynn
fuente
Completamente golfizado o no, esto es increíble. Bien hecho. +1
R. Kap