チェスアプリ開発(11) プロモーションの実装

Python プログラムで動かすフェアリーチェスアプリ開発、連載第11回です。

はチェスの特殊ルールのひとつ、アンパッサンを実装しました。

今回はポーンが昇格して強くなるプロモーションを実装していきます。


プロモーションとは

promotion は英語で昇格という意味です。

ポーンが相手の陣地の最深部(白から見れば8ランク、黒から見れば1ランク)まで進むと、ポーンとキングを除くいずれかの駒に変身します。

どの駒になるかは選択可能ですので、

選択肢を表示して選んでもらう、ユーザとの双方向的なやりとりが必要となります。


動作のプロセスは次のようになります。

  1. ポーンが最深部にたどり着く
  2. どの駒にプロモーションするか選択肢を表示する
  3. ユーザが駒を選択する
  4. 選択された駒を認識する
  5. ポーンが選択された駒に変化する

プロモーションの条件を定義

まずはプロモーションの条件を定義します。

main.py
1
class Game:
2
def __init__(self):
3
...
4
# プロモーションの条件を満たすか
5
self.prom = False
6
...
7
...
8
 
9
def main(self):
10
...
11
if None not in startpos + endpos:
12
try:
13
target = self.gameboard[startpos]
14
except:
15
self.message = "could not find piece; index probably out of range"
16
target = None
17
...
18
self.renew_gameboard(startpos, endpos, self.gameboard)
19
self.promotion(target, endpos)
20
if self.is_check(target.color, self.gameboard):
21
self.message = f"{target.color} player is in check"
22
...
23
 
24
def promotion(self, piece, endpos):
25
'''
26
プロモーションできるとき,True
27
28
Parameters
29
----------
30
piece : obj
31
駒.
32
endpos : tuple > (int, int)
33
終了位置.
34
35
Returns
36
-------
37
bool
38
'''
39
if (piece.name == 'WP' and endpos[1] == 7
40
or piece.name == 'BP' and endpos[1] == 0):
41
self.prom = True

動くたびにプロモーションができる状態かどうかを判定し、その結果を変数に入れて管理します。


選択肢を表示する吹き出しを描画

プロモーションするポーンから吹き出しをのばして、その吹き出しの中に選択肢となる駒を描画することにします。

glVertex()で指定しているのはワールド座標であり、このプログラムでは縦横軸ともに -1.0 ~ 8.0 の値をとるように調整しています()。

utils.py
1
def draw_balloon(x, y):
2
'''
3
プロモーションのときの吹き出しを描画する
4
5
Parameters
6
----------
7
x, y : int
8
駒の座標.
9
'''
10
glColor(0.5, 0.5, 0.5) # 色の指定
11
glBegin(GL_QUADS) # 四角形を描画
12
glVertex(1.0, 2.5)
13
glVertex(1.0, 4.5)
14
glVertex(6.0, 4.5)
15
glVertex(6.0, 2.5)
16
glEnd()
17
glBegin(GL_TRIANGLES) # 三角形を描画
18
glVertex(3.0, 3.5)
19
glVertex(4.0, 3.5)
20
glVertex(x, y)
21
glEnd()

プロモーションできる状態にある、すなわちさきほど設定した変数self.promTrueとなるときに、この吹き出しを描画させます。

main.py
1
def draw(self):
2
...
3
# 可能な移動先の表示
4
if self.select_dest and None not in self.startpos:
5
piece = self.gameboard[self.startpos]
6
draw_available_moves(
7
self.valid_moves(piece, self.startpos, self.gameboard),
8
opponent=self.playersturn != piece.color)
9
# プロモーション
10
if self.prom:
11
draw_balloon(*self.endpos)
12
glDisable(GL_BLEND)
13
glutSwapBuffers()

ここでいったん動作を見てみましょう。

プロモーション 吹き出し

選択肢として4つの駒をこの吹き出しの上に並べて乗せることになります。

駒の描画はdraw_img()を使えばできます(定義はにて)。

main.py
1
def draw(self):
2
...
3
# プロモーション
4
if self.prom:
5
draw_balloon(*self.endpos)
6
piece_color = self.gameboard[self.endpos].color
7
glEnable(GL_TEXTURE_2D)
8
draw_img(2.0, 3.5, piece_ID[piece_color + 'N'])
9
draw_img(3.0, 3.5, piece_ID[piece_color + 'B'])
10
draw_img(4.0, 3.5, piece_ID[piece_color + 'R'])
11
draw_img(5.0, 3.5, piece_ID[piece_color + 'Q'])
12
glDisable(GL_TEXTURE_2D)
13
...

ちなみに、piece_colorはこの時点ではwhiteblackになってしまうので、エラーになってしまいます。

駒色の文字列を変更します。

pieces.py
1
WHITE = "white"
2
WHITE = "W"
3
BLACK = "black"
4
BLACK = "B"

これで選択肢を表示することができました。

プロモーション 吹き出し 駒あり

選択された駒を認識

どれがクリックされたのかを認識させます。

特定の範囲内をクリックしたときにTrueとなるような関数を作ります。

utils.py
1
def on_square(x, y, left, right, bottom, top):
2
'''
3
left < x < right かつ bottom < y < top のとき,True
4
5
Parameters
6
----------
7
x, y : float
8
測定する座標.
9
left, right, bottom, top : float
10
ボタンの左右下上端の座標.
11
12
Returns
13
-------
14
bool
15
'''
16
if left < x < right and bottom < y < top:
17
return True

これを使って、マウスでクリックした位置をもとに、どの駒が選択されたか認識し、

選択された駒のを生成してポーンと置き換えます。

main.py
1
def mouse(self, button, state, x, y):
2
...
3
self.mousepos = window2world(x, y, WSIZE)
4
...
5
# 駒選択
6
elif (self.parse_mouse() in self.gameboard
7
and not self.prom):
8
...
9
# プロモーション
10
if self.prom:
11
piece_color = self.gameboard[self.endpos].color
12
if on_square(*self.mousepos, 1.5, 2.5, 3.0, 4.0):
13
self.gameboard[self.endpos] = Knight(piece_color, piece_color + 'N')
14
self.prom = False
15
if on_square(*self.mousepos, 2.5, 3.5, 3.0, 4.0):
16
self.gameboard[self.endpos] = Bishop(piece_color, piece_color + 'B')
17
self.prom = False
18
if on_square(*self.mousepos, 3.5, 4.5, 3.0, 4.0):
19
self.gameboard[self.endpos] = Rook(piece_color, piece_color + 'R')
20
self.prom = False
21
if on_square(*self.mousepos, 4.5, 5.5, 3.0, 4.0):
22
self.gameboard[self.endpos] = Queen(piece_color, piece_color + 'Q')
23
self.prom = False

これでプロモーションが実装できました!

プロモーション 完成

今回の変更も GitHub に置いておきます。

Release codes after 11 articles · midorimici/chess-program-for-python-and-OpenGL


はついに最後の特殊ルール、キャスリングを実装していきます。

お読みいただきありがとうございました。

ではまた👋