Python プログラムで動かすフェアリーチェスアプリ開発、連載第 5 回です。
は自らチェックされに行くような動きができないようにしました。今回は
やを判定できるようにします。動かせる駒があるかどうかの判定
チェックメイトを判定できるようにしましょう。
チェックメイトとチェックは何が違うかというと、
チェックは一手でチェックじゃなくできるけど、チェックメイトはどうあがいてもチェックを免れない
という点です。
チェックというのがキングが敵駒に攻撃されている状態ですから、
チェックメイトはキングを相手の攻撃範囲外にすることが一手ではできないという状態ですね。
前回で自らチェックされに行くような動きは出来なくなりました。
つまりどう動いてもチェックされてしまうというときはどこにも動けません。
動かせる駒がないとき、チェックメイトまたはステイルメイトであると言えます。
どの駒も動かすことができないとき、True
を返すような
main.py1class Game:2...3def cannotMove(self, color, gameboard):4'''color側が駒を動かせないときTrueを返す'''5for position, piece in gameboard.items():6# 盤面上の駒の位置と駒について7if color == piece.Color:8# 駒色がcolorのとき9for dest in piece.availableMoves(*position, gameboard, Color=color):10# 駒の移動先について11# 移動後にチェック回避できるならFalse12gameboardTmp = copy(gameboard)13gameboardTmp[dest] = gameboardTmp[position]14del gameboardTmp[position]15if not self.isCheck(color, gameboardTmp):16return False17# colorのどの駒をどのように移動させてもチェック回避できなかったとき18return True
13, 14 行目の形は今までも何度か出てきました。
この際この 2 行を表すメソッドを作ってしまいましょう。
main.py1class Game:2...3def renewGameboard(self, startpos, endpos, gameboard):4'''盤面を更新する'''5gameboard[endpos] = gameboard[startpos]6del gameboard[startpos]
これでmain()
とisValidMove()
とcannotMove()
は次のようになります。
main.py1class Game:2...3def main(self):4while True:5...6if target:7...8if self.isValidMove(target, startpos, endpos, self.gameboard):9self.message = "that is a valid move"10self.renewGameboard(startpos, endpos, self.gameboard)11self.isCheck(target.Color, self.gameboard)12...13...14def isValidMove(self, piece, startpos, endpos, gameboard):15'''pieceのstartpos -> endposの動きが可能であるときTrueを返す'''16if endpos in piece.availableMoves(startpos[0], startpos[1], gameboard, Color=piece.Color):17gameboardTmp = copy(gameboard)18self.renewGameboard(startpos, endpos, gameboardTmp)19if self.isCheck(piece.Color, gameboardTmp):20return False21else:22return True23else:24return False25...26def cannotMove(self, color, gameboard):27'''color側が駒を動かせないときTrueを返す'''28for position, piece in gameboard.items():29if color == piece.Color:30for dest in piece.availableMoves(*position, gameboard, Color=color):31gameboardTmp = copy(gameboard)32self.renewGameboard(position, dest, gameboardTmp)33if not self.isCheck(color, gameboardTmp):34return False35return True
さて、これでcannotMove() and isCheck()
ならチェックメイト、
cannotMove() and not isCheck()
ならステイルメイトです。
判定してメッセージ表示
あとはこれを適切な位置においてメッセージも表示させれば OK です。
チェックと同じ場所に置きたいですね。
どこで判定してどこでメッセージ表示しているのでしょうか。
main.py1class Game:2...3def main(self):4while True:5...6if target:7...8if self.isValidMove(target, startpos, endpos, self.gameboard):9self.message = "that is a valid move"10self.renewGameboard(startpos, endpos, self.gameboard)11self.isCheck(target.Color, self.gameboard)12...13...14def isCheck(self, color, gameboard):15kingDict = {}16pieceDict = {BLACK : [], WHITE : []}17for position, piece in gameboard.items():18if type(piece) == King:19kingDict[piece.Color] = position20pieceDict[piece.Color].append((piece, position))21if self.canSeeKing(kingDict[color], pieceDict[opponent[color]], gameboard):22self.message = f"{color} player is in check"23return True
どうやらメッセージはself.message
が請け負っているようです。
せっかくisCheck()
に真偽判定をさせているので、こっちの書き方のほうがすっきりするかな。
main.py1class Game:2...3def main(self):4while True:5...6if target:7...8if self.isValidMove(target, startpos, endpos, self.gameboard):9self.message = "that is a valid move"10self.renewGameboard(startpos, endpos, self.gameboard)11self.isCheck(target.Color, self.gameboard)12if self.isCheck(target.Color, self.gameboard):13self.message = f"{target.Color} player is in check"14...15...16def isCheck(self, color, gameboard):17kingDict = {}18pieceDict = {BLACK : [], WHITE : []}19for position, piece in gameboard.items():20if type(piece) == King:21kingDict[piece.Color] = position22pieceDict[piece.Color].append((piece, position))23if self.canSeeKing(kingDict[color], pieceDict[opponent[color]], gameboard):24self.message = f"{color} player is in check"25return True
移してみました。
同じ要領で、チェックメイトやステイルメイトのメッセージも盛り込めますね。
あと、メイトになるとゲームは終了するので、プログラムを終了させるようにましょう。
main.py1import sys23def main(self):4while True:5...6if target:7...8if self.isValidMove(target, startpos, endpos, self.gameboard):9self.message = "that is a valid move"10self.renewGameboard(startpos, endpos, self.gameboard)11if self.isCheck(target.Color, self.gameboard):12self.message = f"{target.Color} player is in check"13if self.cannotMove(target.Color, self.gameboard):14if self.isCheck(target.Color, self.gameboard):15self.message = f"Checkmate! {opponent[target.Color]} player won!"16sys.exit()17else:18self.message = "Stalemate! It's draw."19sys.exit()20...
これまでの回で、当初指摘されていた問題点のほとんどは解決しました。
キャスリングやアンパッサンなどの実装もしたいのですが、
その前に見た目や遊び方をゲームっぽくしていきたいと思います。
それではまた