34
\$\begingroup\$

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
\$\endgroup\$
  • 7
    \$\begingroup\$ Closely related. \$\endgroup\$ – Dom Hastings Feb 28 '18 at 9:07
  • 2
    \$\begingroup\$ 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. \$\endgroup\$ – Skyler Feb 28 '18 at 15:09
  • 2
    \$\begingroup\$ @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. \$\endgroup\$ – Olivier Grégoire Feb 28 '18 at 15:23
  • 4
    \$\begingroup\$ Looks like alphabet cannons are now canon. \$\endgroup\$ – Carl Witthoft Feb 28 '18 at 16:22
  • 2
    \$\begingroup\$ @ngn Hah, I was tinkering with @TonHospel's Perl solution and came up with 1 byte less, but it only supports up to 14! \$\endgroup\$ – Dom Hastings Feb 28 '18 at 20:30

20 Answers 20

8
\$\begingroup\$

05AB1E, 33 32 29 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
\$\endgroup\$
  • \$\begingroup\$ I feel like Nú» or something like that could be used to print as you go instead of ])~.c \$\endgroup\$ – Magic Octopus Urn Feb 28 '18 at 18:07
  • \$\begingroup\$ All I could come up with is this implementation here but that's worse by 2 bytes. \$\endgroup\$ – Magic Octopus Urn Feb 28 '18 at 18:08
8
\$\begingroup\$

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
\$\endgroup\$
7
\$\begingroup\$

R, 169 163 161 153 150 110 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))
Map(function(x,y,z)a[x,y]<<-z,rep(1:M,c(n:1,1:n)),c(k:1,1:k),head(LETTERS,2*k))
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)

enter image description here

\$\endgroup\$
  • \$\begingroup\$ 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! \$\endgroup\$ – Giuseppe Feb 28 '18 at 18:30
  • \$\begingroup\$ can you use Map instead of mapply? \$\endgroup\$ – JDL Mar 1 '18 at 14:41
  • \$\begingroup\$ @JDL You are right. I always think that Map is a wrapper for lapply instead of mapply. Thanks for 150 \$\endgroup\$ – Vlo Mar 1 '18 at 15:14
  • \$\begingroup\$ 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! \$\endgroup\$ – Giuseppe Mar 1 '18 at 16:07
  • \$\begingroup\$ @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/… \$\endgroup\$ – Vlo Mar 1 '18 at 16:27
6
\$\begingroup\$

Python 2, 140 135 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!

\$\endgroup\$
5
\$\begingroup\$

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
\$\endgroup\$
4
\$\begingroup\$

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
\$\endgroup\$
3
\$\begingroup\$

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);
}
\$\endgroup\$
  • \$\begingroup\$ interesting, I can't compile this (there's no main) but TIO can \$\endgroup\$ – ngn Feb 28 '18 at 13:41
  • 1
    \$\begingroup\$ @ngn It's just a function, you need to add the main to compile it. On TIO, the main is in the footer section. \$\endgroup\$ – Steadybox Feb 28 '18 at 13:44
3
\$\begingroup\$

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.

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

Charcoal, 33 31 bytes

≔⁰ηF…±N⊕θ«¿ι→↓F↔ι«P§αη≦⊕η¿›ι⁰↓↑

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.

F…±N⊕θ«

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.

F↔ι«

Loop for each letter in the column.

P§αη

Print the current letter.

≦⊕η

Increment the letter index.

¿›ι⁰↓↑

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

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

Perl 5, -n 112 92 90 88 bytes

For once the terribly long printf seems to win.

#!/usr/bin/perl -n
$p=$q=$_*($%=$_+1)/2;map{printf"%$%c%$.c
",--$p%26+65,$q++%26+65for--$%..$';$.+=2}//..$_

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Nice improvement! I was trying to get (A..Z)x9 to work, but it was just too short of the limit! Had that for 91 only. :) \$\endgroup\$ – Dom Hastings Feb 28 '18 at 21:06
  • 1
    \$\begingroup\$ @DomHastings Yours was a nice try at synergy between the two almost repeated letter calculations. That one irks me too. \$\endgroup\$ – Ton Hospel Feb 28 '18 at 21:11
