Aiuta gli architetti a visualizzare lo skyline


29

Come parte di un progetto di pianificazione urbana, hai ottenuto l'incarico di creare un programma o una funzione che mostrerà lo skyline della città, dato il contributo degli architetti. Il progetto è solo nella fase di avvio, quindi è sufficiente uno schizzo molto approssimativo. L'approccio più semplice è ovviamente quello di disegnare semplicemente lo skyline nell'arte ASCII.

Tutti gli edifici saranno lungo il fiume, quindi sono tutti allineati. Gli architetti forniranno l'altezza di ogni edificio come input e il tuo codice dovrebbe mostrare lo skyline.

L'input degli architetti sarà un numero intero o un mezzo intero. Se il numero è un numero intero, l'edificio avrà un tetto piano, mentre un mezzo numero intero si tradurrà in un tetto inclinato. Uno zero sarà solo terreno piatto. Le pareti di un edificio sono separate da 3 caratteri, mentre uno zero sarà largo un singolo carattere. Gli edifici adiacenti condividono i muri.

Per dettagli e chiarimenti sull'output, dai un'occhiata agli esempi seguenti:

N = 3
 ___
|   |
|   |
|___|

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

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

n = 0
_

Esempio di input: 3 3.5 0 2

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

Esempio di input: 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

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

I caratteri ASCII utilizzati sono: newline, spazio e /\_|(punti di codice 10, 32, 47, 92, 95, 124).

Regole:

  • È facoltativo creare un programma che accetta solo numeri interi come input, moltiplicando tutti i numeri per due. Quindi, invece di prendere 3 3.5 2, il tuo programma potrebbe richiedere 6 7 4. Se viene scelto il secondo formato di input, un input di 6 dovrebbe tradursi in un edificio a 3 piani, 7 dovrebbe essere un edificio a 3 piani con tetti inclinati, ecc.
  • L'output dovrebbe essere esattamente come descritto sopra, ma gli spazi finali e le nuove righe sono OK.
  • Il formato esatto dell'input è facoltativo. Qualunque sia il migliore nella tua lingua.
  • Il risultato deve essere visualizzato sullo schermo, in modo che gli architetti possano vederlo.
  • Si può presumere che ci sarà almeno un numero intero dato e che verrà fornito solo un input valido.

Questo è codegolf, quindi vince il codice più breve in byte.


1
Come sarebbe un edificio di altezza 0,5?
Tom Carpenter,

Non ci ho pensato davvero. La scelta più ovvia sarebbe solo un tetto spiovente, quasi come una casa di hobbit :-) ma sei libero di scegliere, o puoi supporre che l'ingresso non sarà mai 0,5 ...
Stewie Griffin

1
Al momento accadono cose strane in quanto non ci sono muri (immaginavo che non esistessero alti 0,5), quindi dovrò lavorare un po 'sulla mia risposta.
Tom Carpenter,

Ho appena provato il tuo codice con altezza 0,5, e sono d'accordo, "strano" è una parola molto descrittiva = PI non l'ho esaminato in dettaglio, quindi non sono sicuro di cosa stia succedendo ... Comunque, rispondi è perfettamente valido, puoi presumere che non ci siano 0,5 edifici ...
Stewie Griffin

Risposte:


5

Python 2, 199 193 188 185 byte

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

Questo è un programma completo che accetta numeri interi come input. Esempio di input .


meraviglioso! Devo rubare alcuni di questi trucchi per i futuri golf ...
Quintopia,

5

MATLAB, 219 209 203 byte

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

Questo purtroppo non funziona su Octave . Non del tutto sicuro del perché, sembra essere qualcosa a che fare con il bit disp / flipud che si rompe.

Inoltre, al momento non esiste una definizione di come sia un edificio di altezza 0,5, né alcuna menzione di essi, quindi in questo codice presumo che non siano consentiti.

Di seguito è riportato il codice in un modo leggermente più leggibile:

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

Innanzitutto prendiamo un input come un array e facciamo un'inizializzazione variabile.

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;

Poiché gli edifici a altezza zero sono un dolore - fondamentalmente finiscono con una larghezza che dipende da ciò che sono accanto (anche se ciò che viene stampato non cambia), semplificiamo le cose disegnando abbastanza terreno per tutti gli edifici. Supponiamo che ogni edificio abbia una larghezza di 4 caratteri (perché gli edifici adiacenti si fondono insieme) - quelli di altezza zero non lo sono, ma l'eccesso verrà tagliato in seguito.

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

