Ящик с красивым рисунком (в комплекте маленькие кубики)

18

Красивый шаблон ящик

Доброе утро, PPCG!

На днях, когда я пытался помочь кому-то в переполнении стека, часть его проблемы дала мне идею для этой задачи.

Прежде всего, проверьте следующую форму:

введите описание изображения здесь

Где все черные цифры - это индекс точек в форме, а все синие цифры - это индекс связей между точками.

Теперь, учитывая шестнадцатеричное число от 0x00000 до 0xFFFFF, вам нужно нарисовать фигуру в консоли, используя только символьное пространство и «■» (с помощью символа «o» тоже хорошо).

Вот несколько примеров, где вводится шестнадцатеричное число и выводится форма:

0xE0C25 :
■ ■ ■ ■ ■ ■ ■ ■ ■ 
■               ■ 
■               ■ 
■               ■ 
■ ■ ■ ■ ■       ■ 
        ■       ■ 
        ■       ■ 
        ■       ■ 
        ■ ■ ■ ■ ■
0xC1043 :
■ ■ ■ ■ ■ ■ ■ ■ ■ 
              ■   
            ■     
          ■       
        ■         
      ■           
    ■             
  ■               
■ ■ ■ ■ ■ ■ ■ ■ ■ 
0xE4F27 :
■ ■ ■ ■ ■ ■ ■ ■ ■ 
■       ■       ■ 
■       ■       ■ 
■       ■       ■ 
■ ■ ■ ■ ■ ■ ■ ■ ■ 
■       ■       ■ 
■       ■       ■ 
■       ■       ■ 
■ ■ ■ ■ ■ ■ ■ ■ ■ 
0xF1957 :
■ ■ ■ ■ ■ ■ ■ ■ ■ 
■ ■           ■ ■ 
■   ■       ■   ■ 
■     ■   ■     ■ 
■       ■       ■ 
■     ■   ■     ■ 
■   ■       ■   ■ 
■ ■           ■ ■ 
■ ■ ■ ■ ■ ■ ■ ■ ■ 
0xD0C67 :
■ ■ ■ ■ ■ ■ ■ ■ ■ 
  ■             ■ 
    ■           ■ 
      ■         ■ 
■ ■ ■ ■ ■       ■ 
      ■ ■       ■ 
    ■   ■       ■ 
  ■     ■       ■ 
■ ■ ■ ■ ■ ■ ■ ■ ■ 
0x95E30 :
■ ■ ■ ■ ■       ■ 
  ■     ■     ■ ■ 
    ■   ■   ■   ■ 
      ■ ■ ■     ■ 
■ ■ ■ ■ ■ ■ ■ ■ ■ 
        ■ ■       
        ■   ■     
        ■     ■   
        ■       ■ 
0x95622 :
■ ■ ■ ■ ■       ■ 
  ■     ■     ■   
    ■   ■   ■     
      ■ ■ ■       
■ ■ ■ ■ ■ ■ ■ ■ ■ 
        ■         
        ■         
        ■         
■ ■ ■ ■ ■         
0xC5463 : 
■ ■ ■ ■ ■ ■ ■ ■ ■ 
        ■     ■   
        ■   ■     
        ■ ■       
■ ■ ■ ■ ■         
      ■ ■         
    ■   ■         
  ■     ■         
■ ■ ■ ■ ■ ■ ■ ■ ■ 
0xE5975 :
■ ■ ■ ■ ■ ■ ■ ■ ■ 
■       ■     ■ ■ 
■       ■   ■   ■ 
■       ■ ■     ■ 
■       ■       ■ 
■     ■ ■ ■     ■ 
■   ■   ■   ■   ■ 
■ ■     ■     ■ ■ 
■       ■ ■ ■ ■ ■ 
0xB5E75 :
■ ■ ■ ■ ■       ■ 
■ ■     ■     ■ ■ 
■   ■   ■   ■   ■ 
■     ■ ■ ■     ■ 
■ ■ ■ ■ ■ ■ ■ ■ ■ 
      ■ ■ ■     ■ 
    ■   ■   ■   ■ 
  ■     ■     ■ ■ 
