LoginSignup
1
3

More than 5 years have passed since last update.

Pythonのジェネレータでじゃんけんプログラム

Posted at

Pythonのジェネレータを使ってじゃんけんプログラムを作ってみました。
Pythonのジェネレータに慣れていない方のために少しでも役立てばいいかなと思ったので記事化しました。
コードの全体像はこちらにあります。

グー、チョキ、パーを表現

まずはグー、チョキ、パーを表すEnumを用意します。
ここはジェネレータは関係ありません。

from enum import Enum

class Card(Enum):
    Rock = 0
    Scissors = 1
    Paper = 2

Enumはこんな感じで使えます。

>>> card = Card.Rock  # Card(0)でも同じ
>>> card
<Card.Rock: 0>
>>> card.name
'Rock'
>>> card.value
0

プレイヤーをジェネレータで表現

続いてプレイヤーを表現するジェネレータを用意します。

from random import randint

def Player(name):
    yield name
    while True:
        own = Card(randint(0, 2))
        yield own

このようにyieldを含む関数をジェネレータ関数と呼び、このジェネレータ関数の戻り値はジェネレータオブジェクトです。
next()の引数にジェネレータを渡すとyield valueの行でvalueを返し、ローカル変数を保持したまま処理が中断されます。
もう一度next()に渡すと、中断した箇所から処理を再開します。

このPlayerは最初に名前を返し、以降はnext()に渡されるたびにグー、チョキ、パーをランダムに返し続けます。

>>> taro = Player('Taro')
>>> taro  # taroはジェネレータオブジェクト
<generator object Player at 0x1042c77c8>
>>> next(taro)  # 初回は名前を返す
'Taro'
>>> next(taro)  # 2回目以降はランダムに手札を返す
<Card.Scissors: 1>
>>> next(taro)
<Card.Rock: 0>

じゃんけんの制御フロー

ジェネレータであるプレイヤーを操作するのがこの関数です。
つまり、next()にジェネレータを渡し、戻ってきた値で勝敗を判定したり、それぞれの勝数を残しています。
この関数は、2名のプレイヤーと何勝すればいいかを渡して呼び出すと、即座に動き始めます。

def start(p1, p2, n):
    # 初期化
    count = 0  # 試合数
    count_p1 = 0  # プレイヤー1の勝数
    count_p2 = 0  # プレイヤー2の勝数
    p1_name = next(p1)
    p2_name = next(p2)
    print(f'{p1_name} VS {p2_name}')

    while True:
        # じゃんけん処理
        count += 1
        print(f'#### ROUND {count} ###')
        p1x = next(p1)  # プレイヤー1の手札を取得
        p2x = next(p2)  # プレイヤー2の手札を取得
        result = (p1x.value - p2x.value + 3) % 3  # 勝敗の判定
        if result == 0:
            print(f'{p1x.name}, {p2x.name} => even')
        elif result == 1:
            count_p1 += 1
            print(f'{p1x.name}, {p2x.name} => {p1_name} +1')
        elif result == 2:
            count_p2 += 1
            print(f'{p1x.name}, {p2x.name} => {p2_name} +1')

        # どちらかの勝数がnに届くまで続ける
        if max(count_p1, count_p2) < n:
            print()
            continue

結果の表示とジェネレータに終了を通知

n勝に届いたプレイヤーが現れたら結果を表示します。
最後に同じプレイヤーで何度も勝負できないようにプレイヤーのclose()を呼び出しています。

        # start()の続きで結果の表示
        if n <= count_p1:
            print(f'{p1_name}: {count_p1} {p2_name}: {count_p2}')
            print(f'{p1_name} are winner')
            p1.close()  # 以降はnext()に渡されてもStopIterationを送出する
            p2.close()
            break
        elif n <= count_p2:
            print(f'{p1_name}: {count_p1} {p2_name}: {count_p2}')
            print(f'{p1_name} are loser')
            p1.close()
            p2.close()
            break

プログラムの実行

ここまでのコードは次のコードで実行できます。

def main():
    taro = Player('Taro')
    hanako = Player('Hanako')
    start(taro, hanako, 3)

上記を1つのスクリプトにまとめたら最後にいつものおまじないを書いて実行します。

if __name__ == '__main__':
    main()

今回の結果はこのようになりました。

$ python3 janken.py
Taro VS Hanako
#### ROUND 1 ###
Scissors, Paper => Taro +1

#### ROUND 2 ###
Paper, Scissors => Hanako +1

#### ROUND 3 ###
Rock, Rock => even

#### ROUND 4 ###
Paper, Paper => even

#### ROUND 5 ###
Paper, Scissors => Hanako +1

#### ROUND 6 ###
Paper, Paper => even

#### ROUND 7 ###
Paper, Scissors => Hanako +1
Taro: 1 Hanako: 3
Taro are loser
1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3