Changelog¶
このプロジェクトの変更点はすべてこのファイルに記録する。
フォーマットは Keep a Changelog に、 バージョニングは Semantic Versioning に準拠する。
[Unreleased]¶
Added¶
TreeSearchPlayer.estimate_opponent_team()/estimate_opponent_selection()— 相手推定を項目別に分けた新フック。estimate_opponent_team(battle)は相手ポケモンの モデル(技・特性・アイテム)に推定値を書き込み、estimate_opponent_selection(battle)は相手の選出インデックスの推定をlist[int] | Noneの返り値で返す。estimate_opponent(battle)の既定実装はこの2つに順に委譲するテンプレートメソッドに なった。選出推定の返り値はフレームワーク側(estimate_opponentの既定実装)が 公開済みのselected_indexesとマージする(未包含の推定分のみ追加し、公開済みの インデックスは維持する)ため、利用者がマージ処理を書く必要はない
Changed¶
- 破壊的変更:
TreeSearchPlayer.estimate_opponent()のシグネチャを(battle, opponent)から(battle)に変更した。相手はbattle.opponent(self)で 取得する(evaluate/fallback等、他のフックが「battleのみを受け取り相手はbattle.opponent(self)で取得する」という規約に統一するための変更)。estimate_opponentを直接オーバーライドしているコードはopponent引数を削除し、 関数内でbattle.opponent(self)を呼ぶよう修正する必要がある - 破壊的変更:
estimate_opponentの呼び出し契約を変更した。従来は「相手の合法手 (battle.available_commands(opponent))が未公開で空のときのみ」呼ばれていたが、 estimate_opponent 系フック(estimate_opponent/estimate_opponent_team/estimate_opponent_selection)のいずれかをオーバーライドしていれば、探索の最上位 (choose_command/evaluate_commands)のたびに毎回呼ばれるようになった。この結果、 相手候補は「観測スナップショット由来のコマンド」と「推定情報からCommandManagerが列挙したコマンド」の和集合になり、相手の技が一部だけ公開されている局面でも 未公開スロットの推定によって探索の相手候補が拡張される(実対戦の型推定に相当)。 観測(battle)は毎ターン再構築されるため推定は毎回書き込む必要があるが、 公開済みの情報(revealed な技・選出)を上書きしないよう未公開分のみ補うこと
[0.2.0] - 2026-07-22¶
Added¶
jpoke.players.MinimaxPlayer— 自分の各合法手について、相手が最善(自分に とって最悪)の手を選ぶと仮定したミニマックスで評価する木探索プレイヤー。TreeSearchPlayerの全フック(evaluate/fallback/estimate_opponent/configure_sim)をそのまま継承するBattle.swap_items()にsource: Pokemon | None = None引数を追加。従来はItemManager.swap_items()が持つsource(交換の発生源となるポケモン。source自身が持つねんちゃくのみ無効化する判定に使う)をBattleファサードから渡す 手段がなく、内部ハンドラの「すりかえ」実装がbattle.item_manager.swap_items(...)と内部実装へ直接アクセスせざるを得なかった。既定値Noneのため既存呼び出しの 挙動は変わらないBattle.get_team(player)— 対戦中のプレイヤーのチーム実体を取得する公開アクセサ (player_states[player].teamへの直接アクセスを置き換える)Player.add_pokemon(name, **kwargs)—Pokemonを直接importせずにチームへ ポケモンを追加できる正規ルートjpoke.players.RandomPlayer— 合法手からランダムに選ぶPlayer実装。既定のPlayer.choose_command()(常に先頭のコマンドを選ぶ決定的挙動)ではbattle_against()による統計比較の分散が潰れてしまう問題への対応Battle.play_out(max_turns=100)— 決着がつくかターン上限に達するまで 自動的に対戦を進める便宜メソッド。start()→while not battle.finished and battle.turn < N: battle.step()という定型ループの重複を解消する。Player.battle_against()の内部ループもこれに置き換えたBattle.set_ailment(target, name, count=None)/Battle.set_weather(name, count=5)/Battle.set_terrain(name, count=5)— 状態異常・天候・地形を技を介さず直接 セットする薄い公開ラッパー(既存のailment_manager/weather_manager/terrain_managerへの委譲)。シナリオ構築やcalc_lethal()によるダメージ計算 検証で、対戦を進行させずに状態異常・天候込みの複合致死率を確認できるexamples/07_replay.py—Battle.build_replay_data()/ReplayPlayer/replay_battle()(リプレイの記録・再生一式)を紹介するサンプルを新設jpoke.testingモジュール —tests/test_utils.pyにあった内部テストヘルパー (start_battle/run_move/run_switch/apply_ailment/get_action_order/calc_lethal/fix_damage/fix_random等14関数とCustomPlayer)を本体パッケージへ昇格。pip install jpokeだけで(リポジトリを clone せずに)任意ターンでのピンポイントな状態検証・技の実行ができるようになった。tests/test_utils.pyは後方互換のための薄い再エクスポート層として残している。 移植にあたりBattleに不足していた公開ラッパーを追加した:Battle.set_ailment()にsource/overwrite引数を追加、Battle.set_volatile()/Battle.set_item()/Battle.activate_global_field()/Battle.activate_side_field()/Battle.resolve_action_order()/Battle.calc_move_priority()/Battle.can_switch()/Battle.end_turn()を新設 (いずれもailment_manager等の内部マネージャーへの薄い委譲で、外部コードがbattle.<manager>.<method>()を直接呼ばずに済むようにするための追加)Commandをトップレベルパッケージから再エクスポート(from jpoke import Command。 従来はfrom jpoke.enums import Commandのみ)。Battle/Playerは既にトップ レベルから使えたがCommandだけ欠けていたための追加Battle.has_available_bench(player)/Battle.is_floating(pokemon)/Battle.is_trapped(pokemon)/Battle.is_nervous(pokemon)/Battle.is_hazard_immune(pokemon)/Battle.can_use_last_resort(pokemon)/Battle.get_forced_move_name(pokemon)/Battle.is_first_actor(player)/Battle.is_second_actor(player)—battle.query(PokemonQuery)のうちPokemon/Player単体の引数で完結する判定メソッドをBattle直下に薄い 委譲として追加。can_switch()以外はbattle.query.<method>()の直接呼び出しが 必要でdocs/api/README.mdにも未掲載だったための対応(AttackContext/EventContextを要求する内部専用メソッドは対象外のまま)docs/api/README.md(Pokemon「シナリオ構築系(フォルム変化)」)にPokemon.set_form(name, hp_policy="keep_absolute", set_default_ability=False)を追記。 実装済み(ロトム・ザシアン/ザマゼンタ・オリジンフォルム等の切り替えに内部で使用) ながらdocs/api/README.md・examples/のどちらにも未掲載だったための対応。 合わせてexamples/03_damage_calc/10_form_change_comparison.pyを新設し、set_form()によるロトムのフォルム変化がタイプ・種族値を通じてダメージ・致死率に 与える影響を比較するサンプルを追加docs/api/README.md(Battle「状態取得系」)にBattle.calc_move_priority(attacker, move)/Battle.resolve_speed_order()を追記。examples/02_ai/04_priority_and_command_debug.pyで使用されているが実装済みメソッド自体はdocs/api/README.mdに未掲載で、 「テストユーティリティ」節のインデックス指定版jpoke.testing.calc_move_priority(battle, player_index, move_index=0)のみが記載されていたための対応。両者の違い (Pokemon/Moveオブジェクト指定かインデックス指定か)を相互に明記したdocs/api/README.md(Battle「シナリオ構築系」)にBattle.gain_item(target, name)/Battle.set_item(target, name, source=None)/Battle.remove_item(target, source=None, track_loss=True)/Battle.take_item(target, ignore_sticky_hold=False)/Battle.swap_items(ignore_sticky_hold=False)/Battle.consume_item(target, track_loss=True)を追記。実装済み(item_managerへの薄い委譲)ながらdocs/api/README.md・examples/のどちらにも未掲載だったための対応。状態異常・ 揮発性状態・天候・地形にはそれぞれset_ailment/set_volatile/set_weather/set_terrain用のサンプルがあるのに、持ち物操作系だけシナリオ構築用の直接操作 手段が丸ごと欠落していたため。合わせてexamples/03_damage_calc/12_item_manipulation.pyを新設し、6メソッドの成功/失敗条件の違い(gain_itemは持ち物なしが前提、take_itemは奪う側が持ち物なしが前提、swap_itemsは双方が持ち物を持っていても 実行できる等)を単体で確認するサンプルを追加Battle.copy(copy_logs=False)—event_logger/command_log(対戦開始からの 全履歴)をdeepcopyせず、複製先に空の新規ログを持たせるオプション引数を追加。copy_logs=Falseを指定すると、複製元のログには影響を与えずにコピー負荷を 抑えられる。既定はTrueで従来通りの挙動を維持する。TreeSearchPlayerの内部シミュレーション(evaluate()の既定実装はログを一切参照しない)でcopy_logs=Falseを使うように変更し、探索ノードごとに発生していた 全履歴の無駄なdeepcopyを削減したBattle.build_observation(observer, copy_logs=True)にcopy_logs引数を追加。 従来は内部実装(observation_builder.build())がdeepcopy(battle)を直接呼んで おり、copy()のcopy_logs=False最適化の恩恵を受けられなかった。build_observation()はcommand_manager.py/turn_controller.pyから毎ターンの 行動選択フェーズで必ず呼ばれるホットパスのため、ログを一切参照しない方策でも ターンが進むほど肥大化する全履歴を無条件にdeepcopyし続けるコストがあった。 既定はBattle.copy()と同じTrueで従来通りログを引き継ぐ挙動を維持する (choose_command()/choose_selection()の実装がログを参照する可能性がある 汎用の呼び出し経路の既定は変更していない)。ログを参照しないと分かっている 用途でのみ明示的にcopy_logs=Falseを指定してコピー負荷を削減できるPlayer.battle_against()にon_battle_end: Callable[[Battle], None] | None = None引数を追加。従来はn_battlesループ内で各対戦のBattleを勝敗判定にのみ使い 破棄しており、自己対戦データ収集(強化学習用リプレイ等)のためにbattle_against()を使わずseed + 対戦通番の派生シード規約を手動で再実装する必要があった。 各対戦のplay_out()完了直後にBattleを受け取れるコールバックを追加し、 この重複を解消した。既定値Noneのため既存呼び出しの挙動は変わらないPlayer.add_pokemon()にevs: dict[Stat, int] | None = None/ivs: dict[Stat, int] | None = None引数を追加。指定した場合、生成したPokemonに対し内部でset_evs()/set_ivs()を呼ぶ。従来はadd_pokemon()の戻り値に対して別途set_evs()/set_ivs()を呼ぶ必要があったBattle.is_struggle_only(player)— 指定プレイヤーがわるあがきしか選べない状態 かどうかを判定する薄いヘルパー。従来はget_available_commands(player)が 空の技コマンドと交代コマンドを両方含みうるためget_available_commands(player)[0]でわるあがきを代用すると、交代コマンドが同時に存在する場合に誤ってそちらを 返してしまう罠があった。実際にわるあがきを選ぶ際はCommand.STRUGGLEを そのまま使えばよい(インデックス解決が不要な固定コマンドのため)Battle.copy(omniscient=True)— 複製先のobserverをNoneにするオプション 引数を追加。choose_command()が受け取るbattleは情報隠蔽済みの観測 (observerが自分自身のコピー)であることがあり、木探索の内部シミュレーション ではそのままcopy()した後にsim.observer = Noneを手動で設定する必要が あった。TreeSearchPlayerをこのオプションを使う形に変更した。既定はFalseで従来通りの挙動を維持するRandomPlayer.choose_selection()— 選出番号をbattle.decision_randomで ランダムに選ぶよう追加。従来はPlayer.choose_selection()の既定実装 (先頭から順に選出)を継承しており、choose_command()はランダムでも選出は 固定されたままだった
Changed¶
- 破壊的変更: サポートする最小 Python バージョンを 3.10 から 3.11 に引き上げた
(
requires-python = ">=3.11")。CI のテストマトリクスも 3.10/3.12 から 3.11/3.12 に変更 - 破壊的変更:
TreeSearchPlayerを、具体的な評価アルゴリズムを持たない抽象度の 高い探索フレームワーク基底クラスに変更した。自分の合法手をどう評価するか (_score_command、新設)は具体的なアルゴリズムを実装するサブクラスに委ねる ようになり、TreeSearchPlayer単体をインスタンス化してchoose_command()を 呼ぶとNotImplementedErrorになる。従来の「相手が最悪の手を選ぶと仮定する」 ミニマックス評価は新設のjpoke.players.MinimaxPlayerに移設したため、TreeSearchPlayerを直接使っていたコードはMinimaxPlayerに置き換える必要が ある(evaluate/fallback/estimate_opponent/configure_simの4フックは 従来通りMinimaxPlayerでも利用できる) - 破壊的変更:
Battle.__init__がplayers: tuple[Player, ...]ではなく*players: Playerの可変長引数を受け取るようになった。Battle((player1, player2), ...)ではなくBattle(player1, player2, ...)と書く Battle(n_selected=...)を省略した場合、min(3, 各プレイヤーの手持ち数)を 自動設定するようになった(従来は常に3固定で、手持ちが3未満だとn_selectedの 明示指定が必須だった)Battle.calc_lethal/core.lethal.calc_lethalのmoves引数が技名の文字列 (MoveName)を受け付けるようになった。Move(name)へのラップなしにmoves="ドラゴンテール"のように渡せる(Moveインスタンスでの指定は従来通り 可能)Battle.print_logs(turn)/Battle.get_log_lines(turn)がturn="all"を 受け付けるようになった。1ターン目から現在ターンまでの全ログを一括で 取得・出力できる(turn=Noneで現在ターンのみ、という既存の挙動は変更なし)- 破壊的変更:
examples/をユースケース別ディレクトリ構造に再編した。 従来のフラットな連番(examples/01_quickstart.py〜examples/09_janken_nash_cfr.py)を廃止し、examples/01_basics/,examples/02_ai/,examples/03_damage_calc/,examples/04_research/の ディレクトリに分割、各ディレクトリ内で独立して連番を振り直した (例:examples/01_quickstart.py→examples/01_basics/01_quickstart.py)。 独立した作業が同じ番号を取り合う衝突事故を防ぐための変更 - 破壊的変更:
Battle.consume_item()/ItemManager.consume_item()の第1引数名をmonからtargetにリネームした(gain_item/remove_item/take_item等、 他のアイテム系メソッドとの引数名を統一するための変更)。キーワード引数でmon=...と呼び出している箇所はtarget=...に変更する必要がある (リポジトリ内の既存呼び出しはすべて位置引数のため影響なし) -
破壊的変更:
Pokemon.modify_hp()をPokemon._modify_hp_raw()にリネームした。 従来のdocstringで「内部用。外部からはbattle.modify_hp()を使用」と警告して いたにもかかわらず、アンダースコアなしのメソッド名のままトップレベルfrom jpoke import Pokemonから到達可能で誤って直接呼び出しやすい状態 だった。直接呼ぶとHPのクランプのみが行われ、ON_HP_CHANGE系ハンドラの 発火・瀕死判定・ログ記録がスキップされる罠になっていたため、命名で 内部専用であることを明示した。外部コードは常にBattle.modify_hp()を使うこと -
破壊的変更:
Battle.decision_random(行動選択専用の乱数生成器)の種の派生式をhash((seed, "decision")) & 0xFFFFFFFFから(seed + 0x9E3779B9) & 0xFFFFFFFFに 変更した。strを含むtupleのhash()はPYTHONHASHSEEDによりプロセスごとに ランダム化されるため、同じseedを指定してもdecision_randomが生成する乱数列が プロセスを跨ぐと再現しなかった(RandomPlayer.choose_command()/TreeSearchPlayer.fallback()はこの乱数列を消費するため、これらを経由するコマンド 選択の再現実験・CI比較が壊れうる問題があった)。新しい派生式は整数の算術演算のみでPYTHONHASHSEEDに依存しないため、プロセスを跨いでも同じseedなら同じ乱数列が 再現される。加算する定数(ハッシュ撹拌でよく使われる黄金比由来の値)が0でないため mod 232 上で不動点を持たず、あらゆるseedでBattle.random(種はseedその もの)と衝突しないことが保証される。Battle.copy(reseed=True)側も同様に修正した。 同じseedでもdecision_randomが生成する具体的な乱数列は旧バージョンから 変わる**(ゲーム進行用のBattle.randomの乱数列には影響しない) -
examples/配下のファイル構成を「1ファイル=1事例」の原則で再点検し、詰め込み 過ぎていたファイルを分割・renumberした(01_basics/3→5ファイル、02_ai/3→4ファイル、03_damage_calc/3→9ファイル、04_research/は03_janken_nash_cfr.py/04_janken_nash_fictitious_play.pyの番号を入れ替え)。04_research/03_janken_nash_cfr.pyのhp_bucket()はHP満タン (fraction=1.0)を専用の最上位バケットとして切り出す仕様に変更し、バケット 総数がHP_BUCKETSからHP_BUCKETS + 1になった - 破壊的変更:
Command.is_switch()をプロパティCommand.is_switchに変更した。 同じ真偽値判定を行うis_regular_move/is_terastal/is_megaevol/is_gigamax/is_zmoveがすべて@propertyである中でis_switch()のみ メソッド呼び出しになっており、if cmd.is_switch:と書くとbool化されず bound methodが常にTruthyになるバグの温床だったため統一した。呼び出し側はcmd.is_switch()をcmd.is_switchに変更する必要がある Pokemon.set_ivs()/Pokemon.set_evs()の引数がlist[int]に加えてdict[Stat, int]を受け付けるようになった。Pokemon.set_stats()が先に辞書形式に 対応したのに対し、こちらは[HP, 攻撃, 防御, 特攻, 特防, 素早さ]の暗黙の固定順 リストのままで、要素の並び順を覚えていないと意図しないステータスに書き込まれる (例外は出ない)というset_stats()と同型の罠が残っていたための対応。dict指定 時は指定したステータスのみ更新し、未指定のステータスは既存値を維持する。従来通りlist[int](6要素で全体を置き換え)でも呼び出せるため既存呼び出しの挙動は変わらない- 破壊的変更:
jpoke.testingの「0/1のどちらの陣営・アクティブポケモンか」を表す インデックス引数名を全関数でplayer_idxに統一した。従来はbuild_context/run_move/calc_lethalがatk_idx、apply_ailmentがactive_index、calc_move_priorityがplayer_index、run_switch/can_switchがplayer_idxと4通りに分裂しており、覚えた引数名を別の関数にそのまま使うとTypeErrorになる罠だった。キーワード引数でatk_idx=/active_index=/player_index=を指定している呼び出し箇所はplayer_idx=に変更する必要がある (位置引数での呼び出しは影響なし) Pokemon.render_info()(Pokemon.show()の出力)で、アイテム未所持・ テラスタル未設定時の表示が英語ハードコード("No item"/"No terastal")に なっていたため、それぞれ「アイテムなし」「テラスタルなし」に変更した。 出力文字列のみの変更で構造(区切り文字・並び順)は変わらないため、 この文字列を直接パースしているコードがあれば影響を受けるexamples/03_damage_calc/を「生ダメージロール(01) → 状態操作の単体確認(02) → calc_lethal基本(03) → シナリオ比較(04) → …」という順に組み直し、10→11 ファイルにrenumberした(新設:02_direct_state_manipulation.py。旧01_basic_lethal_calculation.py→03_basic_lethal_calculation.py、旧02_ailment_and_scenario_comparison.py→04_ailment_and_scenario_comparison.py、 旧03_raw_damage_rolls.py→01_raw_damage_rolls.py、以降04〜10は05〜11にそれぞれ1つずつ繰り下げ)
Fixed¶
pyproject.tomlのpackage-dataにjpoke.dataのregulation/*.csv(src/jpoke/data/regulation/item.csv/pokemon.csv)が未登録だったため、 ビルドした wheel からpip installするとpokedex.pyがregulation/pokemon.csvを読めずFileNotFoundErrorになっていた (import jpokeが失敗する状態だった)。登録を追加して解消したsrc/jpoke/core/lethal.pyの_pokemon_states()で、LethalPokemonState.boosts(dict[Stat, int])にdict(state.attacker_boosts or ())/dict(state. defender_boosts or ())(dict[str, int]相当)をそのまま代入しており mypy の 型チェックが通っていなかった。既存のailmentフィールドと同様にcastで 明示するよう修正した(実行時の挙動に変更なし)src/jpoke/utils/__init__.pyの__all__リストがタブインデントになっており ruff(W191)が警告していたため、スペースインデントに統一したexamples/01_getting_started/03_custom_player.ipynb/examples/03_lethal/02_multi_move.ipynbの末尾・行末の余分な空白を除去し、 ruff(W291/W293)の警告を解消したdocs/api/README.mdのCommand章「インスタンスプロパティ・メソッド」表にis_type(command_type)(examples/02_ai/01_custom_player.pyで使用している、"any"/"move"/"switch"を指定できる汎用の種別判定メソッド)が掲載されて いなかったため追記。is_regular_moveとの違い("move"は通常技コマンドに加え、 テラスタル・メガシンカ・ダイマックス・Zワザを伴う技コマンドも含む)も明記した- README.md の「クイックスタート」節が
while battle.judge_winner() is None and battle.turn < 100:/winner = battle.judge_winner()という旧パターンのままで、examples/01_basics/02_quickstart.py(docstringで「READMEのクイックスタートと 同内容」と明記)やdocs/api/README.mdが採用しているwhile not battle.finished and battle.turn < 100:/winner = battle.winnerという表記と食い違っていたため 修正し、表記を統一した(judge_winner()自体は引き続き公開APIとして利用可能) Battle(seed=None)のフォールバックがint(time.time())(秒精度)だったため、 短時間に複数のBattleを生成すると同一シードになり、Player.battle_against()などの多数回対戦がすべて同じ展開になってしまう問題を修正。OSの乱数源から 高エントロピーな値を生成するように変更したPlayer.battle_against(..., seed=...)を指定した場合、対戦ごとにseed + 対戦通番の派生シードを自動的に使うようにし、n_battlesを1回の 呼び出しで指定しても展開が固定化しないようにしたBattle.roll_damage()の通常ロール抽選がrandom.choice()(PRNG内部状態に 依存しrandom()のみを固定するテストヘルパーでは制御できない)だったためrandom.random()ベースの抽選に変更。上記のseed高エントロピー化に伴い、 乱数固定が不十分だった一部テストがダメージ比較で偶発的に失敗する状態を解消したBattle.finishedがself.winner is not Noneだけを見ていたため、judge_winner()を一度も呼ばずにfinishedだけを参照すると、TODスコアで 実際には決着している対戦でもFalseを返し続ける不整合を修正。judge_winner()経由で判定するように変更したBattle.build_observation()(choose_command()/choose_selection()に渡される 観測用コピーを構築する)が乱数生成器randomをdeepcopyで独立複製していたため、RandomPlayer等が観測用コピー側のrandomを消費しても本体のbattle.randomが 一切進まなかった。技を使わず交代コマンドのみが選ばれ続けるターンでは本体の乱数状態が 毎ターン同一のまま固定され、build_observation()が同じ乱数状態から同じコピーを 作り直すことで同じ選択が繰り返される無限交代ループに陥っていた (examples/02_team_battle.pyのseed=1,2,3,5で再現)。当初は観測用コピーのrandomを本体と同一のオブジェクト参照に差し替えて解消したが、これには 「行動選択に使う乱数」と「ゲーム進行に使う乱数」を分離していなかったことに起因する 重大な設計上の欠陥があった(後述の追加修正を参照)- 上記修正で
randomを観測用コピーと本体で共有した結果、Player.choose_command(sim)内で方策がsim.randomを直接触ると、本来は技実行後(ダメージロール・命中判定・ 急所判定等)に消費されるはずの乱数列を行動選択の時点で先取り消費できてしまい、 「これから打つ技が急所に当たるか」を打つ前に知った上で行動を選べるチート的先読みが 可能になる欠陥があった。Battleに行動選択専用の乱数生成器decision_random(seedから決定的に派生させ、Battle.copy(reseed=True)でも派生シードで 再初期化される)を新設し、RandomPlayer.choose_command()/TreeSearchPlayer.fallback()はこちらを使うように変更した。build_observation()はdecision_randomだけを本体と共有し(無限交代ループ対策を 維持)、ゲーム進行用のrandomは元通りdeepcopyによる独立コピーに戻すことで 先読みを不可能にした TreeSearchPlayer._worst_case_over_opponent()内のsim = battle.copy()がreseed引数を省略(既定False)していたため、同じmy_cmdに対する各opp_cmd分岐・各my_cmd分岐が複製元のrandom/decision_randomの状態を そのまま共有し、探索木の兄弟ノード間で乱数系列が相関していた (examples/04_research/03_janken_nash_cfr.pyの自作ロールアウトは既にreseed=Trueを使っており対象外)。battle.copy(reseed=True)に変更し、 各分岐が派生シードで独立に再初期化された乱数系列を使うようにした。configure_simで命中判定・ダメージ乱数等の確率的要素を固定している場合は 探索結果(選ばれるコマンド)に変化はないが、固定していない場合は評価値の 相関が解消されることで変わりうるPokemon.set_stats(stats)が辞書のキー(Stat)を無視し、enumerate()による 挿入順を暗黙の固定順(0=HP, 1=攻撃, 2=防御, ...)とみなして書き込んでいたため、{"atk": 150, "def": 100}のようにキー順が典型順と異なる辞書や6項目に満たない 辞書を渡すと、例外を出さずに意図と異なるステータスへ書き込まれる不具合を修正。stats.items()からキーで対象インデックスを引くように変更した
[0.1.0] - 2026-07-11¶
Added¶
- 初回 PyPI 公開。
pip install jpokeでイベント駆動のポケモンチャンピオンズ シングルバトルシミュレーションを利用できる - 特性 310 件・アイテム 247 件・技 733 件・揮発性状態 66 件・状態異常 7 件・
場の効果(天候・地形・グローバル・サイド)31 件を実装
(詳細は
.internal/progress/配下を参照)