Text Justification
Level: Advanced 60–90 minConcepts: StringsAlgorithms
Solutions: C# | TypeScript | Python
Create a program that justifies text to fit a specified width.
Requirements
- Implement text justification rules:
- Break text into lines of specified width
- Distribute spaces evenly between words
- Left-align the last line of text
- Handle single words per line
- Handle edge cases:
- Empty input
- Input shorter than line width
- Words longer than line width
- Multiple consecutive spaces
- Return appropriate results:
- Justified text as an array of strings
- Error message for invalid inputs
Test Cases
| Input | Width | Expected Output | Notes |
|---|---|---|---|
| ”This is a test” | 16 | [“This is a”, “test”] | Even space distribution |
| ”This is a test” | 14 | [“This is a”, “test”] | Uneven space distribution |
| ”This is a test” | 20 | [“This is a test”] | No justification needed |
| ”This is a very long word” | 10 | [“This is a”, “very long”, “word”] | Multiple line breaks |
| ”Word” | 10 | [“Word”] | Single word |
| ”This is a test” | 16 | [“This is a”, “test”] | Handle multiple spaces |
Edge Cases to Consider
- Empty string
- Null input
- Zero or negative width
- Very long words
- Multiple consecutive spaces or tabs
- Text with line breaks
Tips
- Start with simple cases (no justification needed)
- Add basic line breaking next
- Then implement space distribution
- Finally add special case handling
- Consider using a helper method for space distribution
Reference Walkthrough
Reference implementations in C#, TypeScript, and Python live at tddbuddy-reference-katas/text-justification. This is an F1 kata — ten scenarios covering empty and whitespace-only input, single-word lines, multi-line greedy packing, uneven and even space distribution, collapsing consecutive whitespace, single-word right-padding, and oversize words — shared across all three languages, each a single pure function justify(text, width).
- C# (.NET 8, xUnit, FluentAssertions 6.12.0) — walkthrough
- TypeScript (Node 20, Vitest 1.6, TS 5 strict) — walkthrough
- Python (3.11, pytest) — walkthrough
This kata ships in Agent Full-Bake mode at high gear: the rules land as one commit per language, with a brief walkthrough noting there are no builders because the inputs and outputs are the domain. A greedy line-packer accumulates words, distributes padding evenly across the gaps (remainder left-heavy, the LeetCode-68 convention), right-pads single-word non-last lines, lets oversize words overflow rather than splitting them mid-word, and emits the last line left-aligned with single spaces — not right-padded, matching the typeset convention that the closing line of a paragraph sits flush-left. See the repo’s Gears section for when high gear is the right call.
Related reading
- Katas Are Rehearsal, Not Performance
You don't practice TDD on production code. You practice on katas and bring the muscle memory to production. The gap between knowing TDD and doing TDD is reps. - Test Deletion Is a Privileged Operation
The cheapest way for an agent to make a failing test pass is to delete it. That is logical for the agent and catastrophic for the codebase. Tests are append-only by default. Deletion needs a human author, a separate commit, and a separate review. - Refactor Was Always Where Compounding Lived
Red and green were the visible two thirds of TDD. Refactor was the optional polish. Codebases that compound did the refactor pass; codebases that accrete did not. Agents skip refactor by default because it has no green-bar reward, and a year of skipped refactors is how a codebase loses its shape.