kaboom-engine 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. kaboom_engine-0.1.0/LICENSE +11 -0
  2. kaboom_engine-0.1.0/PKG-INFO +191 -0
  3. kaboom_engine-0.1.0/README.md +181 -0
  4. kaboom_engine-0.1.0/kaboom/__init__.py +32 -0
  5. kaboom_engine-0.1.0/kaboom/cards/__init__.py +4 -0
  6. kaboom_engine-0.1.0/kaboom/cards/card.py +45 -0
  7. kaboom_engine-0.1.0/kaboom/exceptions.py +16 -0
  8. kaboom_engine-0.1.0/kaboom/game/__init__.py +10 -0
  9. kaboom_engine-0.1.0/kaboom/game/actions.py +46 -0
  10. kaboom_engine-0.1.0/kaboom/game/game_state.py +134 -0
  11. kaboom_engine-0.1.0/kaboom/game/phases.py +8 -0
  12. kaboom_engine-0.1.0/kaboom/game/reaction.py +169 -0
  13. kaboom_engine-0.1.0/kaboom/game/results.py +11 -0
  14. kaboom_engine-0.1.0/kaboom/game/turn.py +204 -0
  15. kaboom_engine-0.1.0/kaboom/game/validators.py +14 -0
  16. kaboom_engine-0.1.0/kaboom/players/__init__.py +4 -0
  17. kaboom_engine-0.1.0/kaboom/players/player.py +49 -0
  18. kaboom_engine-0.1.0/kaboom/powers/__init__.py +8 -0
  19. kaboom_engine-0.1.0/kaboom/powers/base.py +14 -0
  20. kaboom_engine-0.1.0/kaboom/powers/blind_swap.py +26 -0
  21. kaboom_engine-0.1.0/kaboom/powers/registry.py +12 -0
  22. kaboom_engine-0.1.0/kaboom/powers/see_and_swap.py +33 -0
  23. kaboom_engine-0.1.0/kaboom/powers/see_other.py +23 -0
  24. kaboom_engine-0.1.0/kaboom/powers/see_self.py +19 -0
  25. kaboom_engine-0.1.0/kaboom/version.py +2 -0
  26. kaboom_engine-0.1.0/kaboom_engine.egg-info/PKG-INFO +191 -0
  27. kaboom_engine-0.1.0/kaboom_engine.egg-info/SOURCES.txt +39 -0
  28. kaboom_engine-0.1.0/kaboom_engine.egg-info/dependency_links.txt +1 -0
  29. kaboom_engine-0.1.0/kaboom_engine.egg-info/top_level.txt +1 -0
  30. kaboom_engine-0.1.0/pyproject.toml +13 -0
  31. kaboom_engine-0.1.0/setup.cfg +4 -0
  32. kaboom_engine-0.1.0/tests/test_cards.py +49 -0
  33. kaboom_engine-0.1.0/tests/test_deck.py +35 -0
  34. kaboom_engine-0.1.0/tests/test_deck_reshuffle.py +14 -0
  35. kaboom_engine-0.1.0/tests/test_endgame.py +28 -0
  36. kaboom_engine-0.1.0/tests/test_engine_invariants.py +50 -0
  37. kaboom_engine-0.1.0/tests/test_players.py +53 -0
  38. kaboom_engine-0.1.0/tests/test_powers.py +117 -0
  39. kaboom_engine-0.1.0/tests/test_reaction_engine.py +164 -0
  40. kaboom_engine-0.1.0/tests/test_start_game_simulation.py +62 -0
  41. kaboom_engine-0.1.0/tests/test_turn_actions.py +64 -0
