Skip to content

Replace the ordinal ladder and make the keyword count a parameter - #20

Merged
dmccoystephenson merged 2 commits into
mainfrom
feature/ordinal-and-keyword-count
Sep 21, 2026
Merged

dmccoystephenson merged 2 commits into
mainfrom
feature/ordinal-and-keyword-count

Conversation

@dmccoystephenson

@dmccoystephenson dmccoystephenson commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

Summary

  • The four-branch if/elif/elif/else ordinal ladder in getKeywords() has been replaced by a small ordinal() method that returns the correct suffix for any number (1st, 2nd, 3rd, 4th, 11th–13th, 21st, 22nd, 23rd, 111th, 121st, …). The old else branch appended "th" unconditionally, so any count past 20 would have produced 21th.
  • The keyword count is no longer a local literal. IdeaCollisionGenerator now takes a numKeywords constructor parameter, defaulting to 10, and getKeywords() reads self.numKeywords. The CLI entry point is untouched and still collects ten.
  • Because createPairs() reads keywords two at a time, the constructor raises ValueError for an odd or non-positive count. This was added after the inline review below pointed out that an odd numKeywords would otherwise reach the IndexError tracked in createPairs() raises IndexError when the keyword count is odd #9 several calls later; rejecting the configuration up front does not decide what a trailing odd keyword should mean, so createPairs() raises IndexError when the keyword count is odd #9 is left as filed.
  • Six tests were added to tests/test_ideaCollisionGenerator.py: a table of ordinal suffixes, the prompts for the 21st–23rd keywords produced through getKeywords() itself, a four-keyword generator collecting four keywords, the default of ten, and the two ValueError cases.

The ten prompts printed today are byte-identical to before — testPromptsUseOrdinalLadder (unchanged) still asserts Enter 1st keyword: through Enter 10th keyword: , and the end-to-end run below shows the same text. No prompt string, output line format, or filename has been altered, so README.md and ideas/example.txt remain accurate as written.

Closes #10

Scope notes

Test plan

All commands were run from the repository root on Python 3.8.10 during this session.

  • python3 -m py_compile src/collide.py src/ideaCollisionGenerator.py → compile OK
  • python3 -m unittest discover -s tests -v → Ran 24 tests in 0.008s / OK (18 before this branch, 24 after)
  • Regression evidence, by stashing only the production change, once per commit:
$ git stash push -- src/ideaCollisionGenerator.py     # first commit: ordinal + parameter
$ python3 -m unittest discover -s tests
ERROR: testKeywordCountDefaultsToTen (test_ideaCollisionGenerator.TestGetKeywords)
ERROR: testKeywordCountIsConfigurable (test_ideaCollisionGenerator.TestGetKeywords)
ERROR: testPromptsAreCorrectPastTwenty (test_ideaCollisionGenerator.TestGetKeywords)
ERROR: testSuffixes (test_ideaCollisionGenerator.TestOrdinal)
Ran 22 tests in 0.007s
FAILED (errors=4)

$ git stash pop
$ python3 -m unittest discover -s tests
Ran 22 tests in 0.009s
OK

$ git stash push -- src/ideaCollisionGenerator.py     # second commit: constructor guard
$ python3 -m unittest discover -s tests
FAIL: testNonPositiveKeywordCountIsRejected (test_ideaCollisionGenerator.TestGetKeywords)
FAIL: testOddKeywordCountIsRejected (test_ideaCollisionGenerator.TestGetKeywords)
Ran 24 tests in 0.008s
FAILED (failures=2)

$ git stash pop
$ python3 -m unittest discover -s tests
Ran 24 tests in 0.008s
OK
  • Full run with all fifteen lines redirected from a fixture (repeated after the second commit; the run below is the first, the second differed only in the shuffled pairing):
$ python3 src/collide.py < collide-ordinal-and-keyword-count-stdin.txt
Enter 1st keyword: Enter 2nd keyword: Enter 3rd keyword: Enter 4th keyword: Enter 5th keyword: Enter 6th keyword: Enter 7th keyword: Enter 8th keyword: Enter 9th keyword: Enter 10th keyword: Enter an idea based off of these keywords: ['world modification', 'social mobility']
Enter an idea: Enter an idea based off of these keywords: ['antagonist', 'interaction']
Enter an idea: Enter an idea based off of these keywords: ['mining', 'branching choices']
Enter an idea: Enter an idea based off of these keywords: ['exploration', 'community']
Enter an idea: Enter an idea based off of these keywords: ['complex relationships', 'fishing']
Enter an idea:

$ wc -l ideas/ideas-2026-09-21_01.02.43.txt
5 ideas/ideas-2026-09-21_01.02.43.txt

$ cat ideas/ideas-2026-09-21_01.02.43.txt
['world modification', 'social mobility']: An innkeeper who buys fish from the docks.
['antagonist', 'interaction']: Tunnels that stay dug between sessions.
['mining', 'branching choices']: A village council that votes on newcomers.
['exploration', 'community']: A rival who remembers every slight.
['complex relationships', 'fishing']: A map that only fills in when walked.

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