■       ■ ■ ■ ■ ■ 
0xF4C75 :
■ ■ ■ ■ ■ ■ ■ ■ ■ 
■ ■     ■       ■ 
■   ■   ■       ■ 
■     ■ ■       ■ 
■ ■ ■ ■ ■       ■ 
      ■ ■ ■     ■ 
    ■   ■   ■   ■ 
  ■     ■     ■ ■ 
■       ■ ■ ■ ■ ■
0xF5D75 :
■ ■ ■ ■ ■ ■ ■ ■ ■ 
■ ■     ■     ■ ■ 
■   ■   ■   ■   ■ 
■     ■ ■ ■     ■ 
■ ■ ■ ■ ■       ■ 
■     ■ ■ ■     ■ 
■   ■   ■   ■   ■ 
■ ■     ■     ■ ■ 
■       ■ ■ ■ ■ ■ 

Вот некоторые объяснения о том, как это работает:

0xFFFFF(16) = 1111 1111 1111 1111 1111(2)

У вас здесь есть 20 бит, каждый бит говорит, существует ли ссылка или нет.

Индекс старшего значащего бита (MSB) равен 0 (ссылка на изображение) или Младший бит (LSB) равен 19 (ссылка на изображение снова).

Вот как это работает для первой фигуры, приведенной в качестве примера:

0xE0C25(16) = 1110 0000 1100 0010 0101(2)

Это означает, что вы будете иметь следующие существующие ссылки: 0,1,2,8,9,14,17,19.

Если вы выделите строки на контрольном изображении этими цифрами, это даст вам следующую форму:

■ ■ ■ ■ ■ ■ ■ ■ ■ 
■               ■ 
■               ■ 
■               ■ 
■ ■ ■ ■ ■       ■ 
        ■       ■ 
        ■       ■ 
        ■       ■ 
        ■ ■ ■ ■ ■

Вот простая и незаметная реализация Python, если вам нужна дополнительная помощь:

patterns = [
  0xE0C25, 0xC1043, 0xE4F27, 0xF1957, 
  0xD0C67, 0x95E30, 0x95622, 0xC5463, 
  0xE5975, 0xB5E75, 0xF4C75, 0xF5D75
]

def printIfTrue(condition, text = "■ "):
  if condition:
    print(text, end="")
  else:
    print(" "*len(text), end="")

def orOnList(cube, indexes):
  return (sum([cube[i] for i in indexes]) > 0)

def printPattern(pattern):
  cube = [True if n == "1" else False for n in str(bin(pattern))[2::]]
  for y in range(9):
    if y == 0: printIfTrue(orOnList(cube, [0, 2, 3]))
    if y == 4: printIfTrue(orOnList(cube, [2, 4, 9, 11, 12]))
    if y == 8: printIfTrue(orOnList(cube, [11, 13, 18]))
    if y in [0, 4, 8]:
      printIfTrue(cube[int((y / 4) + (y * 2))], "■ ■ ■ ")
      if y == 0: printIfTrue(orOnList(cube, [0, 1, 4, 5, 6]))
      if y == 4: printIfTrue(orOnList(cube, [3, 5, 7, 9, 10, 13, 14, 15]))
      if y == 8: printIfTrue(orOnList(cube, [12, 14, 16, 18, 19]))
      printIfTrue(cube[int((y / 4) + (y * 2)) + 1], "■ ■ ■ ")
    elif y in [1, 5]:
      for i in range(7):
        if i in [2, 5]:
          print(" ", end=" ")
        printIfTrue(cube[y * 2 + (1 - (y % 5)) + i])
    elif y in [2, 6]:
      for i in range(5):
        if i in [1, 2, 3, 4]:
          print(" ", end=" ")
        if i in [1, 3]:
          if i == 1 and y == 2:
            printIfTrue(orOnList(cube, [3, 4]))
          elif i == 3 and y == 2:
            printIfTrue(orOnList(cube, [6, 7]))
          if i == 1 and y == 6:
            printIfTrue(orOnList(cube, [12, 13]))
          elif i == 3 and y == 6:
            printIfTrue(orOnList(cube, [15, 16]))
        else:
          printIfTrue(cube[(y * 2 - (1 if y == 6 else 2)) + i + int(i / 4 * 2)])
    elif y in [3, 7]:
      for i in range(7):
        if i in [2, 5]:
          print("  ", end="")
        ri, swap = (y * 2 - 2) + (1 - (y % 5)) + i, [[3, 6, 12, 15], [4, 7, 13, 16]]
        if ri in swap[0]: ri = swap[1][swap[0].index(ri)]
        elif ri in swap[1]: ri = swap[0][swap[1].index(ri)]
        printIfTrue(cube[ri])
    if y == 0: printIfTrue(orOnList(cube, [1, 7, 8]))
    if y == 4: printIfTrue(orOnList(cube, [6, 8, 10, 16, 17]))
    if y == 8: printIfTrue(orOnList(cube, [15, 17, 19]))
    print()

