Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

駒の定義

前提知識: 座標系の定義

このページの要点

  • 駒は PieceType(駒種: 0〜15)と Piece(駒+先後: 0〜31)の 2 階層で表現する
  • 成り変換は +8 オフセット(bit 3 を立てるだけ)で実現し、条件分岐が不要
  • 先後の判定は bit 4 の検査で 1 命令で完了する
  • このインデックス体系は 互換であり、デバッグ時に他エンジンの値と直接比較できる

なぜ 2 階層か

将棋では「駒の種類だけが分かればよい場面」と「誰の駒かも分かる必要がある場面」が明確に分かれています。

  • 持ち駒: 先手の持ち駒に「歩」が3枚。ここでは PieceType だけで十分(先手であることは文脈で分かる)
  • 盤上の駒: 5五に先手の飛車がある。ここでは PieceB_ROOK)が必要

1 つの型で両方をカバーしようとすると、持ち駒のカウントで余分な先後情報を持つか、盤上の駒で先後判定に追加コストが掛かります。 2 階層に分離することで、それぞれの場面で最小限の情報だけを扱えます。

rsshogi における駒の内部表現を解説します。 この番号体系は 参照実装の定義を参考にしています。1

2階層の駒表現

駒は PieceType(駒種)と Piece(駒+先後)の 2 階層で表現されます。

PieceType(駒種)

駒種は 0〜15 のインデックスで表現されます:

インデックス定数名駒種
0NONE
1PAWN
2LANCE
3KNIGHT
4SILVER
5BISHOP
6ROOK飛車
7GOLD
8KING
9PRO_PAWNと金
10PRO_LANCE成香
11PRO_KNIGHT成桂
12PRO_SILVER成銀
13HORSE
14DRAGON
15GOLD_LIKE金相当(金・と金・成香・成桂・成銀をまとめて扱う内部補助種)

成り変換: 成駒は元の駒に +8 を加えたインデックスです:

piece_type.promote() = PieceType(piece_type.0 + 8)

実装: PieceType 構造体

/// 駒種を表す型(先手/後手の区別なし)
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct PieceType(i8);

const _: () = {
    assert!(core::mem::size_of::<PieceType>() == 1);
    assert!(core::mem::align_of::<PieceType>() == 1);
};

ソースコード

実装: 駒種定数

    // 関連定数
    // 非成駒定数
    /// 無効な駒種
    pub const NONE: Self = Self(0);
    /// 歩
    pub const PAWN: Self = Self(1);
    /// 香車
    pub const LANCE: Self = Self(2);
    /// 桂馬
    pub const KNIGHT: Self = Self(3);
    /// 銀
    pub const SILVER: Self = Self(4);
    /// 角
    pub const BISHOP: Self = Self(5);
    /// 飛車
    pub const ROOK: Self = Self(6);
    /// 金
    pub const GOLD: Self = Self(7);
    /// 玉
    pub const KING: Self = Self(8);

    // 成駒定数
    /// と金(成歩)
    pub const PRO_PAWN: Self = Self(9);
    /// 成香
    pub const PRO_LANCE: Self = Self(10);
    /// 成桂
    pub const PRO_KNIGHT: Self = Self(11);
    /// 成銀
    pub const PRO_SILVER: Self = Self(12);
    /// 馬(成角)
    pub const HORSE: Self = Self(13);
    /// 龍(成飛)
    pub const DRAGON: Self = Self(14);
    /// 金相当の駒(GOLD_LIKE)
    pub const GOLD_LIKE: Self = Self(15);

    // メタ定数
    /// 駒種の総数
    pub const COUNT: usize = 16;
    /// 成り変換用オフセット
    pub const PIECE_TYPE_PROMOTE: i8 = 8;
    /// 持ち駒の起点(PAWN)
    pub const PIECE_HAND_ZERO: Self = Self(1);
    /// 持ち駒種類数
    pub const HAND_TABLE_SIZE: usize = 8;

ソースコード

実装: 成り変換メソッド

    /// 成れる駒かどうかを判定
    #[must_use]
    pub const fn is_promotable(self) -> bool {
        self.0 >= Self::PAWN.0 && self.0 <= Self::ROOK.0
    }

    /// 成り駒かどうかを判定
    #[must_use]
    pub const fn is_promoted(self) -> bool {
        self.0 >= Self::PRO_PAWN.0
    }

    /// これ以上成れない駒かどうかを判定
    #[must_use]
    pub const fn is_unpromotable(self) -> bool {
        self.0 >= Self::GOLD.0
    }

    /// 成り駒に変換
    #[must_use]
    pub const fn promote(self) -> Self {
        if self.is_promotable() { Self(self.0 + Self::PIECE_TYPE_PROMOTE) } else { self }
    }

    /// 成り駒を元の駒に戻す(持ち駒変換用)
    #[must_use]
    pub const fn demote(self) -> Self {
        match self.0 {
            9..=14 => Self(self.0 - Self::PIECE_TYPE_PROMOTE), // 成駒を元に戻す
            _ => self,                                         // それ以外はそのまま
        }
    }

