19
\$\begingroup\$

Triangularity is a new esolang developed by Mr. Xcoder where code structure has to follow a very specific pattern:

  • For the nth line of code, there must be exactly 2n-1 characters of the program on it. This causes a triangular/pyramid shape, with the first line having only one character and the rest increasing by increments of 2.
  • Each line must be padded with .s on the left and right, such that the characters are centered on their lines and all lines are padded to the same length. If l is defined as the number of lines in the program, each line in the program must have a length of 2 * l - 1

For example, the program on the left is valid, but the program on the right isn't:

 Valid    |  Invalid  
          |
...A...   |  ABCDE
..BCD..   |  FGH
.EFGHI.   |  IJKLMN
JKLMNOP   |  OPQRS

When laid out in the valid structure, the name becomes obvious.

Task

Your task is to take a single line string as input, representing Triangularity code, and output it converted into valid code as described above.

Specifications for I/O:

  • The input will only contain characters in the range 0x20 - 0x7e
  • The length of the input always will be a square number and thus paddable nicely.
  • You must use dots for the output padding, not something else.

You may input and output through any acceptable method. This is a so the shortest code in bytes wins!

Test cases

input
----
output

g
----
g

PcSa
----
.P.
cSa

DfJ0vCq7G
----
..D..
.fJ0.
vCq7G

7xsB8a1Oqw5fhHX0
----
...7...
..xsB..
.8a1Oq.
w5fhHX0

QNYATbkX2sKZ6IuOmofwhgaef
----
....Q....
...NYA...
..TbkX2..
.sKZ6IuO.
mofwhgaef

ABCDEF"$%& G8"F@
----
...A...
..BCD..
.EF"$%.
& G8"F@

ab.c
----
.a.
b.c

For those who know Triangularity, you'll notice from the last test case that strings don't have to be handled

\$\endgroup\$
6
  • \$\begingroup\$ Sandbox post \$\endgroup\$ Feb 4, 2018 at 18:47
  • 13
    \$\begingroup\$ I realise this is probably futile, but would the downvoter care to explain their vote? I'd love to improve the challenge in anyway I can. \$\endgroup\$ Feb 4, 2018 at 18:52
  • \$\begingroup\$ Are leading or trailing newlines acceptable? \$\endgroup\$
    – Arnauld
    Feb 4, 2018 at 18:58
  • \$\begingroup\$ @Arnauld Yes, leading and trailing white space is perfectly fine. \$\endgroup\$ Feb 4, 2018 at 18:59
  • \$\begingroup\$ Is a list of lines fine? \$\endgroup\$
    – Mr. Xcoder
    Feb 4, 2018 at 20:59

19 Answers 19

20
\$\begingroup\$

Triangularity, 127 bytes

.......).......
......2)1......
...../)IL^.....
....f)rMD@_....
...)2)1/)IL^...
..f+`"'.'*"+E..
.DWReD)2s^)Its.
D+@sh+s+})10cJ.

Try it online!

Explanation

Removing the characters that make up for the padding, we get the following program:

)2)1/)IL^f)rMD@_)2)1/)IL^f+`"'.'*"+EDWReD)2s^)ItsD+@sh+s+})10cJ

... Which is quite length-ish, right? Let's break it down into pieces.

Generating the integers [0 … √len(input))

)2)1/)IL^f)r – Subprogram #1.
)            – Creates a new stack entry, equal to 0. This must precede any integer
               literal, because each character in '0123456789' isn't parsed on its
               own as a literal, but rather they are commands which multiply the ToS
               by 10 and add the value of their digit equivalent. 
 2           – ToS * 10 + 2 = 2.           || STACK: [2]
  )1         – The literal 1.              || STACK: [2, 1]
    /        – Division.                   || STACK: [1 / 2] = [0.5]
     )I      – Get the input at index 0.   || STACK: [0.5, input]
       L     – Length.                     || STACK: [0.5, len(input)]
        ^    – Exponentiation.             || STACK: [len(input) ** 0.5]
         f   – Trim decimals.              || STACK: [int(len(input) ** 0.5)] 
          )r – Create the list [0 .. ToS). || STACK: [[0 ... int(len(input) ** 0.5))]

Generating the dots

MD@_)2)1/)IL^f+`"'.'*"+E – Subprogram #2.
MD                       – For each integer in the range, run some code on a separate
                           stack, preinitialised to two copies of the argument.
  @_                     – Increment and negate the ToS.
    )2)1/)IL^f           – The square root of the length of the input, again.
              +          – Add the two.
               `         – And cast the integer given to a string.
                "'.'*"+  – Prepends the literal "'.'*" to the string representation.
                       E – Evaluate as a Python expression (basically string repetition).