for pattern in patterns:
  printPattern(pattern)

Конечно, он не идеален, и это довольно долго для того, что он должен делать, и именно поэтому вы здесь!

Делать эту программу смехотворно короткой :)

Это код-гольф, поэтому выигрывает самый короткий ответ!

Sygmei
источник
Можем ли мы напечатать в конце один пробел? Ваши примеры содержат их.
orlp
Да :) Это разрешено
Сигмей
4
Разрешен ли графический вывод?
12Me21
1
Вам требуется шестнадцатеричный ввод или десятичный нормально?
Тит
1
Может быть, весь код гольф только доходит до меня, но этот код больно читать ...
Линн

Ответы:

8

JavaScript (ES6), 202 188 187 байт

let f =

n=>`0${x=',16,-54,21,-26,21,21,-26,21,166'}${x},16`.split`,`.map((d,i)=>(k-=d,n)>>i&1&&[..."ooooo"].map(c=>g[p-=(k&3||9)^8]=c,p=k>>2),g=[...(' '.repeat(9)+`
`).repeat(9)],k=356)&&g.join``

console.log(f(0xE0C25))
console.log(f(0xC1043))
console.log(f(0xE4F27))
console.log(f(0xF1957))

Как это устроено

n =>                                                 // given 'n':
  `0${x = ',16,-54,21,-26,21,21,-26,21,166'}${x},16` // build the list of delta values
  .split`,`.map((d, i) =>                            // split the list and iterate
    (k -= d, n) >> i & 1 &&                          // update 'k', test the i-th bit of 'n'
    [..."ooooo"].map(c =>                            // if the bit is set, iterate 5 times:
      g[                                             // 
        p -= (k & 3 || 9) ^ 8                        // compute the direction and update 'p'
      ] = c,                                         // write a 'o' at this position
      p = k >> 2                                     // initial value of 'p'
    ),                                               //
    g = [...(' '.repeat(9) + `\n`).repeat(9)],       // initialization of the 'g' array
    k = 356                                          // initial value of 'k'
  )                                                  //
  && g.join``                                        // yield the final string

Мы работаем над сеткой gиз 9 рядов по 10 символов. Сетка изначально заполнена пробелами, с LineFeed каждый 10-й символ.

Каждый сегмент определяется начальной позицией и направлением.

Направления закодированы следующим образом:

ID | Dir.| Offset
---|-----|-------
 0 |  W  |  -1        Offset encoding formula:
 1 | NE  |  -9        -((ID || 9) ^ 8)
 2 |  N  |  -10
 3 | NW  |  -11

Каждый сегмент закодирован как целое число:

  • направление сохраняется в битах № 0 и № 1
  • начальная позиция сохраняется в битах с № 2 по № 8

Например, сегмент № 3 начинается с позиции 55 и использует 3-е направление. Поэтому он закодирован как (55 << 2) | 3 == 223.

Ниже приведен список целых чисел от сегмента № 19 до сегмента № 0:

356,340,394,373,399,378,357,383,362,196,180,234,213,239,218,197,223,202,36,20

После дельта-кодирования, начиная с 356, он становится:

0,16,-54,21,-26,21,21,-26,21,166,16,-54,21,-26,21,21,-26,21,166,16

Который в конечном итоге кодируется как:

`0${x=',16,-54,21,-26,21,21,-26,21,166'}${x},16`
Arnauld
источник
Упс ... Забыл пробелы между ними. Исправить это.
Арно
5

Python 3, 289 байт