@@ -0,0 +1,11 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arnav Ajay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
11
+ SOFTWARE.
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: kaboom-engine
3
+ Version: 0.1.0
4
+ Summary: Core game engine for the Kaboom card game.
5
+ Author: Arnav Ajay
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Dynamic: license-file
10
+
11
+ # Kaboom Engine
12
+
13
+ A deterministic Python engine for the **Kaboom card game**.
14
+
15
+ The project implements the full game logic including turns, reactions, powers, and endgame rules, designed to be used as a reusable **simulation engine, AI environment, or UI backend**.
16
+
17
+ ---
18
+
19
+ # Features
20
+
21
+ * Complete Kaboom game rules
22
+ * Deterministic turn engine
23
+ * Reaction resolution system
24
+ * Card power mechanics
25
+ * Kaboom endgame logic
26
+ * Deck reshuffle handling
27
+ * Action-based engine architecture
28
+ * Event-based results
29
+ * Fully tested core logic
30
+
31
+ The engine is designed so it can be used for:
32
+
33
+ * CLI or graphical game clients
34
+ * AI agents
35
+ * game simulations
36
+ * multiplayer servers
37
+ * reinforcement learning environments
38
+
39
+ ---
40
+
41
+ # Installation
42
+
43
+ Clone the repository:
44
+
45
+ ```bash
46
+ git clone https://github.com/Arnav-Ajay/kaboom-core.git
47
+ cd kaboom-core
48
+ ```
49
+
50
+ Install locally:
51
+
52
+ ```bash
53
+ pip install -e .
54
+ ```
55
+
56
+ ---
57
+
58
+ # Basic Usage
59
+
60
+ ```python
61
+ from kaboom import GameState, apply_action
62
+ from kaboom.game.actions import Draw, Discard
63
+
64
+ state = GameState.new_game()
65
+
66
+ apply_action(state, Draw(actor_id=0))
67
+ apply_action(state, Discard(actor_id=0))
68
+ ```
69
+
70
+ ---
71
+
72
+ # Running Simulations
73
+
74
+ The engine supports automated play.
75
+
76
+ ```python
77
+ import random
78
+ from kaboom import GameState, apply_action
79
+ from kaboom.game.turn import get_valid_actions
80
+
81
+ state = GameState.new_game()
82
+
83
+ while True:
84
+ actions = get_valid_actions(state)
85
+
86
+ if not actions:
87
+ break
88
+
89
+ action = random.choice(actions)
90
+ apply_action(state, action)
91
+ ```
92
+
93
+ This allows thousands of games to be simulated for testing or AI training.
94
+
95
+ ---
96
+
97
+ # Architecture
98
+
99
+ The engine follows a modular architecture:
100
+
101
+ ```
102
+ GameState
103
+ |
104
+ Action (Draw / Discard / Replace / UsePower / CallKaboom)
105
+ |
106
+ apply_action()
107
+ |
108
+ ActionResult
109
+ ```
110
+
111
+ Key components:
112
+
113
+ ```
114
+ kaboom/cards → card definitions
115
+ kaboom/players → player state
116
+ kaboom/powers → power system
117
+ kaboom/game → turn engine, reactions, validators
118
+ ```
119
+
120
+ This separation keeps game logic independent from any UI layer.
121
+
122
+ ---
123
+
124
+ # Project Structure
125
+
126
+ ```
127
+ kaboom/
128
+ cards/
129
+ players/
130
+ powers/
131
+ game/
132
+ actions.py
133
+ game_state.py
134
+ phases.py
135
+ reaction.py
136
+ results.py
137
+ turn.py
138
+ validators.py
139
+
140
+ tests/
141
+ ```
142
+
143
+ ---
144
+
145
+ # Testing
146
+
147
+ The engine includes a full pytest test suite.
148
+
149
+ Run tests:
150
+
151
+ ```bash
152
+ pytest
153
+ ```
154
+
155
+ Current coverage includes:
156
+
157
+ * card scoring
158
+ * deck creation
159
+ * player management
160
+ * power mechanics
161
+ * turn actions
162
+ * reaction logic
163
+ * Kaboom endgame rules
164
+ * full game initialization
165
+
166
+ ---
167
+
168
+ # Future Improvements
169
+
170
+ Planned enhancements:
171
+
172
+ * CLI interface
173
+ * graphical UI
174
+ * AI player agents
175
+ * multiplayer networking
176
+ * replay and event logging
177
+ * Gym-compatible environment for reinforcement learning
178
+
179
+ ---
180
+
181
+ # License
182
+
183
+ This project is licensed under the MIT License.
184
+
185
+ ---
186
+
187
+ # Author
188
+
189
+ [Arnav Ajay](https://github.com/Arnav-Ajay)
190
+
191
+ ---
@@ -0,0 +1,181 @@
1
+ # Kaboom Engine
2
+
3
+ A deterministic Python engine for the **Kaboom card game**.
4
+
5
+ The project implements the full game logic including turns, reactions, powers, and endgame rules, designed to be used as a reusable **simulation engine, AI environment, or UI backend**.
6
+
7
+ ---
8
+
9
+ # Features
10
+
11
+ * Complete Kaboom game rules
12
+ * Deterministic turn engine
13
+ * Reaction resolution system
14
+ * Card power mechanics
15
+ * Kaboom endgame logic
16
+ * Deck reshuffle handling
17
+ * Action-based engine architecture
18
+ * Event-based results
19
+ * Fully tested core logic
20
+
21
+ The engine is designed so it can be used for:
22
+
23
+ * CLI or graphical game clients
24
+ * AI agents
25
+ * game simulations
26
+ * multiplayer servers
27
+ * reinforcement learning environments
28
+
29
+ ---
30
+
31
+ # Installation
32
+
33
+ Clone the repository:
34
+
35
+ ```bash
36
+ git clone https://github.com/Arnav-Ajay/kaboom-core.git
37
+ cd kaboom-core
38
+ ```
39
+
40
+ Install locally:
41
+
42
+ ```bash
43
+ pip install -e .
44
+ ```
45
+
46
+ ---
47
+
48
+ # Basic Usage
49
+
50
+ ```python
51
+ from kaboom import GameState, apply_action
52
+ from kaboom.game.actions import Draw, Discard
53
+
54
+ state = GameState.new_game()
55
+
56
+ apply_action(state, Draw(actor_id=0))
57
+ apply_action(state, Discard(actor_id=0))
58
+ ```
59
+
60
+ ---
61
+
62
+ # Running Simulations
63
+
64
+ The engine supports automated play.
65
+
66
+ ```python
67
+ import random
68
+ from kaboom import GameState, apply_action
69
+ from kaboom.game.turn import get_valid_actions
70
+
71
+ state = GameState.new_game()
72
+
73
+ while True:
74
+ actions = get_valid_actions(state)
75
+
76
+ if not actions:
77
+ break
78
+
79
+ action = random.choice(actions)
80
+ apply_action(state, action)
81
+ ```
82
+
83
+ This allows thousands of games to be simulated for testing or AI training.
84
+
85
+ ---
86
+
87
+ # Architecture
88
+
89
+ The engine follows a modular architecture:
90
+
91
+ ```
92
+ GameState
93
+ |
94
+ Action (Draw / Discard / Replace / UsePower / CallKaboom)
95
+ |
96
+ apply_action()
97
+ |
98
+ ActionResult
99
+ ```
100
+
101
+ Key components:
102
+
103
+ ```
104
+ kaboom/cards → card definitions
105
+ kaboom/players → player state
106
+ kaboom/powers → power system
107
+ kaboom/game → turn engine, reactions, validators
108
+ ```
109
+
110
+ This separation keeps game logic independent from any UI layer.
111
+
112
+ ---
113
+
114
+ # Project Structure
115
+
116
+ ```
117
+ kaboom/
118
+ cards/
119
+ players/
120
+ powers/
121
+ game/
122
+ actions.py
123
+ game_state.py
124
+ phases.py
125
+ reaction.py
126
+ results.py
127
+ turn.py
128
+ validators.py
129
+
130
+ tests/
131
+ ```
132
+
133
+ ---
134
+
135
+ # Testing
136
+
137
+ The engine includes a full pytest test suite.
138
+
139
+ Run tests:
140
+
141
+ ```bash
142
+ pytest
143
+ ```
144
+
145
+ Current coverage includes:
146
+
147
+ * card scoring
148
+ * deck creation
149
+ * player management
150
+ * power mechanics
151
+ * turn actions
152
+ * reaction logic
153
+ * Kaboom endgame rules
154
+ * full game initialization
155
+
156
+ ---
157
+
158
+ # Future Improvements
159
+
160
+ Planned enhancements:
161
+
162
+ * CLI interface
163
+ * graphical UI
164
+ * AI player agents
165
+ * multiplayer networking
166
+ * replay and event logging
167
+ * Gym-compatible environment for reinforcement learning
168
+
169
+ ---
170
+
171
+ # License
172
+
173
+ This project is licensed under the MIT License.
174
+
175
+ ---
176
+
177
+ # Author
178
+
179
+ [Arnav Ajay](https://github.com/Arnav-Ajay)
180
+
181
+ ---
@@ -0,0 +1,32 @@
1
+ from .cards.card import Card, Suit, Rank
2
+ from .players.player import Player
3
+ from .game.game_state import GameState
4
+ from .game.turn import apply_action, close_reaction, get_valid_actions, is_game_over
5
+ from .game.actions import Draw, Discard, Replace, UsePower, CallKaboom
6
+ from .game.results import ActionResult
7
+ from .game.phases import GamePhase
8
+ from .game.reaction import react_discard_own_cards, react_discard_other_cards, ReactionResult
9
+ from .version import __version__
10
+
11
+ __all__ = [
12
+ "Card",
13
+ "Suit",
14
+ "Rank",
15
+ "Player",
16
+ "GameState",
17
+ "apply_action",
18
+ "Draw",
19
+ "Discard",
20
+ "Replace",
21
+ "UsePower",
22
+ "CallKaboom",
23
+ "react_discard_own_cards",
24
+ "react_discard_other_cards",
25
+ "ReactionResult",
26
+ "GamePhase",
27
+ "ActionResult",
28
+ "close_reaction",
29
+ "get_valid_actions",
30
+ "is_game_over",
31
+ "__version__"
32
+ ]
@@ -0,0 +1,4 @@
1
+ # kaboom/cards/__init__.py
2
+ from .card import Card, Rank, Suit
3
+
4
+ __all__ = ["Card", "Rank", "Suit"]
@@ -0,0 +1,45 @@
1
+ # kaboom/cards/card.py
2
+ from enum import Enum
3
+ from dataclasses import dataclass
4
+
5
+
6
+ class Suit(str, Enum):
7
+ SPADES = "♠"
8
+ HEARTS = "♥"
9
+ DIAMONDS = "♦"
10
+ CLUBS = "♣"
11
+
12
+
13
+ class Rank(str, Enum):
14
+ A = "A"
15
+ TWO = "2"
16
+ THREE = "3"
17
+ FOUR = "4"
18
+ FIVE = "5"
19
+ SIX = "6"
20
+ SEVEN = "7"
21
+ EIGHT = "8"
22
+ NINE = "9"
23
+ TEN = "10"
24
+ J = "J"
25
+ Q = "Q"
26
+ K = "K"
27
+
28
+
29
+ @dataclass(frozen=True, slots=True)
30
+ class Card:
31
+ rank: Rank
32
+ suit: Suit
33
+
34
+ def __str__(self) -> str:
35
+ return f"{self.rank.value}{self.suit.value}"
36
+
37
+ @property
38
+ def score_value(self) -> int:
39
+ if self.rank == Rank.K and self.suit in {Suit.HEARTS, Suit.DIAMONDS}:
40
+ return 0
41
+ if self.rank == Rank.A:
42
+ return 1
43
+ if self.rank in {Rank.J, Rank.Q, Rank.K}:
44
+ return 10
45
+ return int(self.rank.value)
@@ -0,0 +1,16 @@
1
+ # kaboom/exceptions.py
2
+ class KaboomError(Exception):
3
+ """Base exception for Kaboom engine."""
4
+ pass
5
+
6
+ class InvalidActionError(KaboomError):
7
+ """Raised when a player attempts an invalid action."""
8
+ pass
9
+
10
+ class InvalidReactionError(KaboomError):
11
+ """Raised when a reaction attempt is invalid."""
12
+ pass
13
+
14
+ class InvariantViolationError(KaboomError):
15
+ """Raised when game state invariants are violated."""
16
+ pass
@@ -0,0 +1,10 @@
1
+ # kaboom/game/__init__.py
2
+ from .game_state import GameState
3
+ from .reaction import react_discard_own_cards, react_discard_other_cards, ReactionResult
4
+ from .actions import Draw, Discard, Replace, UsePower, CallKaboom
5
+ from .turn import apply_action, close_reaction
6
+ from .results import ActionResult
7
+ from .phases import GamePhase
8
+
9
+ __all__ = ["GameState", "react_discard_own_cards", "react_discard_other_cards", "ActionResult", "GamePhase",
10
+ "Draw", "Discard", "Replace", "UsePower", "CallKaboom", "apply_action", "close_reaction", "ReactionResult"]
@@ -0,0 +1,46 @@
1
+ # kaboom/game/actions.py
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass
5
+ from typing import Optional, Protocol
6
+
7
+ from kaboom.cards.card import Card
8
+
9
+ class Action(Protocol):
10
+ """
11
+ Marker protocol for all turn actions.
12
+ """
13
+ actor_id: int
14
+
15
+ @dataclass(frozen=True, slots=True)
16
+ class Draw(Action):
17
+ actor_id: int
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class Discard(Action):
21
+ actor_id: int
22
+
23
+ @dataclass(frozen=True, slots=True)
24
+ class Replace(Action):
25
+ actor_id: int
26
+ target_index: int # index in player's hand
27
+
28
+ @dataclass(frozen=True, slots=True)
29
+ class UsePower(Action):
30
+ actor_id: int
31
+ power_name: str
32
+ source_card: Card
33
+
34
+ # Power-specific payload (indices, player ids, etc.)
35
+ target_player_id: Optional[int] = None
36
+ target_card_index: Optional[int] = None
37
+ second_target_player_id: Optional[int] = None
38
+ second_target_card_index: Optional[int] = None
39
+
40
+ @dataclass(frozen=True, slots=True)
41
+ class CallKaboom(Action):
42
+ actor_id: int
43
+
44
+ @dataclass(frozen=True, slots=True)
45
+ class CloseReaction(Action):
46
+ actor_id: int
@@ -0,0 +1,134 @@
1
+ # kaboom/game/game_state.py
2
+ from __future__ import annotations
3
+ import random
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import List, Optional
7
+
8
+ from kaboom.cards.card import Card
9
+ from kaboom.players.player import Player
10
+ from kaboom.exceptions import InvalidActionError
11
+ from kaboom.game.phases import GamePhase
12
+
13
+ @dataclass(slots=True)
14
+ class GameState:
15
+ """
16
+ Complete mutable state of a Kaboom game.
17
+ """
18
+ players: List[Player]
19
+ deck: List[Card]
20
+ discard_pile: List[Card] = field(default_factory=list)
21
+ phase: GamePhase = GamePhase.TURN_DRAW
22
+
23
+ current_player_index: int = 0
24
+ round_number: int = 1
25
+
26
+ # Drawn card awaiting action
27
+ drawn_card: Optional[Card] = None
28
+
29
+ # Reaction state
30
+ reaction_rank: Optional[str] = None
31
+ reaction_initiator: Optional[int] = None
32
+ reaction_open: bool = False
33
+
34
+ # Kaboom
35
+ kaboom_called_by: Optional[int] = None
36
+ instant_winner: Optional[int] = None
37
+
38
+ def active_players(self) -> List[Player]:
39
+ return [p for p in self.players if p.active]
40
+
41
+ def current_player(self) -> Player:
42
+ return self.players[self.current_player_index]
43
+
44
+ # def advance_turn(self) -> None:
45
+ # """
46
+ # Move to next active player.
47
+ # """
48
+ # if self.kaboom_called_by is not None:
49
+ # return
50
+
51
+ # n = len(self.players)
52
+ # for _ in range(n):
53
+ # self.current_player_index = (self.current_player_index + 1) % n
54
+ # if self.players[self.current_player_index].active:
55
+ # break
56
+
57
+ # if self.current_player_index == 0:
58
+ # self.round_number += 1
59
+
60
+ def advance_turn(self) -> None:
61
+ """
62
+ Move to next player.
63
+
64
+ If Kaboom has been called, remaining players finish the round.
65
+ When the turn returns to the Kaboom caller, the game ends.
66
+ """
67
+
68
+ n = len(self.players)
69
+
70
+ while True:
71
+ self.current_player_index = (self.current_player_index + 1) % n
72
+
73
+ player = self.players[self.current_player_index]
74
+
75
+ # If Kaboom was called and we reached the caller again → end game
76
+ if (
77
+ self.kaboom_called_by is not None
78
+ and player.id == self.kaboom_called_by
79
+ ):
80
+ self.phase = GamePhase.GAME_OVER
81
+ return
82
+
83
+ # Skip inactive players (kaboom caller is inactive)
84
+ if player.active:
85
+ break
86
+
87
+ if self.current_player_index == 0:
88
+ self.round_number += 1
89
+
90
+ def top_discard(self) -> Optional[Card]:
91
+ return self.discard_pile[-1] if self.discard_pile else None
92
+
93
+ def resolve_player(self, player_id: int) -> Player:
94
+ for p in self.players:
95
+ if p.id == player_id:
96
+ return p
97
+ raise InvalidActionError("Unknown player_id")
98
+
99
+ def ensure_deck(self) -> None:
100
+ """
101
+ Ensure deck has cards.
102
+ If empty, reshuffle discard pile (except top card).
103
+ """
104
+ if self.deck:
105
+ return
106
+
107
+ if len(self.discard_pile) <= 1:
108
+ raise InvalidActionError("No cards left to reshuffle.")
109
+
110
+ top = self.discard_pile.pop()
111
+ self.deck = self.discard_pile
112
+ random.shuffle(self.deck)
113
+
114
+ self.discard_pile = [top]
115
+
116
+ @classmethod
117
+ def new_game(cls, players: list[Player], deck: list[Card]) -> GameState:
118
+ """
119
+ Create a new game state with initial settings.
120
+ """
121
+
122
+ return cls(
123
+ players=players,
124
+ deck=deck,
125
+ discard_pile=[],
126
+ current_player_index=0,
127
+ round_number=1,
128
+ drawn_card=None,
129
+ reaction_rank=None,
130
+ reaction_initiator=None,
131
+ reaction_open=False,
132
+ kaboom_called_by=None,
133
+ instant_winner=None,
134
+ )
@@ -0,0 +1,8 @@
1
+ # kaboom/game/phases.py
2
+ from enum import Enum
3
+
4
+ class GamePhase(str, Enum):
5
+ TURN_DRAW = "turn_draw"
6
+ TURN_RESOLVE = "turn_resolve"
7
+ REACTION = "reaction"
8
+ GAME_OVER = "game_over"