Ora disegniamo a turno ogni edificio.

for a=i

Per prima cosa otteniamo la parte intera dell'altezza poiché questo determinerà quanti '|' abbiamo bisogno.

    b=fix(a);

Ora disegna il muro per questo edificio - se ci sono due edifici adiacenti, il muro per questo nuovo sarà nella stessa colonna del muro dell'ultimo.

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

Controlla se si tratta di un edificio a mezza altezza. Se lo è, il tetto sarà diverso. Per le mezze altezze, il tetto sarà / \mentre quello a tutta altezza sarà ___(Matlab lo replicherà implicitamente da un singolo trattino basso, quindi salva un paio di byte lì). C'è un po 'più di tetto una fila più in alto per gli edifici a mezza altezza, quindi anche questo viene aggiunto.

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

Disegna sul tetto

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

Ora spostati all'inizio del prossimo edificio e disegna nel muro condiviso (se il muro è troppo corto a questo punto, sarà ingrandito quando verrà disegnato il prossimo edificio). Si noti che gli edifici a altezza zero sono 1 di larghezza, gli edifici normali sono 4 di larghezza, quindi semplifichiamo quello che altrimenti sarebbe un if-else trattando (a> 0) come un numero decimale non booleano.

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

Poi arriva un po 'di pirateria informatica per lavorare con edifici di altezza zero. Fondamentalmente ciò che dice questo è se questo edificio era a zero altezza, e quello precedente non lo era, significa che il luogo del prossimo edificio deve essere aumentato di 1 perché un edificio a zero altezza inserito tra due altri edifici è effettivamente il doppio di larghezza - questo rappresenta il muro aggiuntivo che è normalmente condiviso con un edificio adiacente. Teniamo anche traccia di questa altezza dell'edificio per fare questo controllo la prossima volta.

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

Una volta terminato, capovolgi la matrice dell'edificio in modo che sia corretta e visualizzala. Nota che anche qui eliminiamo qualsiasi eccesso di terreno.

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

Quindi, quando eseguiamo questo script, ci viene chiesto il nostro input, ad esempio:

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

Quindi genera l'edificio e visualizza il risultato. Per l'input precedente, viene generato quanto segue:

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

Molto ben fatto!
Stewie Griffin,

4

Kotlin, 447 442 byte

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

Versione non golfata:

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 byte

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

Questo utilizza la codifica "raddoppiata", da passare alla funzione come un elenco. Modifica: rasatura dei byte rifacendo parte del grande condizionale come selettore di array e passando alla codifica raddoppiata. Hai rasato più byte riorganizzando ulteriormente il condizionale e convertendo più logica in aritmetica.

EDIT: xsot è meglio

Spiegazione:

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

dè 1 in più rispetto alla lunghezza dell'array, perché aggiungeremo zero su ciascuna estremità dell'elenco dal secondo elemento fino allo zero che abbiamo aggiunto alla fine. hè l'altezza del disegno. (Dobbiamo dividere per 2 in questo calcolo perché stiamo usando la rappresentazione raddoppiata, che usiamo in modo specifico per evitare di dover lanciare float in ints in tutto il luogo. Aggiungiamo anche 1 prima di dividere così altezze strane - edifici appuntiti-- ottenere un po 'più di spazio rispetto al tipo normale.) oè la stringa di output.

 for i in range(d*h):

Un trucco standard per comprimere un doppio per loop in un singolo per loop. Una volta che facciamo:

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

ora abbiamo ottenuto lo stesso risultato di:

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

ma in un modo che ci mette in rete dieci byte salvati (incluso lo spazio bianco nelle righe seguenti).

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

Attaccare un muro in qualsiasi momento l'altezza dell'edificio attuale o dell'edificio precedente è più alta della linea corrente, purché ci sia almeno un limite dell'edificio qui. È l'equivalente del seguente condizionale:

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

dove b è l'altezza di scansione corrente, a è l'altezza dell'edificio attuale e c è l'altezza dell'edificio precedente. L'ultima parte del condizionale impedisce di mettere le pareti tra gli spazi del terreno.

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

