Допоможіть архітекторам візуалізувати горизонт


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

Правила:

  • Необов’язково робити програму, яка приймає лише введення цілих чисел, множуючи всі числа на два. Тож замість того, щоб брати 3 3.5 2, може взяти вашу програму 6 7 4. Якщо обраний другий формат введення, вхід 6 повинен призвести до 3-поверхового будинку, 7 - 3-поверхового будинку з скатними дахами тощо.
  • Вихід повинен бути точно таким, як описано вище, але пробіли та нові рядки в порядку.
  • Точний формат введення необов’язковий. Те, що є найкращим у вашій мові.
  • Результат повинен бути відображений на екрані, щоб архітектори могли подивитися на нього.
  • Ви можете припустити, що буде принаймні одне ціле число, і буде вказано лише дійсне введення.

Це codegolf, тому найкоротший код у байтах виграє.


1
Як виглядала б будівля висотою 0,5?
Том Карпентер

Не думав про це насправді. Найбільш очевидним вибором буде просто скатний дах, майже як гобітовий будинок :-), але ви вільні у виборі, або ви можете припустити, що вхід ніколи не буде 0,5 ...
Стюі Гріффін

1
Наразі трапляються дивні речі, оскільки стін немає (я вважав, що 0,5 високих не існувало), тому мені доведеться трохи попрацювати над своєю відповіддю.
Том Карпентер

Я щойно спробував ваш код висотою 0,5, і я погоджуюся, "дивно" - це дуже описове слово = PI не переглянув це детально, тому я не впевнений, що відбувається ... Так чи інакше, ви відповідаєте: Цілком справедливо, ви можете припустити, що немає 0,5 будівлі ...
Стюі Гріффін

Відповіді:


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

Це повна програма, яка приймає цілі числа як вхідні дані. Приклад введення .


чудовий! мені доведеться вкрасти деякі з цих хитрощів для майбутніх
гольфів

5

MATLAB, 219 209 203 байт

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) як десятковий номер, а не булевий.

    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]

Потім він генерує будівлю і відображає результат. Для вищевказаного вводу створюється таке:

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

Дуже добре зроблено!
Стюі Гріффін

4

Котлін, 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=''

При цьому використовується "подвоєне" кодування, яке передається функції як список. Редагувати: Поголені байти, повторивши частину великого умовного вибору масиву та переключившись на подвійне кодування. Поголив більше байтів, переставивши умовне ще більше і перетворивши більше логіки в арифметичні.

EDIT: xsot краще

Пояснення:

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

dна 1 більше, ніж довжина масиву, тому що ми будемо додавати нулі на кожному кінці списку від другого елемента до нуля, який ми додали в кінці. h- висота малюнка. (У цьому розрахунку ми повинні ділитися на 2, тому що ми використовуємо подвоєне представлення, яке ми використовуємо спеціально, щоб уникнути необхідності викидати поплавці до точок всьому місці. Також ми додаємо 1, перш ніж ділити такі непарні висоти - точкові будівлі-- отримати трохи більше кліренсу, ніж звичайний вид.) o- це вихідний рядок.

 for i in range(d*h):

Стандартний трюк для згортання подвійного для циклу в одинарного для циклу. Як тільки ми це зробимо:

  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)
плескайте

2

Пітон 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, тому я не можу запропонувати нічого конкретного, чого я боюся ...
Стюі Гріффін

Мені здається, ви видалили пробіли та скоротили назви змінних, а решту залишили незмінними. Вам слід спробувати знайти розумні способи, наприклад, позбутися деяких циклів, використати менше порівнянь тощо. Звичайно, такі речі, як ru(n):return math.ceil(n)відзначення гольфу, але все ж ... Будь ласка, не сприймайте це негативно, я не Хороший гольфіст, і, звичайно, не хороший програміст. Я пропоную вам спробувати її трохи покращити ... Це насправді така забава, як тільки ви зрозумієте, що вам вдається скоротити це. Я пішов від багатьох до 120 до 55 кілька днів тому. Так що це можливо, навіть якщо ви новачок у цьому.
Стюі Гріффін

@StewieGriffin Дякую за це посилання! Я дійсно новачок у коді-гольфінгу, тому для мене це швидше виконання справжнього завдання, а не проведення кодового гольфу. Але дивовижно відкрити можливості різних мов
Cajova_Houba

FTR: Для деяких складніших завдань, таких як ця, я був би радий закінчити її сам =)
Стюі Гріффін

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), а остання стінка відсутня.
Стюі Гріффін

@StewieGriffin Я все ще розбирав помилки, коли розміщував це. 1. Я додав останню стіну. 2. Земля повинна бути шириною 3 символів, тому що похилий дах / _ \ має 3 символи.
ghosts_in_the_code

1
* Земля між будівлями, а не всередині.
Стюі Гріффін

Якщо ви все ще працюєте над цим, можливо, вам захочеться почекати, але ви можете позбутися багатьох байтів, якщо вилучите нові рядки та відступи. Я не виправив основну проблему, але це працює .346 байт замість 401.
Стюі Гріффін

@StewieGriffin Я насправді не збираюся подавати відповідь на гольф, оскільки це все одно занадто довго. Я можу обміняти, що існують кращі мови, де це робиться в 100 байт. Тож мій код є скоріше опорним рішенням для інших.
ghosts_in_the_code
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.