def f(n):
 for r in range(9):print(*(" o"[any(n&1<<ord(c)-97for c in"trq|t|t|t|tspon|s|s|s|sml|r|q||p|o|n||m|l|r||qp||o||nm||l|r|p||q|o|m||n|l|rpkih|k|k|k|qomkjgfe|j|j|j|nljdc|i|h||g|f|e||d|c|i||hg||f||ed||c|i|g||h|f|d||e|c|igb|b|b|b|hfdba|a|a|a|eca".split("|")[r*9+c])]for c in range(9)))

Ничего умного, только жесткое.

orlp
источник
Не мог "trq|t...a|eca".split("|")стать "tqr t...a eca".split()?
Loovjo
@Loovjo Нет, .split()разрушает ||.
17
3

Рубин, 116 байт

->n{s=[' '*17]*9*$/
20.times{|i|j=i%9
n>>19-i&1>0&&5.times{|k|s[i/9*72+(j>1?~-j/3*8+k*18:j*16)+k*(2--j%3*2)]=?O}}
s}

Это зависит от нескольких моделей, которые я наблюдал. Во-первых, шаблон повторяется каждые 9 строк. Во-вторых, если начальные точки горизонтальных линий выбраны надлежащим образом, направления х непрерывно чередуются вправо, влево, прямо.

Неуправляемый в тестовой программе

f=->n{
   s=[' '*17]*9*$/                    #Setup a string of 9 newline separated lines of 17 spaces.
   20.times{|i|                       #For each of the 20 bits..
     j=i%9                            #The pattern repeats every 9 bits.
     n>>19-i&1>0&&                    #If the relevant bit is set,
     5.times{|k|                      #draw each of the 5 points on the relevant line.
       s[i/9*72+                      #There are 9 lines starting on each row. Row y=0 starts at 0 in the string, row y=1 at 72, etc.
       (j>1?~-j/3*8+k*18:j*16)+       #~-j=j-1. For j=2..8, the starting x coordinates are (0,0,1,1,1,2,2)*8. For j=0 and 1 starting x coordinates are 0 and 16. 
       k*(2--j%3*2)                   #From the starting points, draw the lines right,left,straight. Down movement if applicable is given by conditional k*18 above.
       ]=?O                           #Having described the correct index to modify, overwrite it with a O character.
     }
   }
s}                                    #Return the string.


[0xE0C25, 0xC1043, 0xE4F27, 0xF1957, 
  0xD0C67, 0x95E30, 0x95622, 0xC5463, 
  0xE5975, 0xB5E75, 0xF4C75, 0xF5D75].map{|m|puts f[m],'---------'}

Я считаю, что есть 112-байтовое решение с использованием 20-символьной строки и некоторого декодирования для определения параметров 20 строк. Я попробую это позже, если у меня будет время.

Уровень реки St
источник
Хорошее объяснение!
Сигмей,
2

PHP, 142 150 149 байт

for($r="";$c=ord(",(NMKJIGFHDjigfecbd`"[$i]);)if(hexdec($argv[1])>>$i++&1)for($p=96^$c&~$k=3;$k++<8;$p+=7+($c&3?:-6))$r[$p]=o;echo chunk_split($r,9);

печатает форму по мере необходимости; т.е. если нижняя часть пуста, она будет обрезана.
Беги с php -nr '<code>' <input>. Не вводите префикс

Протестируйте это онлайн

Добавьте 11 байтов для вырезания: вставьте ,$r[80]=" "после $r="".

объяснение кодировки

Каждая строка может быть описана с начальной точкой и одним из четырех направлений.
Рисуя на сетке 9x9, начальная позиция варьируется от 0,0до 8,4; или, в сочетании, от 0до 8*9+4=76. К счастью, все начальные точки [0,4,8,36,40,44,72,76]делятся на 4; поэтому код направления [0..3]можно сжать в биты 0 и 1 -> сдвиг не требуется вообще.

Для простоты расчета перемещения курсора 0берется для востока (только направление без вертикального перемещения) и [1,2,3]для юго-запада, юга, юго-востока, где смещение 9(для вертикального перемещения) плюс [-1,0,1]-> [8,9,10]-> delta=code?code+7:1.

