中国将棋のルールで間違えやすい四箇所
馬の足止め、象の目塞ぎ、砲の台越し、両王対面。着手生成を幾何層と合法層に分けることで、間違いの大半は消えました。
中国将棋 は五目並べより一桁複雑で、その複雑さは例外にあります。駒ごとに例外が一つあり、互いに独立で、一つ漏らしてもエラーにはならず、ただ AI が愚かな手を指すだけです。
生成を二層に分けたことで、間違いの大半は消えました。
二層:幾何層と合法層
| 層 | 関数 | 担当 |
|---|---|---|
| 幾何 | pseudoMoves |
駒の動きと、何に塞がれるか |
| 合法 | legalMoves |
指した後、自玉は安全か |
合法層は愚直ですが堅牢です。候補手を一旦指してみて、自玉が狙われていないか、両王が対面していないかを確認し、駄目なら捨てます。
全体制約は一箇所に集めて通す方が、駒ごとに「これで自玉が晒されないか」を書き回すより信頼できます。
中国将棋に「自玉を晒してはならない」という独立したルールはありません。「自玉が取られてはいけない」から従う帰結です。こうした全体制約は事後フィルタに向いています。
落とし穴一:馬の足
馬は L 字に動きますが、隣接する正交のマスに駒(敵味方問わず)があると、その方向へは動けません。八方向それぞれに足があります。
const HORSE = [
[-2, -1, -1, 0], [-2, 1, -1, 0],
[2, -1, 1, 0], [2, 1, 1, 0],
[-1, -2, 0, -1], [1, -2, 0, -1],
[-1, 2, 0, 1], [1, 2, 0, 1],
];
各項は「移動先の差分 + 足の差分」です。八つの if ではなく表にするのは、足と移動先が必ず対になるためです。分けて書くと、二方向が誤った足を共有しやすくなります。
試験方法:空の盤面で馬は 8 手。四つの足を一つずつ塞ぐと、毎回 6 手になるはずです。少なければ対応が誤り、多ければ塞ぐ場所が誤りです。
落とし穴二:象の目
象は田字(斜め二マス)に動き、中間のマス(象の目)に駒があると動けません。加えて河を渡れません。紅の象は 5〜9 行に留まります。
私が最初に誤解したのは「前進しかできない」という点です。行き先が自陣であれば、自軍の底線へ戻ることもできます。判定は ownHalf(side, row) で、「前向きかどうか」ではありません。
落とし穴三:砲の台越し
砲の動きは二段構えで、全体で最も間違えやすい箇所です。
- 取らない時は車と同じ:直線上、空きマスはどこでも、最初の駒の手前で止まる。
- 取る時は台が一つ必要:最初の駒を飛び越え、その次に現れる駒だけを取れる。
// 一段目:台より手前の空きマス
while (inBoard(nr, nc) && !board[index(nr, nc)]) {
out.push({ from: i, to: index(nr, nc), captured: null });
nr += dr; nc += dc;
}
// 台を飛び越える
if (!inBoard(nr, nc)) continue;
nr += dr; nc += dc;
// 二段目:台の次に現れる駒
while (inBoard(nr, nc)) {
const t = board[index(nr, nc)];
if (t) {
if (t.side !== side) out.push({ from: i, to: index(nr, nc), captured: t });
break;
}
nr += dr; nc += dc;
}
重要なのは「何を取れるか」だけでなく、台より奥の空きマスにも行けないことです。私は当初この break を落とし、砲が台の奥の空きマスへ羽を生やしたように飛んでいました。
落とし穴四:両王対面
二人の王が同じ列で、間に何も無い状態は作れません。次の手番の側が両王を晒すことになるからです。実装は王手と同じで、指してから判定します。
この制約は手作りのテスト局面も壊します。うっかり両王を同じ列に置くと局面全体が非合法になり、legalMoves が空配列を返して壊れたように見えます。デバッグで実際に一巡を無駄にし、今は断言に「基準局面では両王を別列に置くこと」というコメントを残しています。
王手判定にも四つの場合
王手かどうかは、相手の指し手を列挙せず、王の位置から逆算します。
| 駒 | 判定方法 |
|---|---|
| 車 | 四方向で最初に現れる駒 |
| 砲 | 四方向で最初の駒を越えた次に現れる駒 |
| 王 | 四方向で最初に現れる駒(対面) |
| 馬 | 八つの馬位置に馬がおり、足が塞がれていない |
馬は向きに注意します。馬が (nr, nc)、王が (kr, kc) のとき、足は王に面した馬の隣のマス、すなわち (nr + lr, nc + lc) で、(lr, lc) は着手生成と同じ表の値です。正引きと逆引きが一つの表を共有できるのも、if ではなくデータにした利点です。
AI:まず一手詰めを見る
中国将棋の王は 3×3 の宮に閉じ込められており、一手詰めは五目並べよりずっと頻繁に起きます。浅い探索はこれを逃すので、根の候補を一つずつ指してみて、相手に応手が無いか調べます。無ければ探索せず即座に返します。
副次的な効果として、必殺手の評価は確定値になるため、画面に 90000 と出れば詰みだと一目で分かります。
棋譜
中国語の棋譜(炮二平五)も実装しました。規則は整っていて、紅は右から一〜九、黒は左から 1〜9 と数え、進/退/平で前進・後退・横移動を表します。
私は「読める」までで止め、同一列に二枚ある場合の前/後の区別は省きました。専用ソフトの仕事で、玩具に求めるものではありません。

コメント
…