Alphabet cannon

34

5

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

ngn

Posted 2018-02-28T08:10:42.990

Reputation: 11 449

7Closely related. – Dom Hastings – 2018-02-28T09:07:34.973

2Why 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 – 2018-02-28T15:09:06.857

2@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 – 2018-02-28T15:23:11.503

@Skyler that depends on where you imagine the ball - in the centre of each letter, or moving from the bottom-left to the top-right point of each vertical block of letters (and later top-left to bottom-right); in either case, I admit there's a bit of discontinuity at the apex, which I ignored for the sake of keeping it easy to golf – ngn – 2018-02-28T15:48:07.633

4Looks like alphabet cannons are now canon. – Carl Witthoft – 2018-02-28T16:22:58.580

What is the maximum n that we need to support? – Dom Hastings – 2018-02-28T20:04:43.533

1@DomHastings if it makes any difference for your code: 15 (strictly less than an arbitrary not-too-high power of 2 that I just made up) – ngn – 2018-02-28T20:19:26.297

2

@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 – 2018-02-28T20:30:14.913

@DomHastings what!? ...diffing and trying to figure out how 14 is special... So, is (A..Z)x9 supposed to return nine copies of the alphabet? I'm not very familiar with Perl. When I tried it, it returned one copy. – ngn – 2018-02-28T20:41:58.110

Yeah, if you do print((A..Z)x9) you'll get 9 copies. Otherwise it just replicates the result of calling print 9 times. I accept 15 as the maximum though. I'll try and think about other ways to reduce byte count instead! Thanks! – Dom Hastings – 2018-02-28T20:50:13.327

1@DomHastings silly me... parentheses, of couse. I don't mind lowering the limit, but I think your improvement for n<=14 is still interesting out-of-the-box thinking and would be appreciated as-is :) – ngn – 2018-02-28T20:57:34.970

Answers

8

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

Emigna

Posted 2018-02-28T08:10:42.990

Reputation: 50 798

I feel like Nú» or something like that could be used to print as you go instead of ])~.c – Magic Octopus Urn – 2018-02-28T18:07:13.797

All I could come up with is this implementation here but that's worse by 2 bytes.

– Magic Octopus Urn – 2018-02-28T18:08:53.893

8

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

recursive

Posted 2018-02-28T08:10:42.990

Reputation: 8 616

7

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

Vlo

Posted 2018-02-28T08:10:42.990

Reputation: 806

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 – 2018-02-28T18:30:54.680

can you use Map instead of mapply? – JDL – 2018-03-01T14:41:54.207

@JDL You are right. I always think that Map is a wrapper for lapply instead of mapply. Thanks for 150 – Vlo – 2018-03-01T15:14:37.403

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 – 2018-03-01T16:07:15.963

@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: https://tio.run/##K/qfpmCj@z@tNC@5JDM/TyNPs7wosyRVIyHaRjdBIzexpCizQkNJQUnH10bXSCtPJ9tGt7g0V8PQKk9TUyc5KTMvRaMotQDI99VJ1sizMtSBymhkg9nZQLaPa0iIa1Cwpo4S0BQdIKn5P03DVPM/AA

– Vlo – 2018-03-01T16:27:34.980

@Vlo nicely done! only 10 bytes to go to get this below 100 :) – Giuseppe – 2018-03-01T16:37:19.663

@Giuseppe If plotting is not against the rules then 73 is possible function(n,k=sum(1:n))plot(rep(1:(2*n),c(n:1,1:n)),c(1:k,k:1),pc=LETTERS) – Vlo – 2018-03-01T16:52:13.293

6

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!

TFeld

Posted 2018-02-28T08:10:42.990

Reputation: 19 246

5

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

Luis Mendo

Posted 2018-02-28T08:10:42.990

Reputation: 87 464

4

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

Steadybox

Posted 2018-02-28T08:10:42.990

Reputation: 15 798

interesting, I can't compile this (there's no main) but TIO can – ngn – 2018-02-28T13:41:17.860

1

@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 – 2018-02-28T13:44:23.353

4

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

Olivier Grégoire

Posted 2018-02-28T08:10:42.990

Reputation: 10 647

3

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.

Neil

Posted 2018-02-28T08:10:42.990

Reputation: 95 035

Seems like there could be a shorter way to do this but not sure how :/ – ASCII-only – 2018-02-28T10:52:39.760

431 bytes – ASCII-only – 2018-02-28T11:03:42.443

3

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.

Joshua

Posted 2018-02-28T08:10:42.990

Reputation: 231

This is not an answer as there is apparently no attempt to golf at all (you even say so). – Olivier Grégoire – 2018-02-28T14:16:53.330

Okay, this is much better! ;-) – Olivier Grégoire – 2018-02-28T15:28:20.780

3

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!

Ton Hospel

Posted 2018-02-28T08:10:42.990

Reputation: 14 114

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. :) – Dom Hastings – 2018-02-28T21:06:34.777

1@DomHastings Yours was a nice try at synergy between the two almost repeated letter calculations. That one irks me too. – Ton Hospel – 2018-02-28T21:11:26.407

2

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.

user2699

Posted 2018-02-28T08:10:42.990

Reputation: 538

2

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

Matthew Jensen

Posted 2018-02-28T08:10:42.990

Reputation: 713

@JoKing Thanks, I always forget about the ~ operator! – Matthew Jensen – 2019-10-03T02:33:18.513

You can also change n and ... or'' to n*' 'and ... for another byte

– Jo King – 2019-10-03T03:16:10.240

2

J, 78 75 bytes

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

Try it online!

-3 thanks to ngn

Jonah

Posted 2018-02-28T08:10:42.990

Reputation: 8 729

1(,|.)@i.@- -> i.@-,i. – ngn – 2019-10-07T15:27:03.063

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.... – Jonah – 2019-10-07T15:32:45.903

1

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

Dead Possum

Posted 2018-02-28T08:10:42.990

Reputation: 3 256

1

Jelly, 35 bytes

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

Try it online!

Erik the Outgolfer

Posted 2018-02-28T08:10:42.990

Reputation: 38 134

1

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!

Asone Tuhid

Posted 2018-02-28T08:10:42.990

Reputation: 1 944

1

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)

Taylor Scott

Posted 2018-02-28T08:10:42.990

Reputation: 6 709

1

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

Taylor Scott

Posted 2018-02-28T08:10:42.990

Reputation: 6 709

1

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.

Koishore Roy

Posted 2018-02-28T08:10:42.990

Reputation: 1 144

1

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!

scrawl

Posted 2018-02-28T08:10:42.990

Reputation: 1 079