2
\$\begingroup\$

Python3 + numpy, 124 115

from pylab import*
def i(N):
 x=zeros((N,2*N),'U');x[r_[N-1:-1:-1,0:N],r_[:2*N]]=map(chr,r_[0:2*N]%26+65)
 return x

This creates an appropriately sized array, finds the indices for the trajectory and assigns the appropriate character to them. The most complex part is generating the characters A-Z, which relies on a very hackish cast of numbers to a string type. The returned object is a unicode array.

Edit: Saved 9 bytes replacing numpy code that generated the characters A-Z ((r_[0:2*N]%26+65).view('U1')[::2]) with map, as suggested here.

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

Python 3, 139 136 bytes

f=lambda n,o=0:n and'\n'.join([f(n-1,o+n).replace('\n','\n ')]+[chr(65+(n+o+~i)%26)+'  '*~-n+chr(65+(n*n+o+i)%26)for i in range(n)])or''

Try it online!

Generates each layer recursively, given the size and offset.

-3 bytes thanks to Jo King

\$\endgroup\$
  • \$\begingroup\$ @JoKing Thanks, I always forget about the ~ operator! \$\endgroup\$ – Matthew Jensen Oct 3 at 2:33
  • \$\begingroup\$ You can also change n and ... or'' to n*' 'and ... for another byte \$\endgroup\$ – Jo King Oct 3 at 3:16
2
\$\begingroup\$

J, 78 75 bytes

(26{.65|.a.)($~#)`(;/@])`(' '$~1+{:@])}i.@+:(,.~(|.,])@i.@-:@#)@#~1+i.@-,i.

Try it online!

-3 thanks to ngn

\$\endgroup\$
  • 1
    \$\begingroup\$ (,|.)@i.@- -> i.@-,i. \$\endgroup\$ – ngn Oct 7 at 15:27
  • \$\begingroup\$ Thanks @ngn. This is one of those where it felt like there should be a solution in 40-50 bytes, but if there is I wasn't able to see it.... \$\endgroup\$ – Jonah Oct 7 at 15:32
1
\$\begingroup\$

Python 2, 182 bytes

I=input()
S=list('ZYXWVUTSRQPONMLKJIHGFEDCBA'*I)
R=range
print zip(*[(' '*(sum(R(abs(i))))+eval('S.pop()+'*abs(i)+"''")[::[-1,1][i>0]]).ljust(sum(range(I+1)))for i in R(-I,I+1)if i])

Try it online!

Returns list of lists of chars. Primitive verification here

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

Jelly, 35 bytes

ṪṚṭ
r1m0RØAṁs⁸U2¦Ç⁶ṁ$;¥\€ÇzZ¥€⁶ẎUZY

Try it online!

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

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
?@(i+n-k,(i^2-i)/2+j-2*j^(!k)+k)Chr$(c+65)
c=Mod(c+1,26)
Next
Next

Because this solution uses graphics mode, it cannot be executed on TIO.

Output

Below is the output for input 7

Program Output (n=7)

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

Ruby, 106 103 bytes

->n,f=2*s=-~n*n/2-1{l=*?A..?Z;(1..n).map{|i|i.times{puts' '*(n-i)+l[(f-s)%26]+' '*~-i*2+l[(s+=1)%26]}}}

Try it online!

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

QBasic 1.1, 124 bytes

Takes input and shoots a cannon. Due to screen size limitations, \$n\$ must be \$\leq 6\$.

INPUT n
CLS
FOR i=-n TO n
FOR j=1TO ABS(i)
k=i>0
LOCATE(i^2-i)/2+j-2*j^-(k=0)-k+1,i+n+k+1
?CHR$(c+65)
c=(c+1)MOD 26
NEXT j,i
\$\endgroup\$
1
\$\begingroup\$

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.

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

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!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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