中国将棋のルールで間違えやすい四箇所

馬の足止め、象の目塞ぎ、砲の台越し、両王対面。着手生成を幾何層と合法層に分けることで、間違いの大半は消えました。

中国将棋 は五目並べより一桁複雑で、その複雑さは例外にあります。駒ごとに例外が一つあり、互いに独立で、一つ漏らしてもエラーにはならず、ただ 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) で、「前向きかどうか」ではありません。

落とし穴三:砲の台越し

砲の動きは二段構えで、全体で最も間違えやすい箇所です。

  1. 取らない時は車と同じ:直線上、空きマスはどこでも、最初の駒の手前で止まる。
  2. 取る時は台が一つ必要:最初の駒を飛び越え、その次に現れる駒だけを取れる。
// 一段目:台より手前の空きマス
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 と数え、進/退/平で前進・後退・横移動を表します。

私は「読める」までで止め、同一列に二枚ある場合の前/後の区別は省きました。専用ソフトの仕事で、玩具に求めるものではありません。

← 記事一覧に戻る

コメント

…