Help understanding code

Hello everyone.
So unfortunately even though I’ve been studying python here for quite some time, I am missing the fundamentals of problem solving with all of the material I’ve been given.
This time, I got stuck in a Ceasers Chiper challenge. I got lost almost immediately on the first question and when I did try to do something I just got everything wrong.

Looking at the solution did not help a lot since I do not understand the thinking behind it.

I was hoping that someone could translate this code to a “explain it to me like I’m a child” kind of translation and maybe the thought behind each line of code, so hopefully I can start tapping into problem solving thinking.

The challange was that someone sent me an encrypted message. It’s encrypted in Caeser’s Chiper.
The offset of the letters is + 10.

alphabet = "abcdefghijklmnopqrstuvwxyz"
punctuation = ".,?'! "
message = "xuo jxuhu! jxyi yi qd unqcfbu ev q squiqh syfxuh. muhu oek qrbu je tusetu yj? y xefu ie! iudt cu q cuiiqwu rqsa myjx jxu iqcu evviuj!"
translated_message = ""
for letter in message:
    if not letter in punctuation:
        letter_value = alphabet.find(letter)
        translated_message += alphabet[(letter_value + 10) % 26]
    else:
        translated_message += letter
print(translated_message)

I am hoping that someone can explain all of this to me so I can get a better understanding behind the thinking.

Don’t be afraid to revisit topics you’re unsure about. The code challenges often require brining several concepts together at once, if you find there’s something you’re unsure about that’s a good thing; it means you know what topic you need a quick refresher on (be that loops, conditional logic, lists and indexes or whatever it may be). Putting things into practice is part of the learning process and part of that struggle is what often helps cement things in your memory.

As for the code it’s going through the existing string, shifting each letter by the given offset to get the correct (original) letter and adding each of these new letters to a new string. So effectively just undoing the encoding where each letter was shifted.


So for the typical latin alphabet the letters on the second row of the example below match the numbers on the first row. You can view the shift as either a shift of the numbers or a shift of the letters (a rotation might be nicer terminology since a shift of one puts ‘z’ at the start of the alphabet, before ‘a’). The third row in the example below shows the latter where the letters have been “rotated” (the final row would be the equivalent shift of the numbers).

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
a b c d e f g h i j k l m n o p q r s t u v w x y z
k l m n o p q r s t u v w x y z a b c d e f g h i j
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9

Make sure the shift is in the right direction here.

If you now replaced the first three letters “xuo” from row 2 with those in row 3 you have a slow but sound method of decoding the cipher. This is how the code works too. The trick is getting the shift right.

Note that the % operator is “modulo”- Modulo operation - Wikipedia such that 2 % 26 = 2, 26 % 26 = 0 and 27 % 26 = 1.

Modulo is how the index is shifted as values above 25 are shifted back to round to 0 or more and can be used to recreate the 4th row in the table. By using the shifted index with the original alphabet the letters can be replaced as needed.

What happens in the shift-

for index in range(26): padded_string = f"{index:>2}" print(padded_string, end=" ") print() for index in range(26): shifted_index = (index + 10) % 26 padded_string = f"{shifted_index:>2}" print(padded_string, end=" ")

Using that offset index on the existing alphabet gets you a letter of the alphabet that has been correctly shifted or offset back to the original letter. Consider writing a function that can both encode and decode and just testing it out a little bit (e.g. start with a simple offset of 1).


Hopefully that’s roughly what you were looking for, or at least a nudge in the right direction. Solving a problem with pen and paper is often a great way to start writing your own algorithms, once you have a sensible logical method in place translating it into code is much easier; trying to do both at once is much harder.

If you’re ever trying to break down existing code so that you understand it, even simple tools like print can be incredibly useful for following the flow of the program (as well as providing run-time values). I’d suggest having a look at some of these lines or even just expressions within them using either print for some simple logging or a more advanced debugging tool.

2 Likes

I appreciate this so much.
I think that my lacking is not in the understanding of the material, but how they tie all together, what’s probably called a work flow. I still can’t make these connections.
For example, why did he had to use modulo on this exercise? I know what modulo is on a standalone level but I wouldn’t have thought of using it on this exercise just because I don’t see the connection.

Also, the ability to debugg would be invaluable to me for understanding code but I can’t find out how to use it on Jupyter Notebook.

There’s a reason programming takes time to learn, various languages and their syntax simply make your task easier (with varying trade-offs). It’s coming up with the logic, translating that into both something the computer can use and something other humans can understand that is the tricky bit. The first few projects or problems you do will always be difficult since it’s just a different way of thinking.

Modulo turns up surprisingly often when you’re writing code, it’s worth getting used to it. However you don’t have to use modulo here, there are other routes to do this like making a dictionary to map letter to letter, using the ascii values of the letters themselves and many others. The given solution isn’t the only way to solve the problem and there’s no claim that it’s the best either.

Many tasks are better when you split them up. Perhaps start with offsetting a single letter. If you can work out how to swap a single letter with an offset one then you can re-use that logic. Make sure you cover edge cases, e.g. ‘a’ and ‘z’ with positive/negative offsets.

Once you have a robust method of exchanging any letter with its offset equivalent then you can expand that logic to multiple letters. Feel free to use multiple functions to do so if it makes more sense to you.

Debugging in a notebook might be a little bit different, have a look for a decent guide, blog post, video or similar. Don’t underestimate just how helpful even basic feedback via print can be though, you don’t always need special run-time debugging.