Pythonプログラミングで

リバーシを作る

Stage 5 石を裏返す機能を実装する

5-2 石を裏返す機能を実装する

とりあえずコードを見ていただきましょうか。

                        
    def turn(self, player, row, col, logger=None):
        logger = logger or local_logger
        
        # out of the board
        if not (InBoard(row) and InBoard(col)):
            logger.info('OUT OF THE BOARD')
            return FAILED

        # there is already a piece
        if self.board[row][col] != EMPTY:
            logger.info('THERE IS ALREADY A PIECE')
            return FAILED

        turned = False
        # searching all the direction for available one
        for direction in WHOLE_DIRECTION:
            focused = [row + direction[ROW], col + direction[COL]]
            if not (InBoard(focused[ROW]) and InBoard(focused[COL])):
                continue
            next_piece = self.board[focused[ROW]][focused[COL]]
            logger.debug('direc = {}, next_piece = {}'.format(direction, next_piece))
            # in case available
            if next_piece == -player and self.turnjudge(player, focused[ROW], focused[COL], direction):
                while self.board[focused[ROW]][focused[COL]] == -player:
                    self.board[focused[ROW]][focused[COL]] = player
                    focused[ROW] += direction[ROW]
                    focused[COL] += direction[COL]
                    turned = True
                
        # in case a piece was turned
        if turned:
            self.board[row][col] = player
            return SUCCEEDED
        # in case any piece was not turned
        else:
            logger.info('THERE IS NO DIRECTION AVAILABLE')
            return FAILED
                        
                    

もうなんかコード長くても驚かなくなったわ

いい兆候です。やっとプログラミングに慣れてきましたね。

~~~~~~~~~~~~~~~

まずは引数から見ていきましょう。とは言っても turnjudge とほとんど変わらないんですが。

player石をおいたプレーヤー番号
row石をおいたマスの row 番号
col石をおいたマスの column 番号
loggerロガー

ほんと見たような表やな

そりゃそうですよ、これ 4-2 の表のコピペみたいなもんですから。

引数入れたらいつも通りロガーを設定してあげてください。

                        
    def turn(self, player, row, col, logger=None):
        logger = logger or local_logger
                        
                    

ほんと説明手抜くよなお前

だって前やってますから。

~~~~~~~~~~~~~~~

turnjudge で石を裏返せるか確認する前に、そんなの確認しなくても石をおけない場合を除外しましょう。指定のマス [row, col] が盤面外に出ている場合と、すでにそこに石が置かれている場合ですね。

                        
        # out of the board
        if not (InBoard(row) and InBoard(col)):
            logger.info('OUT OF THE BOARD')
            return FAILED

        # there is already a piece
        if self.board[row][col] != EMPTY:
            logger.info('THERE IS ALREADY A PIECE')
            return FAILED
                        
                    

InBoard 関数は row か col かどちらかでも False を出したらアウトですから、「両方大丈夫な場合」以外は「石を置けない」FAILED をリターンします。

~~~~~~~~~~~~~~~

さて、ここからは turnjudge をしっかり使っていきますよ。

とりあえず turned = False と書いている部分は無視していただいて結構ですので、for ループの中身を見ていきましょう。

                        
        turned = False
        # searching all the direction for available one
        for direction in WHOLE_DIRECTION:
            focused = [row + direction[ROW], col + direction[COL]]
            if not (InBoard(focused[ROW]) and InBoard(focused[COL])):
                continue
            next_piece = self.board[focused[ROW]][focused[COL]]
            logger.debug('direc = {}, next_piece = {}'.format(direction, next_piece))
            # in case available
            if next_piece == -player and self.turnjudge(player, focused[ROW], focused[COL], direction):
                while self.board[focused[ROW]][focused[COL]] == -player:
                    self.board[focused[ROW]][focused[COL]] = player
                    focused[ROW] += direction[ROW]
                    focused[COL] += direction[COL]
                    turned = True
                        
                    

for ループで direction に全方向を入れてそれぞれループの中を巡らせます。

まずは focused というマスを決めてあげます。「[row, col] の隣のマス」という意味です。

                        
        turned = False
        # searching all the direction for available one
        for direction in WHOLE_DIRECTION:
            focused = [row + direction[ROW], col + direction[COL]]
                        
                    

この focused というマスを色々と弄るのですが、そもそも盤面外にあったらいじることができませんから、focused が盤面外の場合は再び方向選択させますので continue します。

                        
            focused = [row + direction[ROW], col + direction[COL]]
            if not (InBoard(focused[ROW]) and InBoard(focused[COL])):
                continue
            next_piece = self.board[focused[ROW]][focused[COL]]
                        
                    

focused にある石を next_piece という変数に格納します。

                        
            if not (InBoard(focused[ROW]) and InBoard(focused[COL])):
                continue
            next_piece = self.board[focused[ROW]][focused[COL]]
            logger.debug('direc = {}, next_piece = {}'.format(direction, next_piece))
            # in case available
                        
                    

裏返せるならこれは -player になっているはずです。とりあえずログをとっておきましょう。

~~~~~~~~~~~~~~~

さて、本格的に石を裏返します。石を裏返せる条件は

  • next_piece が -player である
  • turnjudge が True である

ですよね。

                        
            logger.debug('direc = {}, next_piece = {}'.format(direction, next_piece))
            # in case available
            if next_piece == -player and self.turnjudge(player, focused[ROW], focused[COL], direction):
                while self.board[focused[ROW]][focused[COL]] == -player:
                        
                    

両方ともクリアしていれば次のステップに入りましょう。focused のマスにある石を裏返します。

でも focused って [row, col] の隣のマスやろ

ええ、ですから裏返すたびに focused を動かしていきます。

                        
            if next_piece == -player and self.turnjudge(player, focused[ROW], focused[COL], direction):
                while self.board[focused[ROW]][focused[COL]] == -player:
                    self.board[focused[ROW]][focused[COL]] = player
                    focused[ROW] += direction[ROW]
                    focused[COL] += direction[COL]
                    turned = True
                        
                    

focused にあるのが相手の石なら自分の石にする、そして focused の位置を direction の方向にずらすと言うことを、自分の石にたどり着くまでやります。

最後の turned ってなんやねん

それについては今からご説明しましょう。

~~~~~~~~~~~~~~~

さて、ここまでコーディングしてきましたけれども、実際に「石を裏返した」ということをどうやって判別しましょうか?

それって調べないといけねーんか。

ええ、例えばこういう場合を考えてみてください。

UNAVAILABLE

黒がこの状態で d6 に石を置こうとしたとしましょう。

待て待て待て、おめーそこに石おけねーよ

ええ、そうですよね。ではそれどうやってそこに「置けない」ことを判別します?

いや、判別って turnjudge でいいじゃねえか

でも turn の中で turnjudge をちゃんと使ってますから、「置けるか置けないか」を判断するメソッドを別に作ったら二度手間ですよね。

まあ、そうなるな

turn の中では実際に石を裏返してるんですから、「マジで石を裏返したら turned を True にする」みたいな変数ほしくないですか?

さっきの turned ってそう言うことだったのね

ええ、つまり

turned == True裏返した
turned == False裏返していない

という判別が効きますので、turned が True なら裏返し成功、False なら失敗としてリターンすることができます。

                        
        # in case a piece was turned
        if turned:
            self.board[row][col] = player
            return SUCCEEDED
        # in case any piece was not turned
        else:
            logger.info('THERE IS NO DIRECTION AVAILABLE')
            return FAILED
                        
                    

これで石を裏返す機能は全て揃いました。あとはゲームを進めるだけです。

NEXT Stage 6 パスと勝敗を判別する