KIF フォーマット仕様
KIF(柿木形式)は、日本の将棋ソフトで最も広く使われているテキスト棋譜フォーマットである。 人間にとっての読みやすさと、移動元座標、消費時間、変化手順を含む構造的な情報量を両立している。
基本構造と文字コード
KIF ファイルは以下の要素を上から順に並べて構成する。
- ヘッダ行:
キー:値形式で対局情報を記述する。 - 初期局面:平手以外の駒落ちや任意局面の場合、手合割または BOD 形式の盤面図を記述する。
- 本手順セクション:
手数----指手---------消費時間--の見出し行に続けて、1 手 1 行で指し手とコメントを記述する。 - 変化手順セクション(任意):
変化:N手の見出し行から始まる分岐手順を記述する。
文字コード
従来ソフトとの互換性のため .kif(Shift_JIS / cp932)と .kifu(UTF-8)の双方が使われる。
rsshogi のバイト列パーサ(Rust の parse_kif_bytes、Python の Record.from_kif_file)は、UTF-8 BOM の有無および UTF-8 / Shift_JIS(Windows-31J / cp932)を自動判別してデコードする。
書き出し時は UTF-8 がデフォルトであり、オプション指定により Shift_JIS(cp932)での出力にも対応する。
1. ヘッダとメタデータ
ヘッダ行は全角コロン : または半角コロン : でキーと値を区切る。
行頭が # で始まる行はファイルコメント行として無視される。
rsshogi が RecordMetadata の専用フィールドへマッピングする標準キーは以下の通りである。
| KIF キー | RecordMetadata の対応先 | 説明 |
|---|---|---|
先手 / 下手 | black_player | 先手(または駒落ちの下手)の対局者名。 |
後手 / 上手 | white_player | 後手(または駒落ちの上手)の対局者名。 |
棋戦 | event | 棋戦名または大会名。 |
場所 | site | 対局場所。 |
開始日時 / 開始日 | start_date | 対局開始日時(開始日時 と 開始日 の双方が start_date に格納される)。 |
終了日時 / 終了日 | end_date | 対局終了日時。 |
持ち時間 | time_control | 共通の持ち時間設定。 |
先手持ち時間 | black_time_control | 先手の個別持ち時間設定。 |
後手持ち時間 | white_time_control | 後手の個別持ち時間設定。 |
最大手数 | max_moves | 最大手数(u32)。 |
持将棋 | impasse_rule | 持将棋ルール。 |
備考 | comment | 棋譜全体に対する備考テキスト。 |
表に含まれないキー(手合割、戦型、対局日、表題、作品番号 など)は、RecordMetadata のカスタム属性(Rust では attributes()、Python では custom 辞書)に格納され、KIF 再出力時に復元される(Python の Record.opening プロパティは custom["戦型"] または custom["OPENING"] を参照する)。
持ち時間は 10分加算5秒、10分+5秒加算、10分+1手5秒加算 の加算表記を読み込む。
15分(1手10秒加算) と全角の + にも対応し、10分(加算なし) や 10分加算 では基本時間を保持する。
基本時間に秒端数がある場合は、base+byoyomi+increment 表記で出力して秒読みや加算と区別する。
秒読みは 10分秒読み30秒 のように区別する。
基本時間を秒に換算した値、秒読み、加算のいずれかが u32 の範囲を超える場合、構造化された time_control は設定しない。
元のヘッダー文字列はカスタム属性に保持し、KIF と JKF の再出力に使う。
2. 初期局面の指定
手合割 に標準の駒落ち(平手、香落ち、右香落ち、角落ち、飛車落ち、飛香落ち、二枚落ち、三枚落ち、四枚落ち、五枚落ち、左五枚落ち、六枚落ち、八枚落ち、十枚落ち)が指定され、かつ盤面図が存在しない場合は、その手合割の初期局面が使用される。
詰将棋や途中局面開始などで BOD 盤面図が含まれる場合は、盤面図と持駒行(先手の持駒:、後手の持駒:)および手番行(後手番 または 上手番 があれば後手番、なければ先手番)から初期局面を構築する。
後手の持駒:なし
9 8 7 6 5 4 3 2 1
+---------------------------+
| ・ ・ ・ ・ ・ ・ ・v歩v玉|一
| ・ ・ ・ ・ ・ ・ ・v銀 ・|二
| ・ ・ ・ ・ ・ ・ ・ ・ ・|三
| ・ ・ ・ ・ ・ ・ ・ ・ ・|四
| ・ ・ ・ ・ ・ ・ ・ ・ ・|五
| ・ ・ ・ ・ ・ ・ ・ ・ ・|六
| ・ ・ ・ ・ ・ ・ ・ ・ ・|七
| ・ ・ ・ ・ ・ ・ ・ ・ ・|八
| ・ ・ ・ ・ ・ ・ ・ ・ ・|九
+---------------------------+
先手の持駒:金二
3. 指し手と消費時間
本手順および変化手順の各行は以下の形式で記述される。
1 7六歩(77) ( 0:03/00:00:03)
2 3四歩(33) ( 0:05/00:00:05)
3 2二角成(88) ( 0:10/00:00:13)
4 同 銀(31) ( 0:02/00:00:07)
5 4五角打 ( 0:08/00:00:21)
- 手数:1 から始まる手数。同手数から分岐する変化手順が存在する場合、エクスポート時は行末に
+が付与される。 - 移動先:全角数字(
1〜9)と漢数字(一〜九)の組、または直前の手と同じマスを表す同(同 銀のように全角スペースを伴う表記も可)。 - 駒種と成または不成:
歩、香、桂、銀、金、角、飛、玉(王)、および成駒のと、成香(杏)、成桂(圭)、成銀(全)、馬、龍(竜)。移動時に成る場合は末尾に成、成らない場合は不成を付す。 - 移動元または打:盤上の移動は半角括弧
(77)で移動元座標を明示する。持ち駒を打つ場合は(77)の代わりに打を記述する。この移動元座標により、相対位置修飾子なしで一意に指し手が決定される。 - 消費時間(任意):
( MM:SS / HH:MM:SS )形式で 1 手の消費時間と累計消費時間を記録する。パース時は 1 手の消費時間がミリ秒(time_ms)に変換されて各ノードに格納され、エクスポート時には手番ごとに累積した合計時間が自動計算される。
4. コメントと評価値(**評価値=)
指し手行の間には *(または ')で始まるコメント行を挿入できる。
- 初期局面コメント:1 手目の指し手より前に現れる
*行は、Record::initial_comment()に格納される。 - 指し手コメント:指し手行の直後に現れる
*行は、直前の指し手ノードのcommentに格納される。
解析行(**評価値=)と視点変換
KIF では ** で始まる行がソフト解析情報として使われる。
特に **評価値=<数値> の形式は、本手順と変化手順の双方で rsshogi の EngineInfo.eval と相互変換される。
| 処理方向 | 変換規則 |
|---|---|
パース時(KIF → Record) | KIF 上の数値を 先手視点 として解釈し、後手番の指し手では符号を反転して 手番視点(指す直前の手番側視点) の EngineInfo.eval に格納する。 |
エクスポート時(Record → KIF) | EngineInfo.eval(手番視点)を 先手視点 に変換(後手番の指し手では符号を反転)し、**評価値=<先手視点の値> として出力する。 |
詰 や +詰 など整数としてパースできない値、または評価値以外の情報を含む **... 行は、通常の指し手コメントとして保持される。
5. 終局特殊手と要約行
手順の末尾には、投了や千日手などの終局特殊手行と、までN手で... という要約行が記述される。
rsshogi がサポートする KIF の終局特殊手は以下の通りである。
| KIF 表記 | SpecialMove | 勝敗結果(GameResult) |
|---|---|---|
投了 | SpecialMove::Resign | 手番の逆側が勝ち(先手番で投了なら WhiteWin)。 |
中断 | SpecialMove::Interrupt | GameResult::Paused |
千日手 | SpecialMove::RepetitionDraw | GameResult::DrawByRepetition |
持将棋 | SpecialMove::Impasse | GameResult::DrawByImpasse |
入玉勝ち | SpecialMove::WinByDeclaration | 宣言した手番側の入玉宣言勝ち。 |
切れ負け | SpecialMove::Timeout | 手番の逆側の時間切れ勝ち。 |
反則勝ち | SpecialMove::WinByIllegalMove | 手番側の反則勝ち。 |
反則負け | SpecialMove::LoseByIllegalMove | 手番の逆側の反則勝ち。 |
不戦勝 | SpecialMove::WinByDefault | 手番側の勝ち。 |
不戦敗 | SpecialMove::LoseByDefault | 手番の逆側の勝ち。 |
詰み | SpecialMove::Mate | 詰まされた手番の逆側が勝ち。 |
不詰 | SpecialMove::NoMate | GameResult::Paused |
最大手数 | SpecialMove::MaxMoves | GameResult::DrawByMaxPlies |
トライ | SpecialMove::Try | GameResult::Paused。勝者は復元しない。 |
終局特殊手行が存在せず要約行(まで120手で最大手数、まで150手で持将棋 など)のみが存在する場合も、要約行から終局種別と GameResult が復元される。
反則と不戦の出力トークンは、記録された勝者と終局時の手番から勝ち/負けを選ぶ。
投了、詰み、時間切れは手番の逆側の勝ち、入玉宣言は手番側の勝ちを表す。
これらのトークンで表せない勝者を持つ Record の KIF 出力は KifError::InvalidLine(Python は ValueError)となる。
勝者を持つトライも、KIF パーサが勝敗を復元できないため出力を拒否する。
KI2 では、時間切れや反則など、要約行で勝者を明示できる終局理由を保持できる。
詰みの要約行は勝者を明示しないため手番の逆側が勝者であることを必要とし、トライの出力は非対応である。
また、初期局面が駒落ちである場合、エクスポート時の要約行では 先手 / 後手 の代わりに 下手 / 上手(例:まで95手で下手の勝ち)が出力される。
終局特殊手行と要約行の双方が存在しない棋譜はエラーにならず、main_terminal = None かつ GameResult::Invalid として保持される。
6. 変化手順(分岐)
本手順の末尾以降に 変化:N手 という見出し行を置くことで、N 手目の指し手を別の手に置き換える分岐手順を記述できる。
変化:3手
3 2五歩(26) ( 0:02/00:00:05)
4 3三角(22) ( 0:04/00:00:09)
複数の 変化:N手 セクションが存在する場合、パーサは直前に構築された手順ラインを遡って N 手目の親ノードを特定し、入れ子の変化手順(分岐からさらに派生する分岐)も木構造として構築する。
コード例
Python
from rsshogi.record import Record
# UTF-8 の KIF ファイルを読み込む(cp932 のファイルには encoding="cp932" を指定する)
record = Record.from_kif_file("sample.kif", encoding="utf-8")
# KIF 文字列の生成と保存
kif_str = record.to_kif()
record.write_kif("output.kif", encoding="utf-8")
Rust
#![allow(unused)]
fn main() {
use rsshogi::records::formats::{
common::{ExportOptions, TextEncoding},
kif::{export_kif, export_kif_bytes_with_options, parse_kif_bytes, parse_kif_str},
};
let record = parse_kif_str(kif_str)?;
let utf8_text = export_kif(&record)?;
let sjis = export_kif_bytes_with_options(
&record,
ExportOptions::new(TextEncoding::ShiftJis),
)?;
let sjis_bytes = sjis.into_bytes();
}