Circular Keyboard Spiral

Japt, 304 264 162 points

Saved 40 points thanks to @ConorO'Brien

;î"历锋㫿鮹㿬崴ꨜꎋΞ"csG

Test it online!

In order to save as many points as possible, the entire string is condensed into 9 Unicode chars by interpreting each run of 3 letters as a base-36 number, then converting to a code point. The program itself takes this compressed string (which costs 110 points, including the quotes) and maps each charcode by converting it to a string in base-36 (G after the ; at the beginning). î takes the first {input} chars of this, which is implicitly printed.


C, score: 544

g(g){write(1,"GHYTFVBNJURDCMKIESXLOWAZPQ",g);}

Try it online!


Spiral, score:  61921   5127   4715   4655  4191

4.X~>v+^#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v
   X *   X X X X X X X X X X X X X X X X X X X X X X X X X
   ! >   h y t f V b n j u [ ( 1 3 2 ) ] U J N B F T Y H G
0;vgv*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*vvv****v+^v+^v+^v+^*v++4
    X X X X X X X X X X X X X X X X X X X X X X X X X X
    v v v v v v v v v v v v v v v v v v v v v v v v v v
    Y y J F V u t U [ G H B n 3 N 2 j ) h g f ] ( 1 b T

An interpreter can be found here.

Explanation:

P.X~ZZ*v+^#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v
   X       X X X X X X X X X X X X X X X X X X X X X X X X X
   !       h y t f V b n j u r d c m k i e s x l o w a z p q
0;vgv*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*v*****************************************************************vP
    X X X X X X X X X X X X X X X X X X X X X X X X X X
    v v v v v v v v v v v v v v v v v v v v v v v v v v
    z y x w V u t s r q p o n m l k j i h g f e d c b a

The program begins at the 0 character on the fourth line. The first piece of code to run is 0;vg. ; takes a number as input and places it in the stack. v places what is in the register (a zero) into the stack. It will be used as the counter. g is a label, when it is reached, the control jumps to the other occurence of the letter g in the code.

So this is where the control is now:

X
v
g

Since there is whitespace in all other directions, the instruction pointer starts moving upwards. v places a zero into the stack, and X pops it from the stack immediately. Because the popped value is zero, the instruction pointer moves to X (otherwise it would treat it as whitespace).

By default, the control flow is in turn-right mode, so now when it reaches the junction, the instruction pointer turns to the right. v yet again pushes a zero into the stack, * increments the register by one.

v*v*v
  X
  v
  g

The next v places what is in the register (number 1) into the stack, the instruction pointer attempts to turn to the right, hitting the next X. The value just added to the stack is popped and placed in the register. Because it is non-zero, X is not entered, and the IP proceeds to the next * on the right instead, again incrementing the value in the register.

v*v*v*v*v
X X X X X
v v v v v
i h g f e

This happens again and again until we reach the end of this part and the line of *s begins. By now the value in the register is 6, which is ASCII letter g minus ASCII letter a. Thus with a line of 97 *s we increment the value in the register to 103, which matches the letter g we want to print. v pushes it into the stack, and P is another label upon hitting which we jump to the other P on the first line of the code.

Here . pops the value from the stack and prints it as a character. After that X pops the extraneous zero from the stack, then ~ compares the two remaining values in the stack (the values being the counter and the input value). If the values are the same, the operator places zero in the stack (otherwise -1 or 1). Again, the control attempts to turn right. X pops the value of the comparison from the stack, if it is zero, X, and after it ! is entered, terminating the program.

P.X~ZZ*v+^
   X
   !

Otherwise the IP continues to the Z, which is a label which in this case jumps only one step to the right. The reason for doing this is that jumping sets the value in the register back to zero. * increments the register and v places the resulting 1 into the stack. + pops the two top elements of the stack (the 1 and the counter), adds them, and places the result in the stack (in effect this increments the counter by one). ^ copies the result from the stack to the register without removing it from the stack.

# decrements the value in the register by one, v pushes the decremented value to the stack, the IP attempts to turn to the right, and the X pops the value from the stack. If the value is non-zero the IP keeps moving to the east, decrementing the value in the register, until it hits zero, and the IP enters an X branch.

#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v#v
 X X X X X X X X X X X X X X X X X X X X X X X X X
 h y t f V b n j u r d c m k i e s x l o w a z p q

The branch leads to a label corresponding to the value of the counter. Upon hitting the label, the control jumps to the other occurence of the label in the section where we started with the label g, starting another iteration. As with the g, the value in the register gets incremented up to the ASCII value of the letter we need to print. Then the character is printed and the counter incremented, another label is selected. This happens until after the last iteration the counter equals the input, and the program terminates.

Edit:

P.X~Zv+^
   X *
   ! Z

Achieves the same thing as

P.X~ZZ*v+^
   X
   !

but with less whitespace.

Edit 2:

vv****v+^v+^v+^v+^*v++P

Can be used instead of:

*****************************************************************vP