14
\$\begingroup\$

Background

Polyagony is a family of hypothetical esolangs where the source code is laid out on a specifically shaped board before running it. It's similar to Hexagony, but various uniform tilings can be used instead of a simple hexagon. The shape of the board and the tiling used is defined by the "mode".

Mode 3/3,6 is a triangular board filled with (3,6)2 tiling. The boards of sizes 1, 2, and 3 look like the following:

 *      *          *
* *    * *        * *
      *   *      *   * 
     * * * *    * * * * 
               *   *   * 
              * * * * * *

In general, the board of size n can be formed by adding two rows under the board of size n-1, where the two new rows are formed by putting n copies of small triangles side by side. So the size 4 can be generated from size 3 as follows:

       *
      * *
     *   *
    * * * *
   *   *   *
  * * * * * *
 1   2   3   4
1 1 2 2 3 3 4 4

Given a source code of length n, the size of the board is determined first, and then each character in the code is sequentially placed on each asterisk from top to bottom, left to right. The rest is filled with no-ops (dots, as in Hexagony).

The board size is chosen so that it is the smallest board that can fit the entirety of the source code. For example, abcdefghi would be placed as

   a
  b c
 d   e
f g h i

and abcdefghij as

     a
    b c
   d   e
  f g h i
 j   .   .
. . . . . .

The minimum board size is 1. If the source code is empty, the laid out result must be a triangle of three no-ops:

 .
. .

Challenge

In order to simplify the challenge a bit, let's assume the source code is just a string of asterisks (*). Given the length of such a program as input, output the laid out result.

The output can be as a single string or a sequence of lines. Trailing whitespaces on each line or after the entire output are OK.

Standard rules apply. The shortest code in bytes wins.

Test cases

The expected outputs for the inputs 0 (three no-ops), 3, 9, and 18 (full triangles of asterisks) are already given above.

Input: 2
Output:
 *
* .

Input: 4
Output:
   *
  * *
 *   .
. . . .

Input: 7
Output:
   *
  * *
 *   *
* * . .

Input: 17
Output:
     *
    * *
   *   *
  * * * *
 *   *   *
* * * * * .
\$\endgroup\$
1
5
\$\begingroup\$

JavaScript (ES7),  127  116 bytes

Draws the output character by character.

n=>(y=0,g=x=>y>w?'':`.*
 `[x<-w?(x=w,++y,2):x*x-->y*y|(y&1?x&1:~x&3^y&3)?3:n&&n/n--]+g(x))(w=(n*2/3-.4)**.5-.5<<1|1)

Try it online!

How?

We define \$w\$ as:

$$w=\left\lfloor \sqrt{2n/3-2/5}-1/2 \right\rfloor\times2+1$$

The top of the triangle is \$(0,0)\$, the bottom-left corner is \$(+w,+w)\$ and the bottom-right corner is \$(-w,+w)\$.

   |    x > 0       x < 0
   |  <--------   -------->
   |  5 4 3 2 1 0 1 2 3 4 5
---+------------------------
 0 |  . . . . . B . . . . .
 1 |  . . . . A . A . . . .
 2 |  . . . B . . . B . . .
 3 |  . . A . A . A . A . .
 4 |  . B . . . B . . . B .
 5 |  A . A . A . A . A . A

There is an active cell on the board if \$|x|\le y\$ and:

  • \$y\$ is odd and \$x\$ is odd (A-cells)
  • or \$y\$ is even and \$y\bmod 4 = x\bmod 4\$ (B-cells)
\$\endgroup\$
1
5
\$\begingroup\$

Charcoal, 40 bytes

NθW‹LKA∨θ¹F⊞OυLυ«J⊗⁻⊗κ⌈υ⊗⌈υP^²UMKA§.*‹μθ

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

Nθ

Input n.

W‹LKA∨θ¹

Repeat until n characters have been output, except always output at least one character (this is a stupid edge case that costs me two bytes).

F⊞OυLυ«

Loop through each triangle of the next row of the board.

J⊗⁻⊗κ⌈υ⊗⌈υ

Jump to the top of the triangle.

P^²

Print arbitrary characters down and left, thus creating the small triangle.

UMKA§.*‹μθ

Update all of the characters according to whether they are within the first n or not. (This is done inside the loop to save a byte, although only the last update actually matters.)

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

