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

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

複数のコンピュータとじゃんけんをして、1人の勝者を決めるプログラムを作成してください。

レベル2:複数のコンピュータとの勝負

参加人数が複数のジャンケンゲームを実装してください。
プレイヤーは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:
            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("じゃんけんをします")
    print("CPUの人数を入力してください")
    n_player = int(input())
    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:
                print("あなたの手は? (0:グー、1:チョキ、2:パー)")
                my_hand = int(input())

                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()

今回のポイント

  • 12行目〜24行目:Playerクラスの実装
    • Playerクラスの属性と用途
      • hand:ジャンケンの手
      • name:名前。勝者を表示するため。
      • cpu:オブジェクトがCPUかを判定するため。
        プレイヤーとの違いは手を入力できるかどうかです。
      • survive:勝ち残っているか判定。
  • 99行目〜119行目:ジャンケンの判定
    • ジャンケンの判定は出された手の種類がポイントです。1種類もしくは全3種類の場合があいこで、2種類の場合が勝敗がつきます。
  • 149行目:ジャンケンの終了条件
    • ジャンケンが終わるのは、勝ち残っているプレイヤーが1人になったらという条件にしました。Playerクラスのsurvive属性を利用しています。

終わりに

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

余談

リクエストを掲載してから毎回自分でもコーディングしているので、実装して思ったことを記載していきたいと思います。
 今回のリクエストは前回の拡張のつもりで出したのですが、あまり流用できず作り直すところが多くなってしまいました。拡張性が足らなかったですね。特にオブジェクト周りですが、CpuオブジェクトからPlayerオブジェクトに命名変更している通り、PlayerもCPUを同じように扱っておいた方が実装が簡単でした。
 アルゴリズムの考え方は、実際にジャンケンをした時に普段どうやって勝敗を判断しているかをイメージして書いています。パーの勝ち、グーの負け、と言った会話がされると思うのでその通りに、勝ち負けの手を取得してます。 

コメント

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