Направление для первой и последней строк является восточным, что приводит к кодам в диапазоне от 0 до 76 [0+0,4+0,0+2,0+3,4+1,4+2,4+3,8+1,8+2,...,44+1,44+2,72+0,76+0]; и битовое значение xor 96 для каждого значения приводит к печатным и беспроблемным кодам ascii [96,100,98,99,101,102,103,105,106,68, 72,70,71,73,74,75,77,78,40,44]-> `dbcefgijDHFGIJKMN(,. Код использует LSB для бита 0, а строка 0 соответствует MSB, поэтому строка должна быть обращена. Finito.

сломать

for($r="";                  // init result string, loop through line codes
    $c=ord(",(NMKJIGFHDjigfecbd`"[$i]);)
    if(hexdec($argv[1])>>$i++&1)// if bit $i is set, draw line 19-$i:
        for($p=96^$c&~$k=3          // init $k to 3, init cursor to value&~3
            ;$k++<8;                // loop 5 times
            $p+=7+($c&3?:-6)            // 2. map [0,1,2,3] to [1,8,9,10], move cursor
        )
            $r[$p]=o;                   // 1. plot
echo chunk_split($r,9);     // insert a linebreak every 9 characters, print

некоторые игры в гольф объяснили

  • Поскольку ^96не влияет на младшие два бита, при извлечении направления его можно игнорировать; поэтому нет необходимости хранить значение в переменной, которая экономит 5 байт на курсоре init.
  • Использование ~3вместо 124сохранения одного байта и позволяет следующий гольф:
  • Инициализация счетчика цикла $k=3внутри $pприсваивания сохраняет два байта,
    и это не повредит предварительному условию (поскольку верхнее значение все еще имеет одну цифру).
  • Использование строки для результата имеет самую короткую возможную инициализацию и прорисовку: когда символ установлен за концом строки, PHP неявно устанавливает пропущенные символы в пробел. И chunk_splitэто самый короткий способ вставить разрывы строк.
    Я даже не хочу знать, сколько еще что-нибудь потребуется.
  • 7+($c&3?:-6)на один байт короче $c&3?$c%4+7:1.
  • Добавлено hexdec()(8 байт) для удовлетворения ограничения ввода.
Titus
источник
2

JavaScript, 184 183 178 168 167 байт

f=
n=>[...`<>K[LM]NO\\^k{lm}no|~`].map((e,i)=>n>>i&1&&[0,2,4,6,8].map(i=>a[(e&102)*4+(e&17||15)*i]=`o`,e=~e.charCodeAt()),a=[...(` `.repeat(31)+`
`).repeat(9)])&&a.join``
<input oninput=o.textContent=f(+this.value)><pre id=o>

Первоначально было 206 байт, но ответ @ Arnauld вдохновил меня на исследование решения одномерного массива. Редактировать: 1 байт сохранен благодаря @ edc65. Сохранено 5 15 байт благодаря @Arnauld. Сохраненный еще байт путем настройки выбора символов.

