22
\$\begingroup\$

Given a positive integer n >= 1, output the first n rows of the following structure:

   #
  # #
  ###
 #   #
 # # #
 ## ##
 #####
#     #
#  #  #
# # # #
# ### #
##   ##
## # ##
### ###
#######

The n-th 1-indexed row is the binary representation of n, mirrored without copying the last character, with # in place of 1 and <space> in place of 0. All rows are centered.

You must output as ASCII-art but you may use any non-whitespace character in place of where I use # in the example. Trailing whitespace is allowed, and a trailing newline is allowed. The output must look like the example, and no extra leading whitespace or leading newlines.

You can view the first 1023 rows of the fractal cathedral here.

To generate larger test cases, here's an ungolfed reference implementation in Python

\$\endgroup\$
4
  • \$\begingroup\$ Nice idea. I wouldn't have guessed binary numbers to have produced such pretty ascii art. \$\endgroup\$
    – Jonah
    Aug 14, 2017 at 1:21
  • \$\begingroup\$ @Jonah Thanks :) Glad you like it \$\endgroup\$
    – hyper-neutrino
    Aug 14, 2017 at 1:22
  • 7
    \$\begingroup\$ Both links are pointing to the generated cathedral. \$\endgroup\$
    – Otávio
    Aug 14, 2017 at 10:30
  • \$\begingroup\$ @Otávio :I will fix, thanks \$\endgroup\$
    – hyper-neutrino
    Aug 14, 2017 at 15:32

24 Answers 24

6
\$\begingroup\$

MATL, 10 bytes

:B2&Zv35*c

Try it online!

Explanation

:      % Implicitly input n. Push range [1 2 ... n]
B      % Convert to binary. Gives a matrix where each row corresponds to
       % a number. Rows have left-padding zeros if needed
2      % Push 2
&Zv    % Symmetrize along sepecified dimension (2nd means horizontally),
       % without repeating the last element
