# Alphabet cannon

A cannonball is fired so that in the first eyeblink of its flight it ascends by N treetops, during the second eyeblink by N-1 treetops, etc until it reaches the highest point of its trajectory. Then it starts falling by 1, 2, etc treetops per eyeblink until it hits the ground. At the same time the cannonball is moving horizontally with a constant velocity of 1 treetop/eyeblink.

Your task is to draw the trajectory with consecutive letters from the English alphabet. If you run out of letters, start again from 'A'. Write a function or a program. The input is an integer N (1≤N≤15). The output can be a character matrix in any reasonable form, for instance a newline-separated string or a list of strings. Letters can be all lowercase or all uppercase. Extra leading and trailing spaces are allowed. Standard loopholes are forbidden. Shorter code is better.

in:
5
out:
OP
N  Q
M  R
L    S
K    T
J    U
I      V
H      W
G      X
F      Y
E        Z
D        A
C        B
B        C
A        D

in:
1
out:
AB
• – Dom Hastings Feb 28 '18 at 9:07
• Why are O and P in the same level in the example? If I read the spec correctly, it seems it should go up one treetop for P and descend by one for Q. – Skyler Feb 28 '18 at 15:09
• @Skyler At every tick, the alphabet goes 1 to the right and N vertically. N decreases every tick as well. Between O and P, the tick goes 1 to the right, but 0 up- or down-wards. – Olivier Grégoire Feb 28 '18 at 15:23
• Looks like alphabet cannons are now canon. – Carl Witthoft Feb 28 '18 at 16:22
• @ngn Hah, I was tinkering with @TonHospel's Perl solution and came up with 1 byte less, but it only supports up to 14! – Dom Hastings Feb 28 '18 at 20:30

# 05AB1E, 333229 28 bytes

>*As∍2äR)ζRIL£vyεN·úJ])˜.c

Try it online!

Explanation

>*                             # push input*(input+1)
As∍                          # take that many characters from the alphabet (with wrap)
2ä                        # split in 2 parts
R)                     # reverse the second part
ζ                    # zip (gives a list of pairs)
R                   # reverse
IL£                # split into parts of sizes equal to [1,2...]
vy              # for each (part y, index N)
ε             # for each pair in that part
N·úJ        # insert N*2 spaces between the characters
]       # end loops
)˜     # wrap in a flattened list
.c   # format as lines padded to equal length
• I feel like Nú» or something like that could be used to print as you go instead of ])~.c – Magic Octopus Urn Feb 28 '18 at 18:07
• All I could come up with is this implementation here but that's worse by 2 bytes. – Magic Octopus Urn Feb 28 '18 at 18:08

# Stax, 29 24 bytes

╦'♫ΓqπL⌂δ@╚n>DI∙Q┴òkεwö╔

Run and debug it online

The corresponding ascii representation of the same program is this.

