import random DEBUG = False # True:1回,False:Montecarlo1000回 WinCheck = 1 # 0: 4ターン目のドローフェイズ終了時, 1: 4ターン目のメインフェイズ終了時 class Card: """ ゲーム内で使用するカードを表すクラス。 Attributes: name (str): カード名 mana_cost (int): 使用マナ priority (int): 使用優先度(小さいほど先に使う) effect (callable): カードの効果を表す関数 """ def __init__(self, name, mana_cost, priority, effect=None): """Cardクラスのコンストラクタ""" self.name = name self.mana_cost = mana_cost self.priority = priority self.effect = effect def play(self, player, log_effects): """ カードの効果を発動する。 Args: player (Player): カードを使用するプレイヤー log_effects (list): 効果の発動内容を記録するリスト """ if self.effect: self.effect(player, self, log_effects) class Player: """ プレイヤーとその状態を表すクラス。 Attributes: deck (list of Card): 山札 hand (list of Card): 手札 max_mana (int): 最大マナ current_mana (int): 現在のマナ """ def __init__(self, deck): """ Playerクラスのコンストラクタ。初期手札を引いてODがなければマリガンする。 Args: deck (list of Card): 初期デッキ """ self.deck = deck[:] random.shuffle(self.deck) self.hand = [] self.max_mana = 0 self.current_mana = 0 self.initial_draw_with_redraw() def initial_draw_with_redraw(self): """ 最初の手札を4枚引く。オーバーディメンションがなければ一度だけ全て戻して引き直す。 DEBUG=Trueのときは最初と引き直し後の手札を表示する。 """ WIN_CARD_NAME = "オーバーディメンション" for i in range(2): self.hand.clear() for _ in range(4): self.draw() # DEBUG表示 if DEBUG: if i == 0: print(f"最初の手札: {[card.name for card in self.hand]}") else: print(f"引き直し後の手札: {[card.name for card in self.hand]}") if any(card.name == WIN_CARD_NAME for card in self.hand): break random.shuffle(self.deck) def draw(self): """ 山札から1枚ドローして手札に加える。 Returns: Card or None: ドローしたカード、または山札切れの場合None """ if self.deck: card = self.deck.pop(0) self.hand.append(card) return card return None def increase_mana(self): """ 最大マナを1増やし現在マナを全回復する(ターン開始時処理)。 """ self.max_mana += 1 self.current_mana = self.max_mana def play_cards(self, log_effects): """ 手札から使用可能なカードを優先度順にすべて使う。 Args: log_effects (list): 効果の発動内容を記録するリスト Returns: list of str: 使用したカード名のリスト """ used_cards = [] hand_copy = self.hand[:] hand_copy.sort(key=lambda c: c.priority) for card in hand_copy: if card in self.hand and card.mana_cost <= self.current_mana: if card.name == "グリードケルプ・ルビィ": other_cards = [c for c in self.hand if c != card and c.name != "グリードケルプ・ルビィ"] if not other_cards: continue self.current_mana -= card.mana_cost card.play(self, log_effects) used_cards.append(card.name) if card in self.hand: self.hand.remove(card) return used_cards def effect_draw1(player, self_card, log_effects): """ カード効果:デッキから1枚ドローする。 """ drawn = player.draw() if log_effects is not None: log_effects.append(f"{self_card.name}の効果: {drawn.name if drawn else '無'}をドロー") def effect_draw2(player, self_card, log_effects): """ カード効果:デッキから2枚ドローする。 """ drawn1 = player.draw() drawn2 = player.draw() if log_effects is not None: log_effects.append(f"{self_card.name}の効果: {drawn1.name if drawn1 else '無'}、{drawn2.name if drawn2 else '無'}をドロー") def effect_greedkelp(player, self_card, log_effects): """ グリードケルプ・ルビィの効果:手札の優先度が一番低いカードをデッキに戻し、1枚ドロー。 自分自身や他のグリードケルプ・ルビィは対象外。 """ other_cards = [c for c in player.hand if c != self_card and c.name != "グリードケルプ・ルビィ"] if not other_cards: if log_effects is not None: log_effects.append("グリードケルプ・ルビィの効果: 対象がいないので発動しない") return card_to_return = max(other_cards, key=lambda c: c.priority) if card_to_return in player.hand: player.hand.remove(card_to_return) player.deck.append(card_to_return) random.shuffle(player.deck) if log_effects is not None: log_effects.append(f"グリードケルプ・ルビィの効果: {card_to_return.name}をデッキに戻す") drawn = player.draw() if log_effects is not None: log_effects.append(f"グリードケルプ・ルビィの効果: {drawn.name if drawn else '無'}をドロー") def create_deck(): """ 指定カードとダミーカードでデッキを作成する。 Returns: list of Card: 作成されたデッキ """ deck = [] for _ in range(3): deck.append(Card("オーバーディメンション", 18, 0)) for _ in range(3): deck.append(Card("知恵の輝き", 1, 1, effect_draw1)) for _ in range(3): deck.append(Card("グリードケルプ・ルビィ", 2, 5, effect_greedkelp)) for _ in range(3): deck.append(Card("虹の奇跡", 2, 4, effect_draw1)) for _ in range(3): deck.append(Card("熾天使の福音", 3, 2, effect_draw2)) for _ in range(3): deck.append(Card("宿題やるですぅ!", 3, 3, effect_draw2)) for i in range(40 - len(deck)): deck.append(Card(f"ダミーカード-{i+1}", 100, 10)) return deck def simulate_game(debug=False): """ 1回分のゲームをシミュレートする。 Args: debug (bool): Trueなら詳細ログを表示 Returns: bool: 勝利ならTrue、敗北ならFalse """ deck = create_deck() player = Player(deck) WIN_CARD_NAME = "オーバーディメンション" turns = 4 for turn in range(1, turns + 1): log_effects = [] used_cards = [] # スタートフェイズ player.increase_mana() if debug: print(f"\n--- ターン{turn} ---") print(f"ターン開始時マナ: {player.current_mana}/{player.max_mana}") print(f"ターン開始時の手札: {[card.name for card in player.hand]}") # ドローフェイズ drawn_card = player.draw() if debug: print(f"ドローしたカード: {drawn_card.name if drawn_card else 'なし'}") # 勝利判定(WinCheck == 0 => ドローフェイズ終了時) if WinCheck == 0: if any(card.name == WIN_CARD_NAME for card in player.hand): if debug: print("手札にオーバーディメンションがあるので勝利!") win = True return True, turn # メインフェイズ used_cards = player.play_cards(log_effects) if debug: print(f"使用したカード: {used_cards}") print(f"発動した効果: {log_effects}") print(f"ターン終了時の手札: {[card.name for card in player.hand]}") print(f"ターン終了時のマナ: {player.current_mana}/{player.max_mana}") print(f"残りデッキ枚数: {len(player.deck)}") # 勝利判定(WinCheck == 1 => メインフェイズ終了時) if WinCheck == 1: if any(card.name == WIN_CARD_NAME for card in player.hand): if debug: print("手札にオーバーディメンションがあるので勝利!") win = True return True, turn if debug: print("\n敗北しました") return False, None def main(): """ メイン関数。DEBUGフラグによりデバッグ用の詳細ログ出力か1000回統計モードを切り替える。 """ if DEBUG: print("=== デバッグモード ===") win, win_turn = simulate_game(debug=True) if win: print(f"このシミュレーションでは{win_turn}ターン目で勝利しました。") else: print("このシミュレーションでは敗北しました。") else: wins = 0 losses = 0 win_turn_stats = {1:0, 2:0, 3:0, 4:0} for _ in range(1000): win, win_turn = simulate_game(debug=False) if win: wins += 1 if win_turn in win_turn_stats: win_turn_stats[win_turn] += 1 else: losses += 1 print(f"1000回プレイ結果:") print(f"勝利回数: {wins}") print(f"敗北回数: {losses}") print("各ターンでの勝利回数:") for turn in range(1, 5): print(f" {turn}ターン目: {win_turn_stats[turn]}") if __name__ == "__main__": main()