Hey everyone, It's been a little while since I've done a "Fun with Python" post, with the last one being the `word_clock.py` script that tells time with words. Today, I've got another fun little project to share: a command-line tool I wrote to convert integers to and from Roman numerals. Roman numerals are an interesting programming puzzle. It's not just a simple one-to-one mapping of numbers to letters; you have to account for the subtractive principle where "IV" is 4 (not "IIII") and "CM" is 900 (not "DCCCC"). It makes for a great little logic challenge. #### The `roman.py` Script I put together a simple Python script to handle these conversions. It's a command-line tool that can take an integer and give you the Roman numeral, or take a Roman numeral and give you the integer. A neat feature I included is support for larger numbers using the **vinculum (or overbar)** notation, where a bar over a numeral multiplies its value by 1,000. So, `X̅` is 10,000, `C̅` is 100,000, and so on. #### How It Works  - **Integer to Roman:** To convert a number like `1994`, the script works its way down from the largest values. It sees that `1994` is bigger than `1000`, so it adds an "M" and subtracts 1000, leaving 994. Then it sees `900`, adds "CM" and subtracts 900, leaving 94. It continues this for `90` ("XC") and `4` ("IV") until it gets to zero, building the final string: `MCMXCIV`. - **Roman to Integer:** Going the other way, it reads the Roman numeral string from left to right, looking for the largest symbol it can match first (so it will see "CM" before it sees "C"). It adds that symbol's value to a running total and then chops that symbol off the string, repeating the process until the string is empty. #### The Code Here is the full script. It's self-contained and doesn't require any external libraries. ```python #!/usr/bin/env python3 import argparse from typing import List, Tuple # Ordered list of Roman numeral symbols (value, symbol) in descending order. # Includes overbar (vinculum) characters for larger numbers. SYMBOLS: List[Tuple[int, str]] = [ (1000000, "M̅"), (100000, "C̅"), (10000, "X̅"), (1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"), (50, "L"), (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I"), ] # Derived mapping for quick symbol → value lookups. SYMBOL_TO_VALUE = {s: v for v, s in SYMBOLS} def int_to_roman(num: int) -> str: """ Convert an integer to a Roman numeral. """ if not 0 < num < 4000000: # Practical upper bound raise ValueError("Integer must be between 1 and 3,999,999.") roman_numeral = "" # Build the numeral greedily from largest to smallest value. for value, symbol in SYMBOLS: while num >= value: roman_numeral += symbol num -= value return roman_numeral def roman_to_int(roman: str) -> int: """ Convert a Roman numeral to an integer. """ if not roman: raise ValueError("Roman numeral cannot be empty.") roman = roman.upper() # Sort symbols by length so multi-char symbols (like 'CM') match first. symbols = sorted(SYMBOL_TO_VALUE.keys(), key=len, reverse=True) value = 0 while roman: matched = False for symbol in symbols: if roman.startswith(symbol): value += SYMBOL_TO_VALUE[symbol] roman = roman[len(symbol) :] matched = True break if not matched: raise ValueError(f"Invalid character or sequence in Roman numeral: '{roman}'") return value def main() -> int: """Entry-point for command-line interface.""" parser = argparse.ArgumentParser( description="Convert between integers and Roman numerals." ) group = parser.add_mutually_exclusive_group(required=True) group.add_argument( "-i", "--integer", type=int, help="Integer to convert to Roman numeral." ) group.add_argument( "-r", "--roman", type=str, help="Roman numeral to convert to integer." ) args = parser.parse_args() try: if args.integer is not None: print(int_to_roman(args.integer)) else: print(roman_to_int(args.roman)) except ValueError as exc: parser.error(str(exc)) return 0 if __name__ == "__main__": main() ``` #### How to Use It Using it from your terminal is straightforward: To convert an integer to a Roman numeral: ```bash python3 roman.py --integer 2025 ``` Output: `MMXXV` To convert a Roman numeral to an integer: ```bash python3 roman.py --roman MCMXCIV ``` Output: `1994` And here's an example with a larger number: ```bash python3 roman.py -i 12345 ``` Output: `X̅MMCCCXLV` It's a simple, fun project and a great way to practice some basic algorithm logic. Hope you enjoy it! As always, Michael Garcia a.k.a. TheCrazyGM
author | thecrazygm | ||||||
---|---|---|---|---|---|---|---|
permlink | fun-with-python-converting-to-and-from-roman-numerals | ||||||
category | hive-186392 | ||||||
json_metadata | {"app":"peakd/2025.6.1","format":"markdown","image":["https://files.peakd.com/file/peakd-hive/thecrazygm/AJoJbbzpWpDQFSrZeCffTahwaLQoFb59zmHRr6HnzicLULHmMHmVSbRTezVH99i.png"],"tags":["dev","python","tribes","archon","proofofbrain","pimp"],"users":[]} | ||||||
created | 2025-06-18 15:33:54 | ||||||
last_update | 2025-06-18 15:35:39 | ||||||
depth | 0 | ||||||
children | 4 | ||||||
last_payout | 1969-12-31 23:59:59 | ||||||
cashout_time | 2025-06-25 15:33:54 | ||||||
total_payout_value | 0.000 HBD | ||||||
curator_payout_value | 0.000 HBD | ||||||
pending_payout_value | 10.234 HBD | ||||||
promoted | 0.000 HBD | ||||||
body_length | 5,113 | ||||||
author_reputation | 78,856,670,221,142 | ||||||
root_title | "Fun with Python: Converting To and From Roman Numerals" | ||||||
beneficiaries |
| ||||||
max_accepted_payout | 1,000,000.000 HBD | ||||||
percent_hbd | 10,000 | ||||||
post_id | 143,428,889 | ||||||
net_rshares | 35,650,282,664,513 | ||||||
author_curate_reward | "" |
voter | weight | wgt% | rshares | pct | time |
---|---|---|---|---|---|
jacor | 0 | 24,293,922,315 | 50% | ||
penguinpablo | 0 | 139,946,560,948 | 14% | ||
funnyman | 0 | 1,470,724,020 | 5.6% | ||
eforucom | 0 | 22,572,896,736 | 100% | ||
moretea | 0 | 2,917,330,431 | 10% | ||
gamer00 | 0 | 19,519,315,306 | 5% | ||
dune69 | 0 | 64,683,586,028 | 100% | ||
mes | 0 | 431,019,582,152 | 24% | ||
mvd | 0 | 852,109,120 | 50% | ||
calmphoenix | 0 | 1,467,828,970 | 30% | ||
ecoinstant | 0 | 66,711,212,197 | 100% | ||
cryptoknight12 | 0 | 49,286,420,394 | 100% | ||
alphacore | 0 | 7,141,185,930 | 7.12% | ||
techken | 0 | 23,493,080,169 | 25% | ||
joeyarnoldvn | 0 | 450,980,731 | 1.47% | ||
grocko | 0 | 17,032,845,119 | 20% | ||
eturnerx | 0 | 268,985,296,017 | 20.2% | ||
dante31 | 0 | 1,999,747,009 | 55% | ||
bambukah | 0 | 6,686,268,269 | 8% | ||
pixelfan | 0 | 53,244,567,394 | 5.8% | ||
likedeeler | 0 | 245,553,092,810 | 100% | ||
hdmed | 0 | 5,344,966,935 | 50% | ||
niallon11 | 0 | 1,877,320,064,502 | 100% | ||
artlover | 0 | 1,528,905,869 | 100% | ||
tomiscurious | 0 | 26,273,532,897 | 4.5% | ||
futurethinker | 0 | 1,463,582,302 | 100% | ||
podanrj | 0 | 1,216,488,645 | 55% | ||
fatman | 0 | 9,257,857,571 | 2% | ||
diosarich | 0 | 518,583,113 | 20% | ||
msp-makeaminnow | 0 | 26,637,835,523 | 28.6% | ||
omra-sky | 0 | 160,308,188,496 | 100% | ||
morwhale | 0 | 831,492,637 | 50% | ||
morwhaleplus | 0 | 592,148,695 | 50% | ||
coolguy123 | 0 | 2,819,996,708 | 1% | ||
morwhalebonus | 0 | 587,872,766 | 50% | ||
jozefkrichards | 0 | 4,938,684,844 | 50% | ||
sneakyninja | 0 | 16,891,647,620 | 24.88% | ||
najat | 0 | 3,086,568,219 | 50% | ||
steembasicincome | 0 | 6,004,770,250,914 | 100% | ||
cryptonized | 0 | 235,637,482 | 14% | ||
deepresearch | 0 | 629,328,193,617 | 16% | ||
hetty-rowan | 0 | 46,855,548,486 | 50% | ||
irisworld | 0 | 567,150,927 | 7.5% | ||
osarueseosato | 0 | 626,326,303 | 100% | ||
jazzhero | 0 | 11,947,405,627 | 15% | ||
fireguardian | 0 | 575,241,866 | 20% | ||
anikys3reasure | 0 | 2,491,176,426 | 50% | ||
promo-mentors | 0 | 710,783,676 | 100% | ||
abrockman | 0 | 2,526,956,847,820 | 100% | ||
ravenmus1c | 0 | 42,517,970,357 | 2% | ||
adamada | 0 | 39,781,447,675 | 20% | ||
sbi2 | 0 | 4,215,147,726,707 | 100% | ||
awesomegames007 | 0 | 1,282,193,526 | 50% | ||
sbi3 | 0 | 2,025,870,116,955 | 100% | ||
beco132 | 0 | 1,791,781,784 | 54% | ||
sbi4 | 0 | 1,284,458,253,165 | 100% | ||
fw206 | 0 | 2,793,784,845,961 | 32% | ||
sbi5 | 0 | 1,040,739,835,705 | 100% | ||
sbi6 | 0 | 806,203,133,722 | 100% | ||
bestofph | 0 | 1,779,529,510 | 20% | ||
teamvn | 0 | 7,718,272,457 | 5.93% | ||
sbi7 | 0 | 600,983,352,082 | 100% | ||
voter002 | 0 | 26,699,103,386 | 63.8% | ||
voter000 | 0 | 26,700,083,234 | 53.9% | ||
mk992039 | 0 | 660,815,583 | 4% | ||
guurry123 | 0 | 7,212,663,018 | 10% | ||
ecoinstats | 0 | 690,957,416,487 | 100% | ||
sbi8 | 0 | 437,001,943,199 | 100% | ||
sbi9 | 0 | 315,806,337,204 | 100% | ||
followjohngalt | 0 | 15,601,365,933 | 100% | ||
sbi10 | 0 | 243,782,532,328 | 100% | ||
jacuzzi | 0 | 672,883,616 | 1.4% | ||
slothlydoesit | 0 | 19,112,403,266 | 10% | ||
instagram-models | 0 | 501,335,368,374 | 100% | ||
hungrybear | 0 | 609,583,283 | 14% | ||
tinyhousecryptos | 0 | 475,043,393 | 5% | ||
gurseerat | 0 | 5,407,864,370 | 20% | ||
lrekt01 | 0 | 6,592,552,937 | 80% | ||
tiffin | 0 | 68,028,961,801 | 100% | ||
everythingsmgirl | 0 | 7,749,855,641 | 50% | ||
lisamgentile1961 | 0 | 8,479,252,008 | 3% | ||
sbi-tokens | 0 | 38,938,626,124 | 49.76% | ||
stem.alfa | 0 | 3,205,938,234 | 50% | ||
qwertm | 0 | 4,878,033,089 | 50% | ||
manclar | 0 | 8,437,493,956 | 50% | ||
neoxvoter | 0 | 2,948,252,547 | 25% | ||
bilpcoinbpc | 0 | 887,174,202 | 5% | ||
treasure.hoard | 0 | 449,654,133,628 | 100% | ||
dpend.active | 0 | 3,801,301,533 | 10% | ||
hivetrending | 0 | 87,293,093,214 | 30% | ||
ykretz | 0 | 1,436,803,851 | 15% | ||
sketching | 0 | 6,944,044,213 | 50% | ||
kiemis | 0 | 9,004,933,149 | 2.5% | ||
invest4free | 0 | 3,759,125,332 | 50% | ||
woelfchen | 0 | 124,213,009,434 | 32% | ||
archon-gov | 0 | 110,995,015,711 | 50% | ||
goliathus | 0 | 817,238,660 | 10% | ||
rudy-dj | 0 | 2,710,822,450 | 30% | ||
captaincryptic | 0 | 31,431,019,807 | 20% | ||
chaos23 | 0 | 1,710,225,183 | 25% | ||
ronasoliva1104 | 0 | 1,501,882,090 | 20% | ||
dadspardan | 0 | 14,035,301,702 | 20% | ||
youloseagain | 0 | 651,390,323 | 5% | ||
szukamnemo | 0 | 26,119,179,518 | 8% | ||
esmeesmith | 0 | 4,095,019,116 | 50% | ||
eturnerx-dbuzz | 0 | 26,689,295,608 | 71.8% | ||
huzzah | 0 | 12,178,108,756 | 40% | ||
emsenn0 | 0 | 4,047,515,544 | 20% | ||
cooperclub | 0 | 5,904,851,332 | 20% | ||
creodas | 0 | 988,910,806 | 30% | ||
cryptoniusrex | 0 | 43,565,004,220 | 100% | ||
wizzitywillican | 0 | 1,528,787,758 | 20% | ||
hive.pizza | 0 | 1,761,363,110,374 | 40% | ||
hive.friends | 0 | 0 | 1% | ||
holovision.stem | 0 | 1,054,037,812 | 50% | ||
thecbp-hiver | 0 | 1,332,413,000 | 20% | ||
john9inch | 0 | 693,768,103 | 20% | ||
szmobacsi | 0 | 818,375,219 | 36% | ||
andriko | 0 | 581,211,724 | 40% | ||
trashyomen | 0 | 1,229,632,732 | 75% | ||
wongi | 0 | 31,447,667,845 | 50% | ||
adulruna | 0 | 22,959,799,626 | 100% | ||
banzafahra | 0 | 3,388,525,241 | 20% | ||
mirroredspork | 0 | 21,551,443,935 | 100% | ||
vrezyy | 0 | 9,039,955,640 | 25% | ||
tub3r0 | 0 | 763,626,588 | 10% | ||
mxm0unite | 0 | 1,610,209,980 | 50% | ||
ray5fan | 0 | 11,027,765,964 | 100% | ||
mcsherriff | 0 | 16,580,210,291 | 100% | ||
techguard | 0 | 507,682,063 | 8% | ||
thedoc07 | 0 | 2,025,971,966 | 20% | ||
tydynrain | 0 | 15,587,436,377 | 10% | ||
chaosmagic23 | 0 | 4,043,311,498 | 50% | ||
svanbo | 0 | 2,101,138,450 | 1% | ||
herman-german | 0 | 7,942,196,505 | 50% | ||
lothbrox | 0 | 856,659,185 | 100% | ||
rulivyxo | 0 | 548,669,472 | 30% | ||
highfist | 0 | 1,699,930,668 | 20% | ||
the-pockets | 0 | 1,166,822,275 | 20% | ||
chrisly.social | 0 | 67,738,798,080 | 50% | ||
ryosai | 0 | 5,715,380,234 | 24% | ||
boeltermc | 0 | 6,830,595,784 | 100% | ||
dalekma | 0 | 657,137,165 | 40% | ||
beststart | 0 | 16,242,402,123 | 5% | ||
fonestreet | 0 | 1,955,068,169 | 25% | ||
holdeck2 | 0 | 1,642,533,312 | 100% | ||
vrezion | 0 | 576,624,391 | 100% | ||
casimodo | 0 | 938,537,222 | 100% | ||
poplar-22 | 0 | 3,549,921,624 | 25% | ||
monsterrerentals | 0 | 33,387,826,360 | 100% | ||
deggial | 0 | 1,609,135,928 | 100% | ||
ngwinndave | 0 | 14,530,934,745 | 50% | ||
masterfarmer | 0 | 587,359,961 | 40% | ||
kasih-sayang | 0 | 891,903,515 | 30% | ||
pof.archon | 0 | 469,847,658 | 50% | ||
slothburn | 0 | 1,316,505,819 | 10% | ||
freecompliments | 0 | 3,121,951,136,042 | 100% | ||
georgesantana73 | 0 | 1,102,221,605 | 100% | ||
tengolotodo.leo | 0 | 4,033,526,597 | 50% | ||
hive-140084 | 0 | 82,614,305,194 | 100% | ||
timix648 | 0 | 1,151,170,699 | 70% | ||
fjworld | 0 | 123,764,060,394 | 100% | ||
converter.bonus | 0 | 844,837,906 | 50% | ||
pepetoken | 0 | 740,317,292 | 10% | ||
mviews | 0 | 58,079,410,429 | 100% | ||
ijebest | 0 | 602,279,621 | 5% | ||
bankrobbery | 0 | 629,193,723 | 50% | ||
fc-curation | 0 | 7,482,781,132 | 100% | ||
prohive | 0 | 475,039,414 | 50% | ||
spoonies | 0 | 40,670,405,574 | 100% | ||
fc-rewards | 0 | 4,042,352,625 | 100% | ||
d-a-d | 0 | 10,575,143,138 | 50% | ||
pepe.voter | 0 | 34,731,101,131 | 50% | ||
blessskateshop | 0 | 57,868,821,695 | 12% | ||
claudiavb | 0 | 18,160,837,207 | 50% | ||
fc-arbitration | 0 | 771,931,878 | 100% | ||
murtaza-7868 | 0 | 746,982,310 | 50% | ||
learn2code | 0 | 1,135,718,050 | 50% | ||
lolz.byte | 0 | 0 | 100% | ||
bbarelyseal | 0 | 0 | 100% | ||
indiasierra | 0 | 1,564,630,075 | 50% | ||
partytime.inleo | 0 | 4,517,741,624 | 10% | ||
franco10 | 0 | 5,217,030,508 | 25% | ||
pakx | 0 | 866,439,347,147 | 50% | ||
trovepower | 0 | 2,409,215,177 | 50% | ||
mmbbot | 0 | 2,292,390,313 | 50% | ||
digi-alt | 0 | 6,612,845,094 | 50% | ||
michael561 | 0 | 5,470,162,313 | 20% | ||
thecrazygm.bank | 0 | 6,594,287,134 | 100% | ||
life7clothing | 0 | 467,490,996 | 20% | ||
longhunter | 0 | 1,863,966,587 | 100% | ||
magic.byte | 0 | 0 | 100% | ||
sports.power.bot | 0 | 0 | 0.01% | ||
savvyfrog | 0 | 770,346,958 | 25% |
All the vibe coders should also learn to code with great content like this! We can make a collextion later. !PAKX !PIMP !PIZZA
author | ecoinstant |
---|---|
permlink | re-thecrazygm-sy287o |
category | hive-186392 |
json_metadata | {"tags":["hive-186392"],"app":"peakd/2025.6.1","image":[],"users":[]} |
created | 2025-06-18 16:23:51 |
last_update | 2025-06-18 16:23:51 |
depth | 1 |
children | 1 |
last_payout | 1969-12-31 23:59:59 |
cashout_time | 2025-06-25 16:23:51 |
total_payout_value | 0.000 HBD |
curator_payout_value | 0.000 HBD |
pending_payout_value | 0.019 HBD |
promoted | 0.000 HBD |
body_length | 127 |
author_reputation | 827,966,936,985,061 |
root_title | "Fun with Python: Converting To and From Roman Numerals" |
beneficiaries | [] |
max_accepted_payout | 1,000,000.000 HBD |
percent_hbd | 10,000 |
post_id | 143,430,045 |
net_rshares | 64,871,363,649 |
author_curate_reward | "" |
voter | weight | wgt% | rshares | pct | time |
---|---|---|---|---|---|
thecrazygm | 0 | 64,871,363,649 | 100% |
<center><table><tr></tr><tr><td><center><img src='https://files.peakd.com/file/peakd-hive/pakx/PakX-logo-transparent.png'><p><sup><a href='https://hive-engine.com/?p=market&t=PAKX'>View or trade </a> <code>PAKX</code> tokens.</sup></p></center></td><td><center>@ecoinstant, PAKX has voted the post by @thecrazygm. (2/2 calls)</p><br><br><p>Use !PAKX command if you hold enough balance to call for a @pakx vote on worthy posts! More details available on <a href='/@pakx'>PAKX Blog</a>.</p></center></td></tr></table></center>
author | pakx |
---|---|
permlink | re-ecoinstant-1750263879 |
category | hive-186392 |
json_metadata | "{"tags": ["pakx", "hivepakistan"], "app": "HiveDiscoMod"}" |
created | 2025-06-18 16:24:42 |
last_update | 2025-06-18 16:24:42 |
depth | 2 |
children | 0 |
last_payout | 1969-12-31 23:59:59 |
cashout_time | 2025-06-25 16:24:42 |
total_payout_value | 0.000 HBD |
curator_payout_value | 0.000 HBD |
pending_payout_value | 0.000 HBD |
promoted | 0.000 HBD |
body_length | 524 |
author_reputation | 32,485,921,861 |
root_title | "Fun with Python: Converting To and From Roman Numerals" |
beneficiaries | [] |
max_accepted_payout | 1,000,000.000 HBD |
percent_hbd | 10,000 |
post_id | 143,430,075 |
net_rshares | 0 |
<center>PIZZA! $PIZZA slices delivered: @ecoinstant<sub>(2/20)</sub> tipped @thecrazygm <sub>Come get [MOON](https://moon.hive.pizza)ed!</sub></center>
author | pizzabot |
---|---|
permlink | re-fun-with-python-converting-to-and-from-roman-numerals-20250618t162412z |
category | hive-186392 |
json_metadata | "{"app": "pizzabot"}" |
created | 2025-06-18 16:24:12 |
last_update | 2025-06-18 16:24:12 |
depth | 1 |
children | 0 |
last_payout | 1969-12-31 23:59:59 |
cashout_time | 2025-06-25 16:24:12 |
total_payout_value | 0.000 HBD |
curator_payout_value | 0.000 HBD |
pending_payout_value | 0.000 HBD |
promoted | 0.000 HBD |
body_length | 156 |
author_reputation | 7,439,344,392,562 |
root_title | "Fun with Python: Converting To and From Roman Numerals" |
beneficiaries | [] |
max_accepted_payout | 1,000,000.000 HBD |
percent_hbd | 10,000 |
post_id | 143,430,057 |
net_rshares | 0 |
While I never learned Roman numerals in depth, I had no idea that they presented such interesting challenges to converting to and from Arabic numerals. Very cool, I learned something! 😁 🙏 💚 ✨ 🤙
author | tydynrain |
---|---|
permlink | re-thecrazygm-2025618t195814502z |
category | hive-186392 |
json_metadata | {"tags":["dev","python","tribes","archon","proofofbrain","pimp"],"app":"ecency/3.2.0-vision","format":"markdown+html"} |
created | 2025-06-19 05:58:18 |
last_update | 2025-06-19 05:58:18 |
depth | 1 |
children | 0 |
last_payout | 1969-12-31 23:59:59 |
cashout_time | 2025-06-26 05:58:18 |
total_payout_value | 0.000 HBD |
curator_payout_value | 0.000 HBD |
pending_payout_value | 0.166 HBD |
promoted | 0.000 HBD |
body_length | 194 |
author_reputation | 199,086,027,820,281 |
root_title | "Fun with Python: Converting To and From Roman Numerals" |
beneficiaries | [] |
max_accepted_payout | 1,000,000.000 HBD |
percent_hbd | 10,000 |
post_id | 143,440,893 |
net_rshares | 575,730,700,622 |
author_curate_reward | "" |
voter | weight | wgt% | rshares | pct | time |
---|---|---|---|---|---|
thecrazygm | 0 | 74,621,759,533 | 100% | ||
ecoinstant | 0 | 69,750,994,595 | 100% | ||
fatman | 0 | 9,258,397,372 | 2% | ||
votehero | 0 | 269,613,763,312 | 54% | ||
msp-makeaminnow | 0 | 26,685,563,471 | 29.1% | ||
investegg | 0 | 26,661,855,661 | 14.1% | ||
promo-mentors | 0 | 708,764,080 | 100% | ||
we-are-lucky | 0 | 44,706,941,437 | 100% | ||
voter001 | 0 | 26,675,264,497 | 28.6% | ||
voter000 | 0 | 26,671,059,331 | 55.1% | ||
savvytester | 0 | 376,337,333 | 100% |