This notebook was prepared by [Thunder Shiviah](https://github.com/ThunderShiviah). Source and license info is on [GitHub](https://github.com/ThunderShiviah/code_guild).

Challenge Notebook

Problem: Implement a function that converts pins to pronouncible nonsense words.

Overview

We have to remember too many numbers: credit card PINs, student ID number, codes to open doors, and on and on. We're warned that we should remember these numbers instead of writing them down, but really the human brain is not well-suited to remembering a lot of meaningless numbers. I'll admit: I write them down. But could I remember more? People aren't any better at remembering random sequences of letters than remembering random sequences of digits. However, we are much better at remembering sounds we can pronounce. The word “fesi” doesn't mean anything to me, but it's easier to remember than the number 2354 or the harder-to-pronounce word “iksf”. It turns out to be pretty easy to convert numbers like 2354 into pronounceable nonsense words like “fesi” To create words that are easy to pronounce, we can build them out of consonant-vowel pairs like “ka” or “te”. As it turns out, if we omit ‘x’ and treat ‘y’ as a consonant, the English alphabet has 20 consonants (bcdfghjklmnpqrstvwyz) and 5 vowels (aeiou). 20×5=10×10=100, so one consonant and a vowel (20×5 combinations) is just enough to represent a pair of decimal digits (10×10 combinations). To convert a number (like my office phone number, 346-4140) into an pronounceable string, we'll need to divide it into two-digit chunks.

We're dividing it up in base 10; the underlying representation in base 2 is not relevant to us in this program. To get the last two (low order) digits in base 10, we can take the remainder when divided by 100, using the ‘%’ operator. To get the rest of the digits, we'll use integer division ‘//’, because we want an integer result (34641, not 34641.40).

Now we want to convert those last two decimal digits, 40, into letters. We have 20 consonants and 5 vowels. Suppose we divide a number between 0 and 99 by 5. The quotient will be a number in the range 0..19, and the remainder will be in the range 0..4:

Just right for picking one of 20 consonants and one of 5 vowels!

If I do this again for the next two digits (41), and the next (46), and then the highest digit (3, treated as 03), my office phone number can be converted to the word “bomelela”. It has a nice ring to it.

Requirements

You will create a Python program to automate the process described above. To keep it simple, your program need convert only 4-digit numbers, like credit card PINs. The input to your program is a 4-digit decimal number. Your output should be a pronounceable word following the process above. If the input is not a 4-digit word the code should throw a ValueError exception (with a useful error message) using a try/except (read about error handling here).

Furthermore, code should follow proper PEP guidelines (see here for a rough overview) and include an adequate docstring (read the PEP about docstrings).

Bonus challenges

If you finish early try the following challenges:

  • Instead of only working with 4 digit numbers, make your program work for any positive integer from 1 to at least 12 digits. Don't do it by making your program long and repetitious ... use a loop, and define Python functions as needed to keep it clear and short.
  • These alphabetic codes don't really help unless we also have a way to convert a word back into a number. Make your program convert either way: If the input is an integer, convert it to a word. If the input is a word of the correct form (consonants alternating with vowels), convert it back to an integer. If the input cannot be encoded or decoded (e.g., abba123 cannot be translated either direction), print a useful error message.

Test Cases

  • convert_pin(4327) --> lohi
  • convert_pin(1298) --> dizo
  • convert_pin(None) --> ValueError
  • convert_pin('absd') --> ValueError
  • convert_pin(0) --> ValueError

Algorithm

Refer to the Solution Notebook. If you are stuck and need a hint, the solution notebook's algorithm discussion might be a good place to start.

Code


In [14]:
## Constants used by this program
CONSONANTS = "bcdfghjklmnpqrstvwyz" 
VOWELS = "aeiou"  

def convert_pin(pin):
    ##FIXME: Replace the rest with your code
    pass

Unit Test

The following unit test is expected to fail until you solve the challenge.


In [1]:
# %load test_foo.py
from nose.tools import assert_equal


class Testconvert_pin(object):

    def test_convert_pin(self):
        assert_equal(convert_pin(None), ValueError)
        assert_equal(convert_pin('absd'), ValueError)
        assert_equal(convert_pin(0), ValueError)
        assert_equal(convert_pin(4327), lohi)
        assert_equal(convert_pin(1298), dizo)
        print('Success: test_convert_pin')

def main():
    test = Testconvert_pin()
    test.test_convert_pin()

if __name__ == '__main__':
    main()


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-4e671bb340f7> in <module>()
     18 
     19 if __name__ == '__main__':
---> 20     main()

<ipython-input-1-4e671bb340f7> in main()
     15 def main():
     16     test = Testconvert_pin()
---> 17     test.test_convert_pin()
     18 
     19 if __name__ == '__main__':

<ipython-input-1-4e671bb340f7> in test_convert_pin(self)
      6 
      7     def test_convert_pin(self):
----> 8         assert_equal(convert_pin(None), ValueError)
      9         assert_equal(convert_pin('absd'), ValueError)
     10         assert_equal(convert_pin(0), ValueError)

NameError: name 'convert_pin' is not defined

Solution Notebook

Review the Solution Notebook for a discussion on algorithms and code solutions.