Questa è la parte che disegna il tetto corretto, selezionando le parti del tetto confrontando l'altezza dell'edificio con l'attuale altezza di scansione. Se un tetto non va qui, stampa un numero appropriato di spazi (3 quando è un edificio reale, ad es. Uno> 0, altrimenti 1). Nota che quando siamo a livello del suolo, non tenta mai di disegnare un tetto, il che significa che gli edifici di dimensioni 0,5 non ottengono tetti a punta. Oh bene.

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

Quando siamo a livello del suolo, vogliamo sottolineature anziché spazi. Li sostituiamo tutti insieme in una volta qui.

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

Poco prima di iniziare l'elaborazione della riga successiva, stampare quella corrente e cancellare la riga di output. Tagliamo l'ultimo carattere perché è lo "_" corrispondente allo spazio terreno aggiunto aggiungendo uno zero all'inizio della funzione. (Abbiamo aggiunto quello zero in modo da non dover aggiungere un caso speciale per inserire un muro giusto, se esiste, che aggiungerebbe molto più codice di quello che abbiamo aggiunto aggiungendo lo 0 e tagliando "_".)


Car-golf. Wow. (Inoltre, +1)
applaude il

2

Python 3

725 byte

608 byte

Codice golfizzato:

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)

Ecco il codice non salvato. Ci sono alcuni commenti, ma l'idea di base è quella di creare edifici con pareti doppie, quindi la linea di fondo appare come:

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

Quindi per ottenere gli indici di quelle doppie pareti e rimuovere quelle colonne in modo da ottenere:

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

Codice:

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

È ora di giocare a golf!


Potresti voler dare un'occhiata qui . Penso che ci sia un grande potenziale di golf qui =) Conosco solo Python di base, quindi non posso suggerire nulla di specifico temo ...
Stewie Griffin

Mi sembra che tu abbia rimosso gli spazi e abbreviato i nomi delle variabili, mantenendo invariato il resto. Dovresti cercare di trovare modi intelligenti per liberarti di alcuni anelli, usare meno confronti, ecc. Naturalmente, cose come ru(n):return math.ceil(n)il golf, ma comunque ... Per favore, non prenderlo in modo negativo, non sono un buon giocatore di golf me stesso, e sicuramente non un buon programmatore. Ti suggerisco di provare a migliorarlo un po '... In realtà è abbastanza divertente quando ti rendi conto che riesci ad accorciarlo. Sono passato da molti a 120 a 55 pochi giorni fa. Quindi è possibile anche se sei nuovo.
Stewie Griffin,

@StewieGriffin Grazie per quel link! Sono davvero un principiante del golf di codice, quindi si tratta più di completare il compito effettivo piuttosto che di praticare il golf di codice per me. Ma è sorprendente scoprire le possibilità di varie lingue
Cajova_Houba,

FTR: Per alcune delle sfide più complesse, come questa, sarei felice di finirlo da solo =)
Stewie Griffin,

2

PHP, 307 297 293 byte

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

Accetta gli argomenti * 2 dalla riga di comando. salva su file, esegui con php <filename> <parameters>.

abbattersi

// 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 ++, non golfato

(o forse non giocabile)

Supponendo che ci siano meno di 100 elementi e ogni elemento sia inferiore a 100. sè il numero di edifici (richiesto in input).

#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;
}
}

Ci sono alcuni errori nell'output ... Il terreno ha una larghezza di 3 caratteri (dovrebbe essere solo 1) e manca l'ultimo muro.
Stewie Griffin

@StewieGriffin Stavo ancora risolvendo gli errori quando l'ho pubblicato. 1. Ho aggiunto l'ultimo muro. 2. Il terreno deve essere largo 3 caratteri, perché il tetto inclinato / _ \ è largo 3 caratteri.
ghosts_in_the_code il

1
* Il terreno tra gli edifici, non all'interno.
Stewie Griffin

Se ci stai ancora lavorando, potresti voler aspettare, ma puoi sbarazzarti di molti byte se rimuovi le nuove righe e il rientro. Non ho risolto il problema a terra, ma questo funziona .346 byte invece di 401.
Stewie Griffin

@StewieGriffin In realtà non ho intenzione di inviare una risposta golf poiché è comunque troppo lungo. Scommetto che esistono lingue migliori in cui viene eseguita in meno di 100 byte. Quindi il mio codice è più di una soluzione di riferimento per gli altri.
ghosts_in_the_code il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.