Python プログラムで動かすフェアリーチェスアプリ開発、連載第3回です。
は、先手の駒色と駒の初期配置を修正しました。今回は、ポーンの最初の動きを付け加えてみましょう。
本来ポーンは、最初の動きだけは2歩まで進むことができます。
駒に関するクラス
まず、Pawn
を見ると、class Pawn(Piece):
とあります。
これはPiece
クラスを継承していることがわかります。
クラスを定義するとき、()内に書かれたものが基底クラス(親クラス)になります。
継承をすると、基底クラスで定義された
やを子クラスでも使うことができます。あと、子クラスから生成された
から基底クラスの属性やメソッドにアクセスできるようになります。それでは、Pawn
の中身を見ていきましょう。(下は原文ママ)
pieces.py1class Pawn(Piece):2def __init__(self,color,name,direction):3self.name = name4self.Color = color5#of course, the smallest piece is the hardest to code. direction should be either 1 or -1, should be -1 if the pawn is traveling "backwards"6self.direction = direction7def availableMoves(self,x,y,gameboard, Color = None):8if Color is None : Color = self.Color9answers = []10if (x+1,y+self.direction) in gameboard and self.noConflict(gameboard, Color, x+1, y+self.direction) : answers.append((x+1,y+self.direction))11if (x-1,y+self.direction) in gameboard and self.noConflict(gameboard, Color, x-1, y+self.direction) : answers.append((x-1,y+self.direction))12if (x,y+self.direction) not in gameboard and Color == self.Color : answers.append((x,y+self.direction))# the condition after the and is to make sure the non-capturing movement (the only fucking one in the game) is not used in the calculation of checkmate13return answers
__init__()
がある
他の駒にはないのにPawn
にだけ
これはPawn
にだけdirection
というプロパティをつける必要があったからでしょう。
他の駒は前後対称な動きをするけど、ポーンだけは前後が決まっています。
direction
には1
を指定すれば白から見て前方向、-1
を指定すれば後方向ですね。
ポーンの特殊な動きを再現
ポーンはちょっと特殊な動きをします。
動き始めだけは2歩まで進めるということの他にも、
進むときは前に1歩しか動けないが、敵駒を取るときにのみ斜め前に1歩進める(斜め前の駒を取れる)のです。次のavailableMoves()
は盤面上の(x, y)
の位置にいる駒の動けるマスを出力します。
なぜわかる?
これは例えば、
その駒が本来動けない場所への移動を指定したときに
invalid move
とプリントされる
→Game
クラスのmain()
の中のif target.isValid(startpos,endpos,target.Color,self.gameboard):
にかからなかった
→target.isValid(startpos,endpos,target.Color,self.gameboard)
はFalse
→Piece
クラスのisValid(self,startpos,endpos,Color,gameboard)
がendpos in self.availableMoves(startpos[0],startpos[1],gameboard, Color = Color)
ならばTrue
を返す
→availableMoves()
は移動可能なマスの位置を表している
というふうにしてわかります。
以下のコードは原文を少し整形しています。
pieces.py1def availableMoves(self, x, y, gameboard, Color=None):2if Color is None:3Color = self.Color4answers = []5if ((x + 1, y + self.direction) in gameboard6and self.noConflict(gameboard, Color, x + 1, y + self.direction)):7answers.append((x + 1, y + self.direction))8if ((x - 1, y + self.direction) in gameboard9and self.noConflict(gameboard, Color, x - 1, y + self.direction)):10answers.append((x - 1, y + self.direction))11if (x, y + self.direction) not in gameboard and Color == self.Color:12answers.append((x, y + self.direction))13return answers
if Color is None:
最初のこのif文は、Color
が特に指定されなかったときのための受け皿みたいなものなので、ここではあまり重要ではありません。
answers
answers
に移動可能なマスを二要素
最終的には[(int, int), ...]
という形になります。(int
は整数という意味)
if ... in gameboard and self.noConflict ...
ここの3つのif文がこの部分の勘所です。
最初の2文は、ポーンの攻撃を表しています。
ポーンは敵駒を取るときにのみ斜め前に1歩進めます。
もし自分の斜め前に駒があり(... in gameboard
)、その駒が自分と異なる色である(self.noConflict(...)
)とき、そのマスに移動可能になりますよ~ということです。
noConflict()
はPiece
クラスで定義されていて、中身はこんな感じです。
pieces.py1def noConflict(self, gameboard, initialColor, x, y):2if (self.isInBounds(x, y)3and ((x, y) not in gameboard4or gameboard[(x, y)].Color != initialColor)):5return True6return False
isInBounds()
は(x, y)
が盤面の範囲内にあるときTrue
を返すものなので、
noConflict()
は(x, y)
が盤面の範囲内にあって味方の駒がない(駒がないもしくは敵の駒がある)マスであるときにTrue
となります。
そこには駒が進出可能ということですね。
if (x, y + self.direction) not in gameboard and Color == self.Color:
目の前に駒がなければ(if (x, y + self.direction) not in gameboard
)、1歩前に移動できますよ~ということです。
Color == self.Color
の部分ですが、
コメントでは、駒を取らない動きがチェックメイトの計算に使われないことを保証するため、と書かれています。
たぶん、キングが相手のポーンの目の前に移動すると、そのマスにポーンが移動できるためチェックと判定されるのを防ごうとしているのかなと思いますが、
すでに(x, y + self.direction) not in gameboard
を通っているので、この部分はなくていいんじゃないかなと思います。
そもそも、コード内でColor != self.Color
になることがないので…
最初の動きを追加
さて、駒の動きの仕組みを理解したところで、最初の動きだけは2歩まで動けるという動きを追加していきます。
最初だけはというのがちょっと難しいかもしれないです。
まずは、駒がないマスならば前2歩まで進めることを表現してみましょう。
pieces.py1def availableMoves(self, x, y, gameboard, Color=None):2if Color is None:3Color = self.Color4answers = []5if ((x + 1, y + self.direction) in gameboard6and self.noConflict(gameboard, Color, x + 1, y + self.direction)):7answers.append((x + 1, y + self.direction))8if ((x - 1, y + self.direction) in gameboard9and self.noConflict(gameboard, Color, x - 1, y + self.direction)):10answers.append((x - 1, y + self.direction))11if (x, y + self.direction) not in gameboard:12answers.append((x, y + self.direction))13if ((x, y + self.direction) not in gameboard14and (x, y + 2*self.direction) not in gameboard):15answers.append((x, y + 2*self.direction))16return answers
ポイントは2マス先に駒がなくても1マス先に駒があれば通れないということです。
さて、最初の動きだけ、という条件をつけていくのですが、
それぞれのポーンにとって最初の動きに特有の環境というものがあります。
初期位置です。
通常のチェスの場合、白ポーンは初めて動くとき必ず2ランクにいて、
2ランクにいるのはいつでもまだ動いていないときです。
この位置情報を条件として付与すればよさそうです。
pieces.py1def availableMoves(self, x, y, gameboard, Color=None):2if Color is None:3Color = self.Color4answers = []5if ((x + 1, y + self.direction) in gameboard6and self.noConflict(gameboard, Color, x + 1, y + self.direction)):7answers.append((x + 1, y + self.direction))8if ((x - 1, y + self.direction) in gameboard9and self.noConflict(gameboard, Color, x - 1, y + self.direction)):10answers.append((x - 1, y + self.direction))11if (x, y + self.direction) not in gameboard:12answers.append((x, y + self.direction))13if (((self.Color == WHITE and y == 1)14or (self.Color == BLACK and y == 6))15and (x, y + self.direction) not in gameboard16and (x, y + 2*self.direction) not in gameboard):17answers.append((x, y + 2*self.direction))18return answers
(x, y)
の形では0
からのスタートになるので、
2ランクはy == 1
となります。
ちゃんと2歩動きました!
最初だけ!
ただ、このやり方は通常のチェスであったから通用しただけであって、
今後フェアリーチェスへの拡張を考えると、もっと柔軟な対応が必要になるのですが、
そのへんはまた今度にしましょう。
今回は駒に動きを追加しました。
はチェックまわりについて見ていきたいと思います。長くなってしまいましたが、お読みいただきありがとうございました!
それではまた