Ajude os arquitetos a visualizar o horizonte

29

Como parte de um projeto de planejamento da cidade, você recebeu a tarefa de criar um programa ou função que exibirá o horizonte da cidade, com algumas informações dos arquitetos. O projeto está apenas na fase de inicialização, portanto, um esboço bastante aproximado é suficiente. A abordagem mais fácil é, obviamente, simplesmente desenhar o horizonte na arte ASCII.

Todos os prédios ficam à beira do rio, portanto, todos estão alinhados. Os arquitetos fornecerão a altura de cada edifício como entrada, e seu código deverá exibir o horizonte.

A entrada dos arquitetos será um inteiro ou um meio inteiro. Se o número for um número inteiro, o edifício terá um telhado plano, enquanto um meio inteiro resultará em um telhado inclinado. Um zero será apenas terreno plano. As paredes de um prédio são separadas por 3 caracteres, enquanto um zero terá um único caractere de largura. Edifícios adjacentes compartilham paredes.

Para detalhes e esclarecimentos sobre a saída, consulte os exemplos abaixo:

N = 3
 ___
|   |
|   |
|___|

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

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

n = 0
_

Exemplo de entrada: 3 3.5 0 2

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

Exemplo de entrada: 0 0 2.5 3 0 4 1

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

Louisville ,0 2 1 3.5 0 4 2 4 2 4 6 1 6 0 5 1

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

Os caracteres ASCII utilizados são: nova linha, espaço e /\_|(pontos de código 10, 32, 47, 92, 95, 124).

Regras:

  • É opcional criar um programa que aceite apenas números inteiros como entrada, multiplicando todos os números por dois. Então, em vez de tomar 3 3.5 2, seu programa pode demorar 6 7 4. Se o segundo formato de entrada for escolhido, uma entrada de 6 deve resultar em um prédio de 3 andares, 7 deve ser um prédio de 3 andares com telhados inclinados etc.
  • A saída deve ser exatamente como descrito acima, mas espaços à direita e novas linhas estão OK.
  • O formato exato da entrada é opcional. O que há de melhor no seu idioma.
  • O resultado deve ser exibido na tela, para que os arquitetos possam dar uma olhada.
  • Você pode assumir que haverá pelo menos um número inteiro fornecido e que somente a entrada válida será fornecida.

Este é um codegolf, então o código mais curto em bytes vence.

Stewie Griffin
fonte
11
Como seria um edifício de altura 0,5?
Tom Carpenter
Realmente não pensei nisso. A escolha mais óbvia seria apenas um telhado inclinado, quase como uma casa hobbit :-) mas você é livre para escolher, ou você pode assumir a entrada nunca será de 0,5 ...
Stewie Griffin
11
No momento, coisas estranhas acontecem porque não há paredes (presumi que não existiam 0,5 de altura), então terei que trabalhar um pouco na minha resposta.
Tom Carpenter
Acabei de experimentar o seu código com a altura 0,5 e concordo: "estranho" é uma palavra muito descritiva = o PI não passou por isso em detalhes, por isso não tenho certeza do que está acontecendo ... De qualquer forma, você responde: perfeitamente válido, você pode assumir que não existem quaisquer 0,5 edifícios ...
Stewie Griffin

Respostas:

5

Python 2, 199 193 188 185 bytes

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

Este é um programa completo que aceita números inteiros como entrada. Exemplo de entrada .

xsot
fonte
Maravilhoso! eu tenho que roubar alguns desses truques para futuros golfs ...
quintopia
5

MATLAB, 219209203 bytes

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))))

Infelizmente, isso não funciona no Octave . Não sei exatamente por que, parece ter algo a ver com o bit disp / flipud que se quebra.

Além disso, atualmente não há definição de como é a aparência de um edifício de 0,5 altura nem menção a eles; portanto, neste código, presumo que eles não são permitidos.

A seguir, o código de uma maneira um pouco mais legível:

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))))

Primeiro, pegamos uma entrada como uma matriz e fazemos alguma inicialização variável.

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;

Como os prédios com altura zero são uma dor - eles basicamente terminam com uma largura que depende do que eles são ao lado (embora o que é impresso não mude), simplificamos as coisas, desenhando terreno suficiente para todos os edifícios. Assumimos que cada edifício terá 4 caracteres de largura (porque os edifícios adjacentes se fundem) - os de altura zero não são, mas o excesso será aparado mais tarde.

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

