Converting base-16 roman numbers to arabic numbers (and vice-versa)
Posted: July 2, 2012 Filed under: Programming Languages | Tags: Mac OS X, Python, Unittest Leave a comment »Here is neat python programming challenge.
A hex roman numeral is very much like the standard roman numeral, except with different values. In normal roman numerals, I = 1, V = 5, X = 10 and so on. In hex roman numerals, I = 1, V = 8, X = 16, L = 128, C = 256, D = 2048 and M = 4096. So for example:
VIIII = 8 + 1 + 1 + 1 + 1 = 12
IX = 16 – 1 = 15
XV = 16 + 8 = 24
XL = 128 – 16 = 112
The goal is to write a program in python that converts it in either direction. If given a decimal number, it should return the hex roman numeral version of the number and if given a hex roman numeral, it should return the decimal version of the number.
I started this by creating a program that performs a normal roman to arabic conversion. This wasn’t too hard, especially since python has a ton of neat features such as as dictionaries and solid string parsing methods. Since I am using unittest to test my code, I’ve named this file roman_numerals.py.
|
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
import sys, re def roman_to_arabic(number): """return the roman numeral string representation of integer number""" roman_dict={"I":1,"V":5,"X":10,"L":50,"C":100,"D":500,"M":1000} lst = [ roman_dict[i] for i in list(number) ] for n in xrange(len(lst)-1): if (lst[n]<lst[n+1]): lst[n]=-lst[n] return(sum(lst)) def arabic_to_roman(number): """return the arabic numeral integer representation of roman string number""" units = ("I","II","III","IV","V","VI","VII","VIII","IX","") tens = ("X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", "") hundreds = ("C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "") thousands = ("M", "MM", "MMM", "MMMM","MMMMM","MMMMM","MMMMMM","MMMMMMM","MMMMMMMM","") #not quite sure how the romans dealt with very small or very large #numbers... also not quite sure how they worked with floating points assert(number<=7000) assert(number>0) a=list(str(number)) #string-ify numbers b=a[-1::-1] #reverse order of list conversion="" if (len(b)>0): conversion=units[eval(b[0])-1]+conversion if (len(b)>1): conversion=tens[eval(b[1])-1]+conversion if (len(b)>2): conversion=hundreds[eval(b[2])-1]+conversion if (len(b)>3): conversion=thousands[eval(b[3])-1]+conversion return(conversion) if __name__== '__main__': try: if (re.match("I|V|X|L|D|C|M", sys.argv[1])): print roman_to_arabic(sys.argv[1]) else: print arabic_to_roman(eval(sys.argv[1])) except: print "Error: You either specified an: \n\t-invalid number \n\t-out of [1 to 7000] range \n\t-inexistent number" |
The package unittest provides a great way to test your programs. I love it. You can pretty much run a another script and it will perform all the necessary assertions as it tests the proper package. Here is my unittest code, which I named test_roman_numerals.py.
|
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 26 27 28 29 30 31 32 |
import unittest, roman_numerals class ProductTestCase(unittest.TestCase): def test_arabic_to_roman(self): self.failUnless("I"==roman_numerals.arabic_to_roman(1)) self.failUnless("III"==roman_numerals.arabic_to_roman(3)) self.failUnless("V"==roman_numerals.arabic_to_roman(5)) self.failUnless("X"==roman_numerals.arabic_to_roman(10)) self.failUnless("XI"==roman_numerals.arabic_to_roman(11)) self.failUnless("VIII"==roman_numerals.arabic_to_roman(8)) self.failUnless("IX"==roman_numerals.arabic_to_roman(9)) self.failUnless("XV"==roman_numerals.arabic_to_roman(15)) self.failUnless("XL"==roman_numerals.arabic_to_roman(40)) self.failUnless("CXV"==roman_numerals.arabic_to_roman(115)) self.failUnless("XLVI"==roman_numerals.arabic_to_roman(46)) self.failUnless("MMXII"==roman_numerals.arabic_to_roman(2012)) def test_roman_to_arabic(self): self.failUnless(1==roman_numerals.roman_to_arabic("I")) self.failUnless(3==roman_numerals.roman_to_arabic("III")) self.failUnless(5==roman_numerals.roman_to_arabic("V")) self.failUnless(10==roman_numerals.roman_to_arabic("X")) self.failUnless(11==roman_numerals.roman_to_arabic("XI")) self.failUnless(8==roman_numerals.roman_to_arabic("VIII")) self.failUnless(9==roman_numerals.roman_to_arabic("IX")) self.failUnless(15==roman_numerals.roman_to_arabic("XV")) self.failUnless(40==roman_numerals.roman_to_arabic("XL")) self.failUnless(115==roman_numerals.roman_to_arabic("CXV")) self.failUnless(46==roman_numerals.roman_to_arabic("XLVI")) self.failUnless(2012==roman_numerals.roman_to_arabic("MMXII")) if __name__== '__main__': unittest.main() |
Here are some screenshots of the program in action: first testing through the command line, and then testing it with unittest.

Taking this base code and making it compatible with base-16 numerals was trivial. All that I needed to do was make a minor modification to the dictionary roman_dict and adding extra elements to the lists units, tens, hundreds and thousands. Of course I had to perform a base conversion with the hex2dec function each time I wanted to access a position in the list.
Here is the code that converts base-16 roman numbers to arabic numbers. I saved this file as roman_numerals_base16.py.
|
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
import sys, re def roman_to_hex_arabic(number): """return the roman numeral string representation of integer number""" roman_dict={"I":1,"V":8,"X":16,"L":128,"C":256,"D":2048,"M":4096} lst = [ roman_dict[i] for i in list(number) ] for n in xrange(len(lst)-1): if (lst[n]<lst[n+1]): lst[n]=-lst[n] return(sum(lst)) def arabic_to_hex_roman(number): """return the arabic numeral integer representation of roman string number""" units = ("I", "II", "III", "IIII", "IIIII", "IIIIII", "IV", "V", "VI", "VII", "VIII", "VIIII", "VIIIII", "VIIIIII", "IX", "") tens = ("X", "XX", "XXX", "XXXX", "XXXXX", "XXXXXX", "XL", "L", "LX", "LXX", "LXXX", "LXXXX", "LXXXXX", "LXXXXXX", "XC", "") hundreds = ("C", "CC", "CCC", "CCCC", "CCCCC", "CCCCCC", "CD", "D", "DC", "DCC", "DCCC", "DCCCC", "DCCCCC", "DCCCCCC", "CM", "") thousands = ("M", "MM", "MMM", "MMMM","MMMMM","MMMMM","MMMMMM","MMMMMMM","MMMMMMMM","") #not quite sure how the romans dealt with very small or very large #numbers... also not quite sure how they worked with floating points assert(number<=7000) assert(number>0) a=list(str(dec2hex(number))) #string-ify numbers b=a[-1::-1] #reverse order of list conversion="" if (len(b)>0): conversion=units[hex2dec(b[0])-1]+conversion if (len(b)>1): conversion=tens[hex2dec(b[1])-1]+conversion if (len(b)>2): conversion=hundreds[hex2dec(b[2])-1]+conversion if (len(b)>3): conversion=thousands[hex2dec(b[3])-1]+conversion return(conversion) def dec2hex(n): """return the hexadecimal string representation of integer n""" return "%X" % n def hex2dec(s): """return the integer value of a hexadecimal string s""" return int(s, 16) if __name__== '__main__': try: if (re.match("I|V|X|L|D|C|M", sys.argv[1])): print roman_to_hex_arabic(sys.argv[1]) else: print arabic_to_hex_roman(eval(sys.argv[1])) except: print "Error: You either specified an: \n\t-invalid number \n\t-out of [1 to 7000] range \n\t-inexistent number" |
And here are my test cases, taken directly from the problem statement and saved as test_roman_numerals_base16.py.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import unittest, roman_numerals_base16 class ProductTestCase(unittest.TestCase): def test_arabic_to_hex_roman(self): self.failUnless("VIIII"==roman_numerals_base16.arabic_to_hex_roman(12)) self.failUnless("IX"==roman_numerals_base16.arabic_to_hex_roman(15)) self.failUnless("XV"==roman_numerals_base16.arabic_to_hex_roman(24)) self.failUnless("XL"==roman_numerals_base16.arabic_to_hex_roman(112)) self.failUnless("XI"==roman_numerals_base16.arabic_to_hex_roman(17)) def test_roman_to_hex_arabic(self): self.failUnless(12==roman_numerals_base16.roman_to_hex_arabic("VIIII")) self.failUnless(15==roman_numerals_base16.roman_to_hex_arabic("IX")) self.failUnless(24==roman_numerals_base16.roman_to_hex_arabic("XV")) self.failUnless(112==roman_numerals_base16.roman_to_hex_arabic("XL")) self.failUnless(17==roman_numerals_base16.roman_to_hex_arabic("XI")) if __name__== '__main__': unittest.main() |
Finally, here is a screenshot of the program in action.
Python and duck curses
Posted: May 18, 2012 Filed under: Programming Languages | Tags: Python Leave a comment »Today at lunch I realized that life would be much easier if I could check the status of my simulations in some sort of interactive menu over ssh. When I got up to my office, someone told me about Python and curses. Curses is pretty much a module that allows you to deal with terminal-independent screen-painting and keyboard-handling over text-based terminals.
So… 10 minutes after reading this neat tutorial, and inspired by the craziness happening at 38 studios, I wrote my first game playable via ssh. You control a duck that collects worms… yes, how vapid.
|
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
import curses, random def draw_duck(top_pos,left_pos): duck=( " _ ", " =')_// ", " (___/ ") screen.clear() for iterator in duck: screen.addstr(top_pos, left_pos, iterator) top_pos+=1 def update_screen(top_pos,left_pos,worm_location,worms_eaten): draw_duck(top_pos,left_pos) if (top_pos+1==worm_location[0]) and (left_pos+1==worm_location[1]): worms_eaten+=1 worm_location=[ random.randrange(1, screen_dimensions[0]), random.randrange(1, screen_dimensions[1]) ] screen.addstr(worm_location[0], worm_location[1],"~") say_this="Score=%d duck=(%d,%d) worm=(%d,%d)" % (worms_eaten, top_pos , left_pos, worm_location[0], worm_location[1]) screen.addstr(21,0,say_this) return (worm_location,worms_eaten) screen = curses.initscr() curses.noecho() curses.curs_set(0) screen.keypad(1) worm_location=[ random.randrange(1, screen_dimensions[0]), random.randrange(1, screen_dimensions[1]) ] worms_eaten=0 screen.addstr("Use the cursor keys to move the duck!\n") top_pos = 1 left_pos = 1 while True: event = screen.getch() if event == ord("q"): break elif event == curses.KEY_UP: top_pos-=1 if (top_pos<=0): top_pos=0 if (top_pos>=20): top_pos=20 worm_location, worms_eaten = update_screen(top_pos,left_pos,worm_location,worms_eaten) elif event == curses.KEY_DOWN: top_pos+=1 if (top_pos<=0): top_pos=0 if (top_pos>=20): top_pos=20 worm_location, worms_eaten = update_screen(top_pos,left_pos,worm_location,worms_eaten) elif event == curses.KEY_LEFT: left_pos-=1 if (left_pos<=0): left_pos=0 if (left_pos>=20): left_pos=20 worm_location, worms_eaten = update_screen(top_pos,left_pos,worm_location,worms_eaten) elif event == curses.KEY_RIGHT: left_pos+=1 if (left_pos<=0): left_pos=0 if (left_pos>=20): left_pos=20 worm_location, worms_eaten = update_screen(top_pos,left_pos,worm_location,worms_eaten) elif event == ord(" "): screen.clear() screen.addstr("Press 'q' to exit") curses.endwin() |
Choose your own adventure… in audio
Posted: May 17, 2012 Filed under: Programming Languages | Tags: Mac OS X, Python Leave a comment »One of the things that bores me the most is driving. Ten minutes into my daily commute and I am checking out my email and reading my twitter feed. Anyway, the other day when I was coming from Boston I had this interesting idea; why not create a choose your own adventure game, which could be played while driving. Instead of reading a book, the book would be read to us by a speech synthesizer. The choices would be done by pressing buttons instead of manually flipping pages. The idea was so neat that I proceeded to create a prototype, so I could send it to a magazine, but the outcome was so bad that I pushed it aside until I find a better technology. For my failed prototype I used the linux open-source flite speech synthesizer and a beagleboard XM. As the driving engine I created a simple script in python that read a particular story and used buttons to control the flow of the adventure. The first problem is that beagleboard requires a pretty clean 5V power supply which is a mess to get in a car. Also, the beagleboard is fairly expensive ($120) to use as a dedicated game engine. Finally, creating a customized OS that is fast enough to launch the a particular application on the beagleboard is not trivial.
Anyway, I still think that audio based interactive entertainment systems have potential, but my technical solution I chose was not the best. Most definitely I will revisit this idea soon with Android phones and tablets. Implementing this in android seems to be very easy, thanks to the IOIO connectors. For those interested, this connector is available for purchase through sparkfun. Regardless, here is a very simple code for my choose your own adventure. It is done in python and if you have a macosx it will read out the entries and choices using the built in speech synthesizer.
|
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
import os #reads the current entry def read_entry(entry_number, filename): f=open(filename,'r') found=False for line in f.readlines(): line=line.strip('\n') if line == '<'+str(entry_number)+'>': found=True if (line == '[choices]') or line == '[end]': found=False if (found==True) and (line!='<'+str(entry_number)+'>'): print line saythis="say %s" % line os.system(saythis) #creates a dictionary with the choices at each entry def read_choices(entry_number, filename): f=open(filename,'r') entry_found=False choice_section=False items={} for line in f.readlines(): line=line.strip('\n') if line == '<'+str(entry_number)+'>': entry_found=True if (entry_found==True) and (choice_section==True) and (line!='[end choices]'): #remove ">" from line line=line.replace('>','') #remove "-" from line line=line.replace('-','') line=line.split('<') x={line[0] : line[1]} items.update(x) if (entry_found==True) and (line=='[choices]'): choice_section=True if (entry_found==True) and (line=='[end choices]'): choice_section=False entry_found=False if (line=='[end]'): entry_found=False return items def ask_choices(choices): saythis="What do you want to do?" print saythis saythis="say %s" % saythis os.system(saythis) i=1 for current_choice in list(choices): saythis='Choice number %d:' % i + '%s' %current_choice print saythis saythis="say %s" % saythis os.system(saythis) i+=1 saythis="What is your choice?" print saythis saythis="say %s" % saythis os.system(saythis) user_choice=input('') key=list(choices)[user_choice-1] #the function return value is next story page return choices.get(key) story_page=1 terminate=False while terminate==False: read_entry(story_page,'test_story.txt') choices=read_choices(story_page,'test_story.txt') #if there are no choices, then we reached the end if len(list(choices))==0: terminate=True else: story_page=ask_choices(choices) |
The story itself (hardwired on the previous file as test_story.txt) is pretty self-explanatory.
You are in the top of a very tall building.
[choices]
- Jump <2>
- Yell <3>
- Do nothing <1>
[end choices]
<2>
You decided to jump… thats too bad.
[end]
<3>
You yell something. No one replies.
[choices]
- Jump <2>
- Do nothing <1>
[end choices]
Finally, here is a screenshot of the program in action.
Enjoy.



