Jelly, 42 38 37 bytes
="⁵Ḣ€Ṗ1;ṙ@"µɼ⁶ṭØAiịɗƒµ®Ṛi@ịØAɗƒ
ḷ⁹©Ç€
Try it online!
Verify each test case
This is extremely liberal with the input format. It takes input in the following way:
- The three rotors (the strings, not the numbers) as the first command line argument, each rotated so that the starting points are the first characters
- The message as the second command line argument
- The 3 specific notches as the third command line argument
- The reflector as the fourth command line argument
In the above TIO link, the Footer converts a triple of numbers (the rotors) into the rotor strings, then rotates them to the correct order, so that you don't have to do it yourself.
However, for 45 bytes we can have a program which takes input in the following way:
- The message, on STDIN
- The three rotors (as the strings) as the first command line argument
- The 3 specific notches as the second command line argument
- The reflector as the third command line argument
- The three starting points for the rotors as the fourth command line argument
Again, the Footer converts the rotors from integers to the corresponding strings.
Finally, if we want to go all the way and just accept 3 inputs (the rotors, as integers, the starting positions and the message (thus have -95 to our score)), we get a score of 44
How it works
As they all use the same underlying algorithm, I'll just explain the 38 byte version in full, and I'll cover how the other two versions adapt the inputs to match the shortest version. For this explanation: M
is the message, R
are the three rotor strings, N
are the notches, F
is the reflector and C
is the value in the register (initially 0).
ḷ⁹©Ç€ - Main link. Takes M on the left and R on the right
⁹ - Yield R
© - And copy it into the register. C = R
ḷ - Discard R and yield M
€ - Over each character in M:
Ç - Call the helper link
Implicitly output the final result
="⁵Ḣ€Ṗ1;ṙ@"µɼ⁶ṭØAiịɗƒµ®Ṛi@ịØAɗƒ - Helper link. Takes a character X on the left
="⁵Ḣ€Ṗ1;ṙ@"µɼ - Rotate the rotors
ɼ - Yield C, run the following on it, save the result to C:
µ - Initially, C = R, the list of rotors
⁵ - Yield the notches, N
" - Pair each notch with each rotor, then:
= - Vectorised equality
Ḣ€ - Take the head of each
This yields a triple of bits [a, b, c]
which indicate which rotor hits its notch
Ṗ - Remove c as rotor 3 has no affect
1; - Prepend 1 as rotor 1 always moves; [1, a, b]
" - Pair 1 with rotor 1, a with rotor 2 and b with rotor 3,
then for each pair:
ṙ@ - Rotate the rotor if the bit is 1
⁶ṭØAiịɗƒ - Send the character through the rotors
⁶ - Yield the reflector F
ṭ - Tack it to the end of the rotors, call this C'
ɗ - Group the previous 3 links into a dyad f(U, L):
where U is a character and L a list of characters
ØA - "ABC...XYZ"
i - Index of U in the alphabet
ị - Index into L
ƒ - Starting with X, reduce C' by the dyad f(U, L)
As C is a triple of lists of characters, this returns
A = f(f(f(f(X, C[1]), C[2]), C[3]), F), a single character
µ®Ṛi@ịØAɗƒ - Send A back through the rotors
µ - Begin a new link with A as the argument
ɗ - Group the previous 3 links into a dyad g(U, L)
i@ - Index of U in L, i
ịØA - i'th letter of the alphabet
® - Yield C
Ṛ - Reverse C
ƒ - Starting with A, reduce rev(C) by the dyad g(U, L)
This yields g(g(g(A, C[3]), C[2]), C[1]),
which is our intended result
How the other 2 work
The 45 byte version
Taking a look at the code, we notice that the only thing that's changed is that the last line is now
ṙ"©⁶O_65¤ṛɠÇ€
The difference between this version and the 37 byte version is how the rotors are taken as input:
The three rotors (the strings, not the numbers) as the first command line argument, each rotated so that the starting points are the first characters
The three rotors (as the strings) as the first command line argument
The new bits here just rotate the rotors for us and take M
from STDIN:
ṙ"©⁶O_65¤ṛɠÇ€ - Main link. Takes R on the left
¤ - Create a nilad:
⁶ - The 3 starting points of the rotors
O - Converted to char points
_65 - Minus 65
" - Zip with each rotor:
ṙ - Rotate the rotor that many steps
© - Save this to the register
ɠ - Read M from STDIN
ṛ - Discard the rotors and yield M
Ç€ - Call the helper link on each character of M
The 139 byte version
Let's take a look at the full code:
="³ị“QEVJZ”¤Ḣ€Ṗ1;ṙ@"µɼ“¡ƝḋœṚṗḶw⁸Aß`’œ?ØA¤ṭØAiịɗƒµ®Ṛi@ịØAɗƒ
ị“F.⁻wṣḊ£tọḅɱ“¥AṡḌỴịk⁼UH9“Ñɱ½#Ẋʋẹ¹⁼UṢ“KzBpñÇḅẊẎḳḣ“¡j5ɼ}ṡỴb\£BṆ’œ?ØAṙ"©⁴O_65¤ṛɠÇ€
Try it online!
This is very similar to our 45 byte version. In fact, here's what's different in the first lines, with everything that is unchanged, replaced with .
..³ị“QEVJZ”¤..........“¡ƝḋœṚṗḶw⁸Aß`’œ?ØA¤.................
Furthermore, you'll note that the end of the first line is ṙ"©⁴O_65¤ṛɠÇ€
, which is the first line of the 45 byte version, meaning that there are really only three changes. In this case, those changes are because of the changes to the input system. Here's the first 2:
⁴
\$\to\$ ³ị“QEVJZ”¤
. Rather than take the notches as input, we use the rotor list (³
) to ị
ndex into the string of notches “QEVJZ”
. The ¤
is simply a precedence marker, telling the program to treat this as a constant
⁵
\$\to\$ “¡ƝḋœṚṗḶw⁸Aß`’œ?ØA¤
. Again, this is changing an input into program data, the reflector in this case. ¤
Once again acts as a precedence marker. However, we have 3 new commands here:
œ?
is a dyad which takes 2 arguments - on the left, an integer x
, and on the right a string, s
- and returns the x
'th permutation of s
ØA
we've already encountered, and is the uppercase alphabet. In this context, it acts as the right argument to œ?
“¡ƝḋœṚṗḶw⁸Aß`’
is the compressed integer \$383316524290458478707255597\$, which is the left argument to œ?
Together, this returns the \$383316524290458478707255597\$th permutation of the uppercase alphabet, or the string "YRUHQSLDPXNGOKMIEBFZCWVJAT"
The final change is the entire start of the last line:
ị“F.⁻wṣḊ£tọḅɱ“¥AṡḌỴịk⁼UH9“Ñɱ½#Ẋʋẹ¹⁼UṢ“KzBpñÇḅẊẎḳḣ“¡j5ɼ}ṡỴb\£BṆ’œ?ØA
Here, we have a list of massive compressed numbers:
“F.⁻wṣḊ£tọḅɱ“¥AṡḌỴịk⁼UH9“Ñɱ½#Ẋʋẹ¹⁼UṢ“KzBpñÇḅẊẎḳḣ“¡j5ɼ}ṡỴb\£BṆ’
Each “
marks the beginning of a new compressed number, and the entire list is
[67892310845892591685803413, 5023890671354916271018308, 16834429128340685633834184, 72949485053238139997554738, 340670280577577536609579431]
We then take the three integers which correspond to the three rotor inputs with ị
, and then use œ?ØA
to generate the alphabet permutations we want for each rotor. Finally, these permutations are fed into the rest of the program, already covered above.