Challenge 10B
A Tale of 2 Secrets › Forums › T.E.M.P.E.S.T. › Challenge 10B
- This topic has 274 replies, 92 voices, and was last updated 4 weeks ago by _madness_.
-
AuthorPosts
-
21st January 2026 at 7:49 pm #115248Crackerjack_404Participant
@Harry are bonus points awarded for submitted code in binary from now on? If so, I’ll be back shortly with a self-modifying Turing machine encoded in whitespace…
As for comments, they are trivial and left as an exercise to the reader.
21st January 2026 at 10:08 pm #115249Crackerjack_404ParticipantI’ll admit to feeling extremely smug after reading that, @madness. I’ll treasure that comment just as much as my Strava kudos… possibly more, given the lack of hills 😀
21st January 2026 at 10:09 pm #115250upsidedownParticipant@Crackerjack_404 you have inspired me to golf this further. This is a 224-character (excluding ciphertext) Python program which prints the decryption of 10B.
b=lambda x:zip(x[::2],x[1::2]);print("".join("SHADOWBCEFGIKLMNPQRTUVXY"[y//4*6-54+x%6]+"FULHEARTBCDGIKMNOPQSVWXY"[x//6*4+y%4]for x,y in b([13*s+c for c,s in b(["A23456789XJQKCDHS".index(x)%13 for x in "7CXS3 H6S7C KSXDA S2CKS ... and so on" if x.isalnum()])])))21st January 2026 at 11:02 pm #115251_madness_Participant@F6EXB_the_frenchy,
Harry censored your program. Maybe resubmit it?
It may sound like I’ve already seen it, but no one else has.21st January 2026 at 11:05 pm #115252_madness_ParticipantSomeone should get some alphabet cards and make a video about the 10B cipher, you know, for the upcoming NCC Youtube channel.
You ready for your closeup, Madness? Harry
22nd January 2026 at 9:51 am #115254Crackerjack_404ParticipantJust me or are the previews not loading?
Not just you. I left them in so madness would see and maybe edit. Harry
22nd January 2026 at 10:57 pm #115299Puzzling_PelicanParticipant@upsidedown
After a bit more golfing, here is a version in 189 characters (excluding ciphertext).
I am curious to see it compressed further, the .replace seems like such a waste, same with %13 being repeated.print(*["SHADOWBCEFGIKLMNPQRTUVXY"[(y:=c+d%13*13)//4*6+(x:=a+b%13*13)%6-54]+"FULHEARTBCDGIKMNOPQSVWXY"[x//6*4+y%4]for a,b,c,d in zip(*[map("A23456789XJQKCDHS".find,"7CXS3 H6S7C KSXDA S2CKS".replace(" ",""))]*4)])23rd January 2026 at 8:07 pm #115302upsidedownParticipant@Puzzling_Pelican your zip(*[map(…)*4]) construction is a neat trick. I feel like there may be a way to compress the strings “SHADOW..” “FULHEART…” “A234…” given their structure, e.g. with b”” strings, but I’ve not found anything concise enough.
25th January 2026 at 7:41 pm #115305ByteInBitsParticipantHelp I’m not too good with python and
I cannot get the code given by @Puzzling_Pelican to run (gives a syntax error)
all others : @_madness_ , @Crackerjack_404 and @upsidedown codes run ok
anyone else have this problem?27th January 2026 at 11:48 am #115308Puzzling_PelicanParticipant@ByteInBits
Are you by any chance using an older version of Python (below 3.8)? If so, your Python will not like my use of the walrus operator := and would throw a syntax error. It wasn’t until version 3.8 that Python overcame its Odovainophobia. Other than that, it should run and correctly decrypt 10B.27th January 2026 at 11:51 am #115311Crackerjack_404Participant@ByteInBits
@Puzzling_Pelican’s code seems to work fine for me (and I will agree it is far more compressed than the version I came up with!)
I’d just make sure that you haven’t accidentally added letters when copying the code in from challenge 10B, becuase I did that in my first run which gave me a syntax error.
27th January 2026 at 4:29 pm #115314ByteInBitsParticipant@Puzzling_Pelican after my post I had a sneeking suspicion that I would need
the latest version of python, my version is 3.7.5 so when I get time to
upgrade I will try again.Thanks so much for answering and that goes to @Crackerjack_404 as well.
This is the reason I always (try to remember) to give the version info with my gp code
9th February 2026 at 12:13 pm #115344AES_of_spadesParticipantOkay, I am admiring all your clevernesses, and would REALLY like to know how your childhoods looked.
Can you assist me in learning Python up to this level? Because I did the GCSE, I got my grade 9, but I still can’t make heads or tails of YOUR code.
What sites/books are you guys learning from?11th February 2026 at 10:45 am #115346upsidedownParticipant@AES_of_spades, as @Puzzling_Pelican is the undisputed winner, I’ll explain their code. It uses a few Python-specific things:
0. An iterable is roughly an object that has a defined iteration order over some values. Or, anything you can iterate over in a for loop. Examples: [1,2,3] or (1,2,3) or range(42) or “iterable”
1. List range syntax: list[start:stop:step] extracts the part of the list between start & stop, in steps of step. list[::-1] reverses the list.
2. The spread operator (*), is applied to an iterable and spreads out its values as multiple (function) arguments. It can also be used in lists (and possibly other places).
x = [1,2,3] print(*x) # same as print(1, 2, 3) print(*x, *x) # same as print(1, 2, 3, 1, 2, 3) y = [2, *x, 3] # same as y = [2, 1, 2, 3, 3] def cool_function(thing, *args): print(thing) # <-- first argument, mandatory print("variable args", args) # <-- 0 or more optional arguments cool_function("one", 2, "three", 4)3. Not used in @Puzzling_Pelican’s program (because it’s quite expensive in characters), lambda defines an anonymous function.
def double(x): return x * 2 double_anom = lambda x: x * 2 assert(double(4) == double_anom(4)) add = lambda x, y: x + y # <-- anonymous function which takes two arguments print(add(1, 2))In Python, functions are first-class: they can be stored in variables (or lists and other data structures), passed as arguments to other functions, and returned from functions.
4. map(function, iterable) and zip(*iterables) are built-in functions. map() applies a function to each element in an iterable, producing a generator. zip() takes a variable number of iterables and groups the first element of each iterable, then the second, and so on, in tuples (parenthesised lists).
x = list(map(lambda x: x*2, [1,2,3])) # <-- x = [2,4,6] y = list(zip(x, x[::-1])) # <-- y = [(2,6), (4,4), (6,2)]5. List comprehension lets you concisely manipulate the contents of an iterable (such as a list) and convert it into a list, dictionary or generator.
x = [1,2,3] y = (y*2 for y in x) # <-- list comprehension that doubles each element in x, producing a generator y = [y*2 for y in x] # <-- same thing, but producing a list z = {y:y*2 for y in x} # <-- similar thing, but producing a dictionary q = [u + v for u, v in zip(x, x[::-1])] # <-- with two bindings: u & v6. The walrus operator, :=, allows you to assign a value and then use it within the same line. The assignment expression (x := value) evaluates to the value as well. So print((x := 1) * 2 + x) gives you 3.
7. Semicolons can be used to separate multiple statements on the same line. x=1;print(x);print(x*2).
This should be everything needed to understand @Puzzling_Pelican’s program. I’m going to break it down into a few subexpressions:
ALPHABET0 = "A23456789XJQKCDHS" CIPHERTEXT = "7CXS3 H6S7C KSXDA S2CKS" MAP_EXPR = map(ALPHABET0.find, CIPHERTEXT.replace(" ","")) ZIP_EXPR = zip(*[MAP_EXPR]*4) ALPHABET1 = "SHADOWBCEFGIKLMNPQRTUVXY" ALPHABET2 = "FULHEARTBCDGIKMNOPQSVWXY" PLAINTEXT_EXPR = [ALPHABET1[(y:=c+d%13*13)//4*6+(x:=a+b%13*13)%6-54] + ALPHABET2[x//6*4+y%4] for a,b,c,d in ZIP_EXPR] print(*PLAINTEXT_EXPR)1. MAP_EXPR: we are applying the ALPHABET0.find function (basically the same as lambda x: ALPHABET0.index(x)) to each element in CIPHERTEXT (with spaces removed). This is later taken mod 13 (%13) to convert the suit & number to integers.
2. ZIP_EXPR: this is a subtle trick. The list [MAP_EXPR] is multiplied by 4, which creates a list of 4 elements containing 4 MAP_EXPR values. These elements are not duplicates; they all refer to the same value. The spread operator is then used so zip() gets 4 arguments which all refer to MAP_EXPR. Equivalently written as zip(MAP_EXPR, MAP_EXPR, MAP_EXPR, MAP_EXPR). zip() essentially works by calling the built-in function next() on each iterable sequentially, in a loop. Because all the arguments reference the same thing, this has the effect of collecting 4 sequential input elements (from MAP_EXPR) into a tuple, as each value of ZIP_EXPR. So if MAP_EXPR = iter((1,2,3,4,5,6,7,8)), list(ZIP_EXPR) = [(1,2,3,4), (5,6,7,8)].
3. PLAINTEXT_EXPR: this uses a list comprehension, binding the ciphertext cards in every group of 4 from ZIP_EXPR to a, b, c & d. b and d are taken mod 13 (%13) to give suit numbers between 0 and 3. a & c are card numbers between 0 and 12. The walrus operator is used to introduce x and y, which map the first and second card in the pair to the 0 to 51 range (there are 52 cards). x & y are then combined to index into the two cipher alphabets.
4. print(*PLAINTEXT_EXPR): this uses the spread operator to provide print() with one argument for each element of PLAINTEXT_EXPR (a list of strings). print() automatically inserts spaces between each argument and outputs them on the same line, so this just helps to cut a few more characters compared to print(“”.join(PLAINTEXT_EXPR)).See if you can figure out how this works:
q=lambda c:[*{k:0 for k in[*c,*range(65,74),*range(75,90)]}.keys()] print(*map(chr, q(b"SHADOW")))11th February 2026 at 10:46 am #115349ThatStrangeDinoParticipantI code in python so these are the sites and places I look for help (sorry all C and C# people): In terms of general coding questions or problems I would recommend Stack Overflow (basically Reddit for coding), GeeksForGeeks (good explanations of functions and syntax and W3schools (similar to GeeksForGeeks). In terms of cipher challenge specific I would definitely recommend ‘A book on classical cryptography’ by our dear madness but that does require a decent prior understanding of concepts such as File Handling and dictionaries. I would not advise using AI for your code not only as it’s against the rules of the challenge but it’s not all that good at recognizing its own mistakes and the chances are you will have to spend hours debugging the code it gave you anyway. Hope I could help.
-
AuthorPosts
- You must be logged in to reply to this topic.