jpoke 0.1.0__py3-none-any.whl

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 (110) hide show
  1. jpoke/__init__.py +22 -0
  2. jpoke/core/__init__.py +12 -0
  3. jpoke/core/ability_manager.py +167 -0
  4. jpoke/core/ailment_manager.py +181 -0
  5. jpoke/core/battle.py +934 -0
  6. jpoke/core/command_manager.py +237 -0
  7. jpoke/core/context.py +112 -0
  8. jpoke/core/damage.py +395 -0
  9. jpoke/core/event_logger.py +283 -0
  10. jpoke/core/event_manager.py +212 -0
  11. jpoke/core/field_manager.py +411 -0
  12. jpoke/core/handler.py +117 -0
  13. jpoke/core/item_manager.py +317 -0
  14. jpoke/core/lethal.py +476 -0
  15. jpoke/core/log_payload.py +172 -0
  16. jpoke/core/move_executor.py +609 -0
  17. jpoke/core/observation_builder.py +185 -0
  18. jpoke/core/player.py +127 -0
  19. jpoke/core/player_state.py +107 -0
  20. jpoke/core/query.py +264 -0
  21. jpoke/core/replay.py +127 -0
  22. jpoke/core/speed_calculator.py +216 -0
  23. jpoke/core/status_manager.py +163 -0
  24. jpoke/core/switch_manager.py +321 -0
  25. jpoke/core/turn_controller.py +354 -0
  26. jpoke/core/volatile_manager.py +172 -0
  27. jpoke/data/__init__.py +11 -0
  28. jpoke/data/ability.py +3326 -0
  29. jpoke/data/ailment.py +109 -0
  30. jpoke/data/field/__init__.py +7 -0
  31. jpoke/data/field/field.py +23 -0
  32. jpoke/data/field/global_field.py +87 -0
  33. jpoke/data/field/side_field.py +180 -0
  34. jpoke/data/field/terrain.py +95 -0
  35. jpoke/data/field/weather.py +125 -0
  36. jpoke/data/item.py +2072 -0
  37. jpoke/data/megaevol.py +98 -0
  38. jpoke/data/models.py +93 -0
  39. jpoke/data/move.py +54 -0
  40. jpoke/data/moves/__init__.py +0 -0
  41. jpoke/data/moves/move_a.py +1355 -0
  42. jpoke/data/moves/move_ha.py +1731 -0
  43. jpoke/data/moves/move_ka.py +1411 -0
  44. jpoke/data/moves/move_ma.py +844 -0
  45. jpoke/data/moves/move_na.py +547 -0
  46. jpoke/data/moves/move_ra.py +374 -0
  47. jpoke/data/moves/move_sa.py +1385 -0
  48. jpoke/data/moves/move_symbol.py +126 -0
  49. jpoke/data/moves/move_ta.py +1668 -0
  50. jpoke/data/moves/move_wa.py +120 -0
  51. jpoke/data/moves/move_ya.py +152 -0
  52. jpoke/data/nature.py +29 -0
  53. jpoke/data/pokedex.json +21864 -0
  54. jpoke/data/pokedex.py +21 -0
  55. jpoke/data/signature_items.py +41 -0
  56. jpoke/data/type_chart.py +443 -0
  57. jpoke/data/volatile.py +989 -0
  58. jpoke/enums/__init__.py +18 -0
  59. jpoke/enums/command.py +234 -0
  60. jpoke/enums/event.py +450 -0
  61. jpoke/enums/interrupt.py +67 -0
  62. jpoke/enums/logcode.py +64 -0
  63. jpoke/exceptions.py +28 -0
  64. jpoke/handlers/__init__.py +0 -0
  65. jpoke/handlers/ability.py +3564 -0
  66. jpoke/handlers/ability_paradox.py +130 -0
  67. jpoke/handlers/ailment.py +165 -0
  68. jpoke/handlers/field.py +611 -0
  69. jpoke/handlers/item.py +2009 -0
  70. jpoke/handlers/lethal.py +761 -0
  71. jpoke/handlers/move.py +192 -0
  72. jpoke/handlers/move_attack.py +3665 -0
  73. jpoke/handlers/move_status.py +3046 -0
  74. jpoke/handlers/volatile.py +1432 -0
  75. jpoke/model/__init__.py +7 -0
  76. jpoke/model/ability.py +77 -0
  77. jpoke/model/ailment.py +70 -0
  78. jpoke/model/effect.py +153 -0
  79. jpoke/model/field.py +76 -0
  80. jpoke/model/item.py +69 -0
  81. jpoke/model/move.py +169 -0
  82. jpoke/model/pokemon.py +1128 -0
  83. jpoke/model/stats.py +47 -0
  84. jpoke/model/volatile.py +59 -0
  85. jpoke/players/__init__.py +9 -0
  86. jpoke/players/tree_search_player.py +298 -0
  87. jpoke/py.typed +0 -0
  88. jpoke/types/__init__.py +73 -0
  89. jpoke/types/ability.py +317 -0
  90. jpoke/types/ailment.py +4 -0
  91. jpoke/types/global_field.py +4 -0
  92. jpoke/types/item.py +270 -0
  93. jpoke/types/literals.py +136 -0
  94. jpoke/types/move.py +722 -0
  95. jpoke/types/poke_env.py +107 -0
  96. jpoke/types/pokemon.py +1292 -0
  97. jpoke/types/side_field.py +4 -0
  98. jpoke/types/terrain.py +4 -0
  99. jpoke/types/volatile.py +78 -0
  100. jpoke/types/weather.py +4 -0
  101. jpoke/utils/__init__.py +8 -0
  102. jpoke/utils/constants.py +20 -0
  103. jpoke/utils/copy_utils.py +45 -0
  104. jpoke/utils/lethal_dist.py +105 -0
  105. jpoke/utils/math.py +41 -0
  106. jpoke-0.1.0.dist-info/METADATA +226 -0
  107. jpoke-0.1.0.dist-info/RECORD +110 -0
  108. jpoke-0.1.0.dist-info/WHEEL +5 -0
  109. jpoke-0.1.0.dist-info/licenses/LICENSE +21 -0
  110. jpoke-0.1.0.dist-info/top_level.txt +1 -0