Trimming the characters at the front

DWReD)2s^)It – Subprogram #3.
D            – Duplicate the result of the expression above.
 W           – Wrap the whole intermediate stack to an array.
  Re         – Reverse the stack and dump the contents separately onto the stack.
    D        – Duplicate the result.
     )2      – Push the literal 2.
       s^    – Swap and perform exponentiation.
         )It – Push the input and trim the characters before that index.

Trimming the characters at the end

sD+@sh+s+ – Subprogram #4.
s         – Swap the top two elements on the stack.
 D+       – Double. Push twice and add.
   @      – Increment.
    sh    – Swap the top two elements and trim the characters after that index.
      +   – Append the first set of dots.
       s+ – And prepend the second set of dots.

Ending the loop and pretty-printing

})10cJ – Subprogram #5.
}      – End the loop.
 )10   – Push the literal 10.
    c  – Convert from code-point to character (yields '\n').
     J – And join the result by newlines.
\$\endgroup\$
4
  • \$\begingroup\$ do you have to score bytes for the "." characters if they are mandated by the intrinsics of the language? \$\endgroup\$
    – JDL
    Feb 5, 2018 at 9:04
  • \$\begingroup\$ @JDL Yes, the program cannot run correctly without them, so I must include them in the byte count :-) \$\endgroup\$
    – Mr. Xcoder
    Feb 5, 2018 at 9:16
  • \$\begingroup\$ Do you need to "trim decimals", seeing as the length of the input is guaranteed to be square? You should also be able to get rod of the J at the end and output an array of lines. Don't know if that's going to save you anything, though, if the end of the last line needs to be padded with . to fill the remaining space. \$\endgroup\$
    – Shaggy
    Feb 5, 2018 at 9:44
  • 1
    \$\begingroup\$ @Shaggy 1) Yes, f is needed because range cannot accept float arguments (even with .0) 2) Getting rid of J doesn’t save any bytes due to the padding, so I chose the prettier output format. \$\endgroup\$
    – Mr. Xcoder
    Feb 5, 2018 at 10:43
8
\$\begingroup\$

Japt, 15 14 10 bytes

Outputs an array of lines.

ò@°T¬v1Ãû.

Try it | Check all test cases


Explantion

