Skip to main content
The National Cipher Challenge

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_.
Viewing 15 posts - 256 through 270 (of 277 total)
  • Author
    Posts
  • #115248
    Crackerjack_404
    Participant

    @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.

    #115249
    Crackerjack_404
    Participant

    I’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 😀

    #115250
    upsidedown
    Participant

    @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()])])))
    
    #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.

    #115252
    _madness_
    Participant

    Someone 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

    #115254
    Crackerjack_404
    Participant

    Just me or are the previews not loading?

    Not just you. I left them in so madness would see and maybe edit. Harry

    #115299
    Puzzling_Pelican
    Participant

    @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)])
    
    #115302
    upsidedown
    Participant

    @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.

    #115305
    ByteInBits
    Participant

    Help 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?

    #115308
    Puzzling_Pelican
    Participant

    @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.

    #115311
    Crackerjack_404
    Participant

    @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.

    #115314
    ByteInBits
    Participant

    @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

    #115344
    AES_of_spades
    Participant

    Okay, 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?

    #115346
    upsidedown
    Participant

    @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 & v
    

    6. 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")))
    
    #115349
    ThatStrangeDino
    Participant

    I 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.

Viewing 15 posts - 256 through 270 (of 277 total)
  • You must be logged in to reply to this topic.
Report a problem