35*    % Multiply by 35 (ASCII code for '#')
c      % Convert to char. Char 0 is shown as space. Implicitly display
\$\endgroup\$
5
  • 1
    \$\begingroup\$ I wonder if it would be helpful to add some sort of builtin that corresponds to multiplying by 35 then converting to char. It seems to be used often \$\endgroup\$ Aug 14, 2017 at 17:45
  • \$\begingroup\$ @ConorO'Brien It is used often, yes. But it would be a two-char built-in, so there would be no gain \$\endgroup\$
    – Luis Mendo
    Aug 14, 2017 at 17:47
  • \$\begingroup\$ No gain? 35*c is 4 characters \$\endgroup\$ Aug 14, 2017 at 18:00
  • \$\begingroup\$ @ConorO'Brien Ah, you mean with 35 fixed? That seems a little specific. On the other hand, some challenges allow any char, so it may be a good idea. Do you think # is the most common? \$\endgroup\$
    – Luis Mendo
    Aug 14, 2017 at 18:43
  • 2
    \$\begingroup\$ For reference, this feature has been implemented (function Zc, with character 35, i.e. #). Thanks, @ConorO'Brien! \$\endgroup\$
    – Luis Mendo
    Aug 14, 2017 at 21:14
5
\$\begingroup\$

05AB1E, 9 bytes

Code:

Lb€û.c0ð:

Uses the 05AB1E encoding. Try it online!

Explanation:

L              # List [1, .., input]
 b             # Convert each to binary
  €û           # Palindromize each binary number
    .c         # Join the array by newlines and centralize
      0ð:      # Replace zeroes by spaces
\$\endgroup\$
4
\$\begingroup\$

Jelly, 12 bytes

RBUz0ZUŒBo⁶Y

Try it online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I also have to go, so I will read comments when I get back. Feel free to out-golf me. \$\endgroup\$
    – Leaky Nun
    Aug 13, 2017 at 23:09
  • \$\begingroup\$ +1 for "feel free to outgolf me" and for the good answer. \$\endgroup\$ Aug 14, 2017 at 1:07
3
\$\begingroup\$

Python 2, 92 bytes

n=input()
for x in range(n):s=bin(2**len(bin(n))/4+x+1)[3:].replace(*'0 ');print s+s[-2::-1]

Try it online!

In Python 3, s=f'{x+1:0{len(bin(n))-2}b}'.replace(*'0 ') is shorter, but int(input()) and parens around the print argument push it up to 95 bytes.

\$\endgroup\$
1
  • \$\begingroup\$ That's a copy of mine :) (but clever use of 2**len(bin(n))/4 anyways) \$\endgroup\$ Aug 14, 2017 at 9:12
3
\$\begingroup\$

JavaScript (ES6), 106 bytes

Uses 1 as the non-whitespace character.

f=(n,k=0)=>k++<n?[...Array(32-Math.clz32(n))].reduce((s,_,i)=>(c=k>>i&1||' ')+s+(i?c:''),'')+`
`+f(n,k):''

Demo

f=(n,k=0)=>k++<n?[...Array(32-Math.clz32(n))].reduce((s,_,i)=>(c=k>>i&1||' ')+s+(i?c:''),'')+`
`+f(n,k):''

O.innerText = f(15)
<pre id=O>

Alternate version (same size)

Without Math.clz32():

f=(n,a=[k=i=0])=>n>>i+1?f(n,a,a[++i]=i):k++<n?a.reduce((s,i)=>(c=k>>i&1||' ')+s+(i?c:''),'')+`
`+f(n,a):''
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Very nice! First time I've seen Math.clz32 - I didn't even know it existed! \$\endgroup\$
    – Birjolaxew
    Aug 14, 2017 at 11:43
  • \$\begingroup\$ @Birjolaxew Yup, this is an ES6 addition. It comes handy every once in a while. \$\endgroup\$
    – Arnauld
    Aug 14, 2017 at 12:47
3
\$\begingroup\$

Husk, 21 20 18 bytes

Thanks @Zgarb for golfing off 2 bytes!

S↑(tfS=↔ΠR" #"←DLḋ

Try it online!

Ungolfed/Explanation

To avoid lengthy padding, this determines the width of the fractal which is given as 2*len(bin(N))-1 and generates all sequences of that length with the symbols #,_ ('_' denotes a space).

Since the Cartesian power is generated in order and the binary numbers are too, this is fine. All we need to do to get the fractal at this point, is filtering out all palindromes and that's basically it:

                    -- implicit input N
S↑(                 -- take N from the following list
        ΠR" #"      --   Cartesian power of [" #"] to
                Lḋ  --     number of bits in bin(N)
               D    --     2*
              ←     --     -1
    fS=↔            --   filter out palindromes
   t                --   drop the first line (all spaces)
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Ṙ; can be just R, and ȯ is unnecessary. Nice answer idea! \$\endgroup\$
    – Zgarb
    Aug 14, 2017 at 17:26
2
\$\begingroup\$

Mathematica, 94 bytes

Column[Row/@Table[s=IntegerDigits[i,2];Join[s,Reverse@Most@s]/.{0->" ",1->"#"},{i,#}],Center]&
\$\endgroup\$
1
  • 2
    \$\begingroup\$ I really have to go, too... \$\endgroup\$
    – ZaMoC
    Aug 13, 2017 at 23:15
2
\$\begingroup\$

Mathematica, 98 bytes

Riffle[Nest[ArrayFlatten@{{0,#,0},{1,0,1},{1,#,1}}&,{1},⌊Log2@#⌋]~Take~#"#"/. 0->" ","
"]<>""&

Try it at the Wolfram sandbox! The and are three bytes each.

It's a different approach from the other answers so far, using the fractal nature of the pattern. The key step is ArrayFlatten@{{0,#,0},{1,0,1},{1,#,1}}&, which does the fractally stuff, best explained in picture form:

                 [    ]
                 [grid]
[    ]           [    ]
[grid]   --->   #      #
[    ]          #[    ]#
                #[grid]#
                #[    ]#

The code repeats this step enough times to get at least n rows, then trims off the extra rows and displays it nicely.

\$\endgroup\$
2
\$\begingroup\$

Gaia, 11 bytes

 #”B¦ₔṫ¦€|ṣ

Try it online!

Explanation

    ¦ₔ       For each number 1..input:
 #”B          Convert it to base 2 and use space as 0 and # as 1
      ṫ¦     Palindromize each
        €|   Centre the lines
          ṣ  Join with newlines
\$\endgroup\$
2
\$\begingroup\$

Python 2, 120 118 107 bytes

thanks @luismendo, @officialaimm, @halvard-hummel

def f(l):
 for a in range(1,l+1):print(bin(a)[2:]+bin(a)[-2:1:-1]).replace(*'0 ').center(len(bin(l+1))*2-4)

Try it online!

\$\endgroup\$
2
  • 2
    \$\begingroup\$ replace(*'0 ') for 2 bytes \$\endgroup\$ Aug 14, 2017 at 4:03
  • 2
    \$\begingroup\$ 107 bytes \$\endgroup\$ Aug 14, 2017 at 6:56
2
\$\begingroup\$

C# (.NET Core), 192 178 bytes 168+23

thank you TheLethalCoder for the help.

x=>new int[x].Select((_,z)=>Convert.ToString(z+1,2).PadLeft((int)Math.Log(x,2)+2).Replace('0',' ')).Aggregate((y,z)=>y+"\n"+z+new string(z.Reverse().Skip(1).ToArray()))

Try it online!

pretty sure this can be reduced by a lot, most likely in the padding and reversing of the string.

\$\endgroup\$
5
  • \$\begingroup\$ Welcome to PPCG! At the moment this answer is just a code snippet. It can be fixed by adding the x=> into the byte count and note you don't need to include the trailing semi-colon. Enumerable.Range(1,x).Select(z is shorter as new int[x].Select((_,z) (I think that's correct). As you are using Linq you should include using System.Linq; into your byte count. You are also using Math so you should include using System; or fully qualify it. Note that this is then shorter as namespace System.Linq{} \$\endgroup\$ Aug 14, 2017 at 16:04
  • \$\begingroup\$ You don't need to include ,' ' in the PadLeft call as a space is the default. \$\endgroup\$ Aug 14, 2017 at 16:07
  • \$\begingroup\$ 168 bytes + 23 for namespace System.Linq{} \$\endgroup\$ Aug 14, 2017 at 16:11
  • \$\begingroup\$ @TheLethalCoder sorry for the inconvenience, it is now fixed. \$\endgroup\$ Aug 14, 2017 at 17:30
  • \$\begingroup\$ No worries +1 from me it is a nice answer :) \$\endgroup\$ Aug 15, 2017 at 7:58
1
\$\begingroup\$

Charcoal, 28 bytes

A…·¹NθW⌈θ«Eθ§ #κ↓⸿AEθ÷κ²θ»‖O

Try it online! Link is to verbose version of code. Explanation:

A…·¹Nθ

Create a list of the first n natural numbers.

W⌈θ«

Repeat until all elements are zero.

Eθ§ #κ

Print the last binary digit of each element of the list as a or #.

↓⸿

Move to the previous column.

AEθ÷κ²θ

Divide all elements of the list by two.

»‖O

Once the left half has been drawn, reflect it.

\$\endgroup\$
1
  • \$\begingroup\$ Current versions of Charcoal have MapAssignRight(IntDivide, 2, q); which saves 3 bytes. \$\endgroup\$
    – Neil
    Nov 25, 2017 at 17:02
1
\$\begingroup\$

J, 29 bytes

' #'{~(],}.@|.)"1@(#.^:_1)@i.

Try it online!

explanation

  • i. integers up to n, the input
  • (#.^:_1) converted to base 2
  • (],}.@|.) row by row ("1 does that part), take the binary number (] is the identity fn), and cat it (,) with its reverse (|.), where the reverse is beheaded (}.).
  • ' #'{~ converts the 1s and 0s to hashes and spaces.
\$\endgroup\$
6
  • \$\begingroup\$ You can use #.inv instead of #.^:_1. \$\endgroup\$ Aug 14, 2017 at 17:46
  • \$\begingroup\$ @ConorO'Brien, thanks, wasn't aware of that. \$\endgroup\$
    – Jonah
    Aug 14, 2017 at 17:47
  • \$\begingroup\$ Isn't this off by one? For n = 1, you print nothing. Anyways, you can shave off a few bytes with a few changes like so ' #'{~(,|.@}:)"1@#:@:>:@i. (if you're allowed to be off by one you can remove 4 more bytes). Basically, use a hook because it performs just like a fork when the left tine is ] and use the built-in #: which AFAIK is about the same as #.inv. EDIT: I figure my answer is similar enough to warrant being a comment, let me know if you think it should be an answer of its own. \$\endgroup\$
    – cole
    Aug 14, 2017 at 17:58
  • \$\begingroup\$ @cole, thanks! i'll update it a bit later. i thought i'd tried #: and it didn't work, but i must be remembering wrong because you're right that it does. \$\endgroup\$
    – Jonah
    Aug 14, 2017 at 18:03
  • \$\begingroup\$ @Jonah you may have tried 2 #: which will only get the right-most digit. Monadic #: functions just like #.inv (or #.&:_1). This differs from dyadic #:, which only gives as many digits as there are atoms in its left argument. \$\endgroup\$
    – cole
    Aug 15, 2017 at 0:59
1
\$\begingroup\$

Proton, 95 bytes

r=>{for i:range(1,r)print(((bin(i)[2to]).rjust(len(bin(r))-2)[to-1,to by-1]).replace('0',' '))}

Try it online!

There are too many bugs to not have too many brackets... I need to fix up the parser...

\$\endgroup\$
1
\$\begingroup\$

Python 2, 89 bytes

n=input()+1
w=' #'
while len(w)<n:w=[s+l+s for s in' #'for l in w]
print'\n'.join(w[1:n])

Try it online!

\$\endgroup\$
1
\$\begingroup\$

SOGL V0.12, 11 bytes

∫2─0@ŗ}¹╬⁴╚

Try it Here!

\$\endgroup\$
1
\$\begingroup\$

PHP, 98 97 95 94+1 bytes

while($r++<$argn)echo$s=strtr(sprintf("%".-~log($argn,2).b,$r),0," "),substr(strrev("
$s"),1);

Run as pipe with -nR or try it online. Uses 1 as non-whitespace.

\$\endgroup\$
2
  • \$\begingroup\$ sorry to spoil it, but something is wrong here. output for $argn=1 and $argn=3 is not correct, and $argn is 0-based (specified was 1-based) \$\endgroup\$ Aug 14, 2017 at 18:17
  • 1
    \$\begingroup\$ @FelixPalmen fixed. The incorrectness was caused by the wrong base. Thanks for noticing. \$\endgroup\$
    – Titus
    Aug 14, 2017 at 18:39
1
\$\begingroup\$

K (ngn/k), 19 bytes

{" #"@+a,1_|a:!x#2}

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Python 2, 93 bytes

n=input()
for i in range(n):s=bin(2**n.bit_length()+i+1)[3:].replace(*'0 ');print s+s[-2::-1]

Try it online!

\$\endgroup\$
0
\$\begingroup\$

C (gcc), 146 108 105 bytes

#define o putchar(33-!(c&(1<<n)))
b;c;p(n){--n?o,p(n),o:o;}f(n){while(n>>++b);while(c++<n)p(b),puts("");}

Try it online!

This is a function f(n) called with the number of rows n, using an exclamation mark (!) as non-whitespace character.

Explanation:

#define o putchar(33-!(c&(1<<n)))
b;c;
p(n)
{
    // least significant bit not yet reached?
    --n?
            // print bit twice with recursive step between
            o,
            p(n),
            o
        // for least significant, just print this bit
        :o;
}

// the main "cathedral function":
f(r)
{
    // determine max number of bits to shift
    while(r>>++b);

    // iterate over rows
    while(c++<r)

        // print row recursively
        p(b),

        // newline
        puts("");
}

/**
 * footer, just calling the function
 */
main(int argc, char **argv)
{
    f(atoi(argv[1]));
}
\$\endgroup\$
1
  • \$\begingroup\$ Suggest --n&&o+p(n);o; instead of --n?o,p(n),o:o; and for(;c++<n;puts(""))p(b); instead of while(c++<n)p(b),puts(""); \$\endgroup\$
    – ceilingcat
    Aug 22, 2018 at 0:15
0
\$\begingroup\$

JavaScript (Node.js), 156 149 bytes

-7 bytes by @ConorO'Brien

f=(n,w=n.toString(2).length,b=n.toString(2).replace(/0/g," "),s=" ".repeat(w-b.length))=>`${--n?f(n,w)+s+b+[...b].reverse().join``.substr(1):s+"1"}
`

Try it online!

Recursive function. Unfortunately JS does not support reversing a string, so 19 bytes are used on turning it into an array and back.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can use [...b] instead of b.split(""); you can also use .join``.substr(1) instead of .join("").substr(1); finally, I think you can use s+1 instead of s+"1" \$\endgroup\$ Aug 14, 2017 at 17:47
0
\$\begingroup\$

Perl 5, 77 + 1 (-n) = 78 bytes

$x=1+(log$_)/log 2;map{$_=sprintf"%0${x}b",$_;y/0/ /;say$_.chop.reverse}1..$_

Try it online!

Using '1' instead of '#' because it saves a couple bytes.

\$\endgroup\$
0
\$\begingroup\$

Stax, 8 bytes

ç▼┼Ö☺ǬÉ

Run and debug it

Shortest answer so far. Uses CP437 charcode 1 in place of #.

ASCII equivalent:

{:B|pm|Cm
\$\endgroup\$
1
  • \$\begingroup\$ you need to use a non-whitespace char, which charcode 1 isn't \$\endgroup\$
    – Razetime
    Feb 10, 2021 at 9:13
0
\$\begingroup\$

Japt -R, 10 bytes

õ_s*iS)êÃû

Try it

õ_s*iS)êÃû     :Implicit input of integer U
õ              :Range [1,U]
 _             :Map
  s            :  Convert to string in base
   *i          :    "*" prepended with
     S         :    Space
      )        :  End base conversion
       ê       :  Palindromise
        Ã      :End map
         û     :Centre pad each element with spaces to the length of the longest
               :Implicit output, joined with newlines
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.