simudaru's blog

Python, Rなどのメモを残していこうと思います。  よろしくお願いいたします。

Pythonでジャンケンゲーム作る(その2)

苦手なオブジェクト指向プログラミングに挑戦中です。
あまりオブジェクト指向になってないかも。

playerの持つべきメンバ変数は、
 ・名前
 ・戦略
 ・手
 ・勝ち数
 ・アクティブかどうか

playerの持つべきメソッドは、
 ・手を決定する
 ・必要なメンバ変数へのget,set

で良いと思うのですが、ゲームの流れを記述する方法が良くわからないのです。
今回はGameRPSというクラスにゲームの流れを記述しましたが、
これでオブジェクト指向になっているのでしょうか。


今回は、複数人でのジャンケンに対応しました。
4人での2本先取のコードです。
勝ち数は何回勝ったかで、アクティブかどうかというのは、
「グー2人、パー2人」のような場合に、パーの2人でもう一度ジャンケンをするため、
パーの2人は「アクティブ」、グーの2人は「アクティブでない」として扱っています。

# -*- coding: utf-8 -*-
'''
    Nセット先取のジャンケンゲーム
    1セットでの勝者は一人。複数人で、一人が勝ちになるまで続ける。
'''
import time
import random

const_max_win = 3
hand_dict = {"r":1, "s":2, "p":3}
hand_print_dict = {1:"グー", 2:"チョキ", 3:"パー"}


class GameRPS:
    
    def __init__(self, win_count_goal):
        self.win_count_goal = win_count_goal
    
    # ジャンケンゲームを開始する
    def start_game_rps(self, player_list):
        print "じゃんけんスタート。\n"
        time.sleep(1.0)
        
        # プレイヤーの誰かが目的勝数に達するまでループ
        while not self.judge_max_win_count(player_list):
            
            # ジャンケンゲーム(誰か一人が勝ち残るまで)
            self.game_rps(player_list)
            
            # ジャンケンゲーム1セット終了時の勝敗メッセージ
            for i in range(len(player_list)):
                print "%s:%s勝" % (player_list[i].get_name(), player_list[i].get_win_count())
            print ""
            time.sleep(1.0)
        
        time.sleep(1.5)
        print "ゲーム終了"
        time.sleep(1.5)
        
        for p in player_list:
            if p.get_win_count() == self.win_count_goal:
                print "%sの勝ちです。" % (p.get_name())
                time.sleep(1.5)
        
        print "終わりです。"
        time.sleep(1.5)
    
    # ジャンケンゲーム(誰か一人が勝ち残るまで)
    def game_rps(self, player_list):
        # 初期化
        flg_draw = False
        for i in range(len(player_list)):
            player_list[i].set_is_active(True)
        
        # 誰か一人が勝ち残るまでループ
        while self.count_active_player(player_list) > 1:
            
            # じゃんけんの掛け声(前半)
            self.shout_rps_1sthalf(flg_draw)
            
            # プレイヤーの手
            phand = [0]*len(player_list)
            for i in range(len(player_list)):
                player_list[i].decide_hand()
                phand[i] = player_list[i].get_hand()
            
            # プレイヤーの手が正しくなければループ
            if not set(phand).issubset(set([0,1,2,3])): continue
            
            # じゃんけんの掛け声(後半)
            self.shout_rps_2ndhalf(flg_draw)
            
            # プレイヤーの手を表示(activeなプレイヤーのみ)
            for i in range(len(player_list)):
                if not player_list[i].get_is_active(): continue
                print "%s:%s" % (player_list[i].get_name(), hand_print_dict[phand[i]])
            time.sleep(1.0)
            
            # ジャンケン勝敗判定
            flg_draw = self.judge_rps(player_list, phand[:])
        
        for p in player_list:
            if p.get_is_active():
                print "%sの勝ちです。\n" % (p.get_name())
        
        # activeの初期化とスコア処理
        for p in player_list:
            if p.get_is_active():
                p.add_win_count()
            p.set_is_active(True)
    
    # 目標勝数に到達したか判定
    def judge_max_win_count(self, player_list):
        win_count_max = 0
        for p in player_list:
            win_count_max = max(win_count_max, p.get_win_count())
        return win_count_max >= self.win_count_goal
    
    # アクティブなplayerの数を返す
    def count_active_player(self, player_list):
        count = 0
        for p in player_list:
            if p.get_is_active():
                count += 1
        return count
    
    # ジャンケン勝敗判定(flg_drawを返す)
    def judge_rps(self, player_list, phand):
        
        # Non-activeは除く
        phand = set(phand) - set([0])
        if len(phand) != 2:
            flg_draw = True
            print "あいこです。\n"
            time.sleep(1.5)
        else:
            flg_draw = False
            if   phand == set([1, 2]):
                win_hand = 1
            elif phand == set([2, 3]):
                win_hand = 2
            elif phand == set([3, 1]):
                win_hand = 3
            print "%sの勝ちです\n" % (hand_print_dict[win_hand])
            time.sleep(1.5)
            
            # win_hand以外の手のplayerはnon-activeにする
            for p in player_list:
                if p.get_hand() != win_hand:
                    p.set_is_active(False)
            
        return flg_draw
    
    # ジャンケンの掛け声(前半)
    def shout_rps_1sthalf(self, flg_draw):
        if flg_draw:
            print "あい"
            time.sleep(0.5)
            print "こで"
            time.sleep(0.5)
            print "……"
        else:
            print "じゃん"
            time.sleep(0.5)
            print "けん"
            time.sleep(0.5)
            print "……"
    
    # ジャンケンの掛け声(後半)
    def shout_rps_2ndhalf(self, flg_draw):
        if flg_draw:
            print "しょ!"
        else:
            print "ぽん!"
        time.sleep(1.0)


