45
\$\begingroup\$

A simple one today. Write the shortest program that draws a "sawtooth alphabet" given a positive integer for the height. You must write the name of your programming language when you come to the letter it starts with.

For example, if your language is Python and the input is 1 the output should be:

ABCDEFGHIJKLMNOPythonQRSTUVWXYZ

If the input is 2 the output should be:

 B D F H J L N Python R T V X Z
A C E G I K M O      Q S U W Y

If the input is 4 the output should be:

   D     J     Python     V
  C E   I K   O      Q   U W
 B   F H   L N        R T   X Z
A     G     M          S     Y

Notes

  • A always starts at the bottom left.
  • Input can be via stdin or function call or similar. Output to stdout.
  • Input above 26 doesn't need to work.
  • No trailing spaces.
\$\endgroup\$
10
  • 10
    \$\begingroup\$ Are programming languages with one letter names allowed? (C, J, K, etc.) \$\endgroup\$
    – isaacg
    Commented Jul 28, 2014 at 6:15
  • \$\begingroup\$ @isaacg Sure. I'd imagine it might be easier in them. \$\endgroup\$ Commented Jul 28, 2014 at 6:16
  • 1
    \$\begingroup\$ Can the input simply be expected to be stored in a variable? \$\endgroup\$ Commented Jul 28, 2014 at 10:17
  • 16
    \$\begingroup\$ Btw, this isn't a sawtooth wave, this is a triangle wave :P \$\endgroup\$
    – qwr
    Commented Jul 29, 2014 at 1:04
  • 1
    \$\begingroup\$ For one letter languages you could have used something like: Ah, Bee, Cee, Dee, Ee, Eff … Eks, Why, Zed. \$\endgroup\$
    – Kijewski
    Commented Jul 29, 2014 at 21:37

27 Answers 27

19
\$\begingroup\$

C, 134

n,m,c,p;
main(r){
for(scanf("%d",&n),m=--n?n*2:1;n+r--;puts(""))
for(c=-1,p=1;c++<25;)
p+=(c-n-r)%m*((c-n+r)%m)?1:1-printf("%*c",p,c+65);
}

Compiles on gcc with a few warnings. Newlines aren't included in the character count.

122 characters if the input is already stored in n.

Thanks to user2992539, tolos and edc65 for improvements.