Agora, extraímos cada edifício por sua vez.

for a=i

Primeiro obtemos a parte inteira da altura, pois isso determinará quantos '|' nós precisamos.

    b=fix(a);

Agora desenhe o muro para este edifício - se houver dois edifícios adjacentes, o muro para este novo edifício estará na mesma coluna que o muro do último.

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

Verifique se este é um edifício de meia altura. Se for, o telhado será diferente. Para as meias alturas, o teto será / \o da altura total ___(o Matlab replicará implicitamente isso a partir de um único sublinhado, portanto, salve alguns bytes lá). Há um pedaço extra de telhado uma linha mais alta para os edifícios de meia altura, de modo que também é adicionado.

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

Desenhe no telhado

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

Agora vá para o início do próximo edifício e desenhe na parede compartilhada (se a parede for muito curta nesse momento, ela será aumentada quando o próximo edifício for desenhado). Observe que os edifícios com altura zero têm 1 de largura, os edifícios normais têm 4 de largura; portanto, simplificamos o que seria um if-else tratando (a> 0) como um número decimal e não um booleano.

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

Em seguida, vem um pouco de hackery para trabalhar com edifícios de altura zero. Basicamente, o que isso diz é que se esse prédio tivesse altura zero, e o anterior não, isso significa que o local do prédio seguinte precisa ser incrementado em 1 porque um prédio de altura zero imprensado entre dois outros edifícios tem efetivamente o dobro da largura - isso é responsável pela parede extra que normalmente é compartilhada com um edifício adjacente. Também acompanhamos a altura do edifício para fazer essa verificação na próxima vez.

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

Depois de concluído, vire a matriz do edifício para a posição correta e exiba-a. Observe que também cortamos qualquer excesso de terra aqui.

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

Portanto, quando executamos esse script, solicitamos nossa entrada, por exemplo:

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

Em seguida, gera o edifício e exibe o resultado. Para a entrada acima, é gerado o seguinte:

                                     ___     ___                   
                                    |   |   |   |  ___             
            _    ___     ___     ___|   |   |   | |   |            
           / \  |   |   |   |   |   |   |   |   | |   |            
   ___    |   | |   |___|   |___|   |   |   |   | |   |            
  |   |___|   | |   |   |   |   |   |   |___|   | |   |___    ___  
__|___|___|___|_|___|___|___|___|___|___|___|___|_|___|___|__|___|_
Tom Carpenter
fonte
Muito bem feito!
Stewie Griffin
4

Kotlin, 447 442 bytes

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();}}

Versão não destruída:

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()
    }
}
succcubbus
fonte
3

Python 2, 357 306 299 294 287 281 276 bytes

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=''

Isso usa a codificação "dobrada", para ser passada para a função como uma lista. Editar: bytes raspados refazendo parte do grande condicional como um seletor de matriz e alternando para a codificação dobrada. Raspou mais bytes reorganizando ainda mais o condicional e convertendo mais lógica em aritmética.

EDIT: xsot é melhor

Explicação:

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

dé 1 a mais que o comprimento da matriz, porque adicionaremos zeros em cada extremidade da lista, do segundo elemento até o zero que adicionamos no final. hé a altura do desenho. (Temos que dividir por 2 neste cálculo porque estamos usando a representação dobrada, que usamos especificamente para evitar a necessidade de lançar flutuadores em ints por todo o lugar. Também adicionamos 1 antes de dividir alturas tão estranhas - edifícios pontudos - obtenha um pouco mais de espaço do que o tipo normal.) oé a string de saída.

 for i in range(d*h):

Um truque padrão para recolher um loop for duplo em um loop for único. Uma vez que fazemos:

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

agora realizamos o mesmo que:

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

mas de uma maneira que nos permita salvar dez bytes (incluindo espaços em branco nas linhas a seguir).

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

Cole uma parede a qualquer momento que a altura do edifício atual ou do edifício anterior for mais alta que a linha atual, desde que haja pelo menos um limite de edifício aqui. É o equivalente à seguinte condicional:

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

onde b é a altura atual da varredura, a é a altura atual do edifício e c é a altura anterior do edifício. A última parte do condicional evita colocar paredes entre os espaços do solo.

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

