Roman Numerals

Level: Beginner 15–30 min

Concepts: AlgorithmsNumbers

Solutions: C# | TypeScript | Python


Create a converter that turns a positive integer (1–3999) into its Roman numeral string.

Requirements

  1. Convert Arabic numbers to Roman numerals in the range 1 to 3999.
  2. Follow standard Roman numeral rules:
    • I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000
    • When a smaller value precedes a larger value, subtract the smaller (e.g., IV = 4, IX = 9, XL = 40, XC = 90, CD = 400, CM = 900)
    • Only the six subtractive pairs above appear; no IL, IC, IM, etc.

Test Cases

Arabic NumberRoman NumeralNotes
1IBasic conversion
4IVSubtractive — gear-shift moment
9IXSubtractive
40XLSubtractive at tens
90XCSubtractive at tens
400CDSubtractive at hundreds
900CMSubtractive at hundreds
1984MCMLXXXIVComposite across four orders of magnitude
3999MMMCMXCIXMaximum valid number

Stretch Goals (Not in the Reference)

The reference implementations cover Arabic → Roman conversion for the nine scenarios above and stop. If you want to extend further:

  • Roman → Arabic (the reverse direction)
  • Input validation — rejecting numbers less than 1 or greater than 3999, non-integers, etc.
  • Roman-input validation — invalid characters, repeated symbols beyond three (e.g., “IIII”, “VV”), invalid subtractive combinations (e.g., “IL”, “IC”)

The canonical TDD teaching arc for this kata is Arabic → Roman only — that is the direction where the subtractives-in-the-table insight lands cleanly. The reverse direction and validation each deserve their own kata session.

Tips

  • Start with the simplest conversions first (1, 2, 3).
  • Notice when 4 arrives that “concatenate I n times” is a dead end — the table of (value, symbol) pairs with subtractives baked in is the move.
  • Add larger values gradually (10, 40, 100, 400, 1000) — the subtractives (9, 90, 900) should pass on arrival once the table has them.

Reference Walkthrough

Full C#, TypeScript, and Python implementations live at tddbuddy-reference-katas/roman-numerals — the same nine scenarios across all three languages, walked commit-by-commit through the TDD cycle.

The reference covers Arabic → Roman only — the reverse direction and input validation are stretch goals and are not implemented there.

This is a Pedagogy mode kata — the walkthroughs step through the TDD cycle commit-by-commit. Watch the gear shift from low (fake-it, hardcoded branches, input-keyed dictionary) to middle once the 4 → IV scenario forces the refactor. The teaching point is the table that beats special cases: triangulating 1 → I, 2 → II, 3 → III tempts you into “repeat I n times”, and 4 breaks it flat. The right move is to promote the lookup from {input → output} to an ordered list of (value, symbol) pairs with subtractives baked in as first-class entries — [(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")] — and a greedy subtract loop. The payoff is the chain of five spec — commits at the end: 9, 90, 900, 1984, and 3999 all pass on arrival once the table is complete. Subtractives are not exceptions to the rule; they are entries in the table. See the repo’s Gears section for why middle gear is where the table lands.