jpoke/__init__.py ADDED
@@ -0,0 +1,22 @@
1
+ """jpokeパッケージ - ポケモンバトルシミュレータ。
2
+
3
+ バトル、プレイヤー、ポケモン、技、特性、アイテムなどの主要クラスと、
4
+ ポケモン図鑑データを提供します。
5
+ """
6
+ from importlib.metadata import version as _version
7
+
8
+ from .core import Battle, Player
9
+ from .model import Pokemon, Ability, Item, Move
10
+ from .data import POKEDEX
11
+
12
+ __version__ = _version("jpoke")
13
+
14
+ __all__ = [
15
+ "Battle",
16
+ "Player",
17
+ "Pokemon",
18
+ "Ability",
19
+ "Item",
20
+ "Move",
21
+ "POKEDEX",
22
+ ]
jpoke/core/__init__.py ADDED
@@ -0,0 +1,12 @@
1
+ from .handler import Handler, HandlerReturn
2
+ from .lethal import StateDist, LethalHandler, LethalContext
3
+ from .context import BaseContext, EventContext, AttackContext
4
+ from .event_manager import EventManager
5
+ from .battle import Battle
6
+ from .player import Player
7
+ from .player_state import PlayerState
8
+ from .ailment_manager import AilmentManager
9
+ from .volatile_manager import VolatileManager
10
+ from .field_manager import SideFieldManager
11
+ from .status_manager import StatusManager
12
+ from .query import PokemonQuery
@@ -0,0 +1,167 @@
1
+ """特性の有効/無効状態管理モジュール。"""
2
+ from __future__ import annotations
3
+ from typing import TYPE_CHECKING, cast
4
+ if TYPE_CHECKING:
5
+ from .battle import Battle
6
+ from jpoke.model import Pokemon
7
+
8
+ from jpoke.utils import fast_copy
9
+ from jpoke.types import AbilityDisabledReason, AbilityName
10
+ from jpoke.enums import Event
11
+ from jpoke.model import Ability
12
+ from .context import EventContext
13
+
14
+
15
+ class AbilityManager:
16
+ """場の特性の有効/無効状態と発動状態を管理するクラス。
17
+
18
+ Attributes:
19
+ battle: 親となるBattleインスタンス
20
+ """
21
+
22
+ def __init__(self, battle: Battle):
23
+ self.battle = battle
24
+
25
+ def __deepcopy__(self, memo):
26
+ cls = self.__class__
27
+ new = cls.__new__(cls)
28
+ memo[id(self)] = new
29
+ fast_copy(self, new, keys_to_deepcopy=[])
30
+ return new
31
+
32
+ def update_reference(self, battle: Battle):
33
+ """Battleインスタンスの参照を更新する。
34
+
35
+ Args:
36
+ battle: 新しいBattleインスタンス
37
+ """
38
+ self.battle = battle
39
+
40
+ @property
41
+ def _events(self):
42
+ return self.battle.events
43
+
44
+ def _register_ability_handlers(self, mon: Pokemon):
45
+ """ポケモンの特性のハンドラをイベントマネージャに登録する。
46
+
47
+ Args:
48
+ mon: 対象のポケモン
49
+ """
50
+ mon.ability.register_handlers(self._events, mon)
51
+
52
+ def _unregister_ability_handlers(self, mon: Pokemon):
53
+ """ポケモンの特性のハンドラをイベントマネージャから解除する。
54
+
55
+ Args:
56
+ mon: 対象のポケモン
57
+ """
58
+ mon.ability.unregister_handlers(self._events, mon)
59
+
60
+ def is_change_blocked(self, mon: Pokemon) -> bool:
61
+ """特性の変更・入れ替えがとくせいガード等により防がれるかどうかを判定する。
62
+
63
+ Args:
64
+ mon: 特性を変更しようとしているポケモン
65
+
66
+ Returns:
67
+ 変更が防がれる場合はTrue
68
+ """
69
+ ctx = EventContext(source=mon)
70
+ return self._events.emit(Event.ON_CHECK_ABILITY_DISABLE, ctx, False)
71
+
72
+ def change_ability(self, mon: Pokemon, ability: AbilityName) -> None:
73
+ """ポケモンの特性を更新し、ハンドラの登録/解除やイベントの発火を行う。
74
+ Args:
75
+ mon: 特性を変更するポケモン
76
+ ability: 新しい特性の名前
77
+ """
78
+ if mon.ability.base_name == ability:
79
+ return
80
+
81
+ if self.is_change_blocked(mon):
82
+ return
83
+
84
+ ctx = EventContext(source=mon)
85
+ is_active = self.battle.is_active(mon)
86
+
87
+ # 対象のポケモンが場に出ている場合は、古い特性のハンドラを解除し、イベントを発火する
88
+ if is_active:
89
+ self._unregister_ability_handlers(mon)
90
+ self._events.emit(Event.ON_ABILITY_DISABLED, ctx)
91
+
92
+ # 新しい特性に更新して公開する
93
+ mon.ability = Ability(ability)
94
+ mon.ability.revealed = True
95
+
96
+ # 対象のポケモンが場に出ている場合は、新しい特性のハンドラを登録し、イベントを発火する
97
+ if is_active:
98
+ self._register_ability_handlers(mon)
99
+ self._events.emit(Event.ON_ABILITY_ENABLED, ctx)
100
+
101
+ def swap_ability(self, mon1: Pokemon, mon2: Pokemon) -> None:
102
+ """2体のポケモンの特性を入れ替える。
103
+
104
+ どちらかの特性の変更がとくせいガード等により防がれる場合、
105
+ 入れ替えは行われない(片方だけ入れ替わることはない)。
106
+
107
+ Args:
108
+ mon1: 1体目のポケモン
109
+ mon2: 2体目のポケモン
110
+ """
111
+ if self.is_change_blocked(mon1) or self.is_change_blocked(mon2):
112
+ return
113
+
114
+ ability1 = mon1.ability.base_name
115
+ ability2 = mon2.ability.base_name
116
+
117
+ # 両者の特性の無効化イベントをまとめて発火する
118
+ self._events.emit(Event.ON_ABILITY_DISABLED)
119
+
120
+ for mon, ability in ((mon1, ability2), (mon2, ability1)):
121
+ self._unregister_ability_handlers(mon)
122
+ mon.ability = Ability(cast(AbilityName, ability))
123
+ self._register_ability_handlers(mon)
124
+
125
+ # 両者の特性の有効化イベントをまとめて発火する
126
+ self._events.emit(Event.ON_ABILITY_ENABLED)
127
+
128
+ def add_disabled_reason(self, mon: Pokemon, reason: AbilityDisabledReason) -> bool:
129
+ """特性を無効にする理由を追加し、有効状態に変化があればイベントを発火する。
130
+ Args:
131
+ mon: 対象のポケモン
132
+ reason: 無効化の理由を示すキー
133
+ Returns:
134
+ 特性の有効状態に変化があった場合はTrue、そうでない場合はFalse
135
+ """
136
+ ctx = EventContext(source=mon)
137
+ if self._events.emit(Event.ON_CHECK_ABILITY_DISABLE, ctx, False):
138
+ return False
139
+
140
+ was_enabled = mon.ability.enabled
141
+ mon.ability.add_disable_reason(reason)
142
+ is_enabled = mon.ability.enabled
143
+
144
+ if was_enabled and not is_enabled:
145
+ self._events.emit(Event.ON_ABILITY_DISABLED, ctx)
146
+ return True
147
+ return False
148
+
149
+ def remove_disabled_reason(self, mon: Pokemon, reason: AbilityDisabledReason) -> bool:
150
+ """特性を無効にする理由を削除し、有効状態に変化があればイベントを発火する。
151
+ Args:
152
+ mon: 対象のポケモン
153
+ reason: 無効化の理由を示すキー
154
+ Returns:
155
+ 特性の有効状態に変化があった場合はTrue、そうでない場合はFalse
156
+ """
157
+ was_enabled = mon.ability.enabled
158
+ mon.ability.remove_disable_reason(reason)
159
+ is_enabled = mon.ability.enabled
160
+
161
+ if not was_enabled and is_enabled:
162
+ self._events.emit(
163
+ Event.ON_ABILITY_ENABLED,
164
+ EventContext(source=mon)
165
+ )
166
+ return True
167
+ return False
@@ -0,0 +1,181 @@
1
+ """ポケモンの状態管理(状態異常・揮発状態)を行うモジュール。
2
+
3
+ Pokemonクラスから状態管理ロジックを分離し、Battleクラスに集約する。
4
+ """
5
+ from __future__ import annotations
6
+ from typing import TYPE_CHECKING
7
+ if TYPE_CHECKING:
8
+ from jpoke.core import Battle, EventManager
9
+
10
+ from jpoke.model import Pokemon, Ailment
11
+ from jpoke.types import AilmentName
12
+ from jpoke.enums import Event, LogCode
13
+ from jpoke.core import EventContext, BaseContext
14
+ from .log_payload import AilmentPayload
15
+ from jpoke.utils import fast_copy
16
+
17
+
18
+ class AilmentManager:
19
+ """ポケモンの状態異常を管理するクラス。
20
+
21
+ 状態異常の付与、治療、ターン経過処理を担当。
22
+ Pokemonクラスから状態異常管理を分離し、単一責任原則を実現。
23
+
24
+ Attributes:
25
+ battle: 親となるBattleインスタンス
26
+ """
27
+
28
+ def __init__(self, battle: Battle):
29
+ self.battle = battle
30
+
31
+ def __deepcopy__(self, memo):
32
+ cls = self.__class__
33
+ new = cls.__new__(cls)
34
+ memo[id(self)] = new
35
+ fast_copy(self, new, keys_to_deepcopy=[])
36
+ return new
37
+
38
+ def update_reference(self, battle: Battle):
39
+ """Battleインスタンスの参照を更新。
40
+
41
+ Args:
42
+ battle: 新しいBattleインスタンス
43
+ """
44
+ self.battle = battle
45
+
46
+ @property
47
+ def _events(self) -> EventManager:
48
+ return self.battle.events
49
+
50
+ def apply(self,
51
+ target: Pokemon,
52
+ name: AilmentName,
53
+ count: int | None = None,
54
+ source: Pokemon | None = None,
55
+ overwrite: bool = False,
56
+ ctx: BaseContext | None = None) -> bool:
57
+ """状態異常を付与する。
58
+
59
+ Args:
60
+ target: 対象のポケモン
61
+ name: 状態異常名
62
+ count: 継続ターン数(ねむりは省略時に Champions 仕様で自動決定)
63
+ source: 状態異常の原因となったポケモン
64
+ overwrite: Trueの場合、既存の状態異常を上書き
65
+ ctx: ON_BEFORE_APPLY_AILMENT イベントの EventContext
66
+ Returns:
67
+ 付与に成功したTrue
68
+
69
+ Note:
70
+ - overwrite=Falseの場合、既に状態異常があれば失敗
71
+ - 同じ状態異常の重ね掛けは不可
72
+ - ねむりの count=None 時は Champions 仕様で自動決定(2: 1/3、3: 2/3)
73
+ """
74
+ # ねむりのcountをChampions仕様で自動決定(count=Noneのとき)
75
+ if name == "ねむり" and count is None:
76
+ count = 2 if self.battle.random.random() < 1 / 3 else 3
77
+
78
+ # overwrite=True でない限り上書き不可
79
+ if target.ailment.is_active and not overwrite:
80
+ return False
81
+
82
+ # overwrite=True でも uncurable は上書き不可
83
+ if target.ailment.is_active and target.ailment.uncurable:
84
+ return False
85
+
86
+ # 重ねがけ不可
87
+ if name == target.ailment.name:
88
+ return False
89
+
90
+ # タイプによる無効化をチェック
91
+ if not self._can_apply_by_type(name, target, source):
92
+ return False
93
+
94
+ # ON_BEFORE_APPLY_AILMENT イベントを発火して特性などによる無効化をチェック
95
+ if ctx is not None:
96
+ apply_ctx = EventContext(source=source, target=target)
97
+ else:
98
+ apply_ctx = EventContext(target=target, source=source)
99
+
100
+ # ハンドラーが空値を返した場合は状態異常を付与しない
101
+ resolved_name = self._events.emit(Event.ON_BEFORE_APPLY_AILMENT, apply_ctx, name)
102
+ if not resolved_name:
103
+ return False
104
+
105
+ # 既存のハンドラを削除
106
+ target.ailment.unregister_handlers(self._events, target)
107
+
108
+ # 新しい状態異常を設定してハンドラ登録
109
+ self.battle.add_event_log(
110
+ target,
111
+ LogCode.AILMENT_APPLIED,
112
+ payload=AilmentPayload(ailment=resolved_name, source=source.name if source else None)
113
+ )
114
+ target.ailment = Ailment(resolved_name, count=count)
115
+ target.ailment.register_handlers(self._events, target)
116
+
117
+ # 付与後イベントを発火(シンクロ等のリアクション用)
118
+ self._events.emit(Event.ON_APPLY_AILMENT, apply_ctx, resolved_name)
119
+ return True
120
+
121
+ def _can_apply_by_type(self,
122
+ ailment: AilmentName,
123
+ target: Pokemon,
124
+ source: Pokemon | None) -> bool:
125
+ """タイプによって状態異常を付与できるか判定する。"""
126
+ match ailment:
127
+ case "どく" | "もうどく":
128
+ if source is not None and source.ability.name == "ふしょく":
129
+ return True
130
+ return not (target.has_type("どく") or target.has_type("はがね"))
131
+ case "やけど":
132
+ return not target.has_type("ほのお")
133
+
134
+ case "まひ":
135
+ return not target.has_type("でんき")
136
+
137
+ case "こおり":
138
+ return not target.has_type("こおり")
139
+
140
+ return True
141
+
142
+ def remove(self, target: Pokemon) -> bool:
143
+ """状態異常を解除する。
144
+
145
+ Args:
146
+ target: 対象のポケモン
147
+
148
+ Returns:
149
+ 解除に成功したらTrue
150
+ """
151
+ if not target.ailment.is_active:
152
+ return False
153
+
154
+ # 回復不能な状態異常は解除しない
155
+ if target.ailment.uncurable:
156
+ return False
157
+
158
+ self.battle.add_event_log(
159
+ target,
160
+ LogCode.AILMENT_REMOVED,
161
+ payload=AilmentPayload(ailment=target.ailment.name)
162
+ )
163
+ target.ailment.unregister_handlers(self._events, target)
164
+ target.ailment = Ailment()
165
+ return True
166
+
167
+ def tick(self, target: Pokemon) -> bool:
168
+ """状態異常のターン経過処理を行う。
169
+
170
+ Args:
171
+ target: 対象のポケモン
172
+
173
+ Returns:
174
+ ターン経過処理を行った場合True、状態異常がない場合False
175
+ """
176
+ if not target.ailment.is_active:
177
+ return False
178
+ target.ailment.tick()
179
+ if target.ailment.count == 0:
180
+ self.remove(target)
181
+ return True