ASCIIでヒップルーフのトップダウンビューをレンダリングする


23

まず、いくつかの用語(source):

  • ヒップの屋根は、「通常、かなり緩やかな斜面で、壁にどこすべての側面の斜面下向き屋根のタイプを」ウィキペディア(Wikipediaを引用)されます
  • 斜面は、屋根の一部である平らな表面です
  • 尾根は、向かい合う2つの屋根の斜面が交わるエッジです
  • ヒップは、垂直の壁に属する2つの斜面が交わる凸状のエッジです
  • 谷は、垂直の壁に属する2つの斜面が出会う凹状の縁です。
  • ヒップとバレーは、対角線エッジと総称されます。

可能な入力:

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

対応する出力:

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

さらに2つのテストケース:

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

対応する出力:

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

あなたの入力は、正方形ピクセルの2次元配列- -屋根によってカバーされるべき領域のビットマップになります。この領域の境界は、ジョーダン曲線、つまり連続的で非自己交差であると想定できます。つまり、屋根のある領域は連続しており、穴はなく、1つのポイントで4つの壁が交わることはありません。有効な入力形式には、改行区切り文字を含む単一の文字列、文字列のリスト、および文字またはブール値の2D配列が含まれます。

屋根を構築するルールは次のとおりです。

  • 屋根のあるエリア(以下、壁と呼ぶ)の各直線部分には、ちょうど1つの隣接する斜面がなければなりません。斜面は、壁から離れるように上昇します。各斜面には、少なくとも1つの隣接する壁があり、斜面に隣接するすべての壁は同一直線上になければなりません。
  • すべての斜面は、水平面に対して同じ(ゼロでない)角度を持たなければなりません。つまり、同じピッチでなければなりません。
  • 斜面は、その境界が屋根付きエリアの境界である表面を形成しなければならない。つまり、傾斜面以外の表面は使用できません。
  • この仕様で複数のソリューション(垂直スケーリングまで)が許可されるシナリオは、仕様のバグと見なされます。修正は遡及的に適用されます。

同様に、屋根は、屋根の各ポイントがトップダウンビューでチェビシェフ距離を使用して測定された屋根の最大勾配を超えない範囲で可能な限り高く配置されるというルールによって定義できます。

あなたの出力は、改行文字または文字列の配列を含む単一の文字列のどちらかは、各出力の一行を示す-屋根のASCIIアート表現しなければなりません。屋根は、4倍のスケールで上から下に表示されます。つまり、フロアプランの各正方形は、出力の5 x 5領域に影響を与え、この5 x 5領域の角が隣接する正方形と共有されるようにします。出力例に示されているように、角の文字は4つの異なる入力正方形の影響を受けます)。出力形状が保持されている限り、余分な空白を使用できます。出力の文字は次のとおりです。

  • 出力が単一の文字列の形式である場合、環境定義の改行マーカー(通常はU + 000A、U + 000D、または両方のペア)が使用されます。
  • (U + 0020スペース)は、屋根付きエリアの外側のポイントまたは斜面の内側のポイントを表します
  • + (U + 002Bプラス記号)は、2つの垂直な壁が隣接する点を表します
  • - (U + 002Dハイフンマイナス)は、水平方向(東西)の壁または尾根を表します
  • / (U + 002F solidus)は、北東から南東に向けられたヒップまたは谷、またはそれらの2つに隣接するポイントを表します。
  • < (U + 003Cより小さい記号)は、2つの斜めのエッジが東に隣接するポイントを表します
  • > (U + 003Eより大きい記号)は、西で2つの斜めのエッジが隣接するポイントを表します
  • \ (U + 005C逆ソリダス)北西から南東に向けられたヒップまたは谷、またはそれらの2つに隣接するポイントを表します
  • ^ (U + 005Eサーカムフレックスアクセント)は、南側に隣接する2つの斜めのエッジを持つポイントを表します
  • V (U + 0056ラテン大文字v)は、北でそれに隣接する2つの斜めのエッジを持つポイントを表します
  • X (U + 0058ラテン大文字x)は、4辺すべてで斜めのエッジが隣接するポイントを表します
  • | (U + 007C垂直バー)は、垂直方向(南北)の壁または尾根を表します

奇数の斜めのエッジが同じポイントで終わることはできないことに注意してください(壁を除く)。各ポイントの近傍を北斜面+南斜面と東斜面+西斜面に分割することで、それを視覚化できます。両方のパーティション間の境界は、斜めのエッジで構成する必要があります。

ご使用の環境でASCIIと互換性のない文字エンコードを使用している場合、環境で使用する文字エンコードで同等の文字(同じグリフまたは最も近い文字)を使用できます。

Ruby の次の(ugい)リファレンス実装は、非空白出力に関して規範的です。特にrenderメソッドに注意してください:

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 

1
さらにテストケースを追加する必要があります。
mbomb007

@ mbomb007が追加されました。それらが占めるスペースを考えると、これ以上追加する必要がありますか?
ジョンドヴォルザーク

@JanDvorakたぶんテストケースを追加します*。そうでなければ、おそらくそれで十分です。
mbomb007

ある[[0,1,1],[1,0,1],[1,1,1]]有効な入力は?(入力には「穴」はありませんが、自己交差点の近くに厄介なコーナーがあります。)
Lynn

@Lynnその場合について心配する必要はありません、それは有効な入力ではありません。あなたが言及したコーナーは、自己交差する境界(または、曲線ではない境界)としてカウントされます。
ジョンドヴォルザーク

回答:


11

Python 2、500バイト

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

ゴルフをするのにうんざりしていて、いいラウンドスコアになったので、ここにあります。

8スペースのインデントはタブです。

次のように、STDINにバイナリマトリックスを渡します。

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]]"

完全にゴルフをしたかどうか、これは驚くべきことです。よくやった。+1
R. Kap
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.