17
\$\begingroup\$

Goal

Write a function or a program that when given a string, for example "The quick brown fox jumps over the lazy dog", outputs:

     T h e   q u
                i
   e r   t h e   c
  v               k
 o             l
                a   b
 s             z   r
  p           y   o
   m   g o d     w
    u           n
     j   x o f

Rules

  1. You may not truncate the input!

  2. The output must be text and formatted as above.

  3. The hexagons must have the shortest possible side length. This is wrong for example:

          T h e   q u i
                       c
          l a z y   d   k
       e             o
      h               g   b
     t                     r
                            o
     r                     w
      e                   n
       v
        o               f
                       o
          s p m u j   x
    

    The outer hexagon above has a side of 7 characters, but clearly it is not optimal. A spiral with an outer hexagon with a side of 6 characters would be able to store all the characters of the string too (as shown previously).

  4. The spiral must look like an Archimedean spiral:

    Archimedean spiral

    • This is not correct:
           T h e   q u
                      i
         e r   t h e   c
        v               k
       o             l
          g         a     b
       s   o d   y z     r
        p               o
         m             w
          u           n
           j   x o f
      
  5. You may start the spiral at any point you like and even go anti-clockwise. Any of these would be valid:

         . . . . . .              . . . . . .              u q   e h T
        .           .            .           .            i
       x   j u m p   .          j u m p s     .          c   e h t   r e
      o           s   .                    o   T        k               v
     f   a z y         .      x   y   d o   v   h          l   . . . .   o
        l       d   o   T    o   z       g   e   e    b   a   .       .
     n       g o   v   h      f   a   . .   r          r   z   . .   .   s
      w   e       e   e            l           q        o   y       .   p
       o   h t   r              n     e h t   u          w     d o g   m
        r           q            w           i            n           u
         b   k c i u              o r b   k c                f o x   j
    

    The dots ('.') indicate "cells" that weren't used. You don't have to include them.

  6. It does not matter if the string is not long enough to form a complete spiral, but the rules above must still hold.

This is (no loopholes!), so the shortest answer in terms of bytes wins!


More examples

Note that there are more than one valid outputs due to rule 5.

ABCDE \$\rightarrow\$

 A B
    C
 E D

##################################################### \$\rightarrow\$

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

0123456789ABCDEF \$\rightarrow\$

   0 1 2 3
          4
           5
F           6
 E         7
  D       8
   C B A 9

-\\\|///-----\\\|/-----\|/---\--- \$\rightarrow\$

            -
             \
  - - - - -   \
 /         \   \
|   - - -   |   |
 \   \     /   /
  \   - - -   /
   \         /
    - - - - -

Racecar, racecar! \$\rightarrow\$

      c e c a
 R   a       r
  a   r !
   c       ,
    e c a r

0123456789AB \$\rightarrow\$

  0 1 2  
       3 
A B     4
 9     5 
  8 7 6  
\$\endgroup\$
8
  • \$\begingroup\$ @JonathanAllan The first output's outer hexagon has a side length of 6 characters. That is the minimum possible to store every character of the example string, which has a length of 43. \$\endgroup\$
    – pan
    Commented May 19, 2023 at 16:53
  • \$\begingroup\$ @JonathanAllan I'm not sure that's possible. But in any case, it's not allowed. \$\endgroup\$
    – pan
    Commented May 19, 2023 at 17:00
  • \$\begingroup\$ @JonathanAllan Yes, I have an example with a side of 5 characters in the More examples section, although the input string obscures it a bit. \$\endgroup\$
    – pan
    Commented May 19, 2023 at 17:35
  • 1
    \$\begingroup\$ @JonathanAllan Thanks! I'll edit the post. \$\endgroup\$
    – pan
    Commented May 19, 2023 at 17:39
  • 1
    \$\begingroup\$ Could you provide a sample output for 0123456789AB? \$\endgroup\$ Commented Jun 5, 2023 at 18:56

5 Answers 5

5
\$\begingroup\$

Charcoal, 61 bytes

Wφ«⎚≔⪪⮌θ¹φ⊞υLυF⁺⭆Φ⮌υ﹪λ²⮌⭆⭆134570×μκ⎇›ν¹μ∧⁼κ¹ω0¿φ«P⊟φM⊕№04κ✳Iκ

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

Wφ«

Repeat until the string is successfully turned into a spiral. (φ is actually a predefined variable here, which is why the loop succeeds at least once.)

Start by clearing any output from the previous loop.

≔⪪⮌θ¹φ

