【じゃんけん実装例】演習で身に付けるpythonプログラミング【vol.3】

【再掲】リクエスト:じゃんけんゲームの作成

複数のコンピュータとじゃんけんをして、1人の勝者を決めるプログラムを作成してください。
想定外の入力を考慮し、正しく動作するようにしてください。

レベル3:複数のコンピュータとの勝負(異常入力への対応)

参加人数が複数のジャンケンゲームを実装してください。
プレイヤーは1人で残りはコンピュータ(CPU)とします。
最終的に1人の勝者が決まるまでジャンケンを続け、勝者の名前を表示するようにしてください。
想定外の入力があっても問題なく動作するようにしてください。

実装例

import numpy as np

N_HAND = 3 # じゃんけんの手の種類
ROCK = 0
SCISSORS = 1
PAPER = 2

WIN = 0
LOSE = 1
AIKO = 2

class Player(object):
    """ 参加者(Player、CPU)を作成するクラス
    Attributes:
        hand (int): じゃんけんの手(グー(0)、チョキ(1)、パー(2))
        name (char): プレイヤーの名前
        cpu (Bool): CPUか否か
        survive (Bool): ゲームに残っているか。
    """
    def __init__(self, name='cpu', cpu=True):
        self.hand = np.random.randint(N_HAND)
        self.name = name
        self.cpu = cpu
        self.survive = True

    def set_hand(self, hand=None):
        """
        じゃんけんの手を決める。
        Args:
            hand (int): じゃんけんの手(グー(0)、チョキ(1)、パー(2))
        Returns:
            None 
        """
        if hand is None:
            self.hand = np.random.randint(N_HAND)
        else:
            # TODO: 例外処理
            self.hand = hand

    def get_hand(self):
        return self.hand

    def set_survive(self, survive):
        self.survive = survive

    def get_survive(self):
        return self.survive

    def get_name(self):
        return self.name


def cnt_survive_player(players):
    """
    残っているplayer数をカウントする
    Args:
        players (list): Playerオブジェクト格納したリスト
    Return:
        cnt (int): 勝ち残っている人数
    """
    cnt = 0
    for player in players:
        if player.get_survive():
            cnt += 1

    return cnt


def get_win_lose(hands):
    """
    あいこ以外のジャンケン判定
    0, 1 : 0 win 
    0, 2 : 2 win
    1,2 : 1 win
    1, 0 : 0 win
    結論:一つ上の数字(mod 3)には勝てる
    Args:
        hands (set): 全Playerの手の集合。
    Return:
        win (int): 勝利した手
        lose (int): 負けた手
    """

    sum_hands = sum(hands) % 3
    if sum_hands == 0: # SCISSORS and PAPER
        win = SCISSORS
        lose = PAPER

    elif sum_hands == 1: # ROCK and SCISSORS
        win = ROCK
        lose = SCISSORS

    elif sum_hands == 2: # Rock and PAPER
        win = PAPER
        lose = ROCK

    return win, lose


def judge_janken_v2(players):
    """
    ジャンケンの判定を行い残っている人数を返す。
    Args:
        players (list): Playerオブジェクト格納したリスト
    Return:
        勝ち残っているplayerの人数
    """
    # 重複している手をまとめる
    hands = []
    for player in players:
        if player.get_survive():
            hands.append(player.get_hand())

    hands = set(hands)

    if (len(hands) == 3) or (len(hands) == 1):
        # あいこの場合
        return cnt_survive_player(players)
    else:
        win_hand, lose_hand = get_win_lose(hands)

    for player in players:
        if player.get_survive():
            hand = player.get_hand()
            if hand == lose_hand:
                print(f"{player.get_name()}は負けました。")
                player.set_survive(False) # 敗退

    return cnt_survive_player(players)


def game_start():
    """
    じゃんけんを開始するメイン関数
    """
    print("じゃんけんをします")
    while True:
        try:
            n_player = int(input("CPUの人数を入力してください: "))
        except ValueError as e:
            print(f'Value Error: {e.args}')
            print('数字を入力してください')
            print('')
            continue
        break
        
    n_survive_player = n_player

    players = []
    # playerインスタンスの作成
    players.append(Player(name='Taro', cpu=False))

    # cpuインスタンスの作成
    for i in range(n_player):
        players.append(Player(f'cpu{i+1}'))

    i = 0
    while n_survive_player > 1: # 勝ち残りが1人になるまで繰り返す
        i += 1
        print(f'{i}回戦目')

        # cpus_hand = {}
        for player in players:
            if player.get_survive() == False:
                continue

            if player.cpu == False:
                while True:
                    try:
                        print("あなたの手は? (0:グー、1:チョキ、2:パー)")
                        my_hand = int(input())
                        print('')
                    except ValueError as e:
                        print(f'Value Error: {e.args}')
                        print('数字を入力してください')
                        continue
                    if my_hand in [ROCK, SCISSORS, PAPER]:
                        break
                    else:
                        print('0:グー、1:チョキ、2:パーのいずれかを入力してください')

                player.set_hand(my_hand)
            else:
                player.set_hand()
            
            print(f'{player.name}の手: {player.get_hand()}')

        # ジャンケンの勝敗判定
        n_survive_player = judge_janken_v2(players)

    # 勝者を表示
    for player in players:
        if player.get_survive():
            winner = player.get_name()
    print(f'優勝は「{winner}」です')


if __name__ == "__main__":
    game_start()

今回のポイント

  • 141行目〜149行目:プレイヤー人数に対する例外処理
    • 数字以外が入力された場合にメッセージを出して、再度入力を求めるようにしています。
    • 例外処理はTry, Except構文を使用しています。Try, Except構文については以下のリンクが分かりやすかったです。
    • 参考リンク:[Python入門] 例外と例外処理の基礎
  • 172行目〜184行目:ジャンケンの手に対する例外処理
    • プレイヤー人数に対する例外処理同様に、数字ではなかった場合の処理を記載し、加えてジャンケンの手ではない場合に再度入力を求めるようにしています。

終わりに

コードを紹介しましたが、実装例と書いているようにあくまで一例です。
今回のコードはGitHubにも上げていきますので、みなさんの実装例も紹介してもらえると幸いです。

余談

ジャンケンゲームの実装例を3回に渡って紹介してきましたので、今回でジャンケン実装は完了にしたいと思います。ゲームについてはオセロゲームの実装例も考えていますが、一旦別な方向にしてオープンデータを扱うリクエストにしていきたいと思います。

コメント

タイトルとURLをコピーしました