Fractal Cathedral

22

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

HyperNeutrino

Posted 2017-08-13T23:04:58.577

Reputation: 26 575

Nice idea. I wouldn't have guessed binary numbers to have produced such pretty ascii art. – Jonah – 2017-08-14T01:21:33.800

@Jonah Thanks :) Glad you like it – HyperNeutrino – 2017-08-14T01:22:20.367

7Both links are pointing to the generated cathedral. – Otávio – 2017-08-14T10:30:34.577

@Otávio :I will fix, thanks – HyperNeutrino – 2017-08-14T15:32:12.620

Answers

6

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

Luis Mendo

Posted 2017-08-13T23:04:58.577

Reputation: 87 464

1I 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 – Conor O'Brien – 2017-08-14T17:45:26.090

@ConorO'Brien It is used often, yes. But it would be a two-char built-in, so there would be no gain – Luis Mendo – 2017-08-14T17:47:53.837

No gain? 35*c is 4 characters – Conor O'Brien – 2017-08-14T18:00:44.830

@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? – Luis Mendo – 2017-08-14T18:43:15.940

2For reference, this feature has been implemented (function Zc, with character 35, i.e. #). Thanks, @ConorO'Brien! – Luis Mendo – 2017-08-14T21:14:54.273

5

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

Adnan

Posted 2017-08-13T23:04:58.577

Reputation: 41 965

4

Jelly, 12 bytes

RBUz0ZUŒBo⁶Y

Try it online!

Leaky Nun

Posted 2017-08-13T23:04:58.577

Reputation: 45 011

1I also have to go, so I will read comments when I get back. Feel free to out-golf me. – Leaky Nun – 2017-08-13T23:09:21.937

+1 for "feel free to outgolf me" and for the good answer. – Magic Octopus Urn – 2017-08-14T01:07:27.017

3

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.

Lynn

Posted 2017-08-13T23:04:58.577

Reputation: 55 648

That's a copy of mine :) (but clever use of 2**len(bin(n))/4 anyways) – Erik the Outgolfer – 2017-08-14T09:12:56.623

3

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

Arnauld

Posted 2017-08-13T23:04:58.577

Reputation: 111 334

1Very nice! First time I've seen Math.clz32 - I didn't even know it existed! – Birjolaxew – 2017-08-14T11:43:23.160

@Birjolaxew Yup, this is an ES6 addition. It comes handy every once in a while. – Arnauld – 2017-08-14T12:47:52.850

3

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)

ბიმო

Posted 2017-08-13T23:04:58.577

Reputation: 15 345

1Ṙ; can be just R, and ȯ is unnecessary. Nice answer idea! – Zgarb – 2017-08-14T17:26:45.727

2

Mathematica, 94 bytes

Column[Row/@Table[s=IntegerDigits[i,2];Join[s,Reverse@Most@s]/.{0->" ",1->"#"},{i,#}],Center]&

J42161217

Posted 2017-08-13T23:04:58.577

Reputation: 15 931

2I really have to go, too... – J42161217 – 2017-08-13T23:15:10.237

2

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!

wrymug

Posted 2017-08-13T23:04:58.577

Reputation: 772

2replace(*'0 ') for 2 bytes – officialaimm – 2017-08-14T04:03:26.963

2107 bytes – Halvard Hummel – 2017-08-14T06:56:21.713

2

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.

Not a tree

Posted 2017-08-13T23:04:58.577

Reputation: 3 106

2

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

Business Cat

Posted 2017-08-13T23:04:58.577

Reputation: 8 927

2

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.

Dennis.Verweij

Posted 2017-08-13T23:04:58.577

Reputation: 101

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{} – TheLethalCoder – 2017-08-14T16:04:50.520

You don't need to include ,' ' in the PadLeft call as a space is the default. – TheLethalCoder – 2017-08-14T16:07:01.013

168 bytes + 23 for namespace System.Linq{} – TheLethalCoder – 2017-08-14T16:11:38.277

@TheLethalCoder sorry for the inconvenience, it is now fixed. – Dennis.Verweij – 2017-08-14T17:30:35.203

No worries +1 from me it is a nice answer :) – TheLethalCoder – 2017-08-15T07:58:21.203

1

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.

Neil

Posted 2017-08-13T23:04:58.577

Reputation: 95 035

Current versions of Charcoal have MapAssignRight(IntDivide, 2, q); which saves 3 bytes. – Neil – 2017-11-25T17:02:12.910

1

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.

Jonah

Posted 2017-08-13T23:04:58.577

Reputation: 8 729

You can use #.inv instead of #.^:_1. – Conor O'Brien – 2017-08-14T17:46:16.020

@ConorO'Brien, thanks, wasn't aware of that. – Jonah – 2017-08-14T17:47:28.103

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. – cole – 2017-08-14T17:58:46.537

@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. – Jonah – 2017-08-14T18:03:09.773

@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. – cole – 2017-08-15T00:59:00.717

@cole, indeed i think that is what i did. thanks for the clarification. it makes sense, after all, that anti-base is, well, anti-base. – Jonah – 2017-08-15T01:00:17.453

1

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

HyperNeutrino

Posted 2017-08-13T23:04:58.577

Reputation: 26 575

1

SOGL V0.12, 11 bytes

∫2─0@ŗ}¹╬⁴╚

Try it Here!

dzaima

Posted 2017-08-13T23:04:58.577

Reputation: 19 048

1

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.

Titus

Posted 2017-08-13T23:04:58.577

Reputation: 13 814

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) – Felix Palmen – 2017-08-14T18:17:33.470

1@FelixPalmen fixed. The incorrectness was caused by the wrong base. Thanks for noticing. – Titus – 2017-08-14T18:39:36.550

1

K (ngn/k), 19 bytes

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

Try it online!

ngn

Posted 2017-08-13T23:04:58.577

Reputation: 11 449

0

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!

Erik the Outgolfer

Posted 2017-08-13T23:04:58.577

Reputation: 38 134

0

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.

Birjolaxew

Posted 2017-08-13T23:04:58.577

Reputation: 323

1You 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" – Conor O'Brien – 2017-08-14T17:47:19.020

0

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!

Rod

Posted 2017-08-13T23:04:58.577

Reputation: 17 588

0

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

Felix Palmen

Posted 2017-08-13T23:04:58.577

Reputation: 3 866

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(""); – ceilingcat – 2018-08-22T00:15:18.897

0

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.

Xcali

Posted 2017-08-13T23:04:58.577

Reputation: 7 671

0

Stax, 8 bytes

ç▼┼Ö☺ǬÉ

Run and debug it

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

ASCII equivalent:

{:B|pm|Cm

Weijun Zhou

Posted 2017-08-13T23:04:58.577

Reputation: 3 396