Vyxal (hyper's old fork) C, 46 45 bytes

3/d4Ė+√2Ė-⌈1∴:£ƛ‛% *Ṫ\%dn*"vṄ;f⁋?×*?d›\.*+f%↵

Try it Online!

Yuck. What a mess.

Uses the formula \$\lceil\sqrt{\frac23x+\frac14}-\frac12\rceil\$ to calculate the required size.

Mainstream Vyxal is completely broken right now - .

3...⌈                                   # Calculate above formula
     1∴                                 # Max(that, 1) for 0 case
       :£                               # Store a copy to register
         ƛ             ;                # Map...
          ‛% *                          # `% ` that many times
              Ṫ                         # With the last char removed
               \%dn*                    # 2n %s
                    "                   # Pair the two
                     vṄ                 # Join each by newline
                        f⁋              # Flatten and join by newlines
                          ?×*           # n *s
                             ?d›\.*+    # Plus 2n+1 .s
                                    f%  # Format the %s by this
                                      ↵ # Split by newlines so C can do its magic
\$\endgroup\$
0
4
\$\begingroup\$

J, 76 69 bytes

-7 bytes thanks to Jonah's approach!

(' *.'{~]+(<]*$$+/\@,))1(<:2&(0&,.,a,._2{.])a=:#:@4 10)@>.+/\@i.I.%&3

Try it online!

  • +/\@i. triangular numbers 0 1 3 6 …

  • I.%&3 search for the index where n%3 would sit inbetween. This is the (halved) height of the triangle.

  • 1>. corner case for 0

  • a=:#:@4 10 starting with the tile saved as a

    0 1 0 0
    1 0 1 0
    
  • _2{.] get the last two rows

  • a,. prepend a on the left

  • 2&(0&,.,…) prepend the previous rows with two 0 before them

  • <: (with a bunch of &s) … do this (halved) height minus one times.

  • $$+/\@, (would be cooler, if something like +/\&., would work!) flatten to a list, scan-reduce with addition, and reshape to the 2d matrix. So

    0 1 0 0 -> 0 1 0 0 1 0 1 0 -> 0 1 1 1 2 2 3 3 -> 0 1 1 1
    1 0 1 0                                          2 2 3 3
    
  • ]+(<]* where it was 0 before, it shall be 0 after all. Compare with the input, and add to the original 2d matrix (so for n=4):

    0 0 0 1 0 0 0
    0 0 1 0 1 0 0
    0 1 0 0 0 2 0
    2 0 2 0 2 0 2
    
  • ' *.'{~ map into the characters needed for the output

\$\endgroup\$
4
  • \$\begingroup\$ (cool!) Indeed, nice trick. A number of nice tricks in here. Even though the overall result isn't satisfying, I don't see any obvious big gains to be had. Even using Arnauld's approach to create a table wouldn't get you much if anything from my quick experiments. \$\endgroup\$ – Jonah Jul 16 at 18:12
  • \$\begingroup\$ I had this Try it online! for creating the board in one go, but I don't think it helps any.... \$\endgroup\$ – Jonah Jul 16 at 18:23
  • 1
    \$\begingroup\$ @Jonah thanks! Managed to juggle some things around with your attempt to save some bytes. Now it feels short enough. :-) \$\endgroup\$ – xash Jul 16 at 18:55
  • \$\begingroup\$ Awesome, well done. \$\endgroup\$ – Jonah Jul 16 at 20:07
4
\$\begingroup\$

Haskell, 269 bytes

import Data.List
f _ r[]=intersperse ' '<$>r
f n r s=let(x,y)=splitAt(3*n)s;(a,b)=splitAt n x in f(n+1)(map(' ':)r++[intersperse ' 'a,b])y
v[]=[]
v(x:y:s)=(' ':x):y:v s
s n=unlines$v$f 1[]$replicate n '*'++replicate(head(filter(>=n)$map(\n->(1+n)*n`div`2*3)[1..])-n)'.'

Clunky non-math solution. Generate a list of *s of length n and pad to nearest triangular number with ., then take out the characters two lines a time.

f generates the tower from list of asterisks and dots. v adjusts the tower so that it looks correct (very annoying). s generates the list of asterisks and dots, passes it to f and join the lines to give the result.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Welcome to Code Golf! Nice first answer! \$\endgroup\$ – Redwolf Programs Jul 17 at 2:36
2
\$\begingroup\$

Jelly, 48 bytes

RS×3R>¬
Ḥ÷3+.25½_.ĊðRżḤ$Rṁ@çị⁾*.ẎK€ÐoK€JU’Ɗ⁶ẋżƲY

Try It Online!

aaaaaaahh this is so bad... >_<

even considering Jelly is not usually optimal for string challenges this feels like such a stupid solution

"borrows" A username's formula, \$\lceil\sqrt{\frac23x+\frac14}-\frac12\rceil\$

