Zigzagify a String

46

4

Write a program (or function) that takes in a non-empty string of any printable ASCII characters.

Print (or return) a zigzagging chain of the characters in the string with every neighboring pair of characters linked by:

  • / if the first character occurs before the second character in normal ASCII order. e.g.

      B
     /
    A
    
  • \ if the first character occurs after the second character in normal ASCII order. e.g.

    B
     \
      A
    
  • - if the first and second characters are the same. e.g.

    A-A
    

So the output for Programming Puzzles & Code Golf would be

                                                        o    
                                                       / \   
  r                         z-z               o   e   G   l  
 / \                       /   \             / \ / \ /     \ 
P   o   r   m-m   n       u     l   s   &   C   d           f
     \ / \ /   \ / \     /       \ / \ / \ /                 
      g   a     i   g   P         e                          
                     \ /                                     
                                                             

If there is only one character in the input string the output would just be that character.

Your program should treat , /, \, and - just the same as all other characters.

e.g. -\//-- \ //- should produce:

      \                      
     / \                     
    -   /-/                  
   /       \                 
 -          ---   \   /-/    
               \ / \ /   \   
                          -  
                           \ 
                             

There should be no extraneous newlines in the output except for a single optional trailing newline. (Note that the empty line in the example just above holds the last space in the the string and is therefore not extraneous.) There may be trailing spaces on any lines in any arrangement.

The shortest code in bytes wins.

One more example - Input:

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679

Output:

                          9   9       8   6   6                                                                                                                                                            
                         / \ / \     / \ / \ / \                                                                                                                                                           
            9   6       8   7   3   3   4   2   4     8       9       8-8                                                                                                                                  
           / \ / \     /         \ /             \   / \     / \     /   \                                                                                                                                 
      4   5   2   5   5           2               3-3   3   7   5   2     4   9       9   9-9   7                                                                                                          
     / \ /         \ /                                   \ /     \ /       \ / \     / \ /   \ / \                                                                                                         
3   1   1           3                                     2       0         1   7   6   3     3   5       8                             8   6                                                              
 \ /                                                                             \ /               \     / \                           / \ / \                                                             
  .                                                                               1                 1   5   2   9             9   3   7   1   4   6   8                                                   9
                                                                                                     \ /     \ / \           / \ / \ /         \ / \ / \                                                 / 
                                                                                                      0       0   7   9     5   2   0           0   2   6       9-9               8   5   4             7  
                                                                                                                   \ / \   /                             \     /   \             / \ / \ / \           /   
                                                                                                                    4   4-4                               2   8     8           4   2   3   2     7   6    
                                                                                                                                                           \ /       \         /             \   / \ /     
                                                                                                                                                            0         6   8   3               1-1   0      
                                                                                                                                                                       \ / \ /                             
                                                                                                                                                                        2   0                              

Calvin's Hobbies

Posted 2015-08-29T03:33:14.317

Reputation: 84 000

Answers

8

Pyth, 69 bytes

aY,JhzZVtzaY,@"-\/"K-<NJ>N~JN=+ZKaY,N=+ZK;jbCmX*\ h-e=GSeMYhG-edhGhdY

Demonstration. Longer inputs still work, but they don't look very good in the fixed-width output box.

I start by constructing a list, in Y, of [character, height] tuples. It is [['P', 0], ['/', -1], ['r', -2], ['\\', -1], ['o', 0], ['\\', 1], ['g', 2]] early on in the Programming Puzzles & Code Golf example.

I then make strings of spaces of the appropriate length, insert the character at the appropriate location, transpose, join on newlines and print.

isaacg

Posted 2015-08-29T03:33:14.317

Reputation: 39 268

7

Julia, 297 bytes

s->(l=length;d=sign(diff([i for i=s]));J=join([[string(s[i],d[i]>0?:'/':d[i]<0?:'\\':'-')for i=1:l(d)],s[end]]);D=reshape([d d]',2l(d));L=l(J);E=extrema(cumsum(d));b=2sumabs(E)+1;A=fill(" ",L,b);c=b-2E[2];for (i,v)=enumerate(J) A[i,c]="$v";i<l(D)&&(c-=D[i])end;for k=1:b println(join(A'[k,:]))end)

Ungolfed:

