Primeiro, alguma terminologia ( fonte ):
- Um telhado de quadril é (citando a Wikipedia) "um tipo de telhado em que todos os lados se inclinam para baixo em direção às paredes, geralmente com uma inclinação bastante suave"
- Uma inclinação é uma superfície plana que faz parte do telhado
- Um cume é uma aresta onde duas encostas opostas se encontram
- Um quadril é uma aresta convexa onde duas inclinações pertencentes a paredes perpendiculares se encontram
- Um vale é uma borda côncava onde duas encostas pertencentes a paredes perpendiculares se encontram
- Quadris e vales devem ser coletivamente referidos como bordas diagonais.
Entrada possível:
** * ***
********
** * **
Saída correspondente:
+-------+ +---+ +-----------+
|\ /| |\ /| |\ /|
| \ / | | V | | \ ^---< |
| \ / | | | | | \ / \ \|
+---+ V +---+ | +---+ X +---+
|\ \ | / \|/ \ / \ |
| >---> | <-------X-------V > |
|/ / | \ /|\ /| |
+---+ ^ +---+ | +-------+ | +---+
| / \ | | | | | |/ /|
| / \ | | ^ | | /---< |
|/ \| |/ \| |/ \|
+-------+ +---+ +-------+
Mais alguns casos de teste:
** *** * * * *
* *** *****
** ***** *****
* * * *** *** ***
* **** * * *
Saídas correspondentes:
+-------+ +-----------+ +---+ +---+ +---+ +---+
|\ /| |\ /| |\ /| |\ /| |\ /| |\ /|
| \---< | | >-------< | | V | | V | | V | | X |
| |\ \| |/ \| | | | | | | | | | |/ \|
| | +---+ +-----------+ +---+ | +---+ | | +-----------+ | | +---+
| | | |\ \|/ /| | |/ \| |
| ^ | | \ V / | | < > |
|/ \| | \ / | | \ / |
+---+ +-------+ +---+ \ / +---+ | \-----------/ |
|\ /| |\ \ \ / / /| | |\ /| |
| >---/ | | >---> X <---< | | | \ / | |
|/ /| | |/ / / \ \ \| | | \ / | |
+---+ +---+ +---+ | | +---+ / \ +---+ +---+ ^ +---+ ^ +---+
|\ /| |\ /| | | | | / \ | |\ \ / \ | | / \ / /|
| V | | V | | | | | / ^ \ | | >---V > | | < V---< |
| | | | | | | | | |/ /|\ \| |/ /| | | |\ \|
| | | | | +-------+ | | +---+ | +---+ +-------+ | | | | +-------+
| | | | |/ \| | | | | | | | | | |
| ^ | | /-----------\ | | ^ | | ^ | | ^ |
|/ \| |/ \| |/ \| |/ \| |/ \|
+---+ +---------------+ +---+ +---+ +---+
Sua entrada será um bitmap - uma matriz 2D de pixels quadrados - da área que deve ser coberta pelo telhado. Você pode supor que o limite dessa área seja uma curva de Jordan - ou seja, contínua e sem interseção automática - ou seja, a área coberta será contínua, sem buracos e nunca haverá quatro paredes se encontrando em um único ponto. Os formatos de entrada válidos incluem uma única sequência com separadores de nova linha, uma lista de sequências e uma matriz 2D de caracteres ou booleanos.
As regras de construção do telhado são:
- Cada segmento reto da área coberta (doravante denominada parede) deve ter exatamente uma inclinação adjacente. A inclinação deve se afastar da parede. Cada declive deve ter pelo menos uma parede adjacente e todas as paredes adjacentes a uma inclinação devem ser colineares.
- Todas as inclinações devem ter o mesmo ângulo (diferente de zero) contra a superfície horizontal. Ou seja, eles devem ter o mesmo tom.
- As encostas devem formar uma superfície cujo limite é o limite da área coberta. Ou seja, nenhuma superfície além das pistas pode ser usada.
- Qualquer cenário em que mais de uma solução (até a escala vertical) seja permitida por esta especificação é considerada um bug na especificação. Quaisquer correções se aplicam retroativamente.
De maneira equivalente, o telhado pode ser definido pela regra de que cada ponto do telhado é colocado o mais alto possível, sem exceder a inclinação máxima para esse telhado, medida usando a distância de Chebyshev na vista de cima para baixo.
Sua saída deve ser uma representação de arte ASCII do telhado - uma única sequência contendo caracteres de nova linha ou uma matriz de sequências, cada uma indicando uma única linha da saída. O telhado deve ser renderizado na vista de cima para baixo em uma escala de 4x - ou seja, cada quadrado da planta deve afetar uma área 5x5 da saída, de modo que os cantos dessa área 5x5 sejam compartilhados com os quadrados vizinhos (de modo que cada caractere de canto é afetado por quatro quadrados de entrada diferentes), conforme indicado no exemplo de saída. Espaço em branco extra é permitido desde que a forma de saída seja preservada. Os caracteres na saída devem ser:
- um marcador de nova linha definido pelo ambiente deve ser usado (geralmente U + 000A, U + 000D ou um par de ambos) se a saída estiver na forma de uma única sequência
(Espaço U + 0020) representa um ponto fora de uma área coberta ou um ponto interno a uma inclinação
+
(U + 002B sinal de mais) representa um ponto com duas paredes perpendiculares adjacentes a ele-
(U + 002D hífen-menos) representa uma parede ou uma crista orientada horizontalmente (leste-oeste)/
(U + 002F solidus) representa um quadril ou um vale orientado de nordeste para sudeste, ou um ponto adjacente a dois deles<
(Sinal menor que U + 003C) representa um ponto com duas arestas diagonais adjacentes a ele no leste>
(U + 003E sinal maior que) representa um ponto com duas arestas diagonais adjacentes a ele no oeste\
(Solidus reverso U + 005C) representa um quadril ou um vale orientado de noroeste a sudeste, ou um ponto adjacente a dois deles^
(Acento circunflexo U + 005E) representa um ponto com duas arestas diagonais adjacentes a ele no sulV
(U + 0056 letra maiúscula latina v) representa um ponto com duas arestas diagonais adjacentes a ele no norteX
(U + 0058 letra maiúscula latina x) representa um ponto com arestas diagonais adjacentes a ele nos quatro lados|
(Barra vertical U + 007C) representa uma parede ou uma crista orientada verticalmente (norte-sul)
Observe que não é possível que um número ímpar de arestas diagonais termine no mesmo ponto (exceto nas paredes). Podemos visualizar isso dividindo a vizinhança de cada ponto em declive norte + declive sul e em declive leste + declive oeste. O limite entre as duas partições deve ser composto de arestas diagonais.
Se o seu ambiente usa uma codificação de caracteres incompatível com ASCII, você pode usar os caracteres equivalentes (o mesmo glifo ou o mais próximo disponível) no caractere que codifica o ambiente.
A implementação de referência (feia) a seguir no Ruby é normativa em relação à saída que não é de espaço em branco. Observe particularmente o render
mé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
*
. Caso contrário, isso provavelmente é suficiente.
[[0,1,1],[1,0,1],[1,1,1]]
uma entrada válida? (A entrada não tem “buracos”, mas há um canto traquinas quase auto-intersecção.)