Esta é a parte que desenha o telhado correto, selecionando as partes do telhado comparando a altura do edifício com a altura atual da varredura. Se um telhado não for aqui, ele imprime um número apropriado de espaços (3 quando é um edifício real, por exemplo, um> 0, caso contrário 1). Observe que, quando estamos no nível do solo, ele nunca tenta desenhar um telhado, o que significa que edifícios de tamanho 0,5 não ficam com telhados pontudos. Ah bem.

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

Quando estamos no nível do solo, queremos sublinhados em vez de espaços. Acabamos de substituí-los todos de uma vez aqui.

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

Antes de começarmos a processar a próxima linha, imprima a atual e limpe a linha de saída. Cortamos o último caractere porque é o "_" correspondente ao espaço no solo que adicionamos adicionando um zero no início da função. (Anexamos esse zero para que não precisássemos adicionar um caso especial para inserir uma parede direita, se existir, o que acrescentaria muito mais código do que adicionamos adicionando o 0 e cortando o "_".)

quintopia
fonte
Carro-golfe. Uau. (Também, +1)
aplaude
2

Python 3

725 bytes

608 bytes

Código de golfe:

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)

Aqui está o código não destruído. Existem alguns comentários, mas a idéia básica é criar edifícios com paredes duplas, portanto, a linha de fundo se parece com:

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

Então, para obter índices dessas paredes duplas e remover essas colunas, obtemos:

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

Código:

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);

Hora de jogar golfe!

Cajova_Houba
fonte
Você pode querer dar uma olhada aqui . Eu acho que há um grande potencial de golfe aqui =) Eu só sei Python básico, então não posso sugerir algo específico que eu tenho medo ...
Stewie Griffin
Parece-me que você removeu espaços e reduziu os nomes das variáveis, mas manteve o restante inalterado. Você deve tentar encontrar maneiras inteligentes de, por exemplo, livrar-se de alguns loops, usar menos comparações etc. É claro, coisas como o ru(n):return math.ceil(n)golfe, mas ainda assim ... Por favor, não tome isso de maneira negativa, eu não sou um bom jogador de golfe e com certeza não um bom programador. Eu sugiro que você tente melhorá-lo um pouco ... Na verdade, é divertido quando você percebe que consegue encurtá-lo. Eu fui de muitos para 120 a 55 alguns dias atrás. Portanto, é possível, mesmo se você é novo nisso.
Stewie Griffin
@StewieGriffin Obrigado por esse link! Eu sou realmente um novato no golfe de código, por isso é mais sobre a conclusão da tarefa real do que em fazer golfe para mim. Mas é incrível descobrir possibilidades de vários idiomas
Cajova_Houba 30/11/2015
FTR: Para alguns dos desafios mais complexos, como este, eu estaria feliz apenas para terminá-lo eu mesmo =)
Stewie Griffin
2

PHP, 307 297 293 bytes

<?$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;

Recebe argumentos * 2 da linha de comando. salve no arquivo, execute com php <filename> <parameters>.

demolir

// 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;
Titus
fonte
1

C ++, ungolfed

(ou talvez ingovernável)

Supondo que haja menos de 100 elementos e cada elemento seja menor que 100. sé o número de edifícios (exigido na entrada).

#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;
}
}
ghosts_in_the_code
fonte
Existem alguns erros na saída ... O terreno possui 3 caracteres de largura (deve ser apenas 1) e a última parede está ausente.
Stewie Griffin
@ StewieGriffin Eu ainda estava resolvendo os erros quando publiquei isso. 1. Adicionei a última parede. 2. O solo deve ter 3 caracteres de largura, porque o teto inclinado / _ \ tem 3 caracteres de largura.
ghosts_in_the_code
11
* O chão entre os prédios, não dentro.
Stewie Griffin
Se você ainda estiver trabalhando nisso, poderá esperar, mas poderá se livrar de muitos bytes se remover as novas linhas e o recuo. Eu não resolvi o problema do solo, mas isso funciona .346 bytes em vez de 401.
Stewie Griffin
@StewieGriffin Na verdade, não pretendo enviar uma resposta para o golfe, pois é muito longo de qualquer maneira. Eu posso apostar que existem linguagens melhores onde isso é feito em menos de 100 bytes. Portanto, meu código é mais uma solução de referência para outras pessoas.
ghosts_in_the_code