function f(s::String)
    # Get the direction for each slash or dash
    # +1 : /, -1 : \, 0 : -
    d = sign(diff([i for i in s]))

    # Interleave the string with the slashes as an array
    t = [string(s[i], d[i] > 0 ? '/' : d[i] < 0 ? '\\' : '-') for i = 1:length(d)]

    # Join the aforementioned array into a string
    J = join([t, s[end]])

    # Interleave D with itself to duplicate each element
    D = reshape(transpose([d d]), 2*length(d))

    # Get the length of the joined string
    L = length(J)

    # Get the maximum and minimum cumulative sum of the differences
    # This determines the upper and lower bounds for the curve
    E = extrema(cumsum(d))

    # Get the total required vertical size for the output curve
    b = 2*sumabs(E) + 1

    # Get the beginning vertical position for the curve
    c = b - 2*E[2]

    # Construct an array of spaces with dimensions corresponding
    # to the curve rotated 90 degrees clockwise
    A = fill(" ", L, b)

    # Fill the array with the curve from top to bottom
    for (i,v) = enumerate(J)
        A[i,c] = "$v"
        i < length(D) && (c -= D[i])
    end

    # Print out the transposed matrix
    for k = 1:b
        println(join(transpose(A)[k,:]))
    end
end

Alex A.

Posted 2015-08-29T03:33:14.317

Reputation: 23 761

5

Javascript (ES6), 360 331 316 302 bytes

Here's my fourth attempt:

s=>{r=[],c=s[m=w=n=0];for(i in s)(i?(d=s[++i])>c?++n:c>d?--n:n:n)<m&&m--,n>w&&w++,c=d;for(i=0,n=w*2;i<(w-m)*2+1;r[i++]=[...' '.repeat(l=s.length*2-1)]);for(i=0;i<l;i++)i%2?(A=s[C=(i-1)/2])<(B=s[C+1])?r[--n,n--][i]='/':A>B?r[++n,n++][i]='\\':r[n][i]='-':r[n][i]=s[i/2];return r.map(x=>x.join``).join`
`}

Not as short as some of the others, but I'm satisfied with it for now.

Oh, so you want to test it? Alright, here you go:

z=s=>{r=[],c=s[m=w=n=0];for(i in s)(i?(d=s[++i])>c?++n:c>d?--n:n:n)<m&&m--,n>w&&w++,c=d;for(i=0,n=w*2;i<(w-m)*2+1;r[i++]=[...' '.repeat(l=s.length*2-1)]);for(i=0;i<l;i++)i%2?(A=s[C=(i-1)/2])<(B=s[C+1])?r[--n,n--][i]='/':A>B?r[++n,n++][i]='\\':r[n][i]='-':r[n][i]=s[i/2];return r.map(x=>x.join``).join('<br>')};

input=document.getElementById("input");
p=document.getElementById("a");
input.addEventListener("keydown", function(){
  setTimeout(function(){p.innerHTML = "<pre>"+z(input.value)+"</pre>";},10);
})
<form>Type or paste your text here: <input type="text" id="input"/></form>

<h3>Output:</h3>
<p id="a"></p>

Have fun!

Updates:

Update 1: Golfed off 29 bytes with a variety of typical techniques.

Update 2: Golfed off 15 more bytes by building the string horizontally from the start, as opposed to building an array of the vertical strings and switching them around, which is what it did before.

Update 3: Saved 14 more bytes.

More golfiness coming soon!

ETHproductions

Posted 2015-08-29T03:33:14.317

Reputation: 47 880

You can save a byte by replacing '\n' with a template string like this

– jrich – 2015-08-30T14:31:29.583

@UndefinedFunction Yeah, I've used that trick before, but forgot to put it in last night. Thanks for the reminder! – ETHproductions – 2015-08-30T17:46:59.817

Your for loops can be squished a lot. Don't waste a whole block of required code with i++. Instead, run most of your for code in there. Also, you don't need braces around single lines of code. – Not that Charles – 2015-08-31T21:36:59.383

It also seems like the only way you use l is to calculate s.length*2-1 and you do that twice. Why not store that value instead? – Not that Charles – 2015-08-31T21:41:05.027

However, it looks like a requirement is to print the string to the screen, and you haven't fulfilled that. Consider using \n instead of <br> and putting to console or alert. – Not that Charles – 2015-08-31T21:43:43.560

1@NotthatCharles Thanks for the tips! I'd just tried out the modified algorithm and not bothered to golf it much more yet. The <br> is just in there so it shows up in the HTML version; if you look closely, I use a template string instead in the actual entry. Also, it's not a requirement: "Print (or return)..." – ETHproductions – 2015-08-31T21:47:32.987

You can save 2 more characters by declaring all the iteration variables separately in that m=w=n=0 block. You need to add extra variables for the other iterations. So, e.g., f=j=m=w=n=0... and then replace the is as needed. Then you can drop the i=0 in the later for loops. You may find you save space without using an for in loop at the beginning, too. – Not that Charles – 2015-09-04T18:11:03.020