\$\endgroup\$
3
  • 3
    \$\begingroup\$ You can use puts("") instead of printf("\n"). \$\endgroup\$
    – Somnium
    Commented Jul 28, 2014 at 17:54
  • 2
    \$\begingroup\$ Save 4 characters with p-=(c-n+r)%m*((c-n-r)%m)?0:printf("%*c",p,c+65); instead of if((c-... \$\endgroup\$
    – user21677
    Commented Jul 28, 2014 at 22:29
  • 1
    \$\begingroup\$ Sorry for previous (deleted) comment.I was wrong. Playing with signs and abusing the fact that the first argument of main is 1 if there are no parameters: 134 n,m,c,p; main(r){ for(scanf("%d",&n),m=--n?n*2:1;n+r--;puts("")) for(c=-1,p=1;c++<25;) p+=(c-n-r)%m*((c-n+r)%m)?1:1-printf("%*c",p,c+65); } \$\endgroup\$
    – edc65
    Commented Jul 29, 2014 at 8:58
15
\$\begingroup\$

if n holds the height:

C + escape codes: 81

x;main(y){for(y=n--;x<26;x++)printf("\033[%d;%dH%c",n?x/n&1?y++:y--:y,x+1,x+65);}

C: 110

x;char a[702]={[0 ...701]=32};main(y){for(y=--n;x<26;a[x*27-1]=10)a[27*(n?x/n&1?y++:y--:y)+x]=x+++65;puts(a);}
\$\endgroup\$
4
  • 5
    \$\begingroup\$ I admit defeat. What on earth. :D \$\endgroup\$ Commented Jul 29, 2014 at 13:30
  • \$\begingroup\$ May I ask for compilation instructions? cc gives error "<bebe.c:1:17: error: ‘n’ undeclared (first use in this function)”. \$\endgroup\$
    – manatwork
    Commented Jul 29, 2014 at 13:43
  • 3
    \$\begingroup\$ well it is way worse than it looks. firstly the second one contains trailing spaces, secondly they both relies on that n is a global variable set to a number of your choice (as Martin has previously asked). \$\endgroup\$
    – bebe
    Commented Jul 29, 2014 at 13:46
  • \$\begingroup\$ Oh, so I have to provide n in any way I can. Lol. Still impressed. \$\endgroup\$
    – manatwork
    Commented Jul 29, 2014 at 14:10
13
\$\begingroup\$

TI-BASIC - 148 bytes (raw text), 186 bytes (graphic)

In response to the OP, the wonderful TI-83 (and newer) comes with a size of 16 x 8 (just using the standard large text) or with a size of 94 x 62 pixels (which with small text is worth about 10 lines).

Now, this has a little problem (to which I would like clarification). The size impositions can't be "ignored" by the interpreter; in other words, if we were to try to set the sawtooth height at 20, it would give an error preventing the full execution of the code. I could write code that would, in an infinite environment, produce the correct output, except it wouldn't run on the machine.

With this being said, I present you the (running) versions of the programs. They all depend on the variable N being set to the desired height in lines before running:

  • Raw text approach

    :ClrHome
    :"ABCDEFGHIJKLMNOPQRSTI-BASICUVWXYZ"→Str1
    :8→R
    :For(C,1,16
    :If N=1
    :Then
    :0→F
    :Else
    :If R<2 or 10-R>N
    :1→F
    :If R>7
    :-1→F
    :End
    :If C≠20
    :Then
    :Output(R,C,sub(Str1,C,1
    :Else
    :Output(R,C,sub(Str1,C,8
    :C+7→C
    :End
    :R+F→R
    :End
    

    In order to make this work regardless of the terminal, change For(C,1,16 to For(C,1,33 and remove the upper bound checking (R<2 or). Here is the output with 5→N:

    enter image description here

  • Graphic approach (this may also need AxisOff for clarity)

    :ClrDraw
    :N*6→N
    :"ABCDEFGHIJKLMNOPQRSTI-BASICUVWXYZ"→Str1
    :57→R
    :For(C,1,56
    :If N=6
    :Then
    :0→F
    :Else
    :If R<7 or 64-R>N
    :6→F
    :If R>51
    :-6→F
    :End
    :If C≠20
    :Then
    :If C>50
    :Then
    :Text(R,C,sub(Str1,C-23,1
    :Else
    :Text(R,C,sub(Str1,C,1
    :End
    :Else
    :Text(R,C,sub(Str1,C,8
    :C+30→C
    :End
    :R+F→R
    :End
    

    This one works OK, with two minor quirks. The height is still a problem, although the width isn't. However, I didn't space the letters, so in some cases (when the letter starts to rise or decline from the sawtooth), the letters may be chopped off by their successors. To make it work regardless of the terminal, remove the upper bound check (R<7 or). Then follows the graph:

    enter image description here

\$\endgroup\$
2
  • \$\begingroup\$ Now I have to port this to my graphing calculator \$\endgroup\$ Commented Jul 29, 2014 at 14:22
  • \$\begingroup\$ Quick comment: Instead of 0→F you can do Delvar F which saves 1 byte once tokenized. Also, I'm sure you can factor the Output/Text towards the end thanks to an inline boolean test condition for the coordinate, and for the C+7→C, write it in a short if (no then/else/end) \$\endgroup\$
    – Adriweb
    Commented Mar 17, 2015 at 2:19
11
\$\begingroup\$

Pure Bash (no coreutils), 181 bytes

m=$1
for l in A Bash {C..Z};{
((m))||s=++
((m>$1-2))&&s=--
for((m=$1==1?1:m,m$s,i=0;i<$1;i++));{
((i-m))&&a[i]+=${l//?/ }||a[i]+=$l
}
}
shopt -s extglob
printf %s\\n "${a[@]%%+( )}"

Output:

Piped to cat -E just to prove there are no trailing newlines.

$ ./sawtooth.sh 1 | cat -E
ABashCDEFGHIJKLMNOPQRSTUVWXYZ$
$ ./sawtooth.sh 5 | cat -E
       E       M       U$
      D F     L N     T V$
     C   G   K   O   S   W$
 Bash     H J     P R     X Z$
A          I       Q       Y$
$ 
\$\endgroup\$
10
\$\begingroup\$

JavaScript (ES6) 231 244

Edit Bug fix, some reordering and a different way of managing height==1
Moreover, changed to a function as this is allowed by OP, so no prompt() for input

No changes to the general algorithm that probably is NOT the best for this challenge

F=h=>{for(p=s=[i=z=b=t=''];++p<h;)i=1,z=b=' ',s[p]=t;for(p=--h,k=64;++k<91;)s[p]+=t+String.fromCharCode(k),k-74||(s=s.map((x,q)=>x+(p-q?'         ':'avascript'))),p-=i,p<0|p>h?(i=-i,p-=i+i,t=z,b=t+t):t+=b;console.log(s.join('\n'))}

Explained

F=h=>{
  // row in s for output must be initialized to ''. In the same step I make other initializations
  for(p=s=[i=z=b=t='']; ++p < h;) // initialize for height 1, all increment and spacing can be null
    i=1,z=b=' ',s[p]=t; // the for body is entered only if height > 1, initializing spacing and increment to the right values
  for(p=--h,k=64;++k<91;)
    s[p]+=t+String.fromCharCode(k),
    k-74||(s=s.map((x,q)=>x+(p-q?'         ':'avascript'))), // if 'J' modify each line of output adding the name or spacing
    p-=i,p<0|p>h?(i=-i,p-=i+i,t=z,b=t+t):t+=b; // index bouncing
  console.log(s.join('\n'))
}

Examples

1

ABCDEFGHIJavascriptKLMNOPQRSTUVWXYZ

3 going up

  C   G            K   O   S   W
 B D F H Javascript L N P R T V X Z
A   E   I            M   Q   U   Y

4 on top

   D     Javascript     P     V
  C E   I          K   O Q   U W
 B   F H            L N   R T   X Z
A     G              M     S     Y

7 going down

      G                    S
     F H                  R T
    E   I                Q   U
   D     Javascript     P     V
  C                K   O       W
 B                  L N         X Z
A                    M           Y
\$\endgroup\$
9
\$\begingroup\$

JAVA (393)

As always a great language for golfing:

public class P{public static void main(String[]a){int h=Integer.valueOf(a[0]);String x="ABCDEFGHIJKLMNOPQRSTUVWXYZ";String[]l=new String[h];Arrays.fill(l,"");int i=h-1;int d=-1;for(char c:x.toCharArray()){for(int n=0;n<l.length;n++){String k=(n==i)?(c=='J')?"Java":c+"":(c=='J')?"    ":" ";l[n]+=k;}if(i==0&&d==-1)d=1;if(i==h-1&&d==1)d=-1;if(h>1)i+=d;}for(String s:l){System.out.println(s);}}}
\$\endgroup\$
4
  • 2
    \$\begingroup\$ Unfortunately, you forgot the import for java.util.Arrays;. :-( \$\endgroup\$
    – Justin
    Commented Jul 28, 2014 at 23:32
  • \$\begingroup\$ But you can imporve your code: eliminate String x="ABC...Z"; and replace the for(char c... loop header with for (char c=65;++c<91;){, I love chars in Java=) \$\endgroup\$
    – flawr
    Commented Jul 29, 2014 at 14:01
  • \$\begingroup\$ Further you can replace c=='J' with c==74, saves two more bytes in total. \$\endgroup\$
    – flawr
    Commented Jul 29, 2014 at 14:08
  • 2
    \$\begingroup\$ I am sure you can do even better if you condense the if you use some XORs for those if statements, but here my improved version: (325) public class p{public static void main(String[]a){int h=Integer.valueOf(a[0]),i=h-1,d=-1,n;String[]l=new String[h];for(n=0;n<h;l[n++]="");for(char c=64;++c<91;){for(n=0;n<h;n++){String k=n==i?c==74?"Java":c+"":c==74?" ":" ";l[n]+=k;}if(i==0&d==-1)d=1;if(i==h-1&d==1)d=-1;i+=h>1?d:0;}for(String s:l)System.out.println(s);}} @Quincunx: no more need for import.util.Arrays; =) \$\endgroup\$
    – flawr
    Commented Jul 29, 2014 at 14:44
9
\$\begingroup\$

Ruby, 112 bytes

o="#{' '*29}
"*h
26.times{|i|o[(h-i%(h<2?1:2*h-2)-1).abs*30+i+(i>17?3:0)]=(i+65).chr}
puts o.gsub('R   ','Ruby')

Expects the input to be stored in h.

Let me know if this needs clarification.

\$\endgroup\$
3
  • \$\begingroup\$ Very minor but you can remove the space after %. \$\endgroup\$ Commented Jul 28, 2014 at 16:09
  • \$\begingroup\$ @Calvin'sHobbies Ha, I didn't trust my syntax highlighting there :D. Will fix it later. \$\endgroup\$ Commented Jul 28, 2014 at 16:16
  • \$\begingroup\$ You can replace puts o... with $><<o.... \$\endgroup\$
    – Jordan
    Commented Aug 30, 2016 at 22:21
7
\$\begingroup\$

J : 75 bytes

NB. without IO |:26([:u:65+i.@[)`(($(,|.@}.@}:)@i.)<"1@,.i.@[)`(' '$~,~)}5
2(1!:2)~|.26([:u:65+i.@[)`(($(,|.@}.@}:)@i.)<"1@,.i.@[)`(' '$~,~)}".1!:1[1

Using the wonderful Amend conjunction. As usual the IO is ugly and clunky, not going into details there. The core solution takes 3 gerunds (a sort of noun-ified verb (aka. function)):

  • a) generating the alphabet
  • b) generating the indices
  • c) generating the matrix to make amends to

    x (abc}) y

a) is a pretty trivial lookup in the ascii table

c) is even more trivial

b) is the interesting one. The morale is that the horizontal indices are supposed to be starting from 0 going to y-1 and back down, repeating this 26 times. Eg. for y == 4:

0 1 2 3 2 1 0 1 2 3 2 1 ...

Implementing this gives for b):

($(,|.@}.@}:)@i.) <"1@,. i.@[)       NB. is equivalent too
(tine0 tine1 tine2)                  NB. a fork with tines defined as
tine0 =: 
  hook0 =: hook0_0 hook0_1           NB. a dyadic hook: x (g h) y -: x g h y
     hook0_0 =: $                    NB. reshape
     hook0_1 =: (hook1_0 hook1_1)@i. NB. do hook1 after making 0-y
       hook1_0=: ,                   NB. append to self
       hook1_1=: |.@}.@}:            NB. rotated version of y after beheading and curtailing
tine2 =: i.@[                  NB. generate 0-x
tine1 =: <"1@,.                NB. glue together coordinates.

And oh yea, handy fact: J's name is ... "J".

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Is it worth to use amend? It seems easier to me to say something like 3 :'|:(26$1+(i.1=y),}:|i:<:y)|.@{."+u:65+i.26' The ugly part in that solution is the 8 characters just to special case height 1, but this can probably be shortened a bit. \$\endgroup\$
    – b_jonas
    Commented Jul 30, 2014 at 9:40
  • \$\begingroup\$ Thanks for the suggestion! Yours is indeed far shorter, and amend is probably overkill. Since it's so different, maybe you'd want to post it as a solution yourself? \$\endgroup\$
    – jpjacobs
    Commented Jul 30, 2014 at 15:34
  • \$\begingroup\$ The spec says "no trailing spaces" and my solution does print trailing spaces. \$\endgroup\$
    – b_jonas
    Commented Jul 30, 2014 at 20:20
5
\$\begingroup\$

R (204)

f=function(x) {
  m=matrix(" ",x,26)
  i=x
  d=ifelse(x==1,0,-1)
  for (j in 1:26) {
    m[i,j]=LETTERS[j]
    if (!((i+d) %in% 1:x)) d=-d
    i=i+d
  }
  for (i in 1:x) cat(m[i,],"\n",sep="")
}

Result

> f(1)
ABCDEFGHIJKLMNOPQRSTUVWXYZ
> f(2)
 B D F H J L N P R T V X Z
A C E G I K M O Q S U W Y 
> f(3)
  C   G   K   O   S   W   
 B D F H J L N P R T V X Z
A   E   I   M   Q   U   Y 
> f(7)
      G           S       
     F H         R T      
    E   I       Q   U     
   D     J     P     V    
  C       K   O       W   
 B         L N         X Z
A           M           Y 
\$\endgroup\$
2
  • \$\begingroup\$ I see what you did there! Very smart... \$\endgroup\$
    – Kroltan
    Commented Jul 28, 2014 at 23:57
  • 1
    \$\begingroup\$ +1 Neat! You can get rid of the spaces around your for statements though (for(i in 1:x)cat(... for instance) or the one around %in%. \$\endgroup\$
    – plannapus
    Commented Jul 29, 2014 at 11:11
4
\$\begingroup\$

J, 67 57 61 characters

echo@dtb"1@|.@|:@(26&,$(u:65+i.26)#~1 j.26$(0#~1=]),<:#-&0 2)

Use as a function:

   echo@dtb"1@|.@|:@(26&,$(u:65+i.26)#~1 j.26$(0#~1=]),<:#-&0 2)
   D     J     P     V
  C E   I K   O Q   U W
 B   F H   L N   R T   X Z
A     G     M     S     Y

Explanation: this solution uses a different approach than the other J solution. Instead of generating a sawtooth wave 0 1 2 3 2 1 0 1 ..., I looked at the spacing between consecutive letters. For instance, for n=4 if you go from the A upwards, wrap over to the second column and reach B, then you find four spaces between A and B. This pattern of spacing between letters is very regular: for n=4 the pattern is 4 4 4 2 2 2 4 4 4 ....

So the idea is to first build the flattened (and transposed) array, and then re-shape it and flip it so it look right. The output routine is straightforward (for being J, at least): dtb is "delete trailing blanks" and "1 says "operate on each line". dtb and echo are both provided by the standard library.

Thanks to Zsbán Ambrus for some golfing help.

\$\endgroup\$
1
  • \$\begingroup\$ 48 byte solution. This works in J 901 but not TIO (can't figure out why it's failing there). And I'm pretty sure it could be golfed further too. \$\endgroup\$
    – Jonah
    Commented Dec 5, 2020 at 3:24
3
\$\begingroup\$

Javascript - 248 224 235

z=~~prompt(),y="ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");y[9]="Javascript";for(w=[],v=0;v<z;v++)w[v]="\n";v--;for(i=0;i<26;i++){for(j=0;j<z;j++)w[j]+=j-v?i-9?" ":"          ":y[i];~~(i/(z-1))%2?v++:v--;}if(z==1)w=y;console.log(w.join(""))

Here is a link to the jsfiddle, where you can test it.
edit: Made a console log for a monospaced font and removed the commas if input==1.

\$\endgroup\$
7
  • \$\begingroup\$ Suggestions: 1: parseInt and Math.floor can be both changed with an integer coercing operation like |0 or ~~. 2: it's shorter to build the w array directly, without split. for(w=[],v=0;v<z;v++)w[v]="\n"; \$\endgroup\$
    – edc65
    Commented Jul 28, 2014 at 8:40
  • \$\begingroup\$ ... and there is not an output statement (console.log or whatever you use in your fiddle, it's part of the char count) \$\endgroup\$
    – edc65
    Commented Jul 28, 2014 at 9:02
  • \$\begingroup\$ @edc65 Thank you for the tips. You were right I forgot the output here so i added an alert, but there is no mono spaced font so it looks pretty chaotic. \$\endgroup\$
    – izlin
    Commented Jul 28, 2014 at 9:25
  • \$\begingroup\$ @izlin Instead of (i==9?" ":" ") you could use (i^9?" ":" "), which saves a character. \$\endgroup\$
    – rev
    Commented Jul 28, 2014 at 12:11
  • 1
    \$\begingroup\$ HELP! can anyone explain? (Why is there twice <code>v=0</code>?) \$\endgroup\$
    – flawr
    Commented Jul 28, 2014 at 14:32
3
\$\begingroup\$

Perl 119

$s.=/26/?++$r&&$/:$m-1&&$r-abs$_%(2*$m-2)+1-$m?$"x(1+3*/15/):/15/?Perl:(A..Z)[$_]for(0..26)x($m=<>);print$s=~s/\s+$//rg

This program takes input from STDIN and prints the result to STDOUT.

And bonus - a version that is against the rules as it prints trailing spaces, but adds some interaction:

#!/usr/bin/perl -p
$r=0;$_=join'',map{/26/?++$r&&$/:$m-1&&$r-abs$_%(2*$m-2)+1-$m?$"x(1+3*/15/):/15/?Perl:(A..Z)[$_]}(0..26)x($m=$_)

...and some tests below:

1
ABCDEFGHIJKLMNOPerlQRSTUVWXYZ
2
 B D F H J L N Perl R T V X Z
A C E G I K M O    Q S U W Y
4
   D     J     Perl     V
  C E   I K   O    Q   U W
 B   F H   L N      R T   X Z
A     G     M        S     Y
6
     F         Perl         Z
    E G       O    Q       Y
   D   H     N      R     X
  C     I   M        S   W
 B       J L          T V
A         K            U
\$\endgroup\$
3
\$\begingroup\$

MetaPost (207)

numeric h,d,y;h=5;d=-1;y=1;u=5mm;pair p;string s;for x=1upto26:p:=(x,y)*u;s:=char(x+64);if x=13:s:="MetaPost";elseif x>13:p:=(x+2,y)*u;fi;label.rt(s,p);if(y=h)or(y=1):d:=d*-1;fi;if h=1:d:=0;fi;y:=y+d;endfor;

enter image description here

You can try it here.

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

Bash (213) (223)

read i
p=0
d=1
printf '\n%.0s' `seq $i`
for l in {A..Z}
do (($p==$i-1))&&((p=$p*-1))
(($i==1))&&p=0
(($p!=0))&&echo -en "\033[s\033[${p#-}A"
[[ "$l" == "B" ]]&&l="Bash"
echo -en $l"\033[s\033[${p#-}B"
((p++))
done

Minor tweaks and we're down to 213.

The original one had a minor bug. Tested and confirmed to work on bash version 4.2.37.

Thanks to @manatwork for pointing out the bug and some tips.

\$\endgroup\$
4
  • \$\begingroup\$ I edited your post by adding syntax highlighting to it, but now I see that it wrongly highlighted a part of your code as a comment, so I rolled it back. I just said this in case you were wondering why I rolled it back :) \$\endgroup\$
    – ProgramFOX
    Commented Jul 28, 2014 at 12:30
  • \$\begingroup\$ @ProgramFOX That's alright. Thanks anyhow! \$\endgroup\$
    – Okw
    Commented Jul 28, 2014 at 14:20
  • \$\begingroup\$ I think there is minor bug somewhere, as the output is slowly descending. (At least in Bash 4.3.) Some minor improvements to reduce the size to 194 characters: omit the sigil in arithmetic evaluation, do not try to double-quote inside double-quoted string: pastebin.com/zKa3zdwR \$\endgroup\$
    – manatwork
    Commented Jul 28, 2014 at 15:28
  • \$\begingroup\$ Can you use newlines instead of some of the ; just to make it a tiny bit more readable? It won't affect your golf score \$\endgroup\$ Commented Jul 28, 2014 at 19:43
2
\$\begingroup\$

Haskell – 432 Bytes (unfortunately...)

This turned out to be much harder than I expected to accomplish purely, hence the hefty byte count. I am sure I (or someone) could do better, but I've spent too much time on this as it is. The golfed version is as follows:

import Data.Matrix
import Data.Vector(toList)
r=repeat
s h=y h$l(take 26(case h of 1->r 1;_->concat$r$[h,h-1..1]++[2..h-1]))['A'..'Z']$matrix h 32(const ' ')<|>matrix h 1(const '\n')
l _[]m=m
l(x:i)(r:e)m=l i e$u m(let z=26-length i in if z<9 then z else z+6)x r
l _ _ m=m
u m c r h=case h of 'H'->t c r"Haskell"m;_->setElem h(r,c)m
t _ _[]m=m
t c r(x:i)m=t(c+1)r i$setElem x(r,c)m
y h m=concat[toList$getRow x m|x<-[1..h]]

To run, load the code into ghci and execute putStr $ s Int where Int is your desired height. You could also add

import System.Environment
main :: IO ()
main = fmap (s . read . head) getArgs >>= putStr

under the imports in a text file, compile it with ghc, and pass the height as a command line argument. Ungolfed version:

import System.Environment
import Data.Matrix
import Data.Vector (toList)

main :: IO ()
main = fmap (sawtooth . read . head) getArgs >>= putStr

type M = Matrix Char

sawtooth :: Int -> String
sawtooth height = let mat     = matrix height 32 (const ' ') <|> matrix height 1 (const '\n')
                      numbers = take 26 (case height of 1 -> repeat 1
                                                        _ -> concat $ repeat $ [height,height-1..1]++[2..height-1])
                      chars   = ['A'..'Z']
                  in toString height $ makeMatrix numbers chars mat

makeMatrix :: [Int] -> String -> M -> M
makeMatrix []     []     mat = mat
makeMatrix (x:xs) (s:ss) mat = makeMatrix xs ss $ updateMatrix
                                                    mat (let z = 26 - length xs in if z < 9 then z else z + 6) x s
makeMatrix _      _      mat = mat

updateMatrix :: M -> Int -> Int -> Char -> M
updateMatrix mat col row char = case char of 'H' -> insertHaskell col row "Haskell" mat
                                             _   -> setElem char (row, col) mat

insertHaskell :: Int -> Int -> String -> M -> M
insertHaskell _   _   []     mat = mat
insertHaskell col row (x:xs) mat = insertHaskell (col+1) row xs $ setElem x (row, col) mat

toString ::Int -> M -> String
toString height mat = concat [ toList $ getRow x mat | x <- [1..height] ]
\$\endgroup\$
1
  • \$\begingroup\$ I believe you can replace const with pure (using the Applicative instance for functions) to save a few bytes. \$\endgroup\$ Commented Apr 20, 2018 at 7:00
2
\$\begingroup\$

C#/LINQ:

using System;
using System.Linq;

namespace SawtoothAlphabet
{
    class Program
    {
        static void Main(string[] args)
        {
            int N = Int32.Parse(args[0]);   // eg 4
            Console.WriteLine(
                String.Join(Environment.NewLine, Enumerable.Range(0, N).Select(line =>
                    String.Concat(Enumerable.Range(0, 26).Select(i =>
                        line == Math.Abs(i % (2 * (N - 1)) - (N - 1))
                            ? (i == 2) ? "C#" : Char.ConvertFromUtf32(i + 'A')
                            : (i == 2) ? "  " : " ")
            ).TrimEnd())));
        }
    }
}

Brief explanation: Enumerable.Range(0, N).Select(...) causes a string to be generated for each line which is eventually concatenated into a single string with String.Join(Environment.NewLine, ...). For each line we cycle through all 26 characters with Enumerable.Range(0, 26).Select(...), the test at the start of the lambda expression determines whether to generate a character or space while the i==2 checks for "C" and converts it to either "C#" or two spaces depending on the line. The String.Concat(...) converts the resulting IEnumerable<char> for each line into a string before passing it to TrimEnd(...) to cleanly strip away any trailing whitespace.

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

K, 60 bytes

`0:'{+{x!y}.'+(26#{(!x),|1_!x-1}x;(((x-1)#" "),)'`c$65+!26)}

Pretty straightforward, and it looks like I just edged out the J solution. :)

First, generate an alphabet:

  `c$65+!26
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"

And a sawtooth wave of an appropriate length:

  26#{(!x),|1_!x-1}5
0 1 2 3 4 3 2 1 0 1 2 3 4 3 2 1 0 1 2 3 4 3 2 1 0 1

Pad each letter of the alphabet with leading spaces:

  (((5-1)#" "),)'"ABC"
("    A"
 "    B"
 "    C")

Zip the alphabet and the square wave together, and rotate each row:

  +(0 1 2;("  A";"  B";"  C"))
((0
  "  A")
 (1
  "  B")
 (2
  "  C"))

  {x!y}.'+(0 1 2;("  A";"  B";"  C"))
("  A"
 " B "
 "C  ")

And the transposition of that is our answer:

  +{x!y}.'+(0 1 2;("  A";"  B";"  C"))
("  C"
 " B "
 "A  ")

Try it here in oK.

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

Javascript (204 185 150 145)

h=m=4;d=h>1?-1:0;o=[];for(a=0;a<35;a++){for(r=h;r;r--)o[r]=(o[r]||"")+(r==m?"ABCDEFGHIJavascriptKLMNOPQRSTUVWXYZ"[a]:" ");if(a<9||a>17){m+=d;if(m<2||m==h)d=-d}}console.log(o.join("\n"))

EDIT

Saved 13 bytes by not building an array and .join("\n"). Required flipping the for loops. Then, with help from C coding son, made code entirely too clever to save another 12 bytes.

Here is the readable version showing the logic change.

for (row = height; row; row--) {
    rowOfNextActiveLetter = 1;
    direction = height > 1 ? -1 : 0;
    output = "";
    for (a = 0; a < 35; a++) {
        output += (row == rowOfNextActiveLetter ? "ABCDEFGHIJavascriptKLMNOPQRSTUVWXYZ"[a] : "");
        if (a < 9 || a > 17) {
            rowOfNextActiveLetter -= direction;
            if (rowOfNextActiveLetter < 2 || rowOfNextActiveLetter == height)direction = -direction
        }
    }
    console.log(output)
}

Golfed (161):

for(r=h;r;r--){m=1;d=h>1?-1:0;o="";for(a=0;a<35;a++){o+=(r==m?"ABCDEFGHIJavascriptKLMNOPQRSTUVWXYZ"[a]:" ");if(a<9||a>17){m-=d;if(m<2||m==h)d=-d}}console.log(o)}

Golfed and obfuscated (149):

for(r=h;r;r--,console.log(o))for(m=1,d=h>1?1:0,o="",a=0;a<35;)if(o+=r==m?"ABCDEFGHIJavascriptKLMNOPQRSTUVWXYZ"[a]:" ",a<9|a++>17)d=(m+=d)<2|m==h?-d:d

Reversed Alphabet and loop (145):

for(r=h;r;r--,console.log(o))for(m=d=1,o="",a=35;a;)if(o+=" ZYXWVUTSRQPONMLKtpircsavaJIHGFEDCBA"[r==m?a:0],a<18|a-->26)d=h<2?0:(m+=d)<2|m==h?-d:d
\$\endgroup\$
3
  • \$\begingroup\$ I like it even if I don't understand it full. But anyway you can cut 9 chars removing substr and using direct index .substr(a,1) => [a] \$\endgroup\$
    – edc65
    Commented Jul 30, 2014 at 8:30
  • \$\begingroup\$ You can narrow it further by using bitwise operators and coalescing iterators for 182: h=m=4;d=1<h?-1:0;o=[];for(a=0;35>a;a++){for(r=h;r;r--)o[r]=(o[r]||"")+(r==m?"ABCDEFGHIJavascriptKLMNOPQRSTUVWXYZ"[a]:" ");if(9>a|17<a)if(m+=d,2>m||m==h)d=-d}console.log(o.join("\n")) \$\endgroup\$ Commented Oct 31, 2014 at 0:59
  • \$\begingroup\$ whoops, cleaned up one too many spaces \$\endgroup\$ Commented Mar 16, 2015 at 16:43
1
\$\begingroup\$

PHP (216) (205)

New version:

$b=array_fill(65,26,' ');$b[80]='   ';$b=array_fill(0,$i,$b);for($j=--$i,$x=64;++$x<91;$i||$j=0){$b[$j][$x]=chr($x);$x==80&&$b[$j][$x]='PHP';$j+=($d=$j<($d?$i:1))*2-1;}echo join("\n",array_map('join',$b));

Old version:

$b=array_fill(0,$i,array_fill(0,28,' '));for($j=--$i,$x=-1;++$x<28;$i||$j=0){$q=&$b[$j];$q[$x]=chr($x-($x&16)/8+65);$x==15&&($q[++$x]='H')*$q[++$x]='P';$j+=($d=$j<($d?$i:1))*2-1;}echo join("\n",array_map('join',$b));

expects the variable $i to be the height.

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

C, 214 169 bytes, no trailing spaces

Thank to @edc65 and @tolos for their helpful suggestions.

#define C ((c-i+n-65)%z&&(c+i+n-67)%z)
n,i,m,c,z;main(){scanf("%d",&n);z=n<2?1:2*n-2;for(;i++<n;){for(m=c=64;c++<90;)m=C?m:c;for(c=64;c++<m;)putchar(C?32:c);puts("");}}
\$\endgroup\$
4
  • 3
    \$\begingroup\$ It's a lost battle, but I persist: #include <stdio.h> is not needed in many cases, be it gcc or any other compiler. It's standard and valid C anyway. \$\endgroup\$
    – edc65
    Commented Jul 28, 2014 at 9:07
  • 2
    \$\begingroup\$ @edc65: Depends on what you consider "valid C". It is AFAIK valid K&R C, but ANSI/ISO C demands that variadic functions (which include scanf and printf) are given prototypes, otherwise you'll get undefined behaviour. #include <stdio.h> is, of course, the shortest way to give a prototype to both scanf and printf. \$\endgroup\$
    – celtschk
    Commented Jul 28, 2014 at 9:34
  • \$\begingroup\$ @celtschk valid K&R is enough for me in code golf. And 'in many cases' (when just using puts for instance) is valid C89. What I want stress out is that is not a strange behaviour of gcc. (Thks for the note about variadic functions, anyway) \$\endgroup\$
    – edc65
    Commented Jul 28, 2014 at 10:15
  • 1
    \$\begingroup\$ Globals are type int and initialized to zero. Might as well drop int declaration for main if you aren't returning anything. Can drop extra parens, replace ' ' with 32 and if(!C)m=c with a ternarny statement, and as I just learned (above) replace printf("\n") with puts("") for 18 characters: n,i,m,c,z;main(){;scanf("%d",&n);z=n<2?1:2*n-2;for(;i<n;i++){for(m=c=65;c<91;c++)m=C?m:c;for(c=65;c<=m;c++)printf("%c",C?32:c);puts("");}} \$\endgroup\$
    – user21677
    Commented Jul 28, 2014 at 22:51
1
\$\begingroup\$

Perl 5 + -p, 79 bytes

Not the shortest, and I think there's a chance this isn't valid (as it uses ANSI escape sequences to position the letters...) - happy to remove if this is deemed invalid.

//;$_=$/x$_;$|-=++$-<$'?0:($-=1),$\.=$_.($|?".":".[A")x($'>1)for A..O,Perl,Q..Z

Try it online!

Explanation

The initial // is purely to store the current value of $_ in $' so $_ can be used in the body of the loop, saving a variable assignment. Then $_ is set to $/ (newline) $_ times, to move the cursor down the screen.

The main body of the program is the for loop that iterates over the alphabet, replacing P with Perl as a bareword. $| can be used as a binary toggle when decremented so if the post-incremented $- is less than $', subtract nothing, keeping the value the same, otherwise, set $- to 1 and decrement that. Next, append to $\ (which is automatically output after $_ at the end of the program thanks to -p) the current letter ($_ - implicitly set by the for loop) with if $| is truthy, form feed (to move the cursor down a line) otherwise \x1b[A to move the cursor up one line, $'>`` times. This last condition is to satisfy the requirement of input 1` being a straight line and feels the most out of place in the answer...

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

C: 142 139 characters

Dreadfully long, I expect to be able to shorten it a bit:

char a[26][27],i,j,p,d;main(n){for(scanf("%d",&n);i<26;d^=!p^p+2>n,p+=d-(d^n>1),i++)for(j=n;j--;a[j][i]=p^j?32:i+65);while(n)puts(a[--n]);}

Slightly more readable:

char a[26][27], i, j, p, d;
main(n) {
    for (
        scanf("%d", &n);
        i < 26;
        d ^= !p ^ p + 2 > n,
        p += d - (d ^ n > 1),
        i++
    )
        for (
            j = n;
            j--;
            a[j][i] = p ^ j
                ? 32
                : i + 65
        );

    while (n)
        puts(a[--n]);
}

Edit: I missed the "no trailing spaces"-rule, but I will be back.

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

Scala, 246 bytes

def f(h:Int)={val a=Seq.fill(h)(Array.fill(32)(' '));var(r,x,d)=(h-1,0,if(h==1)0 else-1);def p(c:Char){a(r)(x)=c;x+=1};for(c<-'A'to'Z'){p(c);if(c==83)"cala"foreach p;r+=d;if(r==0|r==h-1)d*= -1};for(z<-a)println(new String(z)replaceAll(" +$",""))}

reformatted and commented:

def f(h: Int) = {
  val a = Seq.fill(h)(Array.fill(32)(' '));
  // r - row; x - x coordinate, column; d - direction
  var (r, x, d) = (h - 1, 0, if(h==1) 0 else -1); 
  def p(c: Char) { // p for "put the character"
    a(r)(x) = c;
    x += 1
  };
  for(c <- 'A' to 'Z') { 
    p(c);
    if(c == 83)      // 83 == 'S'
      "cala" foreach p;
    r += d;
    if(r == 0 | r == h - 1)     // | is shorter than || 
      d *= -1
  };
  for(z <- a)
    println(new String(z) replaceAll (" +$", ""))  // trimming trailing whitespace
}

Results:

scala> f(4)
   D     J     P         V
  C E   I K   O Q       U W
 B   F H   L N   R     T   X Z
A     G     M     Scala     Y

scala> f(5)
    E       M           U
   D F     L N         T V
  C   G   K   O   Scala   W
 B     H J     P R         X Z
A       I       Q           Y

scala> f(1)
ABCDEFGHIJKLMNOPQRScalaTUVWXYZ
\$\endgroup\$
0
\$\begingroup\$

Python - 137

Input to be stored in i. e.g. i=8

l=[""]*h;k=j=0;y=h-1;exec'l[y]+=" "*(j-len(l[y]))+chr(k+65)+"ython"*(k==15);j=len(l[y]);y-=1^k/(h-(h>2))%2*-2;k+=1;'*26;print"\n".join(l)
\$\endgroup\$
0
\$\begingroup\$

Racket

Here's a clean functional version: suggestions to shorten it welcome.

(define (sawtooth n)
  (define (ST i d m lns)
    (if (null? m) 
        lns
        (let* ([v (first m)]
               [spc (make-string (string-length v) #\space)]
               [I (+ i d)])
          (ST I
              (if (or (zero? I) (= (sub1 n) I)) (* d -1) d)
              (rest m)
              (for/list ([ln lns] [j n])
                       (~a ln (if (= i j) v spc)))))))
  (displayln
   (string-join 
    (ST (sub1 n)
        (if (= n 1) 0 -1) 
        (string-split "A B C D E F G H I J K L M N O P Q Racket S T U V W X Y Z")
        (make-list n "\n")))))

Output

> (sawtooth 1)
ABCDEFGHIJKLMNOPQRacketSTUVWXYZ

> (sawtooth 2)
 B D F H J L N P Racket T V X Z 
A C E G I K M O Q      S U W Y 

> (sawtooth 8)
       H                  V     
      G I                U W    
     F   J              T   X   
    E     K            S     Y  
   D       L     Racket       Z 
  C         M   Q               
 B           N P                
A             O  
\$\endgroup\$
0
\$\begingroup\$

JavaScript, 168 bytes

f=(n,o=0,a=[...Array(n)])=>o<26?f(n,o+1,a.map((s='',i)=>s+(o-9?String.fromCharCode(65+o):'JavaScript').replace(i*i-(o%(2*n-2)-n+1)**2&&/./g,' '))):a.map(s=>s.trimEnd())

Try it online!

Returns an array of lines.

Ungolfed and explained:

f = (
  n,                 // input
  o = 0,             // offset for recursion (current step)
  a = [...Array(n)]  // array containing output lines
) =>
  o < 26 ?    // if not at end yet
    f(        // call function again
      n,      // with same input
      o + 1,  // increment offset
      a.map((s = '', i) =>  // add next letter to each line of a
        s +                 // current line
        (
          o - 9 ?                          // if letter is not J
            String.fromCharCode(65 + o) :  // current letter
            'JavaScript'                   // else 'JavaScript'
        ).replace(                          
          // replace with spaces if not at a triangular wave point
          i != Math.abs(o % (2 * n - 2) - n + 1) &&
          /./g, ' '
        )
      )
    ) :
    a.map(s => s.trimEnd())  // return array with trailing spaces removed
\$\endgroup\$
0
\$\begingroup\$

Scala, 332 bytes

Golfed version. Try it online!

object P extends App{val h=args(0).toInt;val x="ABCDEFGHIJKLMNOPQRSTUVWXYZ";val l=Array.fill[String](h)("");var i=h-1;var d= -1;for(c<-x.toCharArray){for(n<-l.indices){val k=if(n==i){if(c=='S')"Scala"else c.toString}else{if(c=='S')"    "else" "};l(n)+=k};if(i==0&&d== -1)d=1;if(i==h-1&&d==1)d= -1;if(h>1)i+=d};for(s<-l){println(s)}}

Ungolfed version. Try it online!

object P extends App {
  val h = args(0).toInt
  val x = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  val l = Array.fill[String](h)("")
  var i = h - 1
  var d = -1
  for (c <- x.toCharArray) {
    for (n <- l.indices) {
      val k = if (n == i) {
        if (c == 'S') "Scala" else c.toString
      } else {
        if (c == 'S') "    " else " "
      }
      l(n) += k
    }
    if (i == 0 && d == -1) d = 1
    if (i == h - 1 && d == 1) d = -1
    if (h > 1) i += d
  }
  for (s <- l) {
    println(s)
  }
}
\$\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.