Reset the variable to the input, split into characters and reversed to that its characters can be consumed more easily.

⊞υLυ

Build up a list of integers for each pass. (The zero entry is useless and serves only to save bytes.)

F⁺⭆Φ⮌υ﹪λ²⮌⭆⭆134570×μκ⎇›ν¹μ∧⁼κ¹ω0«

Loop over a list of directions for the spiral. This starts with the list of integers, which is reversed and then alternating elements are picked out (again in a byte-saving manner, requiring a further pass through the loop). For each integer, the characters 075431 (representing the directions) are each repeated that many times, except that the last two 11 are replaced with 00 unless they are 31 in which case they are deleted. (This is actually done on the reverse of the strings as that's golfier.) A final 0 is appended before the list is looped over.

¿φ«

If there are still more characters to print, then:

P⊟φ

Output the next character without moving the cursor.

M⊕№04κ✳Iκ

Move two steps horizontally or one step diagonally.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ @ovs Looks like I misread the challenge again... this is a complete rewrite, and should correctly handle all of the edge cases. \$\endgroup\$
    – Neil
    Commented May 20, 2023 at 1:59
3
\$\begingroup\$

Jelly, 63 bytes

JḂḤØ-;+2ÄÄ<LS‘RṚm2x6IŻN+Ɗ+Ɗ’Jx$Żị“¬½®‘d4¤’;C¤+\_«/$‘ŒṬ€ṁa"⁸o/o⁶

A monadic Link that accepts a list of characters and yields a list of lines.

Starts at the top-left of the smallest outer hexagon and spirals inwards, clockwise.

Try it online! Or see a test series.

How?

Split into two parts, for ease of reading...

JḂḤØ-;+2ÄÄ<LS‘RṚm2x6IŻN+Ɗ+Ɗ’Jx$Ż   - Link: list of characters, "Chars"
J                                  - 1-indexed indices -> [1,2,...,len(Chars)]
 Ḃ                                 - mod two -> [1,0,1,0,1,0,...]
  Ḥ                                - double -> [2,0,2,0,2,0,...]
   Ø-;                             - prefix with [-1,1] -> [-1,1,2,0,2,0,2,0,...]
      +2                           - add two -> [1,3,4,2,4,2,4,2,...]
        Ä                          - cumulative sums -> [1,4,8,10,14,16,20,24,...]
         Ä                         - cumulative sums -> [1,5,13,23,37,53,73,95,...]
          <L                       - less than length(Chars)?
                                       e.g. L=9 -> [1,1,0,0,0,0,0,0,0,0,0]
            S                      - sum -> outer hexagon side length
             ‘                     - increment
              R                    - range -> [1,2,3,...,side-1,side,side+1]
               Ṛ                   - reverse -> [side+1,side,side-1,...,3,2,1]
                m2                 - modulo-2 slice -> [side+1,side-1,side-3,...]
                  x6               - repeat each element six times
                          Ɗ        - last three links as a monad - f(X):
                    I              -   deltas of X
                        Ɗ          -   last three links as a monad - f(Y):
                     Ż             -     prepend a zero to Y
                      N            -     negate
                       +           -     add Y (vectorises)
                         +         -   add X
                           ’       - decrement (vectorises)
                                       -> number of steps to take in each direction
                                          - has excess such instructions
                              $    - last two links as a monad f(Z):
                            J      -   1-indexed indices
                             x     -   repeat (vectorises)
                                         -> 1, 7, 13, ... represent East
                                            2, 8, 14, ... represent South-East
                                            etc...
                               Ż   - prefix a zero (to have a starting point)
                                       -> "Instructions"
ị“¬½®‘d4¤’;C¤+\_«/$‘ŒṬ€ṁa"⁸o/o⁶ - ...continued...
            ¤                    - nilad followed by links as a nilad:
        ¤                        -   nilad followed by links as a nilad:
 “¬½®‘                           -     code-page indices -> [7, 10, 8]
      d4                         -     div-mod four -> X = [[1,3],[2,2],[2,0]]
         ’                       -   decrement X        -> [[0,2],[1,1],[1,-1]]
           C                     -   complement X       -> [[0,-2],[-1,-1],[-1,1]]
          ;                      -   concatenate -> [[0,2],[1,1],[1,-1],[0,-2],[-1,-1],[-1,1]]
                                                    -> "Directions"
ị                                - {Instructions} index into {Directions}
              \                  - cumulative reduce by:
             +                   -   addition (vectorises)
                  $              - last two links as a monad:
                 /               -   reduce by:
                «                -     minimum (vectorises)
               _                 -   subtract -> normalises the minimal values to 0
                   ‘             - increment -> make them 1-indexed
                    ŒṬ€          - for each: 2d array of zeros with a one
                                             at the given 2d index
                       ṁ         - mould like Chars (keep first length(Chars))
                        a"⁸      - zipped logical AND (vectorises) Chars
                            /    - reduce by:
                           o     -   logical OR (i.e. layer these together)
                             o⁶  - logical OR (vectorise) with a space character
                                     (replaces remaining zeros with spaces)
\$\endgroup\$
2
\$\begingroup\$

Haskell, 360 bytes

import Data.Array
t(a,b)(c,d)=(a+c,b+d)
s i=[[a!(r,c)|c<-[b..d]]|((p,b),(q,d))<-[b],r<-[p..q]]where
 d=zip(scanl t(0,0)$concat$zipWith replicate(concat$iterate(map(2+))[1,1,1,3,1,3])$cycle[(0,2),(-1,1),(-1,-1),(0,-2),(1,-1),(1,1)])$reverse i
 b=foldl(\((p,b),(q,d))((r,c),e)->((min p r,min b c),(max q r,max d c)))((0,0),(0,0))d
 a=accumArray(flip const)' 'b d

Try it online!

    o u n d   l   
   r           i  
      ,   b a   k 
 g   d       b   e
n   r   ! y     
 i   o       a  
  n   c e r     
   n           I
    i p s   m ' 
\$\endgroup\$
2
\$\begingroup\$

K (ngn/k), 127 119 115 bytes

{(0N,w)#@[""@!4*s*w;(#x)#+\(2*s),(|2,,/--:\(-1 1+w:-2+8*s),2)7!&-2_,/+|2,(-2*|&5 1)+6#,|-1+2*1+!s:-_-%-6!1+#x;:;x]}

-8 : squeezed
-4 : and again

Attempt This Online!

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

Javascript, 365 bytes

f=s=>(b=Math.abs,m=Math.max,w=h=3,x=-2,y=0,l=[...s].reverse().map(n=>(c=y>0?b(x)<y||-x==y?[x+=2,y]:x>0?[++x,--y]:[++x,++y]:y<0?b(x)<-y||(x<0&&x>y-4)||x==-y?[x-=2,y]:x<0?[--x,++y]:[--x,--y]:x>0?[--x,--y]:[++x,++y],w=m(w,b(x)),h=m(h,b(y)),c.push(n),c)),d=Array.from(Array(2*h+1),r=>Array(2*w).fill(' ')),l.map(([x,y,n])=>d[y+h][x+w]=n),d.map(r=>r.join('')).join(`
`))

Depending on the length of the string, there might be some extra whitespace surrounding the spiral on each side.

Ungolfed

From an (empty) start position of (-2, 0) a two-dimensional array gets filled by assigning each consecutive letter to a new position computed from the last. It is not optimal to condense this to six conditions for six directions, for my solution I used eight.

As the spiral is constructed inward-out, its final size is only apparent after completing the assignment.

f = s => {
  b = Math.abs, m = Math.max
  w = h = 3, x = -2, y = 0
  l=[...s].reverse().map(n => {    // map letter n to [x, y, n]
    if(y > 0) {                    // lower half
      if(b(x) < y || -x == y) {    // between the lower corners, left included
        c = [x+=2,y]               // go right
      } else if(x > 0) {           // right lower quadrant
        c = [++x,--y]              // go right and up
      } else {                     // left lower quadrant
        c=[++x,++y]                // go right and down
      }
    } else if(y < 0) {            // upper half
      if(b(x) < -y || (x < 0 && x > y - 4) || x == -y) {
                                  // between the upper corners, right included,
                                  // on the left going two steps further out
        c = [x-=2,y]              // go left
      } else if(x < 0) {          // upper left quadrant
        c = [--x,++y]             // go left and down
      } else {                    // upper right quadrant
        c = [--x,--y]             // go left and up
      }
    } else if(x > 0) {           // right corner
      c = [--x,--y]              // go left and up
    } else {                     // left corner
      c = [++x,++y]              // go right and down
    }
    w=m(w,b(x)),h=m(h,b(y))
    c.push(n)
    return c
  })
  d=Array.from(Array(2*h + 1),r => Array(2*w).fill(' '))
  l.map(([x,y,n]) => {
    d[y+h][x+w] = n              // place letters in array, origin in the middle
  })
  return d.map(r => r.join('')).join('\n')
}
\$\endgroup\$

Your Answer

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

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