Exit slides Esc

Secret Codes

Slide 1 of 48

  1. A folded note lands on your desk:

    nvvg nv zg gsv oryizib

    It looks like nonsense, but your friend promises it hides a real message. Today we crack it by taking text apart, flipping it, and rebuilding it one character at a time.

    Each tool comes as a short ladder — simplest first, then one step up, then the real cipher — with a Notice naming the one new thing each time.

    • Pick one character by its position, and a run of characters with a slice.

    • Clean text with .lower(), .upper(), .strip() and .replace().

    • Test whether a character appears in a string with in.

    • Build a new string in a loop to encode and decode a secret message.

  2. Lesson 1: Take the Note Apart

    A string is text stored in order. Each character has a numbered position, starting at 0. Square brackets select one character by its position.

  3. word = "nvvg"
    print(word[0])

    Output

    n
    

    Positions start at 0, so word[0] is the FIRST character.

  4. word = "nvvg"
    print(word[1])
    print(word[2])

    Output

    v
    v
    

    Each number picks the character sitting at that position.

  5. word = "nvvg"
    print(word[-1])

    Output

    g
    

    A negative position counts back from the end — [-1] is the last character.

  6. word = "code"
    for position in range(4):
        print(word[position])

    Output

    c
    o
    d
    e
    

    A loop can walk every position in order, one character at a time.

  7. A slice selects a RUN of characters. In text[start:stop] Python includes start and stops just before stop.

  8. word = "nvvg"
    print(word[1:4])

    Output

    vvg
    

    [1:4] takes positions 1, 2, 3 — up to but NOT including 4.

  9. word = "nvvg"
    print(word[:2])

    Output

    nv
    

    Leave the start blank to begin at position 0.

  10. word = "nvvg"
    print(word[2:])

    Output

    vg
    

    Leave the stop blank to run all the way to the end.

  11. word = "noon ta teem"
    print(word[::-1])

    Output

    meet at noon
    

    [::-1] steps backward, reversing the whole string — a quick reverse cipher.

  12. Messages often arrive with extra spaces, mixed case, or separator marks. String methods make a CHANGED copy (the original stays the same). This unit uses four: .lower(), .upper(), .strip(), .replace(old, new).

  13. note = "MEET ME"
    print(note.lower())
    print(note)

    Output

    meet me
    MEET ME
    

    .lower() returns a lowercase copy; note itself is unchanged.

  14. quiet = "meet me"
    print(quiet.upper())

    Output

    MEET ME
    

    .upper() returns an uppercase copy.

  15. padded = "   meet me   "
    print(padded.strip())

    Output

    meet me
    

    .strip() removes the spaces from both ends.

  16. coded = "meet_me_at_noon"
    print(coded.replace("_", " "))

    Output

    meet me at noon
    

    .replace(old, new) swaps every old piece for new.

  17. messy = "   MEET_ME_AT_NOON   "
    clean = messy.strip().lower().replace("_", " ")
    print(clean)

    Output

    meet me at noon
    

    Methods can be CHAINED — each one works on the result of the one before it.

  18. Lesson 2: Decode the Folded Note

    Our note uses a swap: the first alphabet letter trades with the last, the second with the second-to-last, and so on. The loop checks all 26 positions to find each coded letter; a letter at position has its partner at 25 - position. Characters not found (a space or mark) are copied unchanged.

  19. letters = "abcdefghijklmnopqrstuvwxyz"
    coded_note = "nvvg nv zg gsv oryizib"
    coded_note = coded_note.lower()
    decoded_note = ""
    
    for letter in coded_note:
        new_letter = letter
        for position in range(26):
            if letters[position] == letter:
                new_letter = letters[25 - position]
        decoded_note = decoded_note + new_letter
    
    print(decoded_note)

    Output

    meet me at the library
    
  20. The note says meet me at the library. Trace one coded character through the inner loop: which position is found, and which partner is used? Why does the accumulator begin as an empty string?

  21. Try it
    coded_note = input("Type the coded note: ")
    letters = "abcdefghijklmnopqrstuvwxyz"
    coded_note = coded_note.lower()
    decoded_note = ""
    
    for letter in coded_note:
        new_letter = letter
        for position in range(26):
            if letters[position] == letter:
                new_letter = letters[25 - position]
        decoded_note = decoded_note + new_letter
    
    print(decoded_note)

    (It reads live input, so it does not run here.)

  22. To WRITE a cipher we must tell letters from marks. The in operator asks whether one value appears inside another.

  23. letters = "abcdefghijklmnopqrstuvwxyz"
    print("m" in letters)

    Output

    True
    

    in reports True/False — is "m" somewhere in letters? Yes.

  24. letters = "abcdefghijklmnopqrstuvwxyz"
    print(" " in letters)
    print("!" in letters)

    Output

    False
    False
    

    A space and a ! are not letters, so in is False for them.

  25. letters = "abcdefghijklmnopqrstuvwxyz"
    message = "hi!"
    for character in message:
        if character in letters:
            print(character + " is a letter")
        else:
            print(character + " is a mark")

    Output

    h is a letter
    i is a letter
    ! is a mark
    

    Inside a loop, in tests each character — here it just labels them; the real cipher will use that test to shift letters and copy marks unchanged.

  26. Lesson 3: Write a Code Your Friend Can Crack

    Now we shift each letter forward, leaving spaces and punctuation untouched. Here is a deliberate bug: a late letter can step past the end of the alphabet. Predict which line the traceback points to when the message starts with z.

  27. Error demo: this code stops with an error
    def encode_without_wrap(message, shift):
        letters = "abcdefghijklmnopqrstuvwxyz"
        message = message.lower()
        result = ""
        for letter in message:
            if letter in letters:
                for position in range(26):
                    if letters[position] == letter:
                        new_position = position + shift
                        result = result + letters[new_position]
            else:
                result = result + letter
        return result
    
    
    print(encode_without_wrap("zoo", 3))
  28. Read a traceback from the bottom: IndexError means a position fell outside the string. The remainder operator repairs the wrap — (position + shift) % 26 is always 0 through 25.

  29. def encode(message, shift):
        letters = "abcdefghijklmnopqrstuvwxyz"
        message = message.lower()
        result = ""
        for letter in message:
            if letter in letters:
                for position in range(26):
                    if letters[position] == letter:
                        new_position = (position + shift) % 26
                        result = result + letters[new_position]
            else:
                result = result + letter
        return result
    
    
    coded_message = encode("Meet me at 4!", 3)
    print(coded_message)

    Output

    phhw ph dw 4!
    
  30. Trace the first m: the scan finds position 12, adds shift 3, selects position 15 — p. For z, 25 + 3 = 28, and 28 % 26 wraps to 2 — c. The result variable is a string accumulator, built left to right.

  31. def decode(message, shift):
        return encode(message, 26 - shift)
    
    
    original_message = "meet me at 4!"
    coded_message = encode(original_message, 3)
    decoded_message = decode(coded_message, 3)
    print(f"Coded: {coded_message}")
    print(f"Decoded: {decoded_message}")

    Output

    Coded: phhw ph dw 4!
    Decoded: meet me at 4!
    
  32. Encoding then decoding is a round trip. Because the encoder lowercases first, the decoded text matches the lowercase original; spaces, the number, and ! survive unchanged.

  33. Try it
    message = input("Type a secret message: ")
    shift = 3
    coded_message = encode(message, shift)
    print(coded_message)

    (It reads live input, so it does not run here.)

  34. Trade coded messages with a partner and share the shift separately. Predict how spaces and punctuation will look, then explain why both friends need the same shift.

  35. Algorithm Extension

    The algorithm-pattern Spotlights for this unit, gathered here for reference — read them alongside the Algorithm Extension exercises.

  36. Pattern Spotlight: Scan until found + stop early

    Kid hook: check items one by one; break when found = stop working early. A person may spot a letter at a glance, but the program checks positions in order. After the comparison finds the letter, break avoids checking the rest. (The scan in the lesson above checks all 26 positions; Exercise 14 adds the break so it can stop early.)

  37. print("a" in "aeiou")

    Output

    True
    

    in tests ONE character against the vowels — 'a' is a vowel.

  38. word = "cat"
    for ch in word:
        print(ch in "aeiou")

    Output

    False
    True
    False
    

    Run the same in test on each character in turn — cat gives False, True, False.

  39. word = "cat"
    position = 0
    for ch in word:
        print(position)
        position = position + 1

    Output

    0
    1
    2
    

    A position counter, stepped once per character, tracks WHERE we are: 0, 1, 2.

  40. word = "cat"
    position = 0
    found = -1
    for ch in word:
        if ch in "aeiou":
            found = position
        position = position + 1
    print(found)

    Output

    1
    

    Instead of printing every step, remember the index of the vowel in found (a -1 stand-in for "none yet") — the vowel of cat is at index 1. Without a break this keeps the LATEST match; R5's break will keep the FIRST.

  41. word = "cat"
    position = 0
    found = -1
    for ch in word:
        if ch in "aeiou":
            found = position
            break
        position = position + 1
    print(found)

    Output

    1
    

    break STOPS at the FIRST vowel instead of scanning the rest — same index 1, less work.

  42. word = "myth"
    position = 0
    found = -1
    for ch in word:
        if ch in "aeiou":
            found = position
            break
        position = position + 1
    print(found)

    Output

    -1
    

    myth has no vowel, so the loop falls through and the -1 stand-in reports "not found".

  43. Pattern Spotlight: Do the same to each (map)

    To apply the same step to every item, producing a new sequence, loop over the message one character at a time and add each transformed character to a new string. A whole-string method changes the message in one call; an explicit per-character loop builds a transformed result for every character, unlike counting only characters that meet a condition.

  44. result = ""
    result = result + "h".upper()
    print(result)

    Output

    H
    

    Transform one character with .upper() and add it to the new string — "h" becomes "H".

  45. result = ""
    result = result + "h".upper()
    result = result + "i".upper()
    print(result)

    Output

    HI
    

    The same step applied again grows the transformed string to "HI".

  46. word = "hi"
    result = ""
    for ch in word:
        result = result + ch.upper()
    print(result)

    Output

    HI
    

    The loop applies the same .upper() transform to EVERY character of "hi", building "HI".

    • Positions start at 0, and word[-1] is the last character.

    • text[start:stop] includes start and stops just before stop; [::-1] reverses.

    • A string method returns a changed copy, and the original stays the same.

    • in asks whether one value appears inside another.

    • (position + shift) % 26 wraps past z, and decoding shifts by 26 - shift.

    You can now write a secret code a friend can crack.