3

Python, 393 bytes

def z(n,h=[]):
 for j in range(len(n)):h.append(sum(cmp(ord(n[i]),ord(n[i+1]))for i in range(j)))
 h=[j-min(h)for j in h]
 for y in range(max(h)*2+2):
  s=""
  for x in range(len(n)):
   if h[x]*2==y:s+=n[x]
   else:s+=" "
   if x==len(n)-1:continue
   c=" "
   if h[x]<h[x+1]and h[x]*2==y-1:c="\\"
   if h[x]>h[x+1]and h[x]*2==y+1:c="/"
   if h[x]==h[x+1]and h[x]*2==y:c="-"
   s+=c
  print s

Run as: z("Zigzag")

Loovjo

Posted 2015-08-29T03:33:14.317

Reputation: 7 357

3

JavaScript (ES6), 202

Using template strings. Indentation spaces and newlines not counted, except the last newline inside backticks that is significant and counted.

Usual note: test running the snippet on any EcmaScript 6 compliant browser (notably not Chrome not MSIE. I tested on Firefox, Safari 9 could go)

f=z=>
  [...z].map(c=>
    (d=0,x=w+c,p&&(
      c<p?o[d=1,g='\\ ',r+=2]||o.push(v,v)
      :c>p?(d=-1,g='/ ',r?r-=2:o=[v,v,...o]):x='-'+c,
      o=o.map((o,i)=>o+(i-r?i-r+d?b:g:x),v+=b)
    ),p=c)
  ,v=w=' ',o=[z[p=r=0]],b=w+w)&&o.join`
`

Ungolfed=z=>
(
  v=' ',o=[z[0]],r=0,p='',
  [...z].map(c=>{
    if (p) {
      if (c < p) {
        if (! o[r+=2])
          o.push(v,v)
        o = o.map((o,i)=>o+(i==r ? ' '+c : i==r-1 ? '\\ ' : '  '))
      } else if (c > p) {
        if (r == 0)
          o = [v,v,...o]
        else
          r -= 2
        o = o.map((o,i)=>o+(i==r ? ' '+c : i==r+1 ? '/ ' : '  '))
      } else {
        o = o.map((o,i)=>o+(i==r ? '-'+c : '  '))
      }
      v += '  '
    }
    p = c
  }),
  o.join`\n`
)

out=x=>O.innerHTML+=x+'\n'

test = [
"Programming Puzzles & Code Golf",  
"-\\//-- \\ //- ",  
"3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"]

test.forEach(t=>out(t+"\n"+f(t)))
<pre id=O></pre>

edc65

Posted 2015-08-29T03:33:14.317

Reputation: 31 086

2

CJam, 79 bytes

l__0=\+2ew::-:g_0\{+_}%);_$0=fm2f*_$W=)S*:E;]z{~\_)"/-\\"=2$@-E\@t@@E\@t}%(;zN*

Try it online

This builds the output column by column, and transposes the result at the end to get the output row by row. This was quite painful overall.

Explanation:

l__   Get input and create a couple of copies.
0=\+  Prepend copy of first letter, since the following code works only with
      at least two letters.
2ew   Make list with pairs of letters.
::-   Calculate differences between pairs...
:g    ... and the sign of the differences.
_0\   Prepare for calculating partial sums of signs by copying list and
      pushing start value 0.
{     Loop for calculating partial sums.
  +_    Add value to sum, and copy for next iteration.
}%    End of loop for partial sums. We got a list of all positions now.
);    Pop off extra copy of last value.
_$0=  Get smallest value.
fm    Subtract smallest value to get 0-based positions for letters.
2f*   Multiply them by 2, since the offsets between letters are 2.
_$W=  Get largest position.
)     Increment by 1 to get height of result.
S*    Build an empty column.
:E;   Store it in variable E.
]     We got the input string, list of relative offsets, and list of
      absolute positions now. Wrap these 3 lists...
