Pythonプログラミングで

リバーシを作る

Stage 4 石をおけるか判別する機能を実装する

4-1 石をおけるか判別する機能をデザインする

今回からようやく石を裏返す機能の実装に入ります。

やっとリバーシらしくなってきたな

ではどうやって裏返しましょうか?

とりあえずコードを見てみましょうか。board.py ファイルの turnjudge と turn の両メソッドで取り扱っていきます。

                        
    def turnjudge(self, player, row, col, direction: list, logger=None):
        logger = logger or self.logger
        
        # out of the board
        if not (InBoard(row) and InBoard(col)):
            logger.debug('{}, {}\tOUT OF THE BOARD'.format(row, col))
            return False

        # BLACK, WHITE or EMPTY
        piece = self.board[row][col]
        
        # EMPTY
        if piece == EMPTY:
            logger.debug('{}, {}\tREACHED TO EMPTY'.format(row, col))
            return False
        # OWN
        elif piece == player:
            logger.debug('{}, {}\tREACHED TO OWN PIEE'.format(row, col))
            return True
        # OPPONENT'S
        elif piece == -player:
            return self.turnjudge(player, row + direction[ROW], col + direction[COL], direction, logger)
        # ERROR
        else:
            logger.error('UNEXPECTED VALUE of PLAYER in putjudge')
            return False
        
    
    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 メソッドだけですから、まだいいんじゃないですか?

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

さて、どうやったら「そのマスに石を置けるか」ということを判定する機能が作れるでしょうか?

さっぱりですか。では、みなさんが実際にリバーシをやるときに「このマスには石がおけるな」ということをどうやって判断しているか振り返ってみましょう。

まずこの盤面を見てみてください。

UNAVAILABLE

この盤面で d2 の位置に黒は石を置くことができるでしょうか?

できねーな

ではこの盤面で、c6 の位置に黒は石を置くことができるでしょうか?

それならできる

今どうやって「できる」「できない」を判断しましたか?

いや、どうやってって、なんつったらいいんかな、あれは

では質問を変えましょう。視線はどのように動かしましたか?

UNAVAILABLE

UNAVAILABLE

こうやって動かしたんじゃないですか?

違うもん。そんなんじゃないもん

違うなら違うで構わないんですけれども、やってることとしては

  1. 石を置きたいマスに注目する
  2. 相手の石が隣接しているマスの方向をたどる
  3. どの方向を見ても相手の石の端に自分の石がなければおけない
  4. どれかしらの方向で相手の石の端に自分の石があればおける

って判断ですよね?これをコンピューターにやらせましょう。

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

では、「石をマスにおけるかおけないか」を判断するアルゴリズムを考えてやります。「どの方向を見ても」とか「どれかしらの方向で」といったものについては for ループとか使えばできますから、とりあえずここでは1つの方向について考えていきましょう。

例えば上だったら「上をみて裏返せる石があるか」を判定すればいいんやな

そういうことです。せっかくなので上の場合を考えてみましょうか。

石を [row, col] の位置に置きたいとします。まず最初に、[row, col] の1つ上のマスに相手の石がなければ当然裏返すことはできませんから、この時点で除外してあげましょう。

すぐ上に相手の石がある場合は、ひょっとしたら裏返すことができるかもしれません。さらに上のマスにも相手の石が続いていくか見てみます。

「さらに上のマスにも」っていうけどさ、
どうやって上のマス見んのさ

まあ while ループとか使えばいいのかもしれませんが、今回は処理がほとんどありません。ここは一つ再起関数でいきましょうか。

さいきかんすー?

アルゴリズムはだいたいこんな感じになります。

UNAVAILABLE UNAVAILABLE

例えば「置いたところから上に石を裏返せるか判断する関数」up(row, col) というものを考えてあげます。挙動としては

  • [row, col] のマスに自分の石がある      → return True
  • [row, col] のマスに自分の石も相手の石もない → return False
  • [row, col] のマスに相手の石がある      → return up(row - 1, col)

としてあげます。リターンする値をとりあえずキープしているわけです。

UNAVAILABLE

こうすれば up(row, col) とするだけで上方向に石を裏返せるか確認できます。

NEXT 4-2 石をおけるか判別する機能を実装する