Python プログラムで動かすフェアリーチェスアプリ開発、連載第 14 回です。
までで通常のチェスが問題なくプレイできるようになり、はコードを整形しました。今回は、いよいよフェアリーチェスの幕開けです!
ところで、フェアリーチェスってなんでしたっけ?
フェアリーチェスは、広く言えば通常のチェスとは異なるルールのもとでプレイされるチェスのことです。
つまり、なんでもありなんです。
盤面が小さくても大きくても丸くても、駒の動きが弱くても強くても、なんでもありです。
とはいっても、盤面の大きさや駒の動きを変えることがほとんどです。
今回は手始めに、駒の動きを変えてしまいましょう。
駒の動きの定義
各駒の動きは、pieces.py
にて定義されています。
例えば、ナイトとルークはこのようになっています。
pieces.py1class Knight(Piece):2abbr = 'N'34def available_moves(self, x, y, gameboard):5return [(xx, yy) for xx, yy in leaper(x, y, 2, 1) if self.no_conflict(gameboard, self.color, xx, yy)]678class Rook(Piece):9abbr = 'R'1011def available_moves(self, x, y, gameboard):12return self.rider(x, y, gameboard, self.color, chess_cardinals)
available_moves
で駒の動きを定義しています。
返り値は2要素のリストです。
これを少しいじってみましょう。
pieces.py1class Knight(Piece):2abbr = 'N'34def available_moves(self, x, y, gameboard):5return [(xx, yy) for xx, yy in leaper(x, y, 2, 3) if self.no_conflict(gameboard, self.color, xx, yy)]678class Rook(Piece):9abbr = 'R'1011def available_moves(self, x, y, gameboard):12return self.rider(x, y, gameboard, self.color, [(1, 2), (2, 1)])
すると、このような動きになりました。
おわかりいただけただろうか…?笑
ナイトのほうは、横に2マス、縦に3マスいったところ((2, 3)方向)に動けるようになりました。
もともとは横に2マス、縦に1マス((2, 1)方向)でしたね。
1を3に変更したので、このような動きになりました。
leaper
という関数によって、一方向を指定しただけでも8方向に広げてくれます。
ルークのほうは、(1, 2)方向と(2, 1)方向に直線的に走るようになりました。
こちらはleaper
がないため、指定した2方向にしか動けず、
動画で動かしたもの以外の3つのルークは一歩も動けません。
しかしrider
メソッドによって、ナイトとは違って走ることができます。
もともとはchess_cardinals
で、これは[(1, 0), (0, 1), (-1, 0), (0, -1)]
のことで、
この方向は十字方向です。
他の駒の動きも、この要領で変えていくことができるし、
自分で新しい駒を定義することもできます。
新しい駒の作成
試しに「ユニコーン」という駒を作成してみましょう。
この駒は、ナイトが走るように、つまりナイト方向に一直線に動きます。
他の駒定義にならって、ユニコーンクラスを定義します。
pieces.py1class Unicorn(Piece):2abbr = 'Un'34def available_moves(self, x, y, gameboard):5return self.rider(x, y, gameboard, self.color, leaper(0, 0, 1, 2))
rider
メソッドとleaper
関数を組み合わせています。
ところで、それぞれの中身は次のようになっています。
pieces.py1class Piece:2...3def rider(self, x, y, gameboard, color, intervals):4'''5Parameters6----------7x, y : int8駒の絶対座標.9gameboard : dict > {(int, int): obj, ...}10盤面.11color : str > 'W', 'B'12駒色.13intervals : list > [(int, int), ...]14移動の方向.相対座標.1516Returns17-------18answers : list > [(int, int), ...]19駒の可能な移動先.20'''21answers = []22for xint, yint in intervals:23# intervals 中の (xint, yint) 方向について24# (xtemp, ytemp) : 盤面上の現在位置絶対座標 (x, y) から25# (xint, yint) 方向に動いたときの絶対座標26xtemp, ytemp = x + xint, y + yint27while self.is_in_bounds(xtemp, ytemp):28# 盤面上に収まるとき29# target : 盤面上の (xtemp, ytemp) の位置に駒があればその駒(obj).30# なければ None.31target = gameboard.get((xtemp, ytemp), None)32if target is None:33# 駒がないのでそのマスには動ける34answers.append((xtemp, ytemp))35elif target.color != color:36# 相手の駒があるので取れる37answers.append((xtemp, ytemp))38# 駒にぶつかったので,これ以上進めない39break40else:41# 自分の駒にぶつかったので,これ以上進めない42break4344# (xtemp, ytemp) から (xint, yint) 方向に一回動く45xtemp, ytemp = xtemp + xint, ytemp + yint46return answers47...48def leaper(x, y, int1, int2):49'''50Parameters51----------52x, y : int53駒の位置.54int1, int2 : int55駒の移動方向 (int1, int2).5657Returns58-------59list > [(int, int), ...]60駒の可能な移動先.61'''62return [(x+int1, y+int2), (x-int1, y+int2), (x+int1, y-int2), (x-int1, y-int2),63(x+int2, y+int1), (x-int2, y+int1), (x+int2, y-int1), (x-int2, y-int1)]
leaper
の最初の2つの引数を0
にしたのは、rider
メソッドに渡すのは方向(相対座標)であって位置(絶対座標)ではないからです。
盤上に配置
さて、駒は作れたわけですが、このままではまだユニコーンが盤上に登場しません。
盤面に駒を置いているのは、main.py
のplace_pieces
メソッドです。
main.py1class Game:2...3def place_pieces(self):4for i in range(0, 8):5self.gameboard[(i, 1)] = Pawn(W, 1)6self.gameboard[(i, 6)] = Pawn(B, -1)78placers = [Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook]910for i in range(0, 8):11self.gameboard[(i, 0)] = placers[i](W)12self.gameboard[(i, 7)] = placers[i](B)
ざっくり言うとgameboard
の特定の位置に特定の駒オブジェクトを配置しています。
とりあえず、次の行を追加します。
main.py1class Game:2...3def place_pieces(self):4for i in range(0, 8):5self.gameboard[(i, 1)] = Pawn(W, 1)6self.gameboard[(i, 6)] = Pawn(B, -1)78placers = [Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook]910for i in range(0, 8):11self.gameboard[(i, 0)] = placers[i](W)12self.gameboard[(i, 7)] = placers[i](B)1314self.gameboard[(0, 2)] = Unicorn(W)
これで、a3 の位置に白のユニコーンが配置されるはずです。
あと、駒を表示するための画像も用意しなければなりません。
/img/W/WUn.png
と/img/B/BUn.png
を作成します。
画像は Alfaerie Variant Chess Graphics をもとに作成しています。
それと、画像と ID の紐づけもしておきます。
pieces.py1piece_names = [Knight, Rook, Bishop, Queen, King, Pawn, Unicorn]23# 画像IDの割り当て4piece_ID = {}5for i, piece in enumerate(piece_names):6piece_ID['W' + piece.abbr] = i + 17piece_ID['B' + piece.abbr] = -(i + 1)
これで動かしてみると、このようになりました。
思った通りの動きになりました!
実際にはこんなところに駒を置くわけにもいかないので、
ナイトの代わりに置いたりするのですが、
その場合は次のように書きます。
main.py1class Game:2...3def place_pieces(self):4for i in range(0, 8):5self.gameboard[(i, 1)] = Pawn(W, 1)6self.gameboard[(i, 6)] = Pawn(B, -1)78placers = [Rook, Knight, Bishop, Queen, King, Bishop, Unicorn, Rook]910for i in range(0, 8):11self.gameboard[(i, 0)] = placers[i](W)12self.gameboard[(i, 7)] = placers[i](B)
これで黒白双方のキングサイドのナイトがユニコーンに変わります。
これも一種のフェアリーチェスですね!
今回はフェアリー駒の導入をしました。
しかし、このままだと一種類のチェスしかできないので、
はさまざまな種類のゲームを切り替えられるようにします。お読みいただきありがとうございました。
ではまた