class Player:
    
    def __init__(self, name, strategy):
        self.name      = name
        self.strategy  = strategy
        self.hand      = 0
        self.win_count = 0
        self.is_active = True
    
    # 手の決定 (activeでなければ0を返す)
    def decide_hand(self):
        if not(self.is_active):
            self.hand = 0
        else:
            if self.strategy == 0:  # ユーザーの入力待ち
                self.hand = self.input_user_hand()
            elif self.strategy == 1:  # ランダムに手を決定
                self.hand = random.randint(1, 3)
            else:
                raise
    
    # 手を返す
    def get_hand(self):
        return self.hand
    
    # 勝ち数を返す
    def get_win_count(self):
        return self.win_count
    
    # アクティブかどうかを返す
    def get_is_active(self):
        return self.is_active
    
    # 名前を返す
    def get_name(self):
        return self.name
    
    # アクティブかどうかを変更
    def set_is_active(self, is_active):
        self.is_active = is_active
    
    # 勝ち数を追加する
    def add_win_count(self):
        self.win_count += 1
    
    # ユーザーの手の入力
    def input_user_hand(self):
        phand_txt = raw_input("(何を出しますか?) (グー:r チョキ:s パー:p) > ")
        if phand_txt not in ("r", "s", "p"):
            phand = -1
            print "不正な文字です。\n"
            time.sleep(1.5)
        else:
            phand = hand_dict[phand_txt]
        
        return phand



if __name__ == "__main__":
    # 初期化
    flg_draw = False
    player_list = []
    player_list.append(Player("あなた", 0))
    player_list.append(Player("あいて", 1))
    player_list.append(Player("相手",   1))
    player_list.append(Player("アイテ", 1))
    game_rps = GameRPS(2)
    game_rps.start_game_rps(player_list)


コードが長くなってきたので、ファイルを分割したいですね。
次はGUI化してみたいです。

あと、結構な長さのコードになってきたので、
サクラエディタでは厳しくなってきました。
eclipsでの環境構築、一度失敗してそれきりでしたが、
再度挑戦したいと思います。