From e54664803e83004f371c9ba1ee79f20db16696d2 Mon Sep 17 00:00:00 2001 From: Daniel McCoy Stephenson Date: Mon, 21 Sep 2026 01:02:58 -0600 Subject: [PATCH 1/2] Replace the ordinal ladder and make the keyword count a parameter The four-branch if/elif chain in getKeywords() appended "th" to every number past 3, so raising the count would have produced "21th". A small ordinal() method now returns the correct suffix for any number, and the count is a constructor parameter (numKeywords, default 10) instead of a local literal. The ten prompts the program prints today are unchanged. Co-Authored-By: Claude Opus 5 (1M context) --- src/ideaCollisionGenerator.py | 36 +++++++++++++------------ tests/test_ideaCollisionGenerator.py | 39 ++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/ideaCollisionGenerator.py b/src/ideaCollisionGenerator.py index 406ac5f..d5cdd9a 100644 --- a/src/ideaCollisionGenerator.py +++ b/src/ideaCollisionGenerator.py @@ -3,27 +3,31 @@ import os class IdeaCollisionGenerator: - def __init__(self): + def __init__(self, numKeywords=10): + self.numKeywords = numKeywords self.keywords = [] self.pairs = [] self.ideas = [] + def ordinal(self, n): + # 1st, 2nd, 3rd, 4th ... 11th, 12th, 13th ... 21st, 22nd, 23rd + if n % 100 in (11, 12, 13): + suffix = "th" + elif n % 10 == 1: + suffix = "st" + elif n % 10 == 2: + suffix = "nd" + elif n % 10 == 3: + suffix = "rd" + else: + suffix = "th" + return str(n) + suffix + def getKeywords(self): - # get 10 keywords from user - numKeywords = 10 - for i in range(numKeywords): - if i == 0: - keyword = input("Enter 1st keyword: ") - self.keywords.append(keyword) - elif i == 1: - keyword = input("Enter 2nd keyword: ") - self.keywords.append(keyword) - elif i == 2: - keyword = input("Enter 3rd keyword: ") - self.keywords.append(keyword) - else: - keyword = input("Enter " + str(i+1) + "th keyword: ") - self.keywords.append(keyword) + # get keywords from user + for i in range(self.numKeywords): + keyword = input("Enter " + self.ordinal(i+1) + " keyword: ") + self.keywords.append(keyword) # randomize keywords random.shuffle(self.keywords) diff --git a/tests/test_ideaCollisionGenerator.py b/tests/test_ideaCollisionGenerator.py index 9d8cbf2..f534587 100644 --- a/tests/test_ideaCollisionGenerator.py +++ b/tests/test_ideaCollisionGenerator.py @@ -38,6 +38,32 @@ def recordingInput(prompt): self.assertEqual(prompts[3], "Enter 4th keyword: ") self.assertEqual(prompts[9], "Enter 10th keyword: ") + def testPromptsAreCorrectPastTwenty(self): + generator = IdeaCollisionGenerator(numKeywords=23) + prompts = [] + + def recordingInput(prompt): + prompts.append(prompt) + return "keyword" + + with mock.patch("builtins.input", recordingInput): + with mock.patch("random.shuffle"): + generator.getKeywords() + + self.assertEqual(prompts[20], "Enter 21st keyword: ") + self.assertEqual(prompts[21], "Enter 22nd keyword: ") + self.assertEqual(prompts[22], "Enter 23rd keyword: ") + + def testKeywordCountIsConfigurable(self): + generator = IdeaCollisionGenerator(numKeywords=4) + with mock.patch("builtins.input", side_effect=["a", "b", "c", "d"]): + with mock.patch("random.shuffle"): + generator.getKeywords() + self.assertEqual(generator.keywords, ["a", "b", "c", "d"]) + + def testKeywordCountDefaultsToTen(self): + self.assertEqual(IdeaCollisionGenerator().numKeywords, 10) + def testKeywordsAreShuffled(self): generator = IdeaCollisionGenerator() with mock.patch("builtins.input", side_effect=[str(i) for i in range(10)]): @@ -54,6 +80,19 @@ def testEmptyKeywordsAreAccepted(self): self.assertEqual(generator.keywords, [""] * 10) +class TestOrdinal(unittest.TestCase): + def testSuffixes(self): + generator = IdeaCollisionGenerator() + expected = { + 1: "1st", 2: "2nd", 3: "3rd", 4: "4th", 10: "10th", + 11: "11th", 12: "12th", 13: "13th", 14: "14th", + 21: "21st", 22: "22nd", 23: "23rd", 24: "24th", + 101: "101st", 111: "111th", 112: "112th", 113: "113th", 121: "121st", + } + for n, text in expected.items(): + self.assertEqual(generator.ordinal(n), text) + + class TestCreatePairs(unittest.TestCase): def testPairsAdjacentKeywords(self): generator = IdeaCollisionGenerator() From 7e31fd5b713c21de9d584f03cc65771cb7b37f7c Mon Sep 17 00:00:00 2001 From: Daniel McCoy Stephenson Date: Mon, 21 Sep 2026 01:05:14 -0600 Subject: [PATCH 2/2] Reject an odd or non-positive keyword count in the constructor createPairs() reads keywords two at a time, so an odd numKeywords would reach the IndexError tracked in #9 several calls later. The constructor now raises ValueError for odd or non-positive counts instead. Co-Authored-By: Claude Opus 5 (1M context) --- src/ideaCollisionGenerator.py | 3 +++ tests/test_ideaCollisionGenerator.py | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ideaCollisionGenerator.py b/src/ideaCollisionGenerator.py index d5cdd9a..6c98bcd 100644 --- a/src/ideaCollisionGenerator.py +++ b/src/ideaCollisionGenerator.py @@ -4,6 +4,9 @@ class IdeaCollisionGenerator: def __init__(self, numKeywords=10): + # keywords are matched in pairs, so the count has to be even + if numKeywords < 2 or numKeywords % 2 != 0: + raise ValueError("numKeywords must be a positive even number") self.numKeywords = numKeywords self.keywords = [] self.pairs = [] diff --git a/tests/test_ideaCollisionGenerator.py b/tests/test_ideaCollisionGenerator.py index f534587..22ada0f 100644 --- a/tests/test_ideaCollisionGenerator.py +++ b/tests/test_ideaCollisionGenerator.py @@ -39,7 +39,7 @@ def recordingInput(prompt): self.assertEqual(prompts[9], "Enter 10th keyword: ") def testPromptsAreCorrectPastTwenty(self): - generator = IdeaCollisionGenerator(numKeywords=23) + generator = IdeaCollisionGenerator(numKeywords=24) prompts = [] def recordingInput(prompt): @@ -64,6 +64,15 @@ def testKeywordCountIsConfigurable(self): def testKeywordCountDefaultsToTen(self): self.assertEqual(IdeaCollisionGenerator().numKeywords, 10) + def testOddKeywordCountIsRejected(self): + # createPairs() needs an even count, so it is refused up front + with self.assertRaises(ValueError): + IdeaCollisionGenerator(numKeywords=3) + + def testNonPositiveKeywordCountIsRejected(self): + with self.assertRaises(ValueError): + IdeaCollisionGenerator(numKeywords=0) + def testKeywordsAreShuffled(self): generator = IdeaCollisionGenerator() with mock.patch("builtins.input", side_effect=[str(i) for i in range(10)]):