RS×3R>¬    Helper Link; given the size and the input, return the flat list of items
R          [1, 2, ..., size]
 S         Sum; nth triangular number
  ×3       Triple
    R      [1, 2, ..., 3/2 n(n+1)]
     >     For each one, is it greater than the input?
      ¬    Logical NOT; is it <= the input?

Ḥ÷3+.25½_.ĊðRżḤ$Rṁ@çị⁾*.ẎK€ÐoK€JU’Ɗ⁶ẋżƲY   Main Link
Ḥ÷3+.25½_.Ċ                                ×2 ÷3 +1/4 sqrt -1/2 ceil
           ð                               Dyadic chain; take the size on the left and the input on the right
            R                              [1, 2, ..., size]
             żḤ$                           Zip with double; [[1, 2], [2, 4], [3, 6], ...]
                R                          Range; [[[1], [1, 2]], [[1, 2], [1, 2, 3, 4]], [[1, 2, 3], [1, 2, 3, 4, 5, 6]], ...]
                 ṁ@                        Mold the right to the above:
                   ç                       Last link (the 1s and 0s for the output values)
                    ị⁾*.                   Index into "*."; 1 becomes * and 0 becomes .
                        Ẏ                  Tighten; flatten once
                         K€Ðo              Join odd indices on spaces
                             K€            Join each line on spaces
                               ====-=-Ʋ    4:
                               -=-Ɗ          3:
                               J               [1, 2, ..., # rows]
                                U              [#, # - 1, ..., 1]
                                 ’             [# - 1, # - 2, ..., 0]
                                   ⁶ẋ        " " * each
                                     ż       Zip with the original; format to triangle
                                       Y   Join on newlines
\$\endgroup\$
1
\$\begingroup\$

Jelly, 38 37 36 bytes

J’Ṛ⁶ẋż
‘RḤĖRðFJ>ṁ⁸ðȦÐḟȯ$Ẏị⁾.*K€⁺ÐoÇY

A monadic link that accepts a non-negative integer and yields a list of characters.

(Note: Removing the trailing Y would give a list of lines but each of those lines would be a list of lists of characters - the leading spaces in the first list and then the rest of the line in a second.)

Try it online! Or see the test-suite.

How?

J’Ṛ⁶ẋż - Link 1, prefix spaces: list of lines with no leading spaces, L
J      - range of length [1..length(L)]
 ’     - decrement -> [0..length(L)-1]
  Ṛ    - reverse -> [length(L)-1..0]
   ⁶ẋ  - space character repeated -> [" ... ", ... , "  ", " ", ""]
     ż - (that) zipped with (L) -> [[" ... ", first], ... ["", last]]

‘RḤĖRðFJ>ṁ⁸ðȦÐḟȯ$Ẏị⁾.*K€⁺ÐoÇY - Main Link: non-negative integer, N
‘                             - increment -> N+1 (this is to create enough rows if N=0)
 R                            - range -> [1,2,3,...,N+1]
  Ḥ                           - double -> [2,4,6,...,2N+2]
   Ė                          - enumerate -> [[1,2],[2,4],[3,6],...,[N+1,2N+2]]
    R                         - range (vectorises) -> [[[1],[1,2]],[[1,2],[1,2,3,4]],...]
     ð     ð                  - dyadic chain, f(X=that, N):
      F                       -   flatten -> [1,1,2,1,2,1,2,3,4,...]
       J                      -   range of length -> [1,2,3,...,3(N+1)(N+2)/2]
        >                     -   greater than (N)? -> e.g. N=4: [0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
         ṁ⁸                   -   mould like X
                                  -> e.g. N=4: [[[0],[0,0]],[[0,1],[1,1,1,1]],[[1,1,1],[1,1,1,1,1,1]],[[1,1,1,1],[1,1,1,1,1,1,1,1]],[[1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1]]],
                $             - last two links as a monad, f(Z=that):
             Ðḟ               -   filter discard those for which:
            Ȧ                 -     any and all?
                                  (removes those "row-pairs" which contain no zeros)
               ȯ              -   logical OR (with Z) (this is to keep [[[1],[1,1]]] when N=0)
                 Ẏ            - tighten -> e.g. N=4: [[[0],[0,0]],[[0,1],[1,1,1,1]]]
                  ị⁾.*        - index into ".*" -> ["*","**","*.","...."]
                      K€      - join each with spaces -> ["*","* *","* .",". . . ."]
                         Ðo   - apply to odd indexed entries:
                        ⁺     -   repeat last link -> ["*","* *","*   .",". . . ."]
                           Ç  - call last Link (Link 1) as a monad
                                -> [["   ","*"],["  ","* *"],[" ","*   ."],["",". . . ."]]
                            Y - join with new lines
                                ->   *
                                    * *
                                   *   .
                                  . . . .
\$\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.