見出し画像

■将棋を作る(4) 王手、詰み

進捗

 1か月以上間が空きましたがコツコツ制作を進め、前回から主に以下を実装しました。
①棋譜データの作成
②王手の判定
③詰みの判定
 今回はちょっとプログラム的な考え方も交えて記事を書いてみます。

①棋譜データの作成

 棋譜データよりも先に王手の判別を作っていましたが、ある理由により棋譜データを先に作った方が良いと感じたため、先に棋譜データを作ることにしました。
 王手を放置したり、ある駒を動かすと自分の王が取られてしまう場合などは警告メッセージを表示して、打った手を元に戻すようにしました。この「元に戻す」処理が意外と煩わしく、元々どこにあった駒で移動先はどこか、誰の駒か、駒が成ったか成らないか、盤上の駒を指したのか、持ち駒を使ったのか、…等々色んな情報を持っておく必要がありました。そこで棋譜データを持っておき、元に戻す際は棋譜データを1手目から遡り、1手前の状態まで復元する方法で実現できました。この履歴を遡る方法は以下の動画を参考にさせて頂きました。

 この方の他の動画も、今回の将棋作りにとても参考になりました。感謝!m(_ _)m
 ちなみに棋譜データをログ出力した内容はこんな感じです。

[1, 6, 2, 5, 2, 8, 0, 0, 0, 9, 0]☗先手 / 7七 → 7六 / 歩 / -- / 非成 / 1P / 取得した駒 = なし / 盤上の駒
[0, 2, 6, 3, 6, 8, 0, 0, 1, 9, 0]☖後手 / 3三 → 3四 / 歩 / -- / 非成 / 2P / 取得した駒 = なし / 盤上の駒
[1, 7, 1, 1, 7, 3, 1, 1, 0, 3, 0]☗先手 / 8八 → 2二 / 馬 / 成 / 成 / 1P / 取得した駒 = 角 / 盤上の駒
[0, 0, 6, 1, 7, 5, 0, 0, 1, 3, 0]☖後手 / 3一 → 2二 / 銀 / -- / 非成 / 2P / 取得した駒 = 角 / 盤上の駒
[1, -1, -1, 4, 5, 3, 0, 0, 0, 9, 1]☗先手 / -- → 4五 / 角 / -- / 非成 / 1P / 取得した駒 = なし / 持ち駒
[0, 0, 3, 1, 3, 4, 0, 0, 1, 9, 0]☖後手 / 6一 → 6二 / 金 / -- / 非成 / 2P / 取得した駒 = なし / 盤上の駒
[1, 4, 5, 3, 6, 3, 0, 0, 0, 8, 0]☗先手 / 4五 → 3四 / 角 / -- / 非成 / 1P / 取得した駒 = 歩 / 盤上の駒

 上から1手目、2手目、…、7手目のデータです。左の数字の羅列が実際にプログラムで扱っている棋譜データ、右の日本語は棋譜データを見易くしたものです。棋譜データの内容は、左から順に以下となっています。
 1.先手・後手(1:先手/0:後手)
 2.移動元の段(0 ~ 8、持ち駒を使った場合移動元は無いので-1)
 3.移動元の筋(0 ~ 8、持ち駒を使った場合移動元は無いので-1)
 4.移動先の段(0 ~ 8)
 5.移動先の筋(0 ~ 8)
 6.駒のID
 7.成りの種類(0:成れない/1:不成→成に変わった/2:成れるが不成のまま)
 8.成り(1:成っている/0:成っていない)
 9.プレイヤーID
 10.取った駒の駒ID
 11.持ち駒を使ったか(1:持ち駒/0:盤上の駒)

 「2」~「5」は将棋の盤面(マス)の情報で、0から始まるのはプログラム上の都合です。盤面のマスはプログラムと画面表示で数え方が異なるため、バグ修正などの最中によく混乱しました…。

