3
\$\begingroup\$

I'm learning Python 3 at the moment, so to test the skills I've learned, I am trying the puzzles at Python Challenge.

I've created some code to solve the 2nd puzzle here. it works, but I think that the way I've done it is very convoluted. Any suggestions on how to solve the puzzle in a simpler way?

(The code basically needs to substitute each letter in a message to the letter 2 spaces to the right of it, such as E->G.)

import string
alphabet = string.ascii_lowercase
letter=0
replaceLetter = 2
times=1
message = input()
newMessage=''
while times < 26:
    newMessage = message.replace(alphabet[letter], str(replaceLetter)+',')
    message = newMessage
    letter = letter + 1
    replaceLetter = replaceLetter + 1
    time = times + 1
    if letter == 26:
        times = 0
        break
newMessage = message.replace('26'+',', 'a')
message = newMessage
newMessage = message.replace('27'+',', 'b')
message = newMessage
number = 25
message = newMessage
while times < 26:
    newMessage = message.replace(str(number)+',', str(alphabet[number]))
    message = newMessage
    letter = letter + 1
    number = number - 1
    time = times + 1
    if number == -1:
        times = 0
        break
print(newMessage)
\$\endgroup\$
2
  • \$\begingroup\$ Take a look at the python dictionary. Consider replacing characters? \$\endgroup\$
    – Martin Beckett
    Commented Dec 3, 2011 at 3:54
  • 1
    \$\begingroup\$ There is a hint in the title of the challenge "What about making trans?". See maketrans. It can be only a couple of lines of code. \$\endgroup\$ Commented Dec 3, 2011 at 6:46

2 Answers 2

3
\$\begingroup\$

I would define a shift function that shifted the letters like so:

from string import whitespace, punctuation

def shift(c, shift_by = 2):
    if c in whitespace + punctuation: return c

    upper_ord, lower_ord, c_ord = ord('A'), ord('a'), ord(c)
    c_rel = (c_ord - lower_ord) if c_ord >= lower_ord else (c_ord - upper_ord)
    offset = lower_ord if c_ord >= lower_ord else upper_ord
    return chr(offset + (c_rel + shift_by) % 26)

Then, to translate a message:

msg = 'a quick brown fox jumped over the lazy dog'
encoded_msg = ''.join(shift(l) for l in msg)

Alternatively, combine Mark Tolonen's maketrans suggestion with g.d.d's deque suggestion to get:

import string
from collections import deque

alphabet = string.ascii_lowercase
alphabet_deque = deque(alphabet)
alphabet_deque.rotate(-2)
rotated_alphabet = ''.join(alphabet_deque)
tbl = string.maketrans(alphabet, rotated_alphabet)

Then, later in the code:

msg = 'a quick brown fox jumped over the lazy dog'
encoded_msg = string.translate(msg, tbl)

This second method only works for lowercase letters, but you can always create two separate translation tables -- one for uppercase letters and another for lowercase letters -- to account for case.

Offtopic note: sometimes I wish Python had Smalltalk-like cascaded message sends. Ah well, one can dream.

\$\endgroup\$
2
\$\begingroup\$

I would do a couple of things differently.

import string
from collections import deque

ascii1 = string.ascii_lowercase

# create a deque to simplify rotation.
d = deque(ascii1)
d.rotate(-2)

ascii2 = ''.join(d)

replacements = dict(zip(ascii1, ascii2))

oldmessage = 'This is a string that we want to run through the cipher.'

newmessage = ''.join(replacements.get(c.lower(), c) for c in oldmessage)
# results in 'vjku ku c uvtkpi vjcv yg ycpv vq twp vjtqwij vjg ekrjgt.'

Note that I didn't do anything here to account for casing.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.