VA*xRr:m|/xH({rix/|1*_%:T)mMm

VA*                             repeat alphabet input times
xRr:m                        [x ... 1, 1 ... x] where x=input
|/xH(                   get consecutive substrings of specified sizes
{           m      map substrings using block
ix<|1*            reverse string if index<x
_%:T)       left-pad to appropriate triangular number
Mm    transpose and output

# R, 169163161153150110 109 bytes

This approach fills in a matrix and then prints the matrix.

Golfed

function(n)write([<-(matrix(" ",M<-2*n,k<-sum(1:n)),cbind(rep(1:M,c(n:1,1:n)),c(k:1,1:k)),LETTERS),1,M,,"")

Thanks @Giuseppe for 153.

Thanks @JDL for 150.

See @Giuseppe's comment for 112, and some edits for 110 now 109. Rip original code.

function(n){a=matrix(" ",M<-2*n,k<-sum(1:n))
cat(rbind(a,"
"),sep="")}

If plotting a valid output then 73 bytes

function(n,k=sum(1:n))plot(rep(1:(2*n),c(n:1,1:n)),c(1:k,k:1),pc=LETTERS)

• 153 bytes -- your solution printed an extra space at the apex which I fixed, and then I also golfed down a few things. Nice answer! – Giuseppe Feb 28 '18 at 18:30
• can you use Map instead of mapply? – JDL Mar 1 '18 at 14:41
• @JDL You are right. I always think that Map is a wrapper for lapply instead of mapply. Thanks for 150 – Vlo Mar 1 '18 at 15:14
• This kept bothering me, because I thought there should be a way to index the matrix by row,column pairs directly with [ rather than having to go through mapply (or Map), so I found a way to do that. I also remembered that write exists and can replace cat for 112 bytes! – Giuseppe Mar 1 '18 at 16:07
• @Giuseppe My comment about "" didn't work, but with [<-, we can manage to squeeze everything within one line, eliminating the need for some variable definitions. 110 bytes: tio.run/##K/qfpmCj@z@tNC@5JDM/… – Vlo Mar 1 '18 at 16:27

# Python 2, 140135 133 bytes

lambda n:[' '*(n-j)+chr(~-i%26+65)+'  '*j+chr((n*-~n-i)%26+65)for i,j in zip(range(n*-~n/2,0,-1),sum([-~i*[i]for i in range(n)],[]))]

Try it online!

# MATL, 29 bytes

,G:tPY"tf1Y2y@?tn+P])Z?]Pv1X!

Try it online!

### How it works

,        % Do twice
G:     %   Push [1 2 ... n], where n is the input
tP     %   Duplicate, flip: pushes [n n-1 ... 1]
Y"     %   Run-length decoding: gives vector with n ones, n-1 twos ... (*)
tf     %   Duplicate, find: gives [1 2 3 ... n*(n-1)/2] (**)
1Y2    %   Push string 'ABC...Z'
y      %   Duplicate from below: pushes [1 2 3 ... n*(n-1)/2]  again
@?     %   If we are in the second iteration
tn   %     Duplicate, length: pushes n*(n-1)/2
+    %     Add: gives [n*(n-1)/2+1 n*(n-1)/2+2 ... n*(n-1)*2]
P    %     Flip: gives [n*(n-1)/2 n*(n-1)/2-1 ... n*(n-1)/2+1]
]      %   End if
)      %   Index (1-based, modular) into the string. Gives a substring
%   with the letters of one half of the parabola (***)
Z?     %   Sparse: creates a char matrix with the substring (***) written
%   at specified row (*) and column (**) positions. The remaining
%   positions contain char(0), which will be displayed as space
]        % End do twice. We now have the two halves of the parabola, but
% oriented horizontally instead of vertically
P        % Flip the second half of the parabola vertically, so that the
% vertex matches in the two halves
v        % Concatenate the two halves vertically
1X!      % Rotate 90 degrees, so that the parabola is oriented vertically.
% Implicitly display

# C, 184 bytes

i,j,k,l,m,h,o;f(n){char L[o=n*n][n*3];for(i=o;i--;)for(L[i][j=n*2]=h=k=0;j--;)L[i][j]=32;for(m=n;!h|~i;m-=1-h*2)for(h+(l=m)?++j:++h;l--;)L[h?i--:++i][j]=65+k++%26;for(;o--;)puts(L+o);}

Try it online!

Unrolled:

i, j, k, l, m, h, o;
f(n)
{
char L[o=n*n][n*3];

for (i=o; i--;)
for (L[i][j=n*2]=h=k=0; j--;)
L[i][j] = 32;

for (m=n; !h|~i; m-=1-h*2)
for (h+(l=m)?++j:++h; l--;)
L[h?i--:++i][j] = 65 + k++%26;

for (; o--;)
puts(L+o);
}
• interesting, I can't compile this (there's no main) but TIO can – ngn Feb 28 '18 at 13:41
• @ngn It's just a function, you need to add the main to compile it. On TIO, the main is in the footer section. – Steadybox Feb 28 '18 at 13:44

# Java (OpenJDK 8), 121 bytes

n->{for(int l=n*++n/2,r=l,i=1,j=0;l>0;j=j-->0?j:i++)System.out.printf("%"+(n-i)+"c%"+(2*i-1)+"c%n",--l%26+65,r++%26+65);}

Try it online!

## Explanation

n->{                             // int-accepting consumer
for(                            //  loop
int l=n*++n/2,                //    declare l (left) is the first character to print.
//              Oh, and n is increased to reduce byte count later.
r=l,                      //            r (right) is the second character to print.
i=1,                      //            i is the "outer-loop" index
j=0;                      //            j is the "inner-loop" index
l>0;                          //    while there are characters to print
j=j-->0?j:i++)                //    simulate two loops in one,
//      where j starts from 0 and always decreases until it reaches 0
//      at which point j is reset to i and i is increased
System.out.printf(             //   Print...
"%"+(n-i)+"c%"+(2*i-1)+"c%n", //    2 characters
//    - the first with n-i-1 whitespaces (remember, n was increased)
//    - the second characters with 2*i-2 whitespaces
--l%26+65,                    //    the first character to print is the left one, we decrease it.
r++%26+65                     //    the second character to print is the right one, we increase it.
);                             //
//  end loop
}                                // end consumer

# Clojure, 417 319 bytes

(defn cannon[n](let[a(map #(char(+ 65 %))(iterate #(if(> % 24)0(inc %))0))m1(reverse(reduce #(concat %(repeat %2(- n %2)))[](range 0(inc n))))p1(map-indexed #(str(apply str(repeat %2 " "))(nth a %))m1)m2(reverse(reduce #(concat %(repeat %2(-(* 2 %2)2)))[](reverse(range 0(inc n)))))p2(reverse(map-indexed #(str(apply str (repeat %2 " "))(nth a(+(count p1)%)))m2))](doseq[x(reverse(map #(str % %2)p1 p2))](println x))))

At some point I got tangled up in reverse calls and gave up on the idea to make it as short as possible. I just wanted to have a working solution. Here you go...

### Sort of ungolfed

(defn cannon [n]
(let [a (map #(char (+ 65 %)) (iterate #(if (> % 24) 0 (inc %)) 0))
m1 (reverse (reduce #(concat % (repeat %2 (- n %2))) [] (range 0 (inc n))))
p1 (map-indexed #(str (apply str (repeat %2 " ")) (nth a %)) m1)
m2 (reverse (reduce #(concat % (repeat %2 (- (* 2 %2) 2))) [] (reverse (range 0 (inc n)))))
p2 (reverse (map-indexed #(str (apply str (repeat %2 " ")) (nth a (+ (count p1) %))) m2))]
(doseq [x (reverse (map #(str % %2) p1 p2))] (println x))))

## Update

Motivated by Olivier's comment, I managed to cut multiple reverse calls and apply some general golfing tricks to cut characters. Also I created aliases for reverse, map-indexed, concat, repeat and str because I used them multiple times each.

(defn c[n](let[a(map #(char(+ 65 %))(iterate #(if(> % 24)0(inc %))0))k #(reduce %[](range 0(inc n)))r #(apply str(repeat % " "))rv reverse m map-indexed c concat t repeat s str p(m #(s(r %2)(nth a %))(rv(k #(c %(t %2(- n %2))))))](rv(map #(s % %2)p(rv(m #(s(r %2)(nth a(+(count p)%)))(k #(c %(t %2(-(* 2 %2)2))))))))))

### Ungolfed

(defn c [n]
(let [a (map
#(char (+ 65 %))
(iterate
#(if (> % 24) 0 (inc %))
0))
k #(reduce
%
[]
(range 0 (inc n)))
r #(apply str (repeat % " "))
rv reverse
m map-indexed
c concat
t repeat
s str
p (m
#(s
(r %2)
(nth a %))
(rv (k #(c % (t %2 (- n %2))))))]
(rv
(map
#(s % %2)
p
(rv
(m
#(s
(r %2)
(nth a (+ (count p) %)))
(k #(c % (t %2 (- (* 2 %2) 2))))))))))

Creates the function c which accepts the value n and returns a list of lines.

• This is not an answer as there is apparently no attempt to golf at all (you even say so). – Olivier Grégoire Feb 28 '18 at 14:16
• Okay, this is much better! ;-) – Olivier Grégoire Feb 28 '18 at 15:28

# Charcoal, 33 31 bytes

≔⁰ηＦ…±Ｎ⊕θ«¿ι→↓Ｆ↔ι«Ｐ§αη≦⊕η¿›ι⁰↓↑

Try it online! Link is to verbose version of code. Edit: Saved 2 bytes thanks to @ASCII-only. Explanation:

≔⁰η

Initialise the current letter as an index into the uppercase alphabet to 0.

Ｆ…±Ｎ⊕θ«

Make a loop from the negation of the input to the input inclusive.

¿ι→↓

Normally each column is to the right of the previous. However, there is no column for zero. Instead, a correction is needed to ensure that the left and right sides align.

Ｆ↔ι«

Loop for each letter in the column.

Ｐ§αη

Print the current letter.

≦⊕η

Increment the letter index.

¿›ι⁰↓↑

Move up or down depending on which side of the trajectory we're on.

• Seems like there could be a shorter way to do this but not sure how :/ – ASCII-only Feb 28 '18 at 10:52
• 31 bytes – ASCII-only Feb 28 '18 at 11:03

# Perl 5, -n 1129290 88 bytes

For once the terribly long printf seems to win.

#!/usr/bin/perl -n

Try it online!

# Yabasic, 125 bytes

An solution that uses graphics mode to print the characters at the correct column and row of the screen.

Input""n
Clear Screen
For i=-n To n
For j=1To Abs(i)
k=i>0
c=(c+1)MOD 26
NEXT j,i

# Python 3, 190 bytes

j,r,c,s=int(input()),range,[],[];a=(j+1)*j;b=a//2
for i in r(j):k=i+1;c.extend([j-k]*k)
for i in r(a):s+=chr(ord('A')+(i%26))
for i in r(b):print(' '*c[i]+s[b-i-1]+' '*(2*(j-c[i]-1))+s[b+i])

Try it online!

I tried my best. Let me know if any optimisations are possible.

# k4, 76 71 bytes

{+|:'p$(-k,|k:+\l)$(x#b),|:'x_b:(i:-1_0,+\l,|l)_a:(2*p:+/l:|1+!x)#.Q.a}

some rearranging+assignments to save 5 bytes

{+|:'(+/l)$(-k,|k:+\l)$(x#i_a),|:'((-x)#i:-1_0,+\l,|l)_a:(2*+/l:|1+!x)#.Q.a}

half-hour effort with some effort to shave off a few bytes, but there's probably much more that can be done here. will come back to it. fun challenge!