マスの数え方の違い

 「7」の成りの種類は、棋譜の表示・読み上げを作る想定で実装しましたが、今のところ使ってないような…。
 棋譜について以下のサイトや日本将棋連盟のサイトを調べた結果、ちょっと時間が掛かりそうなので今回は実装を見送りました(´-ω-`)

②王手の判定

 王手の判定では、王手をかける側、かけられる側で以下をチェックしています。
■王手をかける側
・指した盤上の駒(または持ち駒)の移動可能マスに相手の王があるか
・指した駒以外の自駒のうち、移動可能マスに相手の王があるか(空き王手)上記で王がある場合、指した駒の位置を保持しておき、以下のように王手の種類(?)を判別し易くしています。王手をかけた駒の位置が
 0箇所 → 王手が掛かっていない
 1箇所 → 単体の王手
 2箇所 → 両王手
※将棋のルール上、3箇所以上から同時に王手が掛かる事は無いそうです。
■王手をかけられる側
 駒を指した後、自駒の王に利いている駒がある場合(=王手の放置)、警告メッセージを表示し、指した手を元に戻します。王手は掛けられていないが、駒を移動することで王が取られてしまう場合も同様です。
 駒を指す前に王が取られるかどうかを事前にチェックする方法で実装しようとしましたが、まだ駒を置いていない(盤面が確定していない)状態でチェックするには実装が煩わしくバグも出やすいため、一旦盤面を確定した後にチェックするようにしました。

③詰みの判定

 今回の将棋作成で、詰みの判定が一番難しかったように思います。
ざっくり言うと”何をしても次の手で王が取られるかを判別する”だけですが、いざ実装すると今までのプログラムに結構修正が入りました。
 詰みの判定でチェックが必要なのは、以下の3点です。
  1.王の逃げ場があるか
  2.王手をかけた駒を取ることができる駒があるか
  3.合駒ができるか
これらのいずれも不可能な場合、詰みとしています。両王手の場合、「1」が不可能な時点で詰みとなります。

1.王の逃げ場があるか

 要は「相手の駒が利いていないマスに移動できるか」ですね。これには
・「王の移動可能なマス」
・「相手の駒の移動可能なマス(=利きがあるマス)」
の情報が必要になります。例えば以下の場合、王の逃げ場は緑色のマスの3箇所になります。

王の逃げ場

 オレンジが敵の駒が移動できるマス、紫が王と相手の駒の移動可能な範囲が被るマスです。緑のマスが1箇所でもあれば、その時点で詰みにはならない、と判断できます。
 ここでプログラミングで少しつまずいたのが、以下のような場合です。

プログラミングでつまずいた例

 例1、例2はどちらも詰みの状態です。
 例1は王が5二金を取ると次の手で5三金に取られてしまいますが、王の逃げ場(5二玉)があると判断して詰みになりませんでした。例2についても1二玉と逃げ場があるので同じく詰みにならず…。
 「なんでじゃ!」とデバッグしてみると、移動可能なマスが意図しないものになっていました。下の図の緑が、例1の5三金と例2の1五香の移動可能なマスです。

移動可能なマス

 この移動可能なマスは、将棋のルール上は確かに正しいですが、詰みの判定の際は例外として5二金や1一香~1二香も範囲に含める必要がありました。そこで詰みのチェックをする場合、以下のようにプログラムを修正したら、ちゃんと詰み判定になりました。
・走り駒(香車、角(馬)、飛車(龍))の場合、走る方向(?)に何か駒がある
 → 駒が相手の王の場合は、王がいないものとして扱う。
   相手の王以外の場合は、ルールに沿った移動可能範囲とする。
・走り駒以外の場合、移動可能なマスに自分の駒があってもそこを範囲に含める。

プログラム修正後

2.王手をかけた駒を取ることができる駒があるか

 これは特に難しい事は無く実装できました。自分の駒の移動可能なマスを1つ1つ調べていき、「②王手の判定」で記載した「王手をかけた駒の位置」を含んでいる場合、王手をかけた駒をとることができる、と判断できます。
 このケースの場合、チェックするのは盤上の駒のみで、持ち駒については考慮する必要がありませんね。

3.合駒ができるか

 合駒は、王手をかけた駒と王の間に駒を指すため、チェックするのは必然的に走り駒のみになります。また、王手をかけたのが走り駒であっても、王との間にマスが無ければ合駒は不可能となります。以下の水色は合駒できるマスです。この水色のマスに指せる盤上の駒(または持ち駒)がある場合、詰みになりません。

合駒できる例

 ただし将棋のルールで「二歩」があり、これは盤上に歩がある筋(縦の列)に持ち駒の歩は指せない、というものです。「と金」がある筋には持ち駒の歩を指すことができます。
 合駒ができるかチェックする際、この「二歩」もチェックしています。なので、例えば持ち駒が歩しかない状態では、以下は詰みになります。

歩で合駒できない例

 ここまでのチェックで詰みと判定された場合でも、最後の一手が「持ち駒の歩を指した」場合は打ち歩詰めのルールにより禁止されているため、詰みになりません。

最後に

 ここまで実装してようやく将棋として最低限の動きはできたかな、と思います(千日手のチェックは機会があれば…)。書きたかったプログラム的な話も書けたので、ここで一旦記事を区切りたいと思います。
 今までアクションやらRPGを作ってきましたが、ボードゲームも作ってみると楽しかった!作ってるうちに、駒に体力や攻撃力のパラメータを持たせたり、毒や睡眠状態になったり、盤面に何かギミックを入れてみたりと考えましたが、普通に将棋を作りました(笑)
 しばらくメインのゲーム制作も止まってたし、他の記事も書きたいのでここで区切りますm(_ _)m
 いい気分転換になった(´ー`)

この記事が気に入ったらサポートをしてみませんか?