Помогите архитекторам визуализировать горизонт


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 зданий ...
Stewie Griffin

Ответы:


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

К сожалению, это не работает в Октаве . Не совсем уверен, почему, похоже, что-то связано с битом 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, поэтому мы упростим то, что в противном случае было бы условием if-else, рассматривая (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=''

При этом используется «двойная» кодировка, которая передается функции в виде списка. Редактировать: побрить байты, переделав часть большого условия как селектор массива и переключившись на удвоенную кодировку. Побрит больше байтов, переставляя условные выражения еще больше и преобразовывая больше логики в арифметику.

РЕДАКТИРОВАТЬ: 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):

Стандартный прием для сложения двойного цикла 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, когда это фактическое здание, например,> 0, иначе 1). Обратите внимание, что когда мы находимся на уровне земли, он никогда не пытается нарисовать крышу, что означает, что здания размером 0,5 не имеют заостренных крыш. Ну что ж.

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

Когда мы на уровне земли, мы хотим подчеркивания вместо пробелов. Мы просто заменим их всех сразу.

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

Непосредственно перед тем, как мы начнем обрабатывать следующую строку, напечатайте текущую и очистите строку вывода. Мы отсекаем последний символ, потому что это «_», соответствующее наземному пространству, которое мы добавили, добавив ноль в начале функции. (Мы добавили этот ноль, поэтому нам не нужно было бы добавлять специальный случай для вставки правой стены, если она существует, что добавило бы гораздо больше кода, чем мы добавили бы 0 и обрезание "_".)


Автомобиль-гольф. Вау. (Также +1)
хлоп

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

Мне кажется, что вы удалили пробелы и сократили имена переменных, а остальные оставили без изменений. Вы должны попытаться найти умные способы, например, избавиться от некоторых петель, использовать меньше сравнений и т. Д. Конечно, такие вещи, 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.