駒の定義
前提知識: 座標系の定義
このページの要点
- 駒は PieceType(駒種: 0〜15)と Piece(駒+先後: 0〜31)の 2 階層で表現する
- 成り変換は +8 オフセット(bit 3 を立てるだけ)で実現し、条件分岐が不要
- 先後の判定は bit 4 の検査で 1 命令で完了する
- このインデックス体系は 互換であり、デバッグ時に他エンジンの値と直接比較できる
なぜ 2 階層か
将棋では「駒の種類だけが分かればよい場面」と「誰の駒かも分かる必要がある場面」が明確に分かれています。
- 持ち駒: 先手の持ち駒に「歩」が3枚。ここでは
PieceTypeだけで十分(先手であることは文脈で分かる) - 盤上の駒: 5五に先手の飛車がある。ここでは
Piece(B_ROOK)が必要
1 つの型で両方をカバーしようとすると、持ち駒のカウントで余分な先後情報を持つか、盤上の駒で先後判定に追加コストが掛かります。 2 階層に分離することで、それぞれの場面で最小限の情報だけを扱えます。
rsshogi における駒の内部表現を解説します。 この番号体系は 参照実装の定義を参考にしています。1
2階層の駒表現
駒は PieceType(駒種)と Piece(駒+先後)の 2 階層で表現されます。
PieceType(駒種)
駒種は 0〜15 のインデックスで表現されます:
| インデックス | 定数名 | 駒種 |
|---|---|---|
| 0 | NONE | 空 |
| 1 | PAWN | 歩 |
| 2 | LANCE | 香 |
| 3 | KNIGHT | 桂 |
| 4 | SILVER | 銀 |
| 5 | BISHOP | 角 |
| 6 | ROOK | 飛車 |
| 7 | GOLD | 金 |
| 8 | KING | 玉 |
| 9 | PRO_PAWN | と金 |
| 10 | PRO_LANCE | 成香 |
| 11 | PRO_KNIGHT | 成桂 |
| 12 | PRO_SILVER | 成銀 |
| 13 | HORSE | 馬 |
| 14 | DRAGON | 龍 |
| 15 | GOLD_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 のインデックスで表現されます:
| インデックス範囲 | 定数プレフィックス | 説明 |
|---|---|---|
| 0 | NO_PIECE | 空 |
| 1〜15 | B_* | 先手の駒(例: B_PAWN = 1) |
| 17〜31 | W_* | 後手の駒(例: 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 プロトコルでは駒を以下の文字で表現します:
| PieceType | USI表記 | 成駒のUSI表記 |
|---|---|---|
| PAWN | P | +P |
| LANCE | L | +L |
| KNIGHT | N | +N |
| SILVER | S | +S |
| BISHOP | B | +B |
| ROOK | R | +R |
| GOLD | G | - |
| KING | K | - |
先後は盤面表記では大文字/小文字で区別しますが、持ち駒では区別しません。
将棋特有の考慮事項
チェスでは駒種が 6 種類(Pawn, Knight, Bishop, Rook, Queen, King)で、成りは Pawn のみがクイーンなど別の駒に変身(promotion)します。 将棋では以下の点が大きく異なります。
| 項目 | チェス | 将棋 |
|---|---|---|
| 駒種数 | 6 | 14(生駒 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 ビットに凝縮された指し手のエンコーディングと、その設計上のトレードオフを解説します。
-
YaneuraOu
types.h:219-287(Commit eb2856f) ↩