rabukasim / docs /plans /opcode_combination_test_plan.md
trioskosmos's picture
chore: remove large files for HF Space
9bd4ce5
# オペコード全組み合わせテスト計画
## 概要
このドキュメントは、全オペコードと条件の組み合わせを体系的にテストするための計画です。
ゲーム状態(カード、エネルギー、デッキ、捨て札サイズなど)の変数を考慮した包括的なテストマトリックスを定義します。
---
## 1. オペコード分類
### 1.1 基本操作オペコード(Core Opcodes)
| オペコード | ID | 説明 | テスト優先度 |
|-----------|-----|------|-------------|
| `O_DRAW` | 10 | カードを引く | 高 |
| `O_ADD_BLADES` | 11 | ブレード追加 | 高 |
| `O_ADD_HEARTS` | 12 | ハート追加 | 高 |
| `O_REDUCE_COST` | 13 | コスト削減 | 中 |
| `O_LOOK_DECK` | 14 | デッキを見る | 高 |
| `O_RECOVER_LIVE` | 15 | ライブ回復 | 高 |
| `O_BOOST_SCORE` | 16 | スコア増加 | 高 |
| `O_RECOVER_MEMBER` | 17 | メンバー回復 | 高 |
| `O_BUFF_POWER` | 18 | パワーバフ | 中 |
| `O_IMMUNITY` | 19 | 免疫付与 | 中 |
| `O_MOVE_MEMBER` | 20 | メンバー移動 | 高 |
| `O_SWAP_CARDS` | 21 | カード交換 | 中 |
| `O_SEARCH_DECK` | 22 | デッキ検索 | 高 |
| `O_ENERGY_CHARGE` | 23 | エネルギーチャージ | 高 |
| `O_SET_BLADES` | 24 | ブレード設定 | 中 |
| `O_SET_HEARTS` | 25 | ハート設定 | 中 |
| `O_FORMATION_CHANGE` | 26 | フォーメーション変更 | 低 |
| `O_NEGATE_EFFECT` | 27 | 効果無効化 | 高 |
| `O_ORDER_DECK` | 28 | デッキ順序変更 | 中 |
| `O_META_RULE` | 29 | メタルール | 中 |
### 1.2 選択・検索オペコード(Selection Opcodes)
| オペコード | ID | 説明 | テスト優先度 |
|-----------|-----|------|-------------|
| `O_SELECT_MODE` | 30 | モード選択 | 高 |
| `O_MOVE_TO_DECK` | 31 | デッキに移動 | 高 |
| `O_TAP_OPPONENT` | 32 | 相手をタップ | 高 |
| `O_PLACE_UNDER` | 33 | 下に配置 | 中 |
| `O_REVEAL_CARDS` | 40 | カード公開 | 高 |
| `O_LOOK_AND_CHOOSE` | 41 | 見て選ぶ | 高 |
| `O_CHEER_REVEAL` | 42 | チア公開 | 低 |
| `O_ACTIVATE_MEMBER` | 43 | メンバー活性化 | 高 |
| `O_ADD_TO_HAND` | 44 | 手札に追加 | 高 |
| `O_COLOR_SELECT` | 45 | 色選択 | 高 |
| `O_REPLACE_EFFECT` | 46 | 効果置換 | 中 |
| `O_TRIGGER_REMOTE` | 47 | リモートトリガー | 中 |
| `O_REDUCE_HEART_REQ` | 48 | ハート要件削減 | 中 |
| `O_MODIFY_SCORE_RULE` | 49 | スコアルール変更 | 低 |
| `O_ADD_STAGE_ENERGY` | 50 | ステージエネルギー追加 | 中 |
| `O_SET_TAPPED` | 51 | タップ状態設定 | 高 |
| `O_ADD_CONTINUOUS` | 52 | 継続効果追加 | 中 |
| `O_TAP_MEMBER` | 53 | メンバーをタップ | 高 |
### 1.3 特殊操作オペコード(Special Opcodes)
| オペコード | ID | 説明 | テスト優先度 |
|-----------|-----|------|-------------|
| `O_PLAY_MEMBER_FROM_HAND` | 57 | 手札からプレイ | 高 |
| `O_MOVE_TO_DISCARD` | 58 | 捨て札に移動 | 高 |
| `O_GRANT_ABILITY` | 60 | アビリティ付与 | 中 |
| `O_INCREASE_HEART_COST` | 61 | ハートコスト増加 | 低 |
| `O_REDUCE_YELL_COUNT` | 62 | エール数削減 | 中 |
| `O_PLAY_MEMBER_FROM_DISCARD` | 63 | 捨て札からプレイ | 高 |
| `O_PAY_ENERGY` | 64 | エネルギー支払い | 高 |
| `O_SELECT_MEMBER` | 65 | メンバー選択 | 高 |
| `O_DRAW_UNTIL` | 66 | 指定枚数までドロー | 高 |
| `O_SELECT_PLAYER` | 67 | プレイヤー選択 | 中 |
| `O_SELECT_LIVE` | 68 | ライブ選択 | 高 |
| `O_REVEAL_UNTIL` | 69 | 条件まで公開 | 高 |
| `O_INCREASE_COST` | 70 | コスト増加 | 低 |
| `O_PREVENT_PLAY_TO_SLOT` | 71 | スロットプレイ禁止 | 中 |
| `O_SWAP_AREA` | 72 | エリア交換 | 低 |
| `O_TRANSFORM_HEART` | 73 | ハート変換 | 中 |
| `O_SELECT_CARDS` | 74 | カード選択 | 高 |
| `O_OPPONENT_CHOOSE` | 75 | 相手が選択 | 中 |
| `O_PLAY_LIVE_FROM_DISCARD` | 76 | 捨て札からライブプレイ | 高 |
| `O_REDUCE_LIVE_SET_LIMIT` | 77 | ライブセット制限削減 | 低 |
| `O_PREVENT_ACTIVATE` | 82 | 活性化禁止 | 中 |
| `O_ACTIVATE_ENERGY` | 81 | エネルギー活性化 | 高 |
| `O_PREVENT_SET_TO_SUCCESS_PILE` | 80 | 成功山札への配置禁止 | 低 |
| `O_PREVENT_BATON_TOUCH` | 90 | バトンタッチ禁止 | 中 |
| `O_SET_HEART_COST` | 83 | ハートコスト設定 | 中 |
---
## 2. 条件分類
### 2.1 カウント条件(Count Conditions)
| 条件 | ID | 説明 | テスト変数 |
|------|-----|------|-----------|
| `C_COUNT_STAGE` | 203 | ステージ枚数 | 0, 1, 2, 3 |
| `C_COUNT_HAND` | 204 | 手札枚数 | 0, 1, 3, 5, 7, 10 |
| `C_COUNT_DISCARD` | 205 | 捨て札枚数 | 0, 1, 3, 5, 10 |
| `C_COUNT_ENERGY` | 213 | エネルギー枚数 | 0, 1, 3, 5 |
| `C_COUNT_SUCCESS_LIVE` | 218 | 成功ライブ数 | 0, 1, 2, 3 |
| `C_COUNT_LIVE_ZONE` | 230 | ライブゾーン枚数 | 0, 1, 2, 3 |
| `C_COUNT_GROUP` | 208 | グループ枚数 | 0, 1, 2, 3 |
| `C_COUNT_HEARTS` | 223 | ハート数 | 0, 1, 5, 10 |
| `C_COUNT_BLADES` | 224 | ブレード数 | 0, 1, 3, 5 |
### 2.2 状態条件(State Conditions)
| 条件 | ID | 説明 | テスト変数 |
|------|-----|------|-----------|
| `C_TURN_1` | 200 | ターン1かどうか | true, false |
| `C_IS_CENTER` | 206 | センターかどうか | true, false |
| `C_LIFE_LEAD` | 207 | ライフリード | true, false |
| `C_HAS_MEMBER` | 201 | メンバー所持 | card_id |
| `C_HAS_COLOR` | 202 | 色所持 | color_idx |
| `C_HAS_LIVE_CARD` | 214 | ライブカード所持 | true, false |
| `C_HAND_HAS_NO_LIVE` | 217 | 手札にライブなし | true, false |
| `C_DECK_REFRESHED` | 227 | デッキリフレッシュ済み | true, false |
| `C_HAS_MOVED` | 228 | 移動済み | true, false |
| `C_HAND_INCREASED` | 229 | 手札増加済み | true, false |
| `C_BATON` | 231 | バトン状態 | true, false |
### 2.3 比較条件(Comparison Conditions)
| 条件 | ID | 説明 | テスト変数 |
|------|-----|------|-----------|
| `C_COST_CHECK` | 215 | コスト確認 | threshold, LE/GE |
| `C_RARITY_CHECK` | 216 | レアリティ確認 | rarity |
| `C_SCORE_COMPARE` | 220 | スコア比較 | diff |
| `C_OPPONENT_HAND_DIFF` | 219 | 相手手札差 | diff |
| `C_OPPONENT_ENERGY_DIFF` | 225 | 相手エネルギー差 | diff |
| `C_COST_LEAD` | 240 | コストリード | true, false |
| `C_SCORE_LEAD` | 241 | スコアリード | true, false |
| `C_HEART_LEAD` | 242 | ハートリード | true, false |
| `C_COST_COMPARE` | 246 | コスト比較 | diff |
| `C_BLADE_COMPARE` | 247 | ブレード比較 | diff |
| `C_HEART_COMPARE` | 248 | ハート比較 | diff |
### 2.4 フィルター条件(Filter Conditions)
| 条件 | ID | 説明 | テスト変数 |
|------|-----|------|-----------|
| `C_GROUP_FILTER` | 209 | グループフィルター | group_id |
| `C_SELF_IS_GROUP` | 211 | 自分のグループ | group_id |
| `C_TYPE_CHECK` | 232 | タイプ確認 | member/live |
| `C_IS_IN_DISCARD` | 233 | 捨て札にいる | true, false |
| `C_AREA_CHECK` | 234 | エリア確認 | area |
| `C_OPPONENT_HAS` | 210 | 相手が所持 | card_id |
| `C_OPPONENT_HAS_WAIT` | 249 | 相手がWAIT状態 | count |
---
## 3. ゲーム状態変数
### 3.1 プレイヤー状態
```rust
struct PlayerStateVariables {
// カードゾーン
hand_size: usize, // 0-10
deck_size: usize, // 0-50
discard_size: usize, // 0-20
energy_size: usize, // 0-10
live_zone_size: usize, // 0-3
success_lives_size: usize, // 0-3
// ステージ
stage: [i32; 3], // card_id or -1
stage_tapped: [bool; 3], // tap state
// バフ
blade_buffs: [i32; 3],
heart_buffs: [[u8; 7]; 3],
live_score_bonus: i32,
// フラグ
has_moved: bool,
hand_increased: bool,
deck_refreshed: bool,
}
```
### 3.2 グローバル状態
```rust
struct GlobalStateVariables {
turn: i32, // 1-20
current_player: usize, // 0 or 1
phase: Phase,
modal_answer: i32, // -1, 0, 1, 2
}
```
---
## 4. テストマトリックス
### 4.1 ドロー系オペコードテスト
#### O_DRAW テストマトリックス
| テストID | デッキサイズ | 手札サイズ | ドロー数 | 期待結果 |
|---------|------------|-----------|---------|---------|
| DRAW-01 | 5 | 0 | 1 | hand=1, deck=4 |
| DRAW-02 | 5 | 5 | 3 | hand=8, deck=2 |
| DRAW-03 | 0 | 3 | 1 | デッキリフレッシュ発生 |
| DRAW-04 | 2 | 7 | 3 | hand=9, deck=0, refresh=1 |
| DRAW-05 | 10 | 0 | 10 | hand=10, deck=0 |
#### O_DRAW_UNTIL テストマトリックス
| テストID | デッキサイズ | 現在手札 | 目標枚数 | ドロー数 | 期待結果 |
|---------|------------|---------|---------|---------|---------|
| DUNTIL-01 | 10 | 2 | 5 | 3 | hand=5 |
| DUNTIL-02 | 10 | 7 | 5 | 0 | hand=7(変更なし) |
| DUNTIL-03 | 2 | 3 | 7 | 2 | hand=5, deck=0 |
| DUNTIL-04 | 0 | 3 | 5 | refresh | リフレッシュ後ドロー |
### 4.2 回復系オペコードテスト
#### O_RECOVER_MEMBER テストマトリックス
| テストID | 捨て札内容 | フィルター | 期待結果 |
|---------|-----------|-----------|---------|
| RECM-01 | [M1, M2, M3] | なし | いずれか1枚回復 |
| RECM-02 | [M1(cost=3), M2(cost=5)] | cost<=4 | M1のみ選択可能 |
| RECM-03 | [L1, L2] | type=member | 選択不可 |
| RECM-04 | [] | なし | 効果なし |
| RECM-05 | [M1(μ's), M2(Aqours)] | group=μ's | M1のみ選択可能 |
#### O_RECOVER_LIVE テストマトリックス
| テストID | 捨て札内容 | フィルター | 期待結果 |
|---------|-----------|-----------|---------|
| RECL-01 | [L1, L2] | なし | いずれか1枚回復 |
| RECL-02 | [L1(μ's), L2(Aqours)] | group=μ's | L1のみ選択可能 |
| RECL-03 | [M1, M2] | type=live | 選択不可 |
| RECL-04 | [L1(hearts=8)] | hearts>=5 | L1選択可能 |
### 4.3 検索・選択系オペコードテスト
#### O_LOOK_AND_CHOOSE テストマトリックス
| テストID | デッキ内容 | 見る枚数 | 選択枚数 | フィルター | 期待結果 |
|---------|-----------|---------|---------|-----------|---------|
| LAC-01 | [M1, M2, M3, M4, M5] | 3 | 1 | なし | 1枚handへ、2枚discard |
| LAC-02 | [M1(cost=3), M2(cost=5), M3(cost=2)] | 3 | 1 | cost>=4 | M2のみ選択可能 |
| LAC-03 | [L1, M1, M2] | 3 | 1 | type=live | L1のみ選択可能 |
| LAC-04 | [M1, M2] | 5 | 1 | なし | デッキ不足で2枚のみ |
| LAC-05 | [M1(μ's), M2(Aqours), M3(μ's)] | 3 | 2 | group=μ's | M1, M3選択可能 |
#### O_REVEAL_UNTIL テストマトリックス
| テストID | デッキ内容 | 条件 | 期待結果 |
|---------|-----------|------|---------|
| REVU-01 | [M1, M2, L1, M3] | type=live | L1をhandへ、M1,M2をdiscard |
| REVU-02 | [M1, M2, M3] | type=live | 全枚discard、条件不一致 |
| REVU-03 | [M1(cost=2), M2(cost=5), M3] | cost>=4 | M2をhandへ、M1をdiscard |
| REVU-04 | [L1, L2, L3] | type=live | L1をhandへ |
### 4.4 エネルギー系オペコードテスト
#### O_PAY_ENERGY テストマトリックス
| テストID | エネルギー数 | タップ済み | 支払い数 | 期待結果 |
|---------|------------|-----------|---------|---------|
| PAYE-01 | 3 | 0 | 1 | tapped=1, energy=3 |
| PAYE-02 | 3 | 1 | 2 | tapped=3, energy=3 |
| PAYE-03 | 2 | 0 | 3 | 支払い不可 |
| PAYE-04 | 5 | 2 | 3 | tapped=5 |
| PAYE-05 | 0 | 0 | 1 | 支払い不可 |
#### O_ACTIVATE_ENERGY テストマトリックス
| テストID | エネルギー数 | 活性化数 | 期待結果 |
|---------|------------|---------|---------|
| ACTE-01 | 3 | 2 | tapped=2 |
| ACTE-02 | 3 | 5 | 全枚タップ |
| ACTE-03 | 0 | 1 | 効果なし |
### 4.5 タップ系オペコードテスト
#### O_TAP_OPPONENT テストマトリックス
| テストID | 相手ステージ | フィルター | タップ数 | 期待結果 |
|---------|------------|-----------|---------|---------|
| TAPO-01 | [M1, M2, M3] | なし | 1 | 1枚タップ |
| TAPO-02 | [M1(cost=2), M2(cost=5)] | cost<=3 | 1 | M1のみタップ可能 |
| TAPO-03 | [M1(tapped), M2] | なし | 1 | M2タップ |
| TAPO-04 | [M1, M2] | tapped=true | 1 | 既にタップ済みは対象外 |
| TAPO-05 | [-1, -1, -1] | なし | 1 | 効果なし |
#### O_SET_TAPPED テストマトリックス
| テストID | 対象 | 現在状態 | 設定値 | 期待結果 |
|---------|------|---------|-------|---------|
| SETT-01 | stage[0] | untapped | 1 | tapped |
| SETT-02 | stage[0] | tapped | 0 | untapped |
| SETT-03 | stage[0] | tapped | 1 | tapped(変更なし) |
### 4.6 スコア・バフ系オペコードテスト
#### O_BOOST_SCORE テストマトリックス
| テストID | 現在スコア | 増加値 | 期待結果 |
|---------|-----------|-------|---------|
| BOOS-01 | 0 | 3 | score=3 |
| BOOS-02 | 1000 | 5 | score=1005 |
| BOOS-03 | 0 | 0 | score=0 |
#### O_ADD_BLADES テストマトリックス
| テストID | 現在ブレード | 追加数 | 対象 | 期待結果 |
|---------|------------|-------|------|---------|
| ADDB-01 | 0 | 2 | player | blades=2 |
| ADDB-02 | 3 | 1 | member[0] | blade_buffs[0]=1 |
| ADDB-03 | 0 | 0 | player | blades=0 |
#### O_ADD_HEARTS テストマトリックス
| テストID | 現在ハート | 追加数 | 色 | 期待結果 |
|---------|-----------|-------|-----|---------|
| ADDH-01 | [0,0,0,0,0,0,0] | 2 | pink | heart_buffs[0]=2 |
| ADDH-02 | [1,1,0,0,0,0,0] | 1 | blue | heart_buffs[2]=1 |
| ADDH-03 | [0,0,0,0,0,0,0] | 0 | pink | 変化なし |
### 4.7 条件分岐テスト
#### C_COUNT_HAND 条件テスト
| テストID | 手札枚数 | 閾値 | 比較 | 期待結果 |
|---------|---------|-----|------|---------|
| CCH-01 | 3 | 3 | >= | true |
| CCH-02 | 3 | 4 | >= | false |
| CCH-03 | 0 | 1 | >= | false |
| CCH-04 | 10 | 5 | >= | true |
#### C_COST_CHECK 条件テスト
| テストID | カードコスト | 閾値 | 比較 | 期待結果 |
|---------|------------|-----|------|---------|
| CCOST-01 | 5 | 5 | >= | true |
| CCOST-02 | 5 | 5 | <= | true |
| CCOST-03 | 3 | 5 | >= | false |
| CCOST-04 | 7 | 5 | <= | false |
#### C_LIFE_LEAD 条件テスト
| テストID | P0成功ライブ | P1成功ライブ | 期待結果 |
|---------|------------|------------|---------|
| CLIF-01 | 2 | 1 | true |
| CLIF-02 | 1 | 2 | false |
| CLIF-03 | 1 | 1 | false |
| CLIF-04 | 0 | 0 | false |
---
## 5. 複合テストシナリオ
### 5.1 コスト支払い+効果の複合テスト
```rust
#[test]
fn test_cost_discard_hand_with_filter() {
// コスト: 手札を3枚捨てる(フィルター: 歩夢/かのん/花帆)
// 効果: スコア+3
// Setup
let hand = vec![Ayumu, Kanon, Kaho, Other];
// Expected: 3枚捨ててスコア+3
}
#[test]
fn test_cost_pay_energy_with_insufficient() {
// コスト: エネルギー3支払い
// 状態: エネルギー2のみ
// Expected: 効果発動なし
}
```
### 5.2 連鎖効果テスト
```rust
#[test]
fn test_on_leaves_trigger_chain() {
// メンバーがステージを離れる時、OnLeaves効果が発動
// その効果がさらに別のカードを移動させる
// Setup: stage[0] = CardWithOnLeaves
// Execute: O_MOVE_TO_DISCARD
// Expected: OnLeaves効果が発動
}
#[test]
fn test_trigger_remote_chain() {
// O_TRIGGER_REMOTEで別のカードのアビリティを発動
// そのアビリティがさらにO_TRIGGER_REMOTE
// Expected: 連鎖的にアビリティ発動
}
```
### 5.3 エッジケーステスト
```rust
#[test]
fn test_empty_zone_operations() {
// 空のゾーンに対する操作
// - 空のデッキからドロー
// - 空の捨て札から回復
// - 空のステージでタップ
// Expected: エラーなく処理
}
#[test]
fn test_full_zone_operations() {
// 満杯のゾーンに対する操作
// - 手札10枚でドロー
// - ライブゾーン3枚で追加
// Expected: ルールに従った処理
}
```
---
## 6. テスト実装構造
### 6.1 テストヘルパー関数
```rust
/// ゲーム状態ビルダー
struct GameStateBuilder {
state: GameState,
}
impl GameStateBuilder {
fn new() -> Self;
fn with_hand(mut self, cards: Vec<i32>) -> Self;
fn with_deck(mut self, cards: Vec<i32>) -> Self;
fn with_discard(mut self, cards: Vec<i32>) -> Self;
fn with_energy(mut self, cards: Vec<i32>) -> Self;
fn with_stage(mut self, slot: usize, card_id: i32) -> Self;
fn with_success_lives(mut self, cards: Vec<i32>) -> Self;
fn with_tapped(mut self, slot: usize, tapped: bool) -> Self;
fn build(self) -> GameState;
}
/// アサーションヘルパー
fn assert_hand_size(state: &GameState, player: usize, expected: usize);
fn assert_deck_size(state: &GameState, player: usize, expected: usize);
fn assert_discard_size(state: &GameState, player: usize, expected: usize);
fn assert_score(state: &GameState, player: usize, expected: i32);
fn assert_blades(state: &GameState, player: usize, expected: i32);
fn assert_tapped(state: &GameState, player: usize, slot: usize, expected: bool);
```
### 6.2 テストファイル構成
```
engine_rust_src/src/
├── opcode_tests/
│ ├── mod.rs
│ ├── draw_tests.rs # O_DRAW, O_DRAW_UNTIL
│ ├── recover_tests.rs # O_RECOVER_MEMBER, O_RECOVER_LIVE
│ ├── search_tests.rs # O_LOOK_DECK, O_SEARCH_DECK, O_LOOK_AND_CHOOSE
│ ├── reveal_tests.rs # O_REVEAL_CARDS, O_REVEAL_UNTIL
│ ├── energy_tests.rs # O_PAY_ENERGY, O_ACTIVATE_ENERGY, O_ENERGY_CHARGE
│ ├── tap_tests.rs # O_TAP_OPPONENT, O_SET_TAPPED, O_TAP_MEMBER
│ ├── score_tests.rs # O_BOOST_SCORE, O_SET_SCORE
│ ├── buff_tests.rs # O_ADD_BLADES, O_ADD_HEARTS, O_BUFF_POWER
│ ├── move_tests.rs # O_MOVE_MEMBER, O_MOVE_TO_DECK, O_MOVE_TO_DISCARD
│ ├── select_tests.rs # O_SELECT_MODE, O_SELECT_MEMBER, O_SELECT_CARDS
│ └── condition_tests.rs # 全条件テスト
├── integration_tests/
│ ├── chain_effects.rs # 連鎖効果テスト
│ ├── edge_cases.rs # エッジケーステスト
│ └── full_game_flow.rs # ゲームフローテスト
└── test_helpers.rs
```
---
## 7. テスト実行戦略
### 7.1 ユニットテスト
- 各オペコード単体のテスト
- 各条件単体のテスト
- パラメータ化テストでバリエーションをカバー
### 7.2 統合テスト
- 複数オペコードの組み合わせ
- トリガー連鎖
- ゲームフェーズ遷移
### 7.3 プロパティベーステスト
```rust
use proptest::prelude::*;
proptest! {
#[test]
fn test_draw_never_exceeds_max(
deck_size in 0usize..50,
hand_size in 0usize..10,
draw_count in 1usize..5
) {
// ドロー後の手札が最大値を超えないことを確認
}
#[test]
fn test_energy_payment_never_negative(
energy in 0usize..10,
tapped in 0usize..10,
payment in 1usize..5
) {
// エネルギー支払い後のタップ数が負にならないことを確認
}
}
```
---
## 8. 実カードを使用したテスト
### 8.1 実カードデータの活用
テストでは `load_real_db()` を使用して実際のカードデータベースを読み込みます:
```rust
use crate::test_helpers::load_real_db;
fn get_card_id(db: &CardDatabase, card_no: &str) -> i32 {
db.card_no_to_id.get(card_no).copied().expect("Card not found")
}
```
### 8.2 実カードテストケース
#### O_RECOVER_MEMBER テスト(実カード使用)
| テストID | カード | 日本語テキスト | テスト内容 |
|---------|--------|---------------|-----------|
| RECM-R01 | PL!-sd1-001-SD | 登場:自分の控え室からライブカードを1枚手札に加える | discard=[L1], handにL1追加 |
| RECM-R02 | PL!-sd1-002-SD | 起動:控え室からメンバーカードを1枚手札に加える | discard=[M1], handにM1追加 |
| RECM-R03 | PL!-sd1-003-SD | 登場:控え室からコスト4以下のμ'sメンバーを1枚手札に加える | フィルター条件確認 |
#### O_LOOK_AND_CHOOSE テスト(実カード使用)
| テストID | カード | 日本語テキスト | テスト内容 |
|---------|--------|---------------|-----------|
| LAC-R01 | PL!-sd1-004-SD | 登場:デッキ上5枚を見てμ'sライブを1枚手札に加える | フィルター: group=μ's, type=live |
| LAC-R02 | PL!-sd1-005-SD | 登場:デッキ上3枚を見て1枚手札に加え、残りをデッキに戻す | 順序選択テスト |
#### O_PAY_ENERGY テスト(実カード使用)
| テストID | カード | 日本語テキスト | テスト内容 |
|---------|--------|---------------|-----------|
| PAYE-R01 | LL-bp3-001-R+ | ライブ開始時:エネルギー6支払い、ブレード+3 | energy=6, blades+=3 |
| PAYE-R02 | LL-bp3-001-R+ | エネルギー不足時 | energy=3, 効果発動不可 |
#### O_DISCARD_HAND(コスト)テスト(実カード使用)
| テストID | カード | 日本語テキスト | テスト内容 |
|---------|--------|---------------|-----------|
| DISC-R01 | LL-bp1-001-R+ | 手札の「歩夢/かのん/花帆」を3枚捨ててスコア+3 | フィルター条件確認 |
| DISC-R02 | LL-bp1-001-R+ | フィルター不一致 | hand=[Other], 効果発動不可 |
### 8.3 実カードテストコード例
```rust
/// PL!-sd1-001-SD: 高坂 穂乃果
/// 日本語: 登場:自分の成功ライブカード置き場にカードが2枚以上ある場合、
/// 自分の控え室からライブカードを1枚手札に加える。
#[test]
fn test_real_card_pl_sd1_001_honoka() {
let db = load_real_db();
let card_id = get_card_id(&db, "PL!-sd1-001-SD");
let mut state = create_test_state();
state.ui.silent = true;
// Setup: 成功ライブ2枚、控え室にライブカード
state.players[0].success_lives = vec![
get_card_id(&db, "PL!-sd1-020-SD"), // ライブカード
get_card_id(&db, "PL!-sd1-021-SD"), // ライブカード
];
let live_in_discard = get_card_id(&db, "PL!-sd1-022-SD");
state.players[0].discard = vec![live_in_discard];
// Execute: 登場時トリガー
let ctx = AbilityContext {
player_id: 0,
source_card_id: card_id,
..Default::default()
};
state.trigger_abilities(&db, TriggerType::OnPlay, &ctx);
// Assert: 控え室から手札にライブカードが移動
assert!(state.players[0].hand.contains(&live_in_discard),
"ライブカードが手札に追加されるべき");
}
/// LL-bp1-001-R+: 上原歩夢&澁谷かのん&日野下花帆
/// 日本語: ライブ開始時:手札の「歩夢/かのん/花帆」を3枚捨ててスコア+3
#[test]
fn test_real_card_ll_bp1_001_ayumu_kanon_kaho() {
let db = load_real_db();
let card_id = get_card_id(&db, "LL-bp1-001-R+");
let mut state = create_test_state();
state.ui.silent = true;
state.phase = Phase::PerformanceP1;
// Setup: 手札に歩夢、かのん、花帆
let ayumu = find_card_by_char_name(&db, "歩夢");
let kanon = find_card_by_char_name(&db, "かのん");
let kaho = find_card_by_char_name(&db, "花帆");
state.players[0].hand = vec![ayumu, kanon, kaho, 9999]; // 9999は他のカード
// Execute: ライブ開始時トリガー
let ctx = AbilityContext {
player_id: 0,
source_card_id: card_id,
..Default::default()
};
state.trigger_abilities(&db, TriggerType::OnLiveStart, &ctx);
// Assert: スコア+3
assert_eq!(state.players[0].live_score_bonus, 3,
"スコア+3されるべき");
}
```
### 8.4 実カードID参照テーブル
| カード番号 | card_id | 名前 | 主なオペコード |
|-----------|---------|------|---------------|
| PL!-sd1-001-SD | 1 | 高坂 穂乃果 | O_RECOVER_LIVE, O_ADD_BLADES |
| PL!-sd1-002-SD | 2 | 絢瀬 絵里 | O_RECOVER_MEMBER |
| PL!-sd1-003-SD | 3 | 南 ことり | O_RECOVER_MEMBER, O_ADD_HEARTS |
| PL!-sd1-004-SD | 4 | 園田 海未 | O_LOOK_AND_CHOOSE |
| PL!-sd1-005-SD | 5 | 星空 凛 | O_LOOK_AND_CHOOSE |
| LL-bp1-001-R+ | 9 | 上原歩夢&澁谷かのん&日野下花帆 | O_RECOVER_MEMBER, O_DISCARD_HAND, O_BOOST_SCORE |
---
## 9. 優先順位と実装順序
1. **Phase 1: 基本オペコード(実カード使用)** (高優先度)
- O_DRAW, O_ADD_BLADES, O_ADD_HEARTS
- O_RECOVER_MEMBER, O_RECOVER_LIVE
- O_PAY_ENERGY, O_ACTIVATE_ENERGY
- 実カード: PL!-sd1-001〜005
2. **Phase 2: 選択・検索オペコード(実カード使用)** (高優先度)
- O_LOOK_AND_CHOOSE, O_REVEAL_UNTIL
- O_SELECT_MODE, O_SELECT_MEMBER
- 実カード: PL!-sd1-004, PL!-sd1-005
3. **Phase 3: 条件テスト(実カード使用)** (中優先度)
- C_COUNT_*, C_COST_CHECK
- C_LIFE_LEAD, C_HAS_*
- 実カード: LL-bp1-001-R+
4. **Phase 4: 複合・統合テスト** (中優先度)
- コスト+効果の組み合わせ
- トリガー連鎖
5. **Phase 5: エッジケース** (低優先度)
- 空ゾーン操作
- 境界値テスト