ヌルポインター親衛隊

社内でひとりエンジニアやってます。

Python学習5日め(オセロゲームを作ってみる1)

学習教材に使用している『独学プログラマー』は、次の章(第3部)から開発者ツールの使用方法や正規表現の使い方を扱います。これまではPythonの基礎学習をしてきたので、ここで一つ、自力で一つのプログラムを作ろうと思います。
題材はみんなご存知オセロ。リバーシとも言われるボードゲームです。

オセロの説明

オセロは、表裏が白/黒で着色されたコマ(ピース?)を使用します。2人のプレイヤーは、8*8マスの盤面上にコマを交互に置き、同じ色で挟まれたところは、コマを裏返して色を変えます。自分の色のコマが多いほうが勝者です。説明するまでも無いかもしれません。

classの設計

ゲームに登場するオブジェクトは、「コマ」、「プレイヤー」、「盤面」です。これにゲーム自体を含めた4つのクラスでclassを設計していきます。といっても、オブジェクト指向型のプログラムをスクラッチで書くのは初めてなので、都度、修正します。
今回、各クラスの関係は以下のようにしました。

Game  
├ Player - Piece  
└ Board - Piece  

とりあえず4つのクラスを作りましたが、メソッドを書いていく段階で、「アレ、このメソッドはPlayerが持つべきなのか?はたまた、Boardが持つべきなのか?」と何度も迷いました。そのクラスのインスタンスが主語、メソッドが述語になる関係を意識すればうまくいきそうな気がする。

できたこと

細かい部分に時間を割くと、何時まで経ってもプログラムが動かない、という言葉を最近よく聞くので、「とりあえず動く」を目指して書いています。スパゲッティだなぁ、絶対にもっといい書き方があるはずなんだけどなぁ、と思いながらも、きっとプログラムが動けばそれがリファクタリングの原動力になるんだと思って書きすすめました。

class Piece:
    pieces = ["●", "○"]
    def __init__(self, color):
        self.color = color

    def reverse_piece(self): #Boardが使う
        if self.color == 0:
            self.color = 1
        else:
            self.color = 0

    def get_state(self):
        return self.pieces[self.color]


class Board:
    board = []
    def __init__(self):
        self.board.append("・・・・・・・・")
        self.board = self.board * 8

    def __str__(self):
        stage = "\n".join(self.board)
        return stage

    def __add__(self, another):
        return self.__str__() + another

    def is_already_put(self, x, y):
        if self.board[y-1][x-1] != "・":
            return True

    def set_piece_to(self, x, y, color): #pieceを置くときに呼ぶ
        piece = Piece(color)
        raw_row = self.board[y-1]

        tmp_row = raw_row[:(x-1)] + piece.get_state() + raw_row[x:]
        self.board[y-1] = tmp_row

    def update_board(self): #boad上のpiece色を演算し、更新
        #pieceはプレイヤーが置くのと、Boardで裏返ったりする2種類の挙動がある
        pass


class Player:
    def __init__(self, name, color):
        self.piece_has = 32 #オセロのコマの所持数
        self.name = name
        self.color = color #0 or 1

    def put_piece(self):
        self.piece_has -= 1

class Game:
    def __init__(self):
        self.p1 = Player("Player1", 0) #白
        self.p2 = Player("Player2", 1) #黒
        self.board = Board()

    def turn(self, player):
        while True:
            p_puts = input("{}の手番です([x y]で座標を指定してください):".format(player.name)).split(" ") #[x, y]で返ってくることを期待
            px = int(p_puts[0])
            py = int(p_puts[1])
            if self.board.is_already_put(px, py):
                print("その場所には既にコマが置かれています。")
                continue
            self.board.set_piece_to(px, py, player.color)
            print(self.board)
            break

    def play_game(self):
        self.board.set_piece_to(4, 4, 0)
        self.board.set_piece_to(5, 5, 0)
        self.board.set_piece_to(4, 5, 1)
        self.board.set_piece_to(5, 4, 1)
        #print(id(self.board.board))
        print(self.board + "\nゲームスタート!")

        self.turn(self.p1)
        self.turn(self.p2)


g = Game()
g.play_game()

動かしてみると、以下のようになります。

・・・・・・・・
・・・・・・・・
・・・・・・・・
・・・●○・・・
・・・○●・・・
・・・・・・・・
・・・・・・・・
・・・・・・・・
ゲームスタート!
Player1の手番です([x y]で座標を指定してください):4 5
その場所には既にコマが置かれています。
Player1の手番です([x y]で座標を指定してください):4 3
・・・・・・・・
・・・・・・・・
・・・●・・・・
・・・●○・・・
・・・○●・・・
・・・・・・・・
・・・・・・・・
・・・・・・・・
Player2の手番です([x y]で座標を指定してください):

まだリバーシリバーシたる"反転"は実装できていませんが、一先ず盤面上に2人のプレイヤーが交互にコマを置いてゆくコードが実装できました。

おわりに

独学プログラマーを読むのを途中でやめて、自分でプログラムを書き始めたのは理由があります。本のコードを写経して、そのプログラムが動くのは嬉しいのですが、いまいち自分の力になっている実感が得られなかったためです。自ら考え、形にすることでしか、プログラミングというスキルは磨けないものだということが実感できる取り組みとなりました。