From 260e40a9d99f793859d5e1330fcb4b26e280d029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Thu, 22 Jan 2026 21:11:42 -0800 Subject: [PATCH] Add `line-up` exercise --- config.json | 8 ++ .../practice/line-up/.docs/instructions.md | 19 +++ .../practice/line-up/.docs/introduction.md | 7 + exercises/practice/line-up/.meta/config.json | 19 +++ exercises/practice/line-up/.meta/example.py | 19 +++ exercises/practice/line-up/.meta/template.j2 | 10 ++ exercises/practice/line-up/.meta/tests.toml | 67 ++++++++++ exercises/practice/line-up/line_up.py | 2 + exercises/practice/line-up/line_up_test.py | 125 ++++++++++++++++++ 9 files changed, 276 insertions(+) create mode 100644 exercises/practice/line-up/.docs/instructions.md create mode 100644 exercises/practice/line-up/.docs/introduction.md create mode 100644 exercises/practice/line-up/.meta/config.json create mode 100644 exercises/practice/line-up/.meta/example.py create mode 100644 exercises/practice/line-up/.meta/template.j2 create mode 100644 exercises/practice/line-up/.meta/tests.toml create mode 100644 exercises/practice/line-up/line_up.py create mode 100644 exercises/practice/line-up/line_up_test.py diff --git a/config.json b/config.json index ff5fad2c81e..2bbf7486ad5 100644 --- a/config.json +++ b/config.json @@ -511,6 +511,14 @@ ], "difficulty": 1 }, + { + "slug": "line-up", + "name": "Line Up", + "uuid": "e09d877e-489b-4dbd-89c5-f6b15e867b67", + "practices": ["string-formatting"], + "prerequisites": ["basics", "strings"], + "difficulty": 1 + }, { "slug": "difference-of-squares", "name": "Difference of Squares", diff --git a/exercises/practice/line-up/.docs/instructions.md b/exercises/practice/line-up/.docs/instructions.md new file mode 100644 index 00000000000..9e686ecbffb --- /dev/null +++ b/exercises/practice/line-up/.docs/instructions.md @@ -0,0 +1,19 @@ +# Instructions + +Given a name and a number, your task is to produce a sentence using that name and that number as an [ordinal numeral][ordinal-numeral]. +Yaʻqūb expects to use numbers from 1 up to 999. + +Rules: + +- Numbers ending in 1 (unless ending in 11) → `"st"` +- Numbers ending in 2 (unless ending in 12) → `"nd"` +- Numbers ending in 3 (unless ending in 13) → `"rd"` +- All other numbers → `"th"` + +Examples: + +- `"Mary", 1` → `"Mary, you are the 1st customer we serve today. Thank you!"` +- `"John", 12` → `"John, you are the 12th customer we serve today. Thank you!"` +- `"Dahir", 162` → `"Dahir, you are the 162nd customer we serve today. Thank you!"` + +[ordinal-numeral]: https://en.wikipedia.org/wiki/Ordinal_numeral diff --git a/exercises/practice/line-up/.docs/introduction.md b/exercises/practice/line-up/.docs/introduction.md new file mode 100644 index 00000000000..ea07268ae3b --- /dev/null +++ b/exercises/practice/line-up/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +Your friend Yaʻqūb works the counter at a deli in town, slicing, weighing, and wrapping orders for a line of hungry customers that gets longer every day. +Waiting customers are starting to lose track of who is next, so he wants numbered tickets they can use to track the order in which they arrive. + +To make the customers feel special, he does not want the ticket to have only a number on it. +They shall get a proper English sentence with their name and number on it. diff --git a/exercises/practice/line-up/.meta/config.json b/exercises/practice/line-up/.meta/config.json new file mode 100644 index 00000000000..c7417123792 --- /dev/null +++ b/exercises/practice/line-up/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "line_up.py" + ], + "test": [ + "line_up_test.py" + ], + "example": [ + ".meta/example.py" + ] + }, + "blurb": "Help lining up customers at Yaʻqūb's Deli.", + "source": "mk-mxp, based on previous work from Exercism contributors codedge and neenjaw", + "source_url": "https://forum.exercism.org/t/new-exercise-ordinal-numbers/19147" +} diff --git a/exercises/practice/line-up/.meta/example.py b/exercises/practice/line-up/.meta/example.py new file mode 100644 index 00000000000..a847e80c99c --- /dev/null +++ b/exercises/practice/line-up/.meta/example.py @@ -0,0 +1,19 @@ +def line_up(name, number): + suffix = get_suffix(number) + return f"{name}, you are the {number}{suffix} customer we serve today. Thank you!" + + +def get_suffix(number): + if 11 <= number % 100 <= 13: + return "th" + + mod_10 = number % 10 + + if mod_10 == 1: + return "st" + if mod_10 == 2: + return "nd" + if mod_10 == 3: + return "rd" + + return "th" diff --git a/exercises/practice/line-up/.meta/template.j2 b/exercises/practice/line-up/.meta/template.j2 new file mode 100644 index 00000000000..df7283398b5 --- /dev/null +++ b/exercises/practice/line-up/.meta/template.j2 @@ -0,0 +1,10 @@ +{%- import "generator_macros.j2" as macros with context -%} +{{ macros.canonical_ref() }} + +{{ macros.header(imports=['line_up']) }} + +class {{ exercise | camel_case }}Test(unittest.TestCase): + {% for case in cases -%} + def test_{{ case["description"] | to_snake }}(self): + self.assertEqual(line_up("{{ case["input"]["name"] }}", {{ case["input"]["number"] }}), "{{ case["expected"] }}") + {% endfor -%} diff --git a/exercises/practice/line-up/.meta/tests.toml b/exercises/practice/line-up/.meta/tests.toml new file mode 100644 index 00000000000..36fdf1d0cd3 --- /dev/null +++ b/exercises/practice/line-up/.meta/tests.toml @@ -0,0 +1,67 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[7760d1b8-4864-4db4-953b-0fa7c047dbc0] +description = "format smallest non-exceptional ordinal numeral 4" + +[e8b7c715-6baa-4f7b-8fb3-2fa48044ab7a] +description = "format greatest single digit non-exceptional ordinal numeral 9" + +[f370aae9-7ae7-4247-90ce-e8ff8c6934df] +description = "format non-exceptional ordinal numeral 5" + +[37f10dea-42a2-49de-bb92-0b690b677908] +description = "format non-exceptional ordinal numeral 6" + +[d8dfb9a2-3a1f-4fee-9dae-01af3600054e] +description = "format non-exceptional ordinal numeral 7" + +[505ec372-1803-42b1-9377-6934890fd055] +description = "format non-exceptional ordinal numeral 8" + +[8267072d-be1f-4f70-b34a-76b7557a47b9] +description = "format exceptional ordinal numeral 1" + +[4d8753cb-0364-4b29-84b8-4374a4fa2e3f] +description = "format exceptional ordinal numeral 2" + +[8d44c223-3a7e-4f48-a0ca-78e67bf98aa7] +description = "format exceptional ordinal numeral 3" + +[6c4f6c88-b306-4f40-bc78-97cdd583c21a] +description = "format smallest two digit non-exceptional ordinal numeral 10" + +[e257a43f-d2b1-457a-97df-25f0923fc62a] +description = "format non-exceptional ordinal numeral 11" + +[bb1db695-4d64-457f-81b8-4f5a2107e3f4] +description = "format non-exceptional ordinal numeral 12" + +[60a3187c-9403-4835-97de-4f10ebfd63e2] +description = "format non-exceptional ordinal numeral 13" + +[2bdcebc5-c029-4874-b6cc-e9bec80d603a] +description = "format exceptional ordinal numeral 21" + +[74ee2317-0295-49d2-baf0-d56bcefa14e3] +description = "format exceptional ordinal numeral 62" + +[b37c332d-7f68-40e3-8503-e43cbd67a0c4] +description = "format exceptional ordinal numeral 100" + +[0375f250-ce92-4195-9555-00e28ccc4d99] +description = "format exceptional ordinal numeral 101" + +[0d8a4974-9a8a-45a4-aca7-a9fb473c9836] +description = "format non-exceptional ordinal numeral 112" + +[06b62efe-199e-4ce7-970d-4bf73945713f] +description = "format exceptional ordinal numeral 123" diff --git a/exercises/practice/line-up/line_up.py b/exercises/practice/line-up/line_up.py new file mode 100644 index 00000000000..c6d20c4e0a7 --- /dev/null +++ b/exercises/practice/line-up/line_up.py @@ -0,0 +1,2 @@ +def line_up(name, number): + pass diff --git a/exercises/practice/line-up/line_up_test.py b/exercises/practice/line-up/line_up_test.py new file mode 100644 index 00000000000..1b593c4ddb6 --- /dev/null +++ b/exercises/practice/line-up/line_up_test.py @@ -0,0 +1,125 @@ +# These tests are auto-generated with test data from: +# https://github.com/exercism/problem-specifications/tree/main/exercises/line-up/canonical-data.json +# File last updated on 2026-01-23 + +import unittest + +from line_up import ( + line_up, +) + + +class LineUpTest(unittest.TestCase): + def test_format_smallest_non_exceptional_ordinal_numeral_4(self): + self.assertEqual( + line_up("Gianna", 4), + "Gianna, you are the 4th customer we serve today. Thank you!", + ) + + def test_format_greatest_single_digit_non_exceptional_ordinal_numeral_9(self): + self.assertEqual( + line_up("Maarten", 9), + "Maarten, you are the 9th customer we serve today. Thank you!", + ) + + def test_format_non_exceptional_ordinal_numeral_5(self): + self.assertEqual( + line_up("Petronila", 5), + "Petronila, you are the 5th customer we serve today. Thank you!", + ) + + def test_format_non_exceptional_ordinal_numeral_6(self): + self.assertEqual( + line_up("Attakullakulla", 6), + "Attakullakulla, you are the 6th customer we serve today. Thank you!", + ) + + def test_format_non_exceptional_ordinal_numeral_7(self): + self.assertEqual( + line_up("Kate", 7), + "Kate, you are the 7th customer we serve today. Thank you!", + ) + + def test_format_non_exceptional_ordinal_numeral_8(self): + self.assertEqual( + line_up("Maximiliano", 8), + "Maximiliano, you are the 8th customer we serve today. Thank you!", + ) + + def test_format_exceptional_ordinal_numeral_1(self): + self.assertEqual( + line_up("Mary", 1), + "Mary, you are the 1st customer we serve today. Thank you!", + ) + + def test_format_exceptional_ordinal_numeral_2(self): + self.assertEqual( + line_up("Haruto", 2), + "Haruto, you are the 2nd customer we serve today. Thank you!", + ) + + def test_format_exceptional_ordinal_numeral_3(self): + self.assertEqual( + line_up("Henriette", 3), + "Henriette, you are the 3rd customer we serve today. Thank you!", + ) + + def test_format_smallest_two_digit_non_exceptional_ordinal_numeral_10(self): + self.assertEqual( + line_up("Alvarez", 10), + "Alvarez, you are the 10th customer we serve today. Thank you!", + ) + + def test_format_non_exceptional_ordinal_numeral_11(self): + self.assertEqual( + line_up("Jacqueline", 11), + "Jacqueline, you are the 11th customer we serve today. Thank you!", + ) + + def test_format_non_exceptional_ordinal_numeral_12(self): + self.assertEqual( + line_up("Juan", 12), + "Juan, you are the 12th customer we serve today. Thank you!", + ) + + def test_format_non_exceptional_ordinal_numeral_13(self): + self.assertEqual( + line_up("Patricia", 13), + "Patricia, you are the 13th customer we serve today. Thank you!", + ) + + def test_format_exceptional_ordinal_numeral_21(self): + self.assertEqual( + line_up("Washi", 21), + "Washi, you are the 21st customer we serve today. Thank you!", + ) + + def test_format_exceptional_ordinal_numeral_62(self): + self.assertEqual( + line_up("Nayra", 62), + "Nayra, you are the 62nd customer we serve today. Thank you!", + ) + + def test_format_exceptional_ordinal_numeral_100(self): + self.assertEqual( + line_up("John", 100), + "John, you are the 100th customer we serve today. Thank you!", + ) + + def test_format_exceptional_ordinal_numeral_101(self): + self.assertEqual( + line_up("Zeinab", 101), + "Zeinab, you are the 101st customer we serve today. Thank you!", + ) + + def test_format_non_exceptional_ordinal_numeral_112(self): + self.assertEqual( + line_up("Knud", 112), + "Knud, you are the 112th customer we serve today. Thank you!", + ) + + def test_format_exceptional_ordinal_numeral_123(self): + self.assertEqual( + line_up("Yma", 123), + "Yma, you are the 123rd customer we serve today. Thank you!", + )