Draw the Sawtooth Alphabet

42

3

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.

Calvin's Hobbies

Posted 2014-07-28T05:24:41.787

Reputation: 84 000

9Are programming languages with one letter names allowed? (C, J, K, etc.) – isaacg – 2014-07-28T06:15:33.027

@isaacg Sure. I'd imagine it might be easier in them. – Calvin's Hobbies – 2014-07-28T06:16:27.147

1Can the input simply be expected to be stored in a variable? – Martin Ender – 2014-07-28T10:17:06.197

@MartinBüttner Yes, that's fine. – Calvin's Hobbies – 2014-07-28T15:54:23.940

How do answers deal with reduced terminal spaces (in other words, what can I do if the screen can't handle the width or height of the sawtooth)? – Doktoro Reichard – 2014-07-28T19:23:21.540

@DoktoroReichard Is there relly a terminal that can't fit 26 characters? No matter what, the output should still be the same as it would be when displayed on a large terminal. Its not your problem if a small terminal can't display it properly. – Calvin's Hobbies – 2014-07-28T19:46:50.807

13Btw, this isn't a sawtooth wave, this is a triangle wave :P – qwr – 2014-07-29T01:04:10.577

@qwr ...dang it – Calvin's Hobbies – 2014-07-29T01:12:12.397

For one letter languages you could have used something like: Ah, Bee, Cee, Dee, Ee, Eff … Eks, Why, Zed. – kay - SE is evil – 2014-07-29T21:37:12.447

Answers

14

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);}

bebe

Posted 2014-07-28T05:24:41.787

Reputation: 3 916

5I admit defeat. What on earth. :D – Martin Ender – 2014-07-29T13:30:59.770

May I ask for compilation instructions? cc gives error "<bebe.c:1:17: error: ‘n’ undeclared (first use in this function)”. – manatwork – 2014-07-29T13:43:18.323

2well 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). – bebe – 2014-07-29T13:46:54.033

Oh, so I have to provide n in any way I can. Lol. Still impressed. – manatwork – 2014-07-29T14:10:42.030

19

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.

grc

Posted 2014-07-28T05:24:41.787

Reputation: 18 565

3You can use puts("") instead of printf("\n"). – Somnium – 2014-07-28T17:54:47.787

2Save 4 characters with p-=(c-n+r)%m*((c-n-r)%m)?0:printf("%*c",p,c+65); instead of if((c-... – None – 2014-07-28T22:29:46.563

1Sorry 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); } – edc65 – 2014-07-29T08:58:03.577

13

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

Doktoro Reichard

Posted 2014-07-28T05:24:41.787

Reputation: 419

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) – Adriweb – 2015-03-17T02:19:36.277

Now I have to port this to my graphing calculator – Liam McInroy – 2014-07-29T14:22:06.390

11

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$
$ 

Digital Trauma

Posted 2014-07-28T05:24:41.787

Reputation: 64 644

10

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

edc65

Posted 2014-07-28T05:24:41.787

Reputation: 31 086

9

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.

Martin Ender

Posted 2014-07-28T05:24:41.787

Reputation: 184 808

You can replace puts o... with $><<o.... – Jordan – 2016-08-30T22:21:40.387

Very minor but you can remove the space after %. – Calvin's Hobbies – 2014-07-28T16:09:01.247

@Calvin'sHobbies Ha, I didn't trust my syntax highlighting there :D. Will fix it later. – Martin Ender – 2014-07-28T16:16:09.423

9

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);}}}

Thomas Rüping

Posted 2014-07-28T05:24:41.787

Reputation: 101

2Unfortunately, you forgot the import for java.util.Arrays;. :-( – Justin – 2014-07-28T23:32:35.607

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=) – flawr – 2014-07-29T14:01:10.047

Further you can replace c=='J' with c==74, saves two more bytes in total. – flawr – 2014-07-29T14:08:46.650

2I 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; =) – flawr – 2014-07-29T14:44:47.617

6

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".

jpjacobs

Posted 2014-07-28T05:24:41.787

Reputation: 3 440

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. – b_jonas – 2014-07-30T09:40:13.923

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? – jpjacobs – 2014-07-30T15:34:13.160

The spec says "no trailing spaces" and my solution does print trailing spaces. – b_jonas – 2014-07-30T20:20:26.560

5

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 

djhurio

Posted 2014-07-28T05:24:41.787

Reputation: 1 113

I see what you did there! Very smart... – Kroltan – 2014-07-28T23:57:59.220

1+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%. – plannapus – 2014-07-29T11:11:56.320

3

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.

izlin

Posted 2014-07-28T05:24:41.787

Reputation: 1 497

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"; – edc65 – 2014-07-28T08:40:07.663

... and there is not an output statement (console.log or whatever you use in your fiddle, it's part of the char count) – edc65 – 2014-07-28T09:02:50.683

@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. – izlin – 2014-07-28T09:25:20.140

@izlin Instead of (i==9?" ":" ") you could use (i^9?" ":" "), which saves a character. – rev – 2014-07-28T12:11:51.947

1HELP! can anyone explain? (Why is there twice <code>v=0</code>?) – flawr – 2014-07-28T14:32:27.287

@flawr It could be removed, nice observation. – rev – 2014-07-28T14:48:59.823

The output for input 1 contains commas. – nderscore – 2014-07-29T04:12:55.043

3

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.

flawr

Posted 2014-07-28T05:24:41.787

Reputation: 40 560

3

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

core1024

Posted 2014-07-28T05:24:41.787

Reputation: 1 811

3

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.

FireFly

Posted 2014-07-28T05:24:41.787

Reputation: 7 107

2

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.

Okw

Posted 2014-07-28T05:24:41.787

Reputation: 21

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 :) – ProgramFOX – 2014-07-28T12:30:36.457

@ProgramFOX That's alright. Thanks anyhow! – Okw – 2014-07-28T14:20:46.797

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: http://pastebin.com/zKa3zdwR

– manatwork – 2014-07-28T15:28:45.187

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 – Digital Trauma – 2014-07-28T19:43:39.163

2

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.

Mark Feldman

Posted 2014-07-28T05:24:41.787

Reputation: 21

2

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] ]

DrJPepper

Posted 2014-07-28T05:24:41.787

Reputation: 499

I believe you can replace const with pure (using the Applicative instance for functions) to save a few bytes. – Esolanging Fruit – 2018-04-20T07:00:33.690

1

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("");}}

aland

Posted 2014-07-28T05:24:41.787

Reputation: 111

3It'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. – edc65 – 2014-07-28T09:07:57.807

2@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. – celtschk – 2014-07-28T09:34:48.223

@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) – edc65 – 2014-07-28T10:15:09.090

1Globals 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("");}} – None – 2014-07-28T22:51:16.473

1

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.

Christoph

Posted 2014-07-28T05:24:41.787

Reputation: 1 489

1

Javascript (204 185 150)

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

John Nowlin

Posted 2014-07-28T05:24:41.787

Reputation: 11

whoops, cleaned up one too many spaces – John Nowlin – 2015-03-16T16:43:51.590

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] – edc65 – 2014-07-30T08:30:57.430

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")) – WallyWest – 2014-10-31T00:59:15.343

1

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.

JohnE

Posted 2014-07-28T05:24:41.787

Reputation: 4 632

0

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)

Vectorized

Posted 2014-07-28T05:24:41.787

Reputation: 3 486

0

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.

Fors

Posted 2014-07-28T05:24:41.787

Reputation: 3 020

0

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

Karol S

Posted 2014-07-28T05:24:41.787

Reputation: 161

0

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  

user29983

Posted 2014-07-28T05:24:41.787

Reputation: 1