ソースコード

Piece(駒+先後)

先後の区別を含む駒は 0〜31 のインデックスで表現されます:

インデックス範囲定数プレフィックス説明
0NO_PIECE
1〜15B_*先手の駒(例: B_PAWN = 1)
17〜31W_*後手の駒(例: W_PAWN = 17)

先後フラグ: 後手の駒は先手の駒に +16 を加えたインデックスです:

piece = Piece(piece_type.0 + color.0 * 16)

実装: Piece 構造体

/// 駒を表す型(先手/後手の区別あり)
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Hash)]
pub struct Piece(i8);

const _: () = {
    assert!(core::mem::size_of::<Piece>() == 1);
    assert!(core::mem::align_of::<Piece>() == 1);
};

ソースコード

実装: 駒定数

    // 関連定数
    // 特殊値
    /// 無効な駒
    pub const NONE: Self = Self(0);

    // 先手駒定数
    /// 先手の歩
    pub const B_PAWN: Self = Self(1);
    /// 先手の香
    pub const B_LANCE: Self = Self(2);
    /// 先手の桂
    pub const B_KNIGHT: Self = Self(3);
    /// 先手の銀
    pub const B_SILVER: Self = Self(4);
    /// 先手の角
    pub const B_BISHOP: Self = Self(5);
    /// 先手の飛車
    pub const B_ROOK: Self = Self(6);
    /// 先手の金
    pub const B_GOLD: Self = Self(7);
    /// 先手の玉
    pub const B_KING: Self = Self(8);
    /// 先手のと金
    pub const B_PRO_PAWN: Self = Self(9);
    /// 先手の成香
    pub const B_PRO_LANCE: Self = Self(10);
    /// 先手の成桂
    pub const B_PRO_KNIGHT: Self = Self(11);
    /// 先手の成銀
    pub const B_PRO_SILVER: Self = Self(12);
    /// 先手の馬
    pub const B_HORSE: Self = Self(13);
    /// 先手の龍
    pub const B_DRAGON: Self = Self(14);
    /// 先手の金相当駒(GOLD_LIKE)
    pub const B_GOLD_LIKE: Self = Self(15);

    // 後手駒定数
    /// 後手の歩
    pub const W_PAWN: Self = Self(17);
    /// 後手の香
    pub const W_LANCE: Self = Self(18);
    /// 後手の桂
    pub const W_KNIGHT: Self = Self(19);
    /// 後手の銀
    pub const W_SILVER: Self = Self(20);
    /// 後手の角
    pub const W_BISHOP: Self = Self(21);
    /// 後手の飛車
    pub const W_ROOK: Self = Self(22);
    /// 後手の金
    pub const W_GOLD: Self = Self(23);
    /// 後手の玉
    pub const W_KING: Self = Self(24);
    /// 後手のと金
    pub const W_PRO_PAWN: Self = Self(25);
    /// 後手の成香
    pub const W_PRO_LANCE: Self = Self(26);
    /// 後手の成桂
    pub const W_PRO_KNIGHT: Self = Self(27);
    /// 後手の成銀
    pub const W_PRO_SILVER: Self = Self(28);
    /// 後手の馬
    pub const W_HORSE: Self = Self(29);
    /// 後手の龍
    pub const W_DRAGON: Self = Self(30);
    /// 後手の金相当駒(GOLD_LIKE)
    pub const W_GOLD_LIKE: Self = Self(31);

    // メタ定数
    /// 駒の総数
    pub const COUNT: usize = 32;
    /// 成りフラグ
    pub const PROMOTE_OFFSET: i8 = 8;
    /// 後手フラグ
    pub const COLOR_BIT: i8 = 16;
    /// 持ち駒種類数
    pub const HAND_TABLE_SIZE: usize = 8;
    /// 非成駒の終端
    pub const RAW_COUNT: usize = 8;

ソースコード

エンコーディングの構造

Piece のエンコーディングはビットフラグで整理できます:

Piece = [先後フラグ(bit 4)] | [駒種(bit 0-3)] └ bit 3 が成りフラグ(生駒 +8 で立つ)

例:
B_PAWN   = 0b00001 = 1   (先手・歩)
B_HORSE  = 0b01101 = 13  (先手・馬 = 成角)
W_PAWN   = 0b10001 = 17  (後手・歩)
W_DRAGON = 0b11110 = 30  (後手・龍 = 成飛)

駒種は bit 0-3 の 4 ビットPiece::piece_type()self.0 & 15 で取得)、先後は bit 4& 0b10000)です。 成りフラグは独立したビットではなく、4 ビットの駒種フィールドのうち bit 3(= +8)が成りを表します。

この構造により、以下のアクセサが簡潔になります:

実装: 駒アクセサ

    /// 手番と駒種から駒を作成
    #[must_use]
    pub const fn from_parts(color: Color, piece_type: PieceType) -> Self {
        let base = piece_type.0;
        if color.raw() == Color::WHITE.raw() { Self(base | Self::COLOR_BIT) } else { Self(base) }
    }

    /// 駒の手番を取得
    #[must_use]
    pub const fn color(self) -> Color {
        if (self.0 & Self::COLOR_BIT) != 0 { Color::WHITE } else { Color::BLACK }
    }

    /// 駒種を取得
    #[must_use]
    pub const fn piece_type(self) -> PieceType {
        PieceType::new(self.0 & 15)
    }
    /// 空(駒なし)かどうかを判定
    #[inline]
    #[must_use]
    pub const fn is_empty(self) -> bool {
        self.0 == 0
    }

ソースコード

ビット操作による最適化

なぜ金を飛車の後ろに配置するのか

駒種のインデックス配置は、成り処理の効率化を考慮して設計されています:

1: PAWN   →  9: PRO_PAWN    (+8)
2: LANCE  → 10: PRO_LANCE   (+8)
3: KNIGHT → 11: PRO_KNIGHT  (+8)
4: SILVER → 12: PRO_SILVER  (+8)
5: BISHOP → 13: HORSE       (+8)
6: ROOK   → 14: DRAGON      (+8)
7: GOLD   (成れない)
8: KING   (成れない)

この配置により、成り処理が単純なビット演算で実現できます:

// 成る: bit 3 を立てる(+8)
promoted_piece = piece | 8;

// 元の駒に戻す(持ち駒変換用): bit 3 を落とす
demoted_piece = piece & !8;

もし金が歩の直後(インデックス2)に配置されていた場合、成り判定で複雑な条件分岐が必要になります。 金を後ろに配置することで、「成れる駒(1-6)」と「成れない駒(7-8)」の境界が明確になります。

KINGを8にする理由

玉を8番目に配置する理由も、ビット操作による効率化です:

// 玉かどうかの判定
if piece_type == 8 {
    // 玉の処理
}

// より効率的: bit 3 だけをチェック
if (piece_type & 8) != 0 && piece_type < 9 {
    // 成れない駒(金=7または玉=8)
}

この設計により、多くの条件判定がビット演算で高速に実行できます。

USI 表記

USI プロトコルでは駒を以下の文字で表現します:

PieceTypeUSI表記成駒のUSI表記
PAWNP+P
LANCEL+L
KNIGHTN+N
SILVERS+S
BISHOPB+B
ROOKR+R
GOLDG-
KINGK-

先後は盤面表記では大文字/小文字で区別しますが、持ち駒では区別しません。

将棋特有の考慮事項

チェスでは駒種が 6 種類(Pawn, Knight, Bishop, Rook, Queen, King)で、成りは Pawn のみがクイーンなど別の駒に変身(promotion)します。 将棋では以下の点が大きく異なります。

項目チェス将棋
駒種数614(生駒 8 + 成駒 6)
成りPawn のみ → 別駒種に変身金・玉以外の6種 → 元の駒種 +8
持ち駒なしあり → PieceType だけで管理
先後の駒配列サイズ12(6×2)32(16×2、うち index 16 は未使用)

この複雑さが 2 階層構造と +8 成りオフセットの設計を導いています。

落とし穴

NO_PIECE (0) と B_PAWN (1) の境界

board[sq] を初期化し忘れると値が 0 になり、NO_PIECE(空マス)と解釈されます。 逆に board[sq] == NO_PIECE のチェックを忘れて駒の種類を取得すると、不正な PieceType(0) が返ります。

成駒の demote 忘れ

駒を取って持ち駒に加える際、成駒を元の駒種に戻す(demote)必要があります。

// ❌ 間違い: 成銀を持ち駒に加えてしまう
hand.add(captured.piece_type());  // PRO_SILVER (12) が持ち駒に入る

// ✅ 正しい: 生駒に戻してから加える
hand.add(captured.piece_type().demote());  // SILVER (4) が持ち駒に入る

Piece のインデックス 16 は未使用

Piece のインデックス空間は 0〜31 ですが、16 は使用されていません(W_PAWN は 17)。 配列のサイズを Piece::NUM(= 32)で確保すれば問題ありませんが、 「後手の駒は先手 +16」というルールから W_NO_PIECE = 16 が存在しうる点に注意してください。

まとめ

  • PieceType: 駒種(0〜15)。持ち駒の管理やインデックスに使用
  • Piece: 駒種+先後(0〜31)。盤面の各マスに格納
  • 成り変換: +8(bit 3 を立てる)だけで完了。分岐不要
  • 先後判定: bit 4 のチェック 1 回で完了
  • インデックス設計は 互換で、既存エンジンの知見をそのまま活用可能

次に読む

指し手の表現: 16 ビットに凝縮された指し手のエンコーディングと、その設計上のトレードオフを解説します。



  1. YaneuraOu types.h:219-287Commit eb2856f