z     ... and transpose to get triplets of [letter offset position].
{     Loop over triplets.
  ~     Unwrap triplet.
  \     Swap offset to front.
  _)    Copy and increment so that offset is in range 0-2.
  "/-\\"  List of connection characters ordered by offset.
  =     Pick out connection character for offset.
  2$@   Get position and copy of offset to top.
  -     Subtract to get position of connection character.
  E     Empty column.
  \@    Shuffle position and character back to top. Yes, this is awkward.
  t     Place connection character in empty line. Done with first column.
  @@    Shuffle letter and position to top.
  E     Empty column.
  \@    Stack shuffling again to get things in the right order.
  t     Place letter in empty line. Done with second column.
}%    End of main loop for triplets.
(;    Pop off first column, which is an extra connector.
z     Transpose the whole thing for output by row.
N*    Join with newlines.

Reto Koradi

Posted 2015-08-29T03:33:14.317

Reputation: 4 870

1

Perl 5, 230 214

@A=split(//,pop);$y=$m=256;map{$c=ord$_;$d=$c<=>$p;$t=$d>0?'/':$d<0?'\\':'-';$B[$x++][$y-=$d]=$t;$B[$x++][$y-=$d]=$_;$m=$y,if$m>$y;$M=$y,if$M<$y;$p=$c}@A;for$j($m..$M){for$i(1..$x){$s.=$B[$i][$j]||$"}$s.=$/}print$s

Test

$ perl zigzag.pl "zigge zagge hoi hoi hoi"
z
 \
  i
   \
    g-g
       \
        e   z   g-g       o       o       o
         \ / \ /   \     / \     / \     / \
              a     e   h   i   h   i   h   i
                     \ /     \ /     \ /


$ 

LukStorms

Posted 2015-08-29T03:33:14.317

Reputation: 1 776

1

Ruby, 158

Saved 6 bytes thanks to histocrat. Thanks!

->s,*i{i[x=n=k=(4*m=s=~/$/).times{i<<'  '*m}/2][j=0]=l=s[/./]
$'.chars{|c|i[k-=d=c<=>l][j+1]=%w{- / \\}[d]
i[k-=d][j+=2]=l=c
n,x=[x,n,k].minmax}
puts i[n..x]}

Not that Charles

Posted 2015-08-29T03:33:14.317

Reputation: 1 905

1You can set i to an empty array using ->s,*i{. And if you replace s[0] with s[/./], I think you can replace s[1..-1] with $'. – histocrat – 2015-09-04T17:44:28.320

@histocrat Great! Thanks! I thought you needed parens for multi-param lambda declarations, but apparently that's only JS. – Not that Charles – 2015-09-04T18:15:23.943

1

K, 86

{-1@+((d#\:" "),'1_,/("\\-/"1+e),'x)@\:!|/d:(|/b)+-:b:1_+\,/2#'e:{(x>0)-x<0}@-':6h$x;}  

.

k){-1@+((d#\:" "),'1_,/("\\-/"1+e),'x)@\:!|/d:(|/b)+-:b:1_+\,/2#'e:{(x>0)-x<0}@-':6h$x;} "Programming Puzzles & Code Golf"
                                                        o
                                                       / \
  r                         z-z               o   e   G   l
 / \                       /   \             / \ / \ /     \
P   o   r   m-m   n       u     l   s   &   C   d           f
     \ / \ /   \ / \     /       \ / \ / \ /
      g   a     i   g   P         e
                     \ /

Ungolfed:

f:{
    dir:{(x>0)-x<0}-':[*a;a:"i"$x];          //directional moves (-1, 0, 1)
    chars:1_,/("\\-/"1+dir),'x;              //array of input string combined with directional indicators
    depths:(|/b)+-:b:1_+\,/2#'dir;           //depth for each char, normalised to start at 0
    -1@+((depths#\:" "),'chars)@\:!|/depths; //Pad each character by the calculated depths, extend each string to a uniform length and transpose
    }

tmartin

Posted 2015-08-29T03:33:14.317

Reputation: 3 917

0

Python with Numpy: 218 bytes

It is worth it to waste 19 bytes for importing numpy.

Golfed:

from numpy import*
z=zip
r=raw_input()
s=sign(diff(map(ord,r[0]+r)))
c=cumsum(s)
p=2*(max(c)-c)+1
for L in z(*[c.rjust(i).ljust(max(p))for _ in z(z(p+s,array(list('-/\\'))[s]),z(p,r))for i,c in _][1:]):print''.join(L)

Ungolfed:

from numpy import *

letters = raw_input()
#letters = 'Programming Puzzles & Code Golf'
s = sign(diff(map(ord, letters[0] + letters)))
c = cumsum(s)
lines = array(list('-/\\'))[s]

letter_heights = 2 * (max(c) - c) + 1
line_heights = letter_heights + s

columns = [symbol.rjust(height).ljust(max(letter_heights))
    for pair in zip(                    # interleave two lists of (height, symbol) pairs...
        zip(line_heights,   lines),
        zip(letter_heights, letters)
    )
    for height, symbol in pair          # ... and flatten.
][1:]                                   # leave dummy '-' out
for row in zip(*columns):
    print ''.join(row)

Roberto Bonvallet

Posted 2015-08-29T03:33:14.317

Reputation: 101