ò@     Ã       :Partition at characters where the following function returns true
  °T           :  Increment T (initially 0)
    ¬          :  Square root
     v1        :  Divisible by 1?
               :(Or, in other words, split after every character with a 1-based index that's a perfect square)
        û.     :Centre pad each element with .s to the length of the longest element

Original Solution

ʬÆsTT±X+°XÃû.

Try it

Ê                  :Length of input
 ¬                 :Square root
  Æ        à       :Range [0,ʬ) and pass each X through a function
   s               :  Slice input
    T              :    from index T, initially 0
     T±X+°X        :    to index T incremented by X plus X incremented
            û.     :Centre pad each element with .s to the length of the longest element
\$\endgroup\$
7
\$\begingroup\$

Husk, 15 bytes

Ṡzö`JR2tR'.ṡCİ1

Try it online!

Explanation

Ṡzö`JR2tR'.ṡCİ1  Implicit input, say s = "DfJ0vCq7G".
             İ1  List of odd positive integers: [1,3,5,7,..
            C    Cut s to those lengths: x = ["D","fJ0","vCq7G"]
           ṡ     Reversed indices of x: y = [3,2,1]
Ṡz               Zip x and y using this function:
                  Arguments are a string and a number, e.g. r = "fJ0" and n = 2.
        R'.       n copies of '.': ".."
       t          Drop first element: "."
     R2           Two copies of this: [".","."]
  ö`J             Join by r: ".fJ0."
                 Result is ["..D..",".fJ0.","vCq7G"]; implicitly print on separate lines.
\$\endgroup\$
7
\$\begingroup\$

05AB1E, 20 19 18 bytes

Saved a byte thanks to Magic Octopus Urn

ā·<£õKRvy'.N×.ø}r»

Try it online!

Explanation

ā                    # push the list [1 ... len(input)]
 ·<                  # multiply each by 2 and decrement each, making a list of odd numbers
   £                 # split the input into chunks of these sizes
    õK               # remove empty strings
      R              # reverse list
       vy      }     # for each y in the list
             .ø      # surround it with
         '.N×        # "." (dot) repeated N times, where N is the current iteration index
                r    # reverse the stack
                 »   # join stack by newlines
\$\endgroup\$
5
  • \$\begingroup\$ ÅÉ for odds may help? \$\endgroup\$ Feb 5, 2018 at 12:12
  • \$\begingroup\$ Something like, g;ÅÉ£Rvy'.N×.ø}r»? But not that because that's not right lol. \$\endgroup\$ Feb 5, 2018 at 12:20
  • \$\begingroup\$ @MagicOctopusUrn: ÅÉ would definitely help if we could find out the length of the bottom row in 2 bytes. I don't know if we can though. Might be another way to incorporate it as well. \$\endgroup\$
    – Emigna
    Feb 5, 2018 at 12:25
  • \$\begingroup\$ @MagicOctopusUrn: I had this same solution earlier except I used )R instead of r which didn't save any bytes :/ \$\endgroup\$
    – Emigna
    Feb 5, 2018 at 12:29
  • \$\begingroup\$ I was trying to find a way to "reverse the loop" to just print as it occurs, no ideas on that though. \$\endgroup\$ Feb 5, 2018 at 13:05
5
\$\begingroup\$

Python 2, 83 bytes

i=input();u=int(len(i)**.5)
for t in range(u):g="."*(u+~t);print g+i[t*t:][:t-~t]+g

Try it online!

\$\endgroup\$
5
\$\begingroup\$

Jelly,  22  19 bytes

J²‘Ṭœṗ⁸Ṛz”.Zµṙ"JC$Ṛ

A monadic link returning a list of lists of characters (the lines)

Try it online!

How?

J²‘Ṭœṗ⁸Ṛz”.Zµṙ"JC$Ṛ - Link: list of characters e.g. "DfJ0vCq7G"
J                   - range of length               [1,2,3,4,5,6,7,8,9]
 ²                  - square (vectorises)           [1,4,9,16,25,36,49,64,81]
  ‘                 - increment                     [2,5,10,17,26,37,50,65,82]
   Ṭ                - untruth (1s at those indices) [0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,...]
      ⁸             - chain's left argument         "DfJ0vCq7G"
    œṗ              - partition at indexes          ["D","fJ0","vCq7G"]
       Ṛ            - reverse                       ["vCq7G","fJ0","D"]
         ”.         - literal '.'                   '.'
        z           - transpose with filler         ["vfD","CJ.","q0.","7..","G.."]
           Z        - transpose                     ["vCq7G","fJ0..","D...."]
            µ       - start a new monadic chain
                 $  - last two links as a monad:
               J    -   range of length             [1,2,3]
                C   -   complement (vectorises)     [0,-1,-2]
              "     - zip with:
             ṙ      -   rotate left by              ["vCq7G",".fJ0.","..D.."]
                  Ṛ - reverse                       ["..D..",".fJ0.","vCq7G"]
\$\endgroup\$
5
\$\begingroup\$

JavaScript (ES7), 82 78 bytes

f=(s,k=1-s.length**.5*2,p='')=>s&&f(s.slice(0,k),k+2,p+'.')+`
`+p+s.slice(k)+p

Test cases

f=(s,k=1-s.length**.5*2,p='')=>s&&f(s.slice(0,k),k+2,p+'.')+`
`+p+s.slice(k)+p

console.log(f('g'))
console.log(f('PcSa'))
console.log(f('DfJ0vCq7G'))
console.log(f('7xsB8a1Oqw5fhHX0'))
console.log(f('QNYATbkX2sKZ6IuOmofwhgaef'))
console.log(f('ABCDEF"$%& G8"F@'))

Commented

f = (                       // f = recursive function taking:
  s,                        //   s = input string
  k = 1 - s.length**.5 * 2, //   k = additive inverse of the length of the base
  p = ''                    //   p = padding string
) =>                        //
  s &&                      // if s is not empty:
    f(                      //   do a recursive call with:
      s.slice(0, k),        //     s without the last -k characters
      k + 2,                //     the updated base length (2 less characters)
      p + '.'               //     the updated padding string
    ) +                     //   end of recursive call()
    `\n` +                  //   append a line feed
    p +                     //   append the left padding string
    s.slice(k) +            //   append the last -k characters of s
    p                       //   append the right padding string
\$\endgroup\$
3
  • \$\begingroup\$ [minor] the '/n' symbol became an actual line break in the code block at the top - consider escaping it for clarity :) \$\endgroup\$
    – G0BLiN
    Feb 5, 2018 at 16:35
  • \$\begingroup\$ @G0BLiN This is the other way around: this is indeed a literal line break in the golfed code, which was replaced with an escaped line break in the ungolfed version for clarity. :) \$\endgroup\$
    – Arnauld
    Feb 5, 2018 at 16:43
  • \$\begingroup\$ Ha, joke's on me, then... :-) \$\endgroup\$
    – G0BLiN
    Feb 5, 2018 at 16:46
2
\$\begingroup\$

05AB1E, 25 bytes

gÅÉ£õKð'ø‡.c¶¡ζøð'.‡'øð‡»

Try it online!

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

MATL, 21 bytes

tnX^eRP&1ZvGyg(46y~(!

Try it online!

Explanation

Consider input 'DfJ0vCq7G' as an example. The stack contents are shown separated by commas, with the top element last. Rows in a 2D array use semicolon as separator.

t      % Implicit input: string. Duplicate
       % STACK: 'DfJ0vCq7G',
                'DfJ0vCq7G'
nX^    % Number of elements. Square root
       % STACK: 'DfJ0vCq7G',
                3
e      % Reshape with that many rows (in column major order)
       % STACK: ['D0q';
                 'fv7';
                 'JCG']
R      % Upper triangular part: set elements below diagonal to char(0)
       % (displayed as space)
       % STACK: ['D0q';
                 ' v7';
                 '  G']
P      % Flip vertically
       % STACK: ['  G';
                 ' v7';
                 'D0q']
&1Zv   % Reflect vertically
       % STACK: ['  G';
                 ' v7';
                 'D0q';
                 ' v7';
                 '  G']
G      % Push input again
       % STACK: ['  G';
                 ' v7';
                 'D0q';
                 ' v7';
                 '  G'],
                'DfJ0vCq7G'
yg     % Duplicate from below and convert to logical. This gives true for
       % for nonzero chars (the entries where input chars will be written)
       % STACK: ['  G';
                 ' v7';
                 'D0q';
                 ' v7';
                 '  G'],
                'DfJ0vCq7G',
                [0 0 1;
                 0 1 1;
                 1 1 1;
                 0 1 1;
                 0 0 1]
(      % Assignment indexing: write values at those positions
       % STACK: ['  v';
                 ' fC';
                 'DJq';
                 ' 07';
                 '  G']
46     % Push 46, which is ASCII for '.'
       % STACK: ['  v';
                 ' fC';
                 'DJq';
                 ' 07';
                 '  G'],
                 46
y~     % Duplicate from below and apply logical negate. This gives true
       % for char(0) (the entries where '.' will be written)
       % STACK: ['  G';
                 ' v7';
                 'D0q';
                 ' v7';
                 '  G'],
                46
                [1 1 0;
                 1 0 0;
                 0 0 0;
                 1 0 0;
                 1 1 0]
(      % Assignment indexing: write value at those positions
       % STACK: ['..G';
                 '.v7';
                 'D0q';
                 '.v7';
                 '..G'],
!      % Transpose. Implicit display
       % STACK: ['..D..';
                 '.fJ0.';
                 'vCq7G']
\$\endgroup\$
2
\$\begingroup\$

Clean, 107 94 89 88 bytes

import StdEnv
@s _[]=s
@s n r= @([['.':l]++['.']\\l<-s]++[take n r])(n+2)(drop n r)

@[]1

Try it online! Example usage: @[]1 ['ABCDEF"$%& G8"F@'].

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

Haskell, 84 68 bytes

[]%1
(s%n)[]=s
(s%n)r=(['.':l++"."|l<-s]++[take n r])%(n+2)$drop n r

Try it online!

Example usage: []%1 $ "abcd" yields the list of lines [".a.","bcd"].

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

Perl, 56 52 bytes

Includes +3 for -p

#!/usr/bin/perl -p
$_=("."x y///c**.5)=~s%.%$'@{[$&x/$`$`./g]}$'
%rg

Give input on STDIN (in principle without final newline, but that only matters for the empty input)

\$\endgroup\$
2
  • 1
    \$\begingroup\$ according to PCG rules -p costs only 1 byte \$\endgroup\$
    – mik
    Feb 5, 2018 at 12:28
  • \$\begingroup\$ @mik Only if the code doesn't contain '. But this code does, so it needs to be put in a file (or escaping on the commandline) which needs 3 extra characters compared to the normal #! line. So in this case it is +3 (for normal code you will see I indeed only count + for p) \$\endgroup\$
    – Ton Hospel
    Feb 5, 2018 at 13:05
1
\$\begingroup\$

Red, 227 203 bytes

f: func[s][l: to-integer(length? s)** 0.5
n: 0 foreach m parse s[collect[(r: []repeat i l[append r reduce['keep i * 2 - 1
charset[not{Я}]]])r]][v: copy""insert/dup v"."l - n: n + 1 print rejoin[v m v]]]

Try it online!

Ungolfed:

f: func[s][
l: to-integer (length? s) ** 0.5
n: 0
foreach m parse s [ 
    collect [
        (r: []
        repeat i l [ append r reduce [
            'keep i * 2 - 1 charset [ not{Я} ]]])
    r ]] 
    [v: copy ""
    insert/dup v "." l - n: n + 1
    print rejoin [v m v]]
]
\$\endgroup\$
1
\$\begingroup\$

Retina, 88 72 71 bytes

S1`
+m`^(.)+¶(?>(?<-1>.)+)..(?!¶)
$&¶
P^'.m`^.(?=(..)*)(?<-1>.)*
P'.`.+

Try it online! Edit: Saved 12 13 bytes thanks to @MartinEnder. Explanation:

S1`

Split first character into its own line to get the ball rolling.

+m`^(.)+¶(?>(?<-1>.)+)..(?!¶)
$&¶

Chop each line two characters longer than the previous one.

P^'.m`^.(?=(..)*)(?<-1>.)*

Left-pad the first half of each line, effectively centring them.

P'.`.+

Right-pad all of the lines.

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

Charcoal, 21 19 bytes

UB.F₂Lθ«P✂θXι²X⊕ι²↙

Try it online! Link is to verbose version of code. Edit: Saved 2 bytes by discovering SquareRoot. Explanation:

UB.                 Set the background fill to `.`
      θ             (First) input
     L              Length
    ₂               Square root
   F   «            Loop over implicit range
            ι   ι   Current value
               ⊕    Incremented
             ²   ²  Literal 2
           X  X     Power
         ✂θ         Slice the (first) input string
        P           Print without moving the cursor
                  ↙ Move down left
\$\endgroup\$
4
  • \$\begingroup\$ :/ This almost works but it appears Charcoal is a bit buggy. This works though, I think? \$\endgroup\$
    – ASCII-only
    Feb 28, 2018 at 11:41
  • \$\begingroup\$ @ASCII-only ... what black magic is this? \$\endgroup\$
    – Neil
    Feb 28, 2018 at 21:54
  • \$\begingroup\$ Fill basically checks for null bytes (i.e. the character used for space where nothing is drawn), so you can just draw a polygon with null bytes (well, if it even works correctly >_>) and fill that. Obviously this isn't exactly intended since you can actually see the null bytes even before the fill for some reason >_> \$\endgroup\$
    – ASCII-only
    Feb 28, 2018 at 22:21
  • \$\begingroup\$ this should work properly next pull \$\endgroup\$
    – ASCII-only
    Apr 6, 2018 at 11:43
0
\$\begingroup\$

Python 2, 84 bytes

s=input()
l=int(len(s)**.5)
for i in range(l):print s[i*i:][:i-~i].center(l*2-1,'.')

Try it online!

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

Clean, 123 bytes

import StdEnv
?n l#(a,b)=splitAt n l
|b>[]=[a: ?(n+2)b]=[a]
$s#s= ?1s
=[c++l++c\\l<-s&i<-[1..],c<-[repeatn(length s-i)'.']]

Try it online!

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Some friendly competition. \$\endgroup\$
    – Laikoni
    Feb 4, 2018 at 23:21
  • \$\begingroup\$ @Laikoni Ooh, that's my favourite kind of competition! \$\endgroup\$
    – Οurous
    Feb 4, 2018 at 23:41
0
\$\begingroup\$

Ruby, 73 66 bytes

->s{(1..z=s.size**0.5).map{|q|s[q*q-2*q+1...q*q].center 2*z-1,?.}}

Try it online!

-5 bytes: Return an array of strings instead of printing them

-2 bytes: Declare z in place instead of ahead of time

Ungolfed:

->s{
  (1..z=s.size**0.5).map{|q|   # Map the range [1,sqrt(s.size)]
    s[q*q-2*q+1...q*q]         # To the relevant portion of s,
      .center 2*z-1, ?.        #   padded left and right with . characters
  }
}

Declaring a variable r=q-1 so that I can take s[r*r...q*q] saves exactly zero bytes.

Using .center instead of padding manually also saves zero bytes, but I like it better.

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

Stax, 19 bytes

╚└÷2╬#►o┴X≡¥ΩåV∙¬?♥

Run and debug it

ASCII equivalent:

z;%|q{Hv,:/~]+F|Cm0]'.R
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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