Нил
источник
[0,1,2,3,4]короче
edc65
Я думаю, что вы можете сохранить 4 байта, используя [67,65,52,36,51,50,34,49,48,35,33,20,4,19,18,2,17,16,3,1]и[0,2,4,6,8].map(i=>a[(e&102)*4+(e&17||15)*i]='o')
Арно
1
Или вы можете использовать [..."ecVFUTDSREC6&54$32%#"]и [0,2,4,6,8].map(i=>a[(e&102)*4+(e&17||15)*i]='o',e=e.charCodeAt()-34)сохранить еще 10 байтов.
Арно
@Arnauld Вы, кажется, недооценили свои сбережения на 1, и мне также удалось отыграть лишний байт, используя ~вместо этого -34(к сожалению, я не справляюсь с `\`, поэтому я не сохраняю 2 байта).
Нил
Интересно, если бы вы могли заменить это '\' на символ ASCII # 220.
Arnauld
1

Пакетный, 491 байт

@set n=%1
@for %%i in ("720896 524288 524288 524288 843776 262144 262144 262144 268288" "131072 65536 0 32768 16384 8192 0 4096 2048" "131072 0 98304 0 16384 0 12288 0 2048" "131072 32768 0 65536 16384 4096 0 8192 2048" "165248 1024 1024 1024 89312 512 512 512 10764" "256 128 0 64 32 16 0 8 4" "256 0 192 0 32 0 24 0 4" "256 64 0 128 32 8 0 16 4" "322 2 2 2 171 1 1 1 17")do @set s=&(for %%j in (%%~i)do @set/am=%1^&%%j&call:c)&call echo(%%s%%
:c
@if %m%==0 (set s=%s%  )else set s=%s%o 

Примечание: последняя строка заканчивается пробелом. Помещение ifусловия с переменной внутри forцикла выходит за рамки пакета, поэтому для него требуется собственная подпрограмма. Поскольку ничего не видно, я проваливаюсь в него, чтобы выйти. ~Unquotes струны во внешнем контуре , позволяя внутреннюю петлю к петле над числами. Числа - это просто битовые маски для всех мест, где oнужно рисовать s.

Нил
источник
1

C 267 262 260 256 символов

Подсчет побегов как 1 символа

void f(int i){char*k="\0\x1\x2\x3\x4\x4\x5\x6\x7\x8\0\x9\x12\x1b\x24\0\xa\x14\x1e\x28\x4\xc\x14\x1c\x2d\x4\xd\x16\x1f\x28\x4\xe\x18\x22\x2c\x8\x10\x18\x20\x28\x8\x11\x1a\x23\x2c\x24\x25\x26\x27\x28\x28\x29\x2a\x2b\x2c\x24\x2d\x36\x3f\x48\x24\x2e\x38\x42\x4c\x28\x30\x38\x40\x48\x28\x31\x3a\x43\x4c\x28\x31\x3a\x43\x4c\x28\x32\x3c\x46\x50\x2c\x35\x3e\x47\x50\x48\x49\x4a\x4b\x4c\x4c\x4d\x4e\x4f\x50";for(int n=0,s,l;n<81;!(++n%9)&&putchar(10))for(s=l=0;s<20;!(++l%5||++s^20)&&putchar(32))if(i<<s&1<<19&&k[l]==n&&putchar(111))break;}

k - это поиск, в котором указаны поля, в которые нужно добавить 'o'.

Попробуйте онлайн!

Ahemone
источник
1

Befunge, 468 байт

~:85+`!#v_86*-:9`7*-48*%\82**+
3%2:/2\<$v0%2:/2\*g02*!g03%2:/2\*!+4g07%2:/2\*g02*!-8g06%2:/2\*g02*!-4g0
g!*20g*^00>50g*\2/:2%00g8-!*40g*\2/:2%30g8-!*20g*\2/:2%60g66+-!*\2/:2%70
`\5:p00:<g^*!-8g00%2:\-10:\p07-g00:p06+g00:p05`3:p04`\5:p03:<0\p02`3:p01
#o 8`#@_^4>*50g*\2/2%00g!*40g*0\>:#<1#\+_$!1+4g,48*,\1+:8`!#^_55+,$\1+:
g03%2:/2<-^!g00%2:/2\*g01*!g03%2:/2\*g01*!g07%2:/2\*!-4g06%2:/2\*g01*!-4
70g4-!*\^>!*50g*\2/:2%00g4-!*40g*\2/:2%30g8-!*10g*\2/:2%60g8-!*10g*\2/:2%

Попробуйте онлайн!

Первая строка читает строку из stdin, оценивая ее как шестнадцатеричное число. Остальная часть кода по сути представляет собой просто двойной цикл по координатам x / y сетки, с массивным логическим вычислением, определяющим, oследует ли выводить для каждого местоположения.

В принципе, для каждой из 20 точек сетки существует отдельное условие (первые четыре):

(y==0) * (x<5) * bit0
(y==0) * (x>3) * bit1
(x==0) * (y<5) * bit2
(x==y) * (y<5) * bit3

И затем, как только мы вычислили все 20 из них, мы ИЛИ партию вместе, и если этот результат верен, мы выведем a o, в противном случае мы выведем пробел.

У Befunge нет ничего в плане операций с битами, поэтому, чтобы извлечь биты из ввода, мы просто многократно оцениваем, n%2а затем проходим n/=220 вычислений условий.

Джеймс Холдернесс
источник