Modernize ircnukes: Python 3, typed, irc library, unit tests#1
Modernize ircnukes: Python 3, typed, irc library, unit tests#1giannitedesco with Copilot wants to merge 7 commits into
Conversation
- Port all code from Python 2 to Python 3 - Add mypy --strict compatible type hints throughout - Add Google-style docstrings to all public APIs - Replace irclib with the modern irc (jaraco/irc) library - Refactor nukebot.py as irc.bot.SingleServerIRCBot subclass - Add nukes/globals.py with constants and exceptions - Fix circular imports using TYPE_CHECKING guards - Add 36 unit tests in tests/test_game.py - Add requirements.txt and .gitignore Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Restore missing apocalypse continuation message in warhead.py - Restore missing 'misses a turn' notification in warhead.py - Improve test_missile_deploys_as_weapon with explicit weapon assertion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: giannitedesco <221359+giannitedesco@users.noreply.github.com>
| NUKE_YIELD_50MT: int = 50 | ||
| NUKE_YIELD_75MT: int = 75 | ||
| NUKE_YIELD_100MT: int = 100 | ||
| NUKE_YIELD_200MT: int = 200 |
There was a problem hiding this comment.
yeah, or solve this as a type where int(x) is self.yield and x.popdead is the death calculation, could be a NamedTuple perhaps
There was a problem hiding this comment.
Done in d7c20ac. Added NukeYield(IntEnum) where int(NukeYield.MT100) == 100 and NukeYield.MT100.popdead == 25. The .popdead property uses a module-level lookup dict, so warhead.py's separate _BODYCOUNTS dict is gone. Old NUKE_YIELD_* aliases kept for compatibility.
| """Player is fully eliminated.""" | ||
|
|
||
| CARD_STACK_LEN: int = 2 | ||
| """Number of cards in a player's queue.""" |
There was a problem hiding this comment.
Done in d7c20ac. Added GameState(IntEnum) (INIT/PEACE/WAR/OVER) and PlayerState(IntEnum) (ALIVE/RETALIATE/DEAD). The old GAME_STATE_* / PLAYER_STATE_* names are kept as backward-compatible aliases pointing at the enum members.
| from card import card | ||
| from globals import * | ||
|
|
||
| class propaganda(card): |
There was a problem hiding this comment.
Use PEP8 style, eg. for classes, everything should pass flake8 / bandit etc..
There was a problem hiding this comment.
Done in d7c20ac. All game classes renamed to PascalCase: Card, Deck, Player, Game, Warhead, Missile, Bomber, Propaganda, IrcNukes. flake8 --max-line-length=100 (with pep8-naming and flake8-bandit) now exits 0.
| NUKE_YIELD_50MT: 15, | ||
| NUKE_YIELD_75MT: 20, | ||
| NUKE_YIELD_100MT: 25, | ||
| NUKE_YIELD_200MT: 50, |
There was a problem hiding this comment.
bodycounts can probably be encoded in a yield enum
There was a problem hiding this comment.
Done in d7c20ac. _BODYCOUNTS removed; Warhead.calc_fallout now uses self.megatons.popdead directly from the NukeYield enum.
| from card import card | ||
| from globals import * | ||
|
|
||
| class missile(card): |
There was a problem hiding this comment.
Done in d7c20ac — missile is now Missile (along with all other game classes being renamed to PascalCase).
- Replace GAME_STATE_* and PLAYER_STATE_* int constants with GameState and PlayerState IntEnum classes in nukes/globals.py - Replace NUKE_YIELD_* constants and _BODYCOUNTS dict with NukeYield(IntEnum) carrying a popdead property in nukes/globals.py - Rename all game classes to PascalCase: card->Card, deck->Deck, player->Player, game->Game, warhead->Warhead, missile->Missile, bomber->Bomber, propaganda->Propaganda, ircnukes->IrcNukes - Remove internal-invariant asserts; replace with proper exceptions - Add # noqa: S311 to intentional random.randint calls - Remove unused imports from game.py - Keep backward-compat lowercase aliases in all modules and __init__.py - Update nukebot.py, nukage.py, and tests/test_game.py to use new names Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…om deck - Rename test_kill_no_game -> test_terminate_no_game to match method name - Restore weapon: Any | None annotation for type clarity - Add None guard in Game.__get_pop() to handle empty population deck Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add GameState(IntEnum), PlayerState(IntEnum) replacing int constants - Add NukeYield(IntEnum) with .popdead property; remove _BODYCOUNTS dict - Rename all game classes to PascalCase (Card, Deck, Player, Game, Warhead, Missile, Bomber, Propaganda, IrcNukes) - Keep backward-compat lowercase aliases in each module and __init__ - Fix flake8: remove unused imports, replace asserts with proper exceptions, add noqa: S311 for intentional game randomness - Fix game.__get_pop() return type annotation (int | None) - Raise GameLogicError in calc_fallout when player has no game Co-authored-by: giannitedesco <221359+giannitedesco@users.noreply.github.com>
The codebase was written for Python 2 using a defunct
ircliblibrary. This PR ports everything to Python 3.14-compatible code, wires up the modernirc(jaraco) library, addsmypy --stricttype annotations, docstrings, and a unit test suite.nukes/ package
from globals import *→from .globals import *); explicit__all__in__init__.pyhas_key(), barefilter()/map(),except E, e:syntax throughoutTYPE_CHECKINGguardsCard,Deck,Player,Game,Warhead,Missile,Bomber,Propaganda; backward-compatible lowercase aliases retainedGameState(IntEnum)andPlayerState(IntEnum)NukeYield(IntEnum)carrying a.popdeadproperty (base population kill); standalone_BODYCOUNTSdict removedflake8(withpep8-namingandflake8-bandit); asserts replaced with proper exceptions;# noqa: S311on intentional game-randomnessrandom.randintcallsnukebot.py — irc library migration
irc.bot.SingleServerIRCBotsubclass; event handlers useon_*methodsev.source/ev.target/ev.arguments[0](attributes, not calls)NUKEBOT_NICK,NUKEBOT_HOST,NUKEBOT_CHAN, etc.)TokenBucketported to usetime.monotonic()IrcNukesclass (renamed fromircnukes) wraps the game engineircnukes.py
irclib.irc_lower→irc.strings.lowerprivmsgcallable typed asCallable[[str, str], None];None-safe guards addedTesting & tooling
tests/test_game.pycovering player, deck, game state machine, all card types, exceptions, and the IRC wrapperrequirements.txt:irc>=20.5.0,mypy>=1.0.0.gitignoreaddedOriginal prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.