Stage 6 勝敗を判定する
6-3 チェックメイト・ステイルメイトを判定する
チェックメイトで勝ちたい。あわよくば相手や機械に『チェックメイト』と言わせてチヤホヤされてみたい
チェスのゲームをやる人はみんなこういう頭をしています。その期待に応えてあげるのがゲームデザイナーの役目じゃないですか。応えてあげましょうよ。
ということでチェックメイトを判別する機能をこしらえます。それが board.py の checkmatejudge メソッドになります。
def checkmatejudge(self, matee, logger=None):
### LOGGER SETTING
logger = logger or self.logger
# COUNTING CHECK; if not checked, it is not checkmate
if self.checkcounter(matee) in [False, 0]:
logger.debug('NOT CHECKED; it is not checkmate')
return False
# SEARCHING ALL THE MOVES MATEE CAN
for frFILE in range(SIZE):
for frRANK in range(SIZE):
if fundam.PosNeg(self.board[frFILE][frRANK]) == matee:
# searching all TO the piece can reach
for toFILE in range(SIZE):
for toRANK in range(SIZE):
# cloning the board
local_board = Board(board=self.board, target=self.ep_target, castl_k=self.castl_k, castl_q=self.castl_q, player=matee)
# moving the local board and counting up check
if local_board.move(frFILE, frRANK, toFILE, toRANK, Q) and local_board.checkcounter(matee) == 0:
logger.info('THERE IS {}, {} -> {}, {}'.format(frFILE,frRANK,toFILE,toRANK))
return False
logger.debug('motion from "FR = {}, {}" is unavailable'.format(frFILE, frRANK))
# completing the loop, there is no way to flee
return True
~~~~~~~~~~~~~~~
まずは引数です。
def checkmatejudge(self, matee, logger=None):
引数の matee はチェックメイト
される側
のプレーヤーです。お間違えなきよう。
まずはいつも通りロガーを設定しましょう。
def checkmatejudge(self, matee, logger=None):
### LOGGER SETTING
logger = logger or self.logger
なんだよいつも通りって。しっかり説明せえよ
なら 4-1 でもみてみればいいんじゃないですか。
チェックメイトというのは「すでにチェックされていて、どの駒をどのように動かしてもチェックを逃れられない」状況ですよね。チェックされていなければそもそもチェックメイトではありません。
ですからメソッドの一番上ではチェックされていない状況に対して False をリターンしています。
# COUNTING CHECK; if not checked, it is not checkmate
if self.checkcounter(matee) in [False, 0]:
logger.debug('NOT CHECKED')
return False
~~~~~~~~~~~~~~~
ここから先のフローチャートを先に上げておきます。確認してください。
幾重もの for ループは「matee のすべての駒の正しい動かし方」をシラミつぶしに見ていることを意味します。まず外側の 2 つで移動元 fr となるマスをおさえます。
# SEARCHING ALL THE MOVES MATEE CAN
for frFILE in range(SIZE):
for frRANK in range(SIZE):
if fundam.PosNeg(self.board[frFILE][frRANK]) == matee:
それが matee の駒であれば、次の 2 重 for ループで駒の移動先 to をシラミつぶしに探します。
# SEARCHING ALL THE MOVES MATEE CAN
for frFILE in range(SIZE):
for frRANK in range(SIZE):
if fundam.PosNeg(self.board[frFILE][frRANK]) == matee:
# searching all TO the piece can reach
for toFILE in range(SIZE):
for toRANK in range(SIZE):
~~~~~~~~~~~~~~~
さて、移動元 fr と移動先 to が決まったところで、この後どうしましょうか。プログラムする人間の心理としては
現状の self から駒を動かしてチェックを回避できるか見てみたい
と思っています。要はチェックを解消できる方法が一つでもあればチェックメイトじゃないわけですから。
ですが self で一度駒を動かしてしまうと再び元の状態に戻せない以上、この Board そのものに手をつけて駒を動かすのは禁忌です。言い換えると、このループの内側では
- self を完璧に再現して駒を動かしてみたい
- self 自体を触ることは許されない
というジレンマを抱えています。そこで local_board という、self を完コピした、まったく新しいインスタンスを作ります。
完コピ?どうやんの
local_board を宣言するときの引数に self の情報をありったけ突っ込むんです。
# searching all TO the piece can reach
for toFILE in range(SIZE):
for toRANK in range(SIZE):
# cloning board
local_board = Board(board=self.board, target=self.ep_target, castl_k=self.castl_k, castl_q=self.castl_q, player=matee)
影武者みたいなもんやな。でも最後の player は matee で self とは違うな
~~~~~~~~~~~~~~~
local_board を宣言したあとに local_board.move を発動して駒を動かします。移動元と移動先は for ループのパラメーターどおりです。
# cloning board
local_board = Board(board=self.board, target=self.ep_target, castl_k=self.castl_k, castl_q=self.castl_q, player=matee)
# moving the local board and count up check
if local_board.move(frFILE, frRANK, toFILE, toRANK, Q) and local_board.checkcounter(matee) == 0:
logger.info('THERE IS {}, {} -> {}, {}'.format(frFILE,frRANK,toFILE,toRANK))
return False
if の中で move を発動してるけど、これで実際に local_board の駒動いてんのか?
大丈夫ですよ。move のリターン値「移動成功/失敗」というのを計算する過程で駒を動かしていますから。
ここで local_board について、
- move から True (移動成功)
- checkcounter から 0 (チェックされていない)
が得られれば「移動してチェックを回避できる」という意味になりますから、チェックメイトではないということになります。False をリターンしてください。
# moving the local board and count up check
if local_board.move(frFILE, frRANK, toFILE, toRANK, Q) and local_board.checkcounter(matee) == 0:
logger.info('THERE IS {}, {} -> {}, {}'.format(frFILE,frRANK,toFILE,toRANK))
return False
移動元 FR の駒をどうやって動かしてもチェックを回避できない時はロガーでその旨知らせてあげます。
logger.debug('"FR = {}, {}" was unavailable'.format(frFILE, frRANK))
ループを抜けきってしまいますと、「どのように動かしてもチェックを免れることはできない」ということになってしまいますので、「チェックメイトされてる」True をリターンしましょう。
# completing the loop, there is no way to flee
return True
~~~~~~~~~~~~~~~
今度は決着のつき方3つ目, ステイルメイトです。stalematejudge メソッドで判別します。
def stalematejudge(self, matee, logger=None):
### LOGGER SETTING
logger = logger or self.logger
### COUNTING CHECKS; checked, it is not stalemate
if self.checkcounter(matee) not in [0, False]:
logger.debug('CHECKED; it is not stalemate')
return False
### SEARCHING ALL THE MOVES MATEE CAN
for frFILE in range(SIZE):
for frRANK in range(SIZE):
if fundam.PosNeg(self.board[frFILE][frRANK]) == matee:
# searching all TO the piece can reach
for toFILE in range(SIZE):
for toRANK in range(SIZE):
# cloning the board
local_board = Board(board=self.board, target=self.ep_target, castl_k=self.castl_k, castl_q=self.castl_q, player=matee)
# moving the local board and counting up check
if local_board.move(frFILE, frRANK, toFILE, toRANK, Q) and local_board.checkcounter(matee) == 0:
logger.info('THERE IS {}, {} -> {}, {}'.format(frFILE,frRANK,toFILE,toRANK))
return False
logger.debug('motion from "FR = {}, {}" is unavailable'.format(frFILE, frRANK))
# completing the loop, there is no way to avoid check when moving
logger.info('STALEMATE. {} cannot move'.format(self.player))
return True
相変わらず引数の matee は「ステイルメイトされる側」つまり動けなくなってしまったか確認したい方のプレーヤー番号です。
def stalematejudge(self, matee , logger=None):
もう強調しなくていいでしょ?しましょうか?引数はステイルメイト
される側
~~~~~~~~~~~~~~~
やることはほぼほぼチェックメイトと変わりません。唯一の違いは、「現状でチェックを受けていない」ことです。メソッドの頭で調べています。
### LOGGER SETTING
logger = logger or self.logger
### COUNTING CHECKS; checked, it is not stalemate
if self.checkcounter(matee) not in [0, False]:
logger.debug('CHECKED')
return False
6-2 を確認すればわかりますが、もし checkcounter が 0 でも False でもなければ間違いなくチェックされていますから、ステイルメイトとは言えません。
この第一関門を突破したものだけが次なるステップへ進むことができます。4 重の for ループです。そう、6-3 のチェックメイトとまったく同じことをします。コピペでいいですよ、コピペで。
### SEARCHING ALL THE MOVES MATEE CAN
for frFILE in range(SIZE):
for frRANK in range(SIZE):
if fundam.PosNeg(self.board[frFILE][frRANK]) == matee:
# searching all TO the piece can reach
for toFILE in range(SIZE):
for toRANK in range(SIZE):
# cloning board
local_board = Board(board=self.board, target=self.ep_target, castl_k=self.castl_k, castl_q=self.castl_q, player=matee)
# moving the local board and count up check
if local_board.move(frFILE, frRANK, toFILE, toRANK, Q) and local_board.checkcounter(matee) == 0:
logger.info('THERE IS {}, {} -> {}, {}'.format(frFILE,frRANK,toFILE,toRANK))
return False
logger.debug('"FR = {}, {}" was unavailable'.format(frFILE, frRANK))
# completing the loop, there is no way to avoid check when moving
logger.info('STALEMATE. {} cannot move'.format(self.player))
return True