帮助建筑师形象化天际线


29

作为城市规划项目的一部分,在建筑师的一些建议下,您已经获得了创建程序或功能的任务,该程序或功能将显示城市天际线。该项目仅处于启动阶段,因此非常粗略的草图就足够了。当然,最简单的方法是简单地以ASCII艺术形式绘制天际线。

所有建筑物都将在河边,因此它们全部对齐。建筑师会将每个建筑物的高度作为输入,您的代码应显示天际线。

架构师的输入将是整数或半整数。如果数字是整数,则建筑物将具有平坦的屋顶,而半整数将导致倾斜的屋顶。零将是平坦的地面。建筑物的墙壁相隔3个字符,而零将是单个字符宽。相邻建筑物共用墙壁。

有关输出的详细信息和说明,请查看以下示例:

N = 3
 ___
|   |
|   |
|___|

N = 3.5
  _      
 / \
|   |
|   |
|___|

N = 6
 ___
|   |
|   |
|   |
|   |
|   |
|___|

n = 0
_

输入示例: 3 3.5 0 2

      _
 ___ / \  
|   |   |  ___
|   |   | |   |
|___|___|_|___|

输入示例: 0 0 2.5 3 0 4 1

             ___
    _  ___  |   |
   / \|   | |   |
  |   |   | |   |___
__|___|___|_|___|___|

路易斯维尔0 2 1 3.5 0 4 2 4 2 4 6 1 6 0 5 1

                                    ___     ___
                                   |   |   |   |  ___
           _    ___     ___     ___|   |   |   | |   |
          / \  |   |   |   |   |   |   |   |   | |   |
  ___    |   | |   |___|   |___|   |   |   |   | |   |
 |   |___|   | |   |   |   |   |   |   |___|   | |   |___
_|___|___|___|_|___|___|___|___|___|___|___|___|_|___|___|

所使用的ASCII字符为:换行符,空格和/\_|(代码点10、32、47、92、95、124)。

规则:

  • 通过将所有数字乘以2,可以使程序只接受整数作为输入是可选的。因此,3 3.5 2您的程序可能会采用而不是采用6 7 4。如果选择第二种输入格式,则输入6会导致一个3层建筑物,而7则应该是一个带有倾斜屋顶等的3层建筑物。
  • 输出应完全如上所述,但是尾随空格和换行符可以。
  • 输入的确切格式是可选的。以您的语言为准。
  • 结果必须显示在屏幕上,以便架构师可以查看结果。
  • 您可以假定至少有一个整数,并且只给出有效输入。

这是codegolf,因此以字节为单位的最短代码获胜。


1
高度为0.5的建筑物是什么样的?
汤姆·卡彭特

真的没想到。最明显的选择就是倾斜的屋顶,几乎就像霍比特人的住所:-),但是您可以自由选择,或者可以假设输入永远不会是0.5 ...
Stewie Griffin

1
此刻发生了奇怪的事情,因为没有墙(我假设不存在0.5高),所以我将不得不对我的答案进行一些处理。
汤姆·卡彭特

我刚刚尝试了高度为0.5的代码,并且我同意,“怪异”是一个非常具有描述性的词= PI尚未详细介绍它,所以我不确定发生了什么...总之,您的回答是完全有效,您可以假设没有0.5座建筑物...
Stewie Griffin 2015年

Answers:


5

Python 2中,199个 193 188 185字节

a=map(int,raw_input().split())
l=max(a)+1|1
while~l:print''.join((x%2*'/  _\\ '[x<l::2]*(x<=l<x+4)or'_ '[x|1!=l>1]*3)[x<1:x+2]+'| '[x<=l>=y]*(x+y>0)for x,y in zip([0]+a,a+[0]))[1:];l-=2

这是一个接受整数作为输入的完整程序。输入示例


精彩!我必须在未来的高尔夫比赛中窃取一些技巧...
quintopia

5

MATLAB,219209203字节

i=input('');x=1;c=0;m(1:4*numel(i))='_';for a=i;b=fix(a);m(1:b,x)='|';s=95;if a~=b;m(b+2,x+2)=95;s='/ \';end;m(b+1,x+(1:3))=s;x=x+(a>0)*3+1;m(1:b,x)='|';x=x+(a<1&c>0);c=a;end;disp(flipud(m(:,1:x-(a<1))))

不幸的是,这在Octave上不起作用。不能完全确定为什么,这似乎与中断的disp / flipud位有关。

另外,目前尚无关于0.5高度建筑物外观的定义,也没有任何提及,因此在此代码中,我认为它们是不允许的。

以下是代码的可读性:

i=input(''); %e.g. [0 0 2 1 3.5 0 4 2 4 2 4 6 1 6 0 5 1 0 0 1 0]
x=1;
c=0;
m(1:4*numel(i))='_';
for a=i;
    b=fix(a);
    m(1:b,x)='|';
    s=95;
    if a~=b;
        m(b+2,x+2)=95;
        s='/ \';
    end;
    m(b+1,x+(1:3))=s;
    x=x+(a>0)*3+1;
    m(1:b,x)='|';
    x=x+(a<1&c>0);
    c=a;
end;
disp(flipud(m(:,1:x-(a<1))))

首先,我们将输入作为数组,并进行一些变量初始化。

i=input(''); %e.g. [0 0 2 1 3.5 0 4 2 4 2 4 6 1 6 0 5 1]
x=1;
c=0;

因为零高度的建筑物很痛苦-它们的宽度最终取决于其旁边的内容(尽管打印的内容不会改变),所以我们通过为所有建筑物绘制足够的地面来简化事情。我们假设每座建筑物的宽度为4个字符(因为相邻建筑物合并在一起)-高度为零的建筑物不是,但是多余的部分将在以后进行修剪。

m(1:4*numel(i))='_';

现在,我们依次绘制每个建筑物。

for a=i

首先我们得到高度的整数部分,因为这将确定多少个“ |” 我们需要。

    b=fix(a);

现在,绘制该建筑物的墙-如果有两栋相邻的建筑物,则新建筑物的墙将与最后一栋建筑物的墙在同一列中。

    m(1:b,x)='|';

检查这是否是半高的建筑物。如果是这样,则屋顶将不同。对于半高,屋顶将是/ \全高,而顶板将是半高___(Matlab将隐式地从单个下划线复制该顶,因此在此处保存几个字节)。半高建筑物的屋顶要高出一排,因此也增加了。

    s=95;
    if a~=b;
        m(b+2,x+2)=95;
        s='/ \';
    end;

在屋顶画

    m(b+1,x+(1:3))=s;

现在移至下一个建筑物的起点并绘制共享墙(如果此时墙太短,则在绘制下一个建筑物时它将变大)。请注意,零高度建筑物为1宽,普通建筑物为4宽,因此我们通过将(a> 0)视为十进制数而不是布尔值来简化否则为if-else的情况。

    x=x+(a>0)*3+1;
    m(1:b,x)='|';

接下来是一些用于零高度建筑物的黑客技术。基本上,这就是说如果该建筑物的高度为零,而之前的建筑物不是零高度,则意味着下一栋建筑物的位置需要增加1,因为夹在其他两座建筑物之间的零高度建筑物实际上是其两倍宽-占额外的墙,通常与相邻建筑物共享。我们还将跟踪此建筑物的高度,以便下次进行此检查。

    x=x+(a<1&c>0);
    c=a;
end;

完成后,将建筑矩阵翻转为正确的方向,然后显示它。请注意,我们也在此处修剪了多余的地面。

disp(flipud(m(:,1:x-(a<1))))

因此,当我们运行此脚本时,要求我们提供输入,例如:

[0 0 2 1 3.5 0 4 2 4 2 4 6 1 6 0 5 1 0 0 1 0]

然后,它生成建筑物并显示结果。对于以上输入,将生成以下内容:

                                     ___     ___                   
                                    |   |   |   |  ___             
            _    ___     ___     ___|   |   |   | |   |            
           / \  |   |   |   |   |   |   |   |   | |   |            
   ___    |   | |   |___|   |___|   |   |   |   | |   |            
  |   |___|   | |   |   |   |   |   |   |___|   | |   |___    ___  
__|___|___|___|_|___|___|___|___|___|___|___|___|_|___|___|__|___|_

做得太好了!
Stewie Griffin

4

Kotlin,447 442字节

val a={s:String->val f=s.split(" ").map{it.toFloat()}.toFloatArray();val m=(f.max()!!+1).toInt();for(d in m downTo 0){var l=0f;for(c in f){val h=c.toInt();print(if(h==d&&d!=0)if(h<l-0.5)"|" else{" "}+if(c>h)"/ \\" else "___" else if(h<d)if(d<l-0.5)"|" else{" "}+if(h==0)" " else if((c+0.5).toInt()==d)" _ " else "   " else{if(h==0)if(l<1)"  " else "| " else "|   "}.replace(' ',if(d==0)'_' else ' '));l=c;};if(d<l-0.5)print("|");println();}}

非高尔夫版本:

val ungolfed: (String) -> Unit = {
    s ->

    val floats = s.split(" ").map { it.toFloat() }.toFloatArray()
    val maxH = (floats.max()!! + 1).toInt()

    for (drawHeight in maxH downTo 0) {
        var lastBuildingH = 0f
        for (f in floats) {
            val buildingH = f.toInt()
            if (drawHeight == 0) {
                // Baseline
                if (buildingH == 0)
                    if (lastBuildingH.toInt() == 0) print("__")
                    else print("|_")
                else print("|___")
            } else if (buildingH == drawHeight) {
                // Ceiling
                if (buildingH < lastBuildingH - 0.5) print("|")
                else print(" ")
                if (f > buildingH) print("/ \\")
                else print("___")
            } else if (buildingH < drawHeight) {
                // Above building
                if (drawHeight < lastBuildingH - 0.5) print("|")
                else print(" ")
                if (buildingH == 0) print(" ")
                else {
                    if ((f + 0.5).toInt() == drawHeight) print(" _ ")
                    else print("   ")
                }
            } else {
                if (buildingH == 0) print("| ")
                else print("|   ")
            }
            lastBuildingH = f;
        }
        if (drawHeight < lastBuildingH - 0.5) print("|")
        println()
    }
}

3

Python 2中,357个 306 299 294 287 281 276字节

def s(l):
 d=len(l)+1;l=[0]+l+[0];h=(max(l)+3)/2;o=''
 for i in range(d*h):
  a=l[i%d+1];c=l[i%d];b=2*(h-1-i/d);o+="|"if(a>b+1)+(c>b+1)else" "*(a+c>0);o+=" _/__  _\\"[a-b+1::3]if b*(1>=abs(a-b))else" "*(1+2*(a>0))
  if b==0:o=o.replace(" ","_")
  if i%d==d-1:print o[:-1];o=''

这使用“加倍”编码,以列表形式传递给函数。编辑:通过重做部分大条件作为数组选择器,并切换到加倍编码,从而减少了字节大小。通过重新排列更多的条件并将更多的逻辑转换为算术来节省更多的字节。

编辑:xsot的更好

说明:

d=len(l)+1;l=[0]+l+[0];m=max(l);h=m/2+m%2+1;o=''

d比数组的长度大1,因为我们要在列表的每一端添加零,从第二个元素到最后添加的零。h是图形的高度。(在此计算中,我们必须除以2,因为我们使用的是双精度表示法,我们专门用于避免将浮点数转换为整型整数。在除以奇数高的高度(尖尖的建筑物- o输出的字符串比常规类型的间隙大一点。)

 for i in range(d*h):

将double for循环折叠为单个for循环的标准技巧。完成后:

  a=l[i%d+1];c=l[i%d];b=2*(h-1-i/d)

我们现在完成了与以下操作相同的操作:

for b in range(2*h-2,-2,-2):
 for j in range(d):
  a=l[j+1];c=l[j]

但可以节省十个字节(包括以下行中的空格)。

  o+="|"if(a>b+1)+(c>b+1)else" "*(a+c>0)

只要当前建筑物或先前建筑物的高度高于当前线,就可以在任何时候粘贴墙,只要此处至少有一个建筑物边界。等效于以下条件:

  o+=("|" if a>b+1 or c>b+1 else " ") if a or c else ""

其中b是当前扫描高度,a是当前建筑物高度,c是先前建筑物高度。有条件的后半部分防止将墙放置在地面空间之间。

  o+=" _/__  _\\"[a-b+1::3]if b*(1>=abs(a-b))else" "*(1+2*(a>0))

这是绘制正确屋顶的部分,通过将建筑物的高度与当前扫描高度进行比较来选择屋顶部分。如果屋顶不在这里,它将打印适当数量的空间(当是实际建筑物时为3,例如a> 0,否则为1)。请注意,在地面上时,它永远不会尝试绘制屋顶,这意味着0.5尺寸的建筑物不会得到尖锐的屋顶。那好吧。

  if b==0:o=o.replace(" ","_")

在地面上时,我们需要下划线而不是空格。我们只是在这里一次全部替换它们。

  if i%d==d-1:print o[:-1];o=''

在我们开始处理下一行之前,请打印当前行并清除输出行。我们将最后一个字符截掉,因为它是对应于我们通过在函数的开头添加零来添加的底空格的“ _”。(我们附加了零,因此我们无需添加特殊情况来插入右墙(如果存在),它将比通过添加0并切掉“ _”添加的代码多得多。)


打高尔夫球。哇。(也+1)
2015年

2

Python 3

725字节

608字节

高尔夫代码:

import sys,math;
m,l,w,s,bh,ls,ins,r,a="|   |","___","|"," ",0,[],[],range,sys.argv[1:]
def ru(n):return math.ceil(n)
def bl(h,n):
    if(n>ru(h)):return(s*5,s)[h==0]
    if(h==0):return"_"
    if(n==0):return w+l+w
    if(n<h-1):return m
    return("  _  "," / \ ")[n==ru(h)-1]if(h%1)else(s+l+s,m)[n==h-1]
for arg in a:
    f=ru(float(arg))
    if(bh<f):bh=f
for i in r(bh,-1,-1):
    ln=""
    for bld in a:ln+=bl(float(bld),i)
    ls.append(ln)
for i in r(len(ls[-1])-1):
    if(ls[-1][i]==ls[-1][i+1]==w):ins.append(i-len(ins))
for ln in ls:
    for i in ins:ln=(ln[:i]+ln[i+1:],ln[:i+1]+ln[i+2:])[ln[i]==w]
    print(ln)

这是未分类的代码。有一些评论,但基本思想是用双壁创建建筑物,因此底线看起来像:

_|___||___|_|___||___|

然后获取这些双壁的索引并删除那些列,因此我们得到:

_|___|___|_|___|___|

码:

import sys
import numbers
import math

mid="|   |";
l="___";
w="|";
s=" ";

def printList(lst):
    for it in lst:
        print(it);

# h = height of building
# l = line numeber starting at 0
def buildingline(h,n):
    #if (h==0):
    #   return " " if(n>math.ceil(h)) else "   ";
    if(n>math.ceil(h)):
        return s if(h == 0) else s*5;
    if(h==0): return "_";
    if(n==0): return w+l+w;
    if(n<h-1): return mid;
    if(h.is_integer()):
        return mid if(n==h-1) else  s+l+s;
    else:
        return " / \ " if (n==math.ceil(h)-1) else "  _  "; 
# max height
bh=0;

for arg in sys.argv[1:]:
    f = math.ceil(float(arg));
    if(bh<f):bh=f;

# lines for printing
lines = []

for i in range(bh,-1,-1):
    line="";
    for bld in sys.argv[1:]:
        bld=float(bld);
        line += buildingline(bld,i);
        #lh = bld;
    lines.append(line);

#for line in lines:
#   print(line);
#printList(lines);


# column merging
#find indexes for merging (if there are | | next to each other)
indexes = [];
for i in range(len(lines[-1])-1):
    if (lines[-1][i]=='|' and lines[-1][i+1] == '|'):
        indexes.append(i-len(indexes));

#printList(indexes);

#index counter
for line in lines:
    newLine = line;
    for i in indexes:
        if newLine[i] == '|' :
            newLine=newLine[:i+1] + newLine[i+2:];
        else : newLine = newLine[:i] + newLine[i+1:];
    print(newLine);

是时候打高尔夫球了!


您可能想在这里看看。我认为这里有很多打高尔夫球的潜力=)我只知道基本的Python,所以我不能建议我担心的任何具体问题……
Stewie Griffin 2015年

在我看来,您已经删除了空格并缩短了变量名,但其余部分保持不变。您应该尝试寻找巧妙的方法,例如摆脱一些循环,使用较少的比较等。当然,像ru(n):return math.ceil(n)打高尔夫球这样的事情,但是仍然...请不要以负面的方式接受它,我不是我自己是一个优秀的高尔夫球手,并且肯定不是一个好的程序员。我建议您尝试对它进行一些改进……一旦意识到自己要缩短它,那实际上就是一种乐趣。几天前,我的人数从很多增加到120到55。因此,即使您是新手,也有可能。
Stewie Griffin

@StewieGriffin谢谢您的链接!我真的是代码编写的新手,所以更多的是完成实际任务,而不是为我执行代码编写。但是发现各种语言的可能性真是令人惊讶
Cajova_Houba 2015年

FTR:对于某些更复杂的挑战,例如这一挑战,我很高兴自己完成挑战=)
Stewie Griffin

2

PHP,307个 297 293字节

<?$r=str_pad("",$p=((max($argv)+1)>>1)*$w=4*$argc,str_pad("\n",$w," ",0));for(;++$i<$argc&&$r[$p++]=_;$m=$n)if($n=$argv[$i]){$q=$p+=!$m;eval($x='$r[$q-1]=$r[$q]=$r[$q+1]=_;');for($h=$n>>1;$h--;$q-=$w)$r[$q-2]=$r[$q+2]="|";$n&1?($r[$q-1]="/")&($r[$q-$w]=_)&$r[$q+1]="\\":eval($x);$p+=3;}echo$r;

从命令行获取参数* 2。保存到文件,运行php <filename> <parameters>

分解

// initialize result    
$r=str_pad("",              // nested str_pad is 3 bytes shorter than a loop
    $p=                     // cursor=(max height-1)*(max width)=(start of last line)
    ((max($argv)+1)>>1)     // max height-1
    *
    $w=4*$argc              // we need at least 4*($argc-1)-1, +1 for newline
    ,
    // one line
    str_pad("\n",$w," ",0)  // (`str_pad("",$w-1)."\n"` is one byte shorter,
);                          // but requires `$w+1`)

// draw skyline
for(;
    ++$i<$argc              // loop through arguments
    &&$r[$p++]=_                // 0. draw empty ground and go one forward
    ;
    $m=$n                       // 7. remember value
)
    if($n=$argv[$i])            // if there is a house
    {
        $q=                         // 2. copy $p to $q
        $p+=!$m;                    // 1. go one forward if there was no house before this
        // offset all further positions by -2 (overwrite empty ground, share walls)
        eval($x=                    // 3. draw floor
        '$r[$q-1]=$r[$q]=$r[$q+1]=_;'
        );
        for($h=$n>>1;$h--;$q-=$w)   // 4. draw walls
            $r[$q-2]=$r[$q+2]="|";
        $n&1                        // 5. draw roof
            ?($r[$q-1]="/")&($r[$q-$w]=_)&$r[$q+1]="\\"
            :eval($x)               // (eval saved 7 bytes)
        ;                           // (ternary saved 6 bytes over `if`)
        $p+=3;                      // 6. go three forward (5-2)
    }

// output
echo$r;

1

C ++,无语言

(或者可能无法使用)

假设元素少于100个,每个元素小于100个。s是建筑物的数量(输入中要求)。

#include <iostream>
using namespace std;
int main()
{
float a[100];
int i,j,s;
cin>>s;
for(i=0;i<s;++i)
 cin>>a[i];
for(i=100;i>=1;--i)
{
for(j=0;j<s;++j)
{
if((a[j]>=i)||(a[j-1]>=i))
 cout<<"|";
else
 cout<<" ";
if(i==1)
 cout<<"___";
else if(a[j]+1==i)
 cout<<"___";
else if(a[j]+1.5==i)
 cout<<" _ ";
else if(a[j]+0.5==i)
 cout<<"/ \\";
else cout<<"   ";
}
if(a[s-1]>=i)
 cout<<"|";
cout<<endl;
}
}

输出中有一些错误...地面为3个字符宽(应仅为1个字符),并且最后一堵墙丢失了。
Stewie Griffin

@StewieGriffin发布此消息时,我仍在整理错误。1.我加了最后一堵墙。2.地面必须为3个字符宽,因为倾斜的屋顶/ _ \为3个字符宽。
ghosts_in_the_code 2015年

1
* 建筑物之间的地面,而不是内部。
Stewie Griffin

如果您仍在处理它,则可能要等待,但是如果删除换行符和缩进,则可以摆脱很多字节。我没有固定在地面的问题,但这个工程 .346字节而不是401
Stewie新

@StewieGriffin我实际上不打算提交高尔夫球答案,因为反正它太长了。我敢打赌,有更好的语言可以用不到100个字节来完成。因此,我的代码更像是他人的参考解决方案。
ghosts_in_the_code 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.