🤖 Generated with Claude Code


drafted by Claude on behalf of Daniel Stephenson

dmccoystephenson and others added 2 commits September 21, 2026 01:02
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review rubric

Scored against the diff at 7e31fd5 and the command output recorded in the PR body. The inline review was performed in the same session (no independent reviewer is configured on this repository); its one finding — that the new numKeywords parameter made the #9 IndexError reachable without any validation — was accepted and addressed in the second commit.

Universal

  • Scope: PASS — two files changed (src/ideaCollisionGenerator.py, tests/test_ideaCollisionGenerator.py), both required by getKeywords() ordinal ladder produces "21th" and the keyword count is a magic number #10. No formatting, rename, or comment churn outside the replaced block.
  • Tests-new: PASS — ordinal() is covered by testSuffixes (18 values including the teen and 101/111/121 cases); the numKeywords parameter by testKeywordCountIsConfigurable, testKeywordCountDefaultsToTen, testOddKeywordCountIsRejected, testNonPositiveKeywordCountIsRejected; the past-20 prompts through getKeywords() by testPromptsAreCorrectPastTwenty.
  • Tests-fix: PASS — stash-and-run performed once per commit, output in the PR body: 4 errors with the first commit stashed, 2 failures with the second stashed, OK with each restored.
  • Sibling structure: PASS — new tests follow the existing mock.patch("builtins.input", …) + mock.patch("random.shuffle") pattern and camelCase test names; a new TestOrdinal class sits beside the existing per-method classes.
  • Sibling renames: PASS — no identifier was renamed.
  • Docs: PASS — README.md states "Ten keywords are collected" and quotes Enter 1st keyword: … Enter 10th keyword:; both remain true because the CLI still constructs the generator with the default. ideas/example.txt is untouched and the output format is unchanged.
  • Issue resolution: PASS — both halves of getKeywords() ordinal ladder produces "21th" and the keyword count is a magic number #10 are addressed: the ladder is gone (testPromptsAreCorrectPastTwenty asserts 21st, 22nd, 23rd) and the count is a constructor parameter.
  • Manual validation: PASS — py_compile clean; Ran 24 tests / OK; a full 15-line redirected run printed the ten prompts and wrote a 5-line file. CI (Tests, Python 3.8 and 3.13) was pending at the time of writing and is re-checked before merge.

Repo-specific

  • Ran, not read: PASS — every prompt line and output line quoted in the PR body came from the run recorded there (ideas/ideas-2026-09-21_01.02.43.txt).
  • Stdin always redirected: PASS — the only invocation is python3 src/collide.py < collide-ordinal-and-keyword-count-stdin.txt, a 15-line fixture.
  • camelCase preserved: PASS — numKeywords, ordinal, getKeywords unchanged in style; no snake_case introduced.
  • Stdlib only: PASS — no new import.
  • Python 3.8-safe: PASS — plain if/elif, %, tuple membership; verified on 3.8.10.
  • Output contract preserved: PASS — writeToFile() untouched; the run wrote ['a', 'b']: idea lines to ideas/ideas-<timestamp>.txt.
  • Prompt strings intact: PASS — the printed text for keywords 1–10 is byte-identical (the unchanged testPromptsUseOrdinalLadder asserts five of them; the run shows all ten). The source literals were replaced by a single expression, which is the point of getKeywords() ordinal ladder produces "21th" and the keyword count is a magic number #10; the observable interface is what the do-not-auto-merge entry protects, and it did not change.
  • Entry point still runnable: PASS — src/collide.py untouched; ran end to end twice this session.
  • License claims consistent: PASS — no file naming a licence, owner, or URL was touched.
  • No artifacts committed: PASS — git status after staging showed only the two intended files; the generated ideas/ideas-*.txt files are ignored and the scratch fixtures are untracked and removed after this comment.
  • No inflation: PASS — one new 12-line method and one constructor parameter; no packaging, framework, or directory added.

Judgement call flagged for the owner: the constructor guard's message and its < 2 floor are a design choice made this session, not one #10 asked for. A two-keyword run (one pair) is allowed; zero and odd counts are refused. If a one-element trailing pair is later chosen for #9, the guard's odd-count branch would simply be removed.

Path-anchored notes (no inline comments were posted — see the harness note in the PR body's Gardener line):

  • src/ideaCollisionGenerator.py:9 — ValueError raised for odd/non-positive numKeywords; see the judgement call above.
  • tests/test_ideaCollisionGenerator.py:42 — the past-20 test uses 24 keywords rather than 23 because of that guard; it still asserts the 21st–23rd prompts.

This comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

@dmccoystephenson
dmccoystephenson merged commit 74b9d12 into main Sep 21, 2026
2 checks passed
@dmccoystephenson
dmccoystephenson deleted the feature/ordinal-and-keyword-count branch September 21, 2026 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

getKeywords() ordinal ladder produces "21th" and the keyword count is a magic number

1 participant