Python プログラムで動かすフェアリーチェスアプリ開発、連載第 10 回です。
は駒の移動にアニメーションを追加しました。今回はチェスの特殊ルールであるアンパッサンを実装していきたいと思います。
アンパッサンとは
アンパッサンとはフランス語で en passant と書きます。
en + passer の現在分詞なのでおそらくジェロンディフというもので、
同時性や手段などさまざまな意味を表します。
英語で言うと分詞構文 passing というようなところで、
「通りながら」というような意味合いでしょうか。
ポーンは最初に動くときにだけ2歩まで前進することができるのですが、
相手のポーンが2歩前進した直後で、自分のポーンがそのポーンとある位置関係にあるときにのみ、
相手のそのポーンを取ることができます。
これをアンパッサンといいます。
これを実装するためには、アンパッサンができる条件をしっかりと把握しておく必要があります。
- 取るのも取られるのもポーンである。
- 相手のポーンが2マス進んだ直後である。
- 関わるポーンは隣接している。つまり同じランク(行)にいて、隣のファイル(列)にいる。
これらの条件の判定に使うために、関わる2つの駒の位置情報が必要です。
相手のポーンが2歩進んだ直後にのみ値が入る変数を設定する
条件式に使うために、相手のポーンが2歩進んだ直後にのみその座標を表す変数を設定します。
main.py1class Game:2def __init__(self):3...4self.advanced2_pos = None5...6...7def main(self):8...9if target and target.color == self.playersturn:10...11# 相手のポーンが2歩進んだ12if target.abbr == 'P':13if endpos[1] == startpos[1] + 2*target.direction:14self.advanced2_pos = endpos15else:16self.advanced2_pos = None17self.renew_gameboard(startpos, endpos, self.gameboard)18...
self.advanced2
はポーンが2歩進んだときに、次に何かの駒が動かされるまでendpos
(2歩進んだポーンの位置)となります。
ポーンの2歩の動きではないときには、この変数はNone
となります。
if
文によって「相手のポーンが2歩進んだ直後である」という条件が判別されています。
アンパッサンの条件式を定義する
条件が複雑なので、関数の返り値として条件式を返してもらうことにします。
下を白側とすると、位置関係は次の図のようになります。
main.py1def en_passant_requirements(self, piece, startpos, endpos):2'''3アンパッサンの条件を満たすとき True を返す45Parameters6----------7piece : obj8動かす駒.9startpos, endpos : tuple > (int, int)10開始位置,終了位置.1112Returns13-------14bool15'''16return (piece.abbr == 'P'17and self.advanced2_pos18and startpos[1] == endpos[1] - piece.direction19and startpos[1] == self.advanced2_pos[1]20and endpos[1] == self.advanced2_pos[1] + piece.direction21and abs(startpos[0] - endpos[0]) == 122and abs(startpos[0] - self.advanced2_pos[0]) == 123and endpos[0] == self.advanced2_pos[0])
piece.abbr == 'P'
によって、取る駒がポーンであることが保証されます。
self.advanced2_pos
は直前にポーンが2歩進んだとき以外はNone
を返すので、
取られる駒がポーンであること、相手のポーンが2マス進んだ直後であることも保証されます。
残りの条件は上図の位置関係を表しています。
条件を満たすとき、そのマスに駒を動かすことができるようにする
これは移動可能なマスのリストを出力するメソッドvalid_moves()
を編集します。
main.py1def valid_moves(self, piece, startpos, gameboard):2...3result = piece.available_moves(*startpos, gameboard, color=piece.color)4# アンパッサン5for endpos in ([(i, 2) for i in range(8)] + [(i, 5) for i in range(8)]):6if self.en_passant_requirements(piece, startpos, endpos):7result += [endpos]8# チェック回避のため動き縛り9...
アンパッサンをしたポーンの移動先のマスの候補としては、3ランクと6ランクのマスがあります。
[(i, 2) for i in range(8)] + [(i, 5) for i in range(8)]
は、これらのマスをリスト内包表記で取り出しているものです。
アンパッサンをされた駒を取り除く
盤面を更新するメソッドrenew_gameboard()
を編集するのですが、ここではアンパッサンが起きたときにのみ駒を取り除きたいので、
アンパッサンが起きる可能性があるときにのみTrue
となる
main.py1def __init__(self):2...3self.en_passant = False4...5...67def valid_moves(self, piece, startpos, gameboard):8...9result = piece.available_moves(*startpos, gameboard, color=piece.color)10# アンパッサン11self.en_passant = False12for endpos in ([(i, 2) for i in range(8)] + [(i, 5) for i in range(8)]):13if self.en_passant_requirements(piece, startpos, endpos):14self.en_passant = True15result += [endpos]16# チェック回避のため動き縛り17...18...1920def renew_gameboard(self, startpos, endpos, gameboard):21'''22盤面を更新する2324Parameters25----------26startpos, endpos : tuple > (int, int)27開始位置,終了位置.絶対座標.28gameboard : dict > {(int, int): obj, ...}29盤面.30'''31color = gameboard[startpos].color32gameboard[endpos] = gameboard[startpos]33del gameboard[startpos]34# アンパッサン35if self.en_passant:36if (color == WHITE37and gameboard.get((endpos[0], endpos[1] - 1))):38if (gameboard[endpos[0], endpos[1] - 1].name == 'BP'):39del gameboard[(endpos[0], endpos[1] - 1)]40elif (color == BLACK41and gameboard.get((endpos[0], endpos[1] + 1))):42if (gameboard[endpos[0], endpos[1] + 1].name == 'WP'):43del gameboard[(endpos[0], endpos[1] + 1)]
これでアンパッサンを実装することができました!
(おまけ)これまでのコードまとめ
これまでにいろんなところをいじっていろんなところが変わってしまったため、もとのコードと大きく離れてしまっています。
10 回目ということで、今まで編集してきたコードをまとめたものを GitHub に置いておきます。
→ Release codes after 10 articles · midorimici/chess-program-for-python-and-OpenGL
はプロモーションを実装していきたいと思います。
お読みいただきありがとうございました。
では