Skip to content

将棋の棋譜読み込み・相互変換・戦法抽出・AI

License

Notifications You must be signed in to change notification settings

akicho8/bioshogi

Repository files navigation

将棋の棋譜相互変換、棋譜から戦術抽出、超弱いAIなどのライブラリ

https://raw.github.com/akicho8/bioshogi/master/bioshogi.png デモ

container = Container::Basic.start
[
  "7六歩", "8四歩", "7八金", "3二金",
  "2六歩", "8五歩", "7七角", "3四歩",
  "8八銀", "7七角成",
].each{|input|
  container.execute(input)
}
puts container.board
pp container.to_kif_a
# >>   9 8 7 6 5 4 3 2 1
# >> +---------------------------+
# >> |v香v桂v銀v金v玉 ・v銀v桂v香|一
# >> | ・v飛 ・ ・ ・ ・v金 ・ ・|二
# >> |v歩 ・v歩v歩v歩v歩 ・v歩v歩|三
# >> | ・ ・ ・ ・ ・ ・v歩 ・ ・|四
# >> | ・v歩 ・ ・ ・ ・ ・ ・ ・|五
# >> | ・ ・ 歩 ・ ・ ・ ・ 歩 ・|六
# >> | 歩 歩v馬 歩 歩 歩 歩 ・ 歩|七
# >> | ・ 銀 金 ・ ・ ・ ・ 飛 ・|八
# >> | 香 桂 ・ ・ 玉 金 銀 桂 香|九
# >> +---------------------------+
# >> ["▲7六歩(77)",
# >>  "▽8四歩(83)",
# >>  "▲7八金(69)",
# >>  "▽3二金(41)",
# >>  "▲2六歩(27)",
# >>  "▽8五歩(84)",
# >>  "▲7七角(88)",
# >>  "▽3四歩(33)",
# >>  "▲8八銀(79)",
# >>  "▽7七角成(22)"]

サンプルコード

初期配置文字列のパース

info = Soldier.from_str("7六と")
info[:place].name # => "7六"
info[:promoted]   # => true
info[:piece].name # => "歩"

AI同士の対局 (ルールを守った上でランダムに指してみる)

container = Container::Basic.start
loop do
  hand = container.current_player.brain.create_all_hands.sample
  container.execute(hand)
  captured_soldier = container.opponent_player.executor.captured_soldier
  if executor.captured_soldier && executor.captured_soldier.piece.key == :king
    break
  end
end

p container
# >> 78手目: ▽後手番
# >>   9 8 7 6 5 4 3 2 1
# >> +---------------------------+
# >> | 角v桂v銀 ・v金 ・ ・ ・v香|一
# >> |v香 ・ ・ ・ ・v銀 ・ ・ 歩|二
# >> |v歩v歩v飛v歩 圭 ・ ・ ・v桂|三
# >> | ・ ・v歩 ・v金 ・ ・v歩 ・|四
# >> | ・ ・ ・ ・ 角 歩 ・ 歩v歩|五
# >> | ・ ・ 歩 ・ ・v歩v歩 ・ ・|六
# >> | 歩 歩 ・ 歩 歩 ・ ・ ・ 銀|七
# >> | 香 ・ ・ 金 ・ 飛 金 ・ 香|八
# >> | ・ 銀 ・ 玉 ・ ・ ・ 桂 ・|九
# >> +---------------------------+
# >> ▲先手の持駒:歩 玉
# >> ▽後手の持駒:歩

puts container.to_ki2_a.group_by.with_index{|v, i|i / 8}.values.collect{|v|v.join(" ")}
# >> ▲3八金(39) ▽4六歩打 ▲1二歩打 ▽3六歩(35) ▲5三桂成(65)
# >> ▲4八玉 ▽5二金 ▲5六歩 ▽6二飛 ▲1六歩 ▽7二飛 ▲5九玉 ▽6二玉
# >> ▲2六歩 ▽4二金 ▲4八飛 ▽5四歩 ▲7八銀 ▽5一金 ▲7六歩 ▽5五歩
# >> ▲同歩 ▽3二金 ▲5八飛 ▽3四歩 ▲7七桂 ▽9二香 ▲1八飛 ▽4二金
# >> ▲6八金 ▽8二飛 ▲2八銀 ▽4四歩 ▲1五歩 ▽4三金 ▲1七銀 ▽2四歩
# >> ▲6五桂 ▽3三金 ▲9八香 ▽4二銀 ▲2八飛 ▽3一角 ▲2七飛 ▽5二玉
# >> ▲9九角 ▽4三金 ▲3九金 ▽4五歩 ▲8九銀 ▽1四歩 ▲5四歩 ▽1三桂
# >> ▲4六歩 ▽6二飛 ▲4五歩 ▽5四金 ▲5七歩打 ▽2二角 ▲1八香 ▽5三玉
# >> ▲2五歩 ▽7四歩 ▲6九玉 ▽5二飛 ▲2六飛 ▽4八歩打 ▲2八飛 ▽7二飛
# >> ▲4八飛 ▽5五角 ▲同角 ▽7三飛 ▲3六歩 ▽3五歩 ▲9一角打 ▽1五歩
# >> ▲3八金 ▽4六歩打 ▲1二歩打 ▽3六歩 ▲5三桂成

第23期竜王戦七番勝負第6局の再生

KIFファイルを読み込み

info = Bioshogi::Parser.parse(Pathname("ryuou20101214.kif"))
pp info.pi.header
# >> {"対局ID"=>"333",
# >>  "開始日時"=>"2010/12/14 9:00",
# >>  "終了日時"=>"2010/12/15 19:13",
# >>  "表題"=>"竜王戦",
# >>  "棋戦"=>"第23期竜王戦七番勝負第6局",
# >>  "持ち時間"=>"各8時間",
# >>  "消費時間"=>"146▲479△471",
# >>  "場所"=>"岐阜・ホテルアソシア高山リゾート",
# >>  "手合割"=>"平手",
# >>  "先手"=>"羽生善治",
# >>  "後手"=>"渡辺 明"}

棋譜を入力していく

container = Container::Basic.start
info.pi.move_infos.each{|info|
  container.execute(info[:input])
}

最終形

puts container.inspect
# >> 147:▲先手番
# >>   9 8 7 6 5 4 3 2 1
# >> +---------------------------+
# >> | ・v桂 ・ ・ 馬 ・ ・v桂v香|一
# >> |v飛 ・ ・ ・ ・ と ・ ・ ・|二
# >> | ・ ・ ・ 全v歩 ・v玉 ・ ・|三
# >> | ・ ・ ・ ・ ・ ・v桂 ・v金|四
# >> | ・v歩 ・ ・ ・ 銀v歩v歩v歩|五
# >> |v歩 ・ 歩v角 ・ ・ ・ ・ ・|六
# >> | ・ 歩 銀v歩vと ・ ・ ・ ・|七
# >> | 歩 ・ 玉 香 ・ ・ ・ ・ 香|八
# >> | 香 桂 ・ ・ ・ ・ 飛 ・ ・|九
# >> +---------------------------+
# >> blackの持駒:歩三金
# >> whiteの持駒:金二歩三銀

KIF形式の棋譜確認

puts container.to_kif_a.group_by.with_index{|v, i|i / 8}.values.collect{|v|v.join(" ")}
# >> ▲7六歩(77) ▽8四歩(83) ▲7八金(69) ▽3二金(41) ▲2六歩(27) ▽8五歩(84) ▲7七角(88) ▽3四歩(33)
# >> ▲8八銀(79) ▽7七角成(22) ▲7七銀(88) ▽4二銀(31) ▲3八銀(39) ▽7二銀(71) ▲9六歩(97) ▽9四歩(93)
# >> ▲4六歩(47) ▽6四歩(63) ▲4七銀(38) ▽6三銀(72) ▲6八玉(59) ▽3三銀(42) ▲5八金(49) ▽5四銀(63)
# >> ▲3六歩(37) ▽4二玉(51) ▲7九玉(68) ▽6五歩(64) ▲5六銀(47) ▽5二金(61) ▲1六歩(17) ▽1四歩(13)
# >> ▲3七桂(29) ▽3一玉(42) ▲4七金(58) ▽4四歩(43) ▲2五歩(26) ▽4三金(52) ▲8八玉(79) ▽2二玉(31)
# >> ▲4八金(47) ▽4二金(43) ▲2九飛(28) ▽4三金(42) ▲1八香(19) ▽9二香(91) ▲2八飛(29) ▽4二金(43)
# >> ▲2六飛(28) ▽5二金(42) ▲2九飛(26) ▽4三金(52) ▲2八飛(29) ▽4二金(43) ▲2七飛(28) ▽5二金(42)
# >> ▲4五歩(46) ▽4三金(52) ▲4四歩(45) ▽4四金(43) ▲2九飛(27) ▽4三金(44) ▲4六角打 ▽9三香(92)
# >> ▲4五歩打 ▽4二金(43) ▲4七銀(56) ▽9二飛(82) ▲3五歩(36) ▽3五歩(34) ▲3五角(46) ▽6四角打
# >> ▲5六歩(57) ▽9五歩(94) ▲9五歩(96) ▽9六歩打 ▲5七角(35) ▽9五香(93) ▲9八歩打 ▽3四歩打
# >> ▲3六銀(47) ▽7四歩(73) ▲1五歩(16) ▽1五歩(14) ▲2四歩(25) ▽2四銀(33) ▲2五銀(36) ▽4六歩打
# >> ▲2四銀(25) ▽2四歩(23) ▲8三銀打 ▽5二飛(92) ▲7四銀成(83) ▽9一角(64) ▲2四飛(29) ▽2三金(32)
# >> ▲2六飛(24) ▽2五歩打 ▲2五桂(37) ▽2四歩打 ▲1二歩打 ▽1二玉(22) ▲8四角(57) ▽4七歩成(46)
# >> ▲4七金(48) ▽1四金(23) ▲9五角(84) ▽2五歩(24) ▲3六飛(26) ▽2三玉(12) ▲5五歩(56) ▽4五銀(54)
# >> ▲3九飛(36) ▽4六歩打 ▲3六金(47) ▽3六銀(45) ▲3六飛(39) ▽4七歩成(46) ▲6三全(74) ▽9二飛(52)
# >> ▲5一角成(95) ▽6九銀打 ▲4五銀打 ▽2二桂打 ▲4三歩打 ▽3三金(42) ▲3五歩打 ▽3五歩(34)
# >> ▲3九飛(36) ▽7八銀成(69) ▲7八玉(88) ▽5五角(91) ▲3四歩打 ▽3四桂(22) ▲4二歩成(43) ▽5七と(47)
# >> ▲6九香打 ▽6六歩(65) ▲6六歩(67) ▽6八歩打 ▲6八香(69) ▽6七歩打 ▲4四銀打 ▽6六角(55)
# >> ▲3三銀成(44) ▽3三玉(23)

KI2形式の棋譜確認

puts container.to_ki2_a.group_by.with_index{|v, i|i / 8}.values.collect{|v|v.join(" ")}
# >> ▲7六歩 ▽8四歩 ▲7八金 ▽3二金 ▲2六歩 ▽8五歩 ▲7七角 ▽3四歩
# >> ▲8八銀 ▽7七角成 ▲同銀 ▽4二銀 ▲3八銀 ▽7二銀 ▲9六歩 ▽9四歩
# >> ▲4六歩 ▽6四歩 ▲4七銀 ▽6三銀 ▲6八玉 ▽3三銀 ▲5八金 ▽5四銀
# >> ▲3六歩 ▽4二玉 ▲7九玉 ▽6五歩 ▲5六銀 ▽5二金 ▲1六歩 ▽1四歩
# >> ▲3七桂 ▽3一玉 ▲4七金 ▽4四歩 ▲2五歩 ▽4三金 ▲8八玉 ▽2二玉
# >> ▲4八金 ▽4二金 ▲2九飛 ▽4三金 ▲1八香 ▽9二香 ▲2八飛 ▽4二金
# >> ▲2六飛 ▽5二金 ▲2九飛 ▽4三金 ▲2八飛 ▽4二金 ▲2七飛 ▽5二金
# >> ▲4五歩 ▽4三金 ▲4四歩 ▽同金 ▲2九飛 ▽4三金 ▲4六角打 ▽9三香
# >> ▲4五歩打 ▽4二金 ▲4七銀 ▽9二飛 ▲3五歩 ▽同歩 ▲同角 ▽6四角打
# >> ▲5六歩 ▽9五歩 ▲同歩 ▽9六歩打 ▲5七角 ▽9五香 ▲9八歩打 ▽3四歩打
# >> ▲3六銀 ▽7四歩 ▲1五歩 ▽同歩 ▲2四歩 ▽同銀 ▲2五銀 ▽4六歩打
# >> ▲2四銀 ▽同歩 ▲8三銀打 ▽5二飛 ▲7四銀成 ▽9一角 ▲2四飛 ▽2三金
# >> ▲2六飛 ▽2五歩打 ▲同桂 ▽2四歩打 ▲1二歩打 ▽同玉 ▲8四角 ▽4七歩成
# >> ▲同金 ▽1四金 ▲9五角 ▽2五歩 ▲3六飛 ▽2三玉 ▲5五歩 ▽4五銀
# >> ▲3九飛 ▽4六歩打 ▲3六金 ▽同銀 ▲同飛 ▽4七歩成 ▲6三全 ▽9二飛
# >> ▲5一角成 ▽6九銀打 ▲4五銀打 ▽2二桂打 ▲4三歩打 ▽3三金 ▲3五歩打 ▽同歩
# >> ▲3九飛 ▽7八銀成 ▲同玉 ▽5五角 ▲3四歩打 ▽同桂 ▲4二歩成 ▽5七と
# >> ▲6九香打 ▽6六歩 ▲同歩 ▽6八歩打 ▲同香 ▽6七歩打 ▲4四銀打 ▽6六角
# >> ▲3三銀成 ▽同玉

駒が動ける場所

container = Container::Basic.start
player = container.player_at(:black)
player.soldier_create("5五馬")
player.soldiers.first.move_list.each{|place|
  player.soldier_create("#{place}馬")
}
puts container.board
# >>   9 8 7 6 5 4 3 2 1
# >> +---------------------------+
# >> | 馬 ・ ・ ・ ・ ・ ・ ・ 馬|一
# >> | ・ 馬 ・ ・ ・ ・ ・ 馬 ・|二
# >> | ・ ・ 馬 ・ ・ ・ 馬 ・ ・|三
# >> | ・ ・ ・ 馬 馬 馬 ・ ・ ・|四
# >> | ・ ・ ・ 馬 馬 馬 ・ ・ ・|五
# >> | ・ ・ ・ 馬 馬 馬 ・ ・ ・|六
# >> | ・ ・ 馬 ・ ・ ・ 馬 ・ ・|七
# >> | ・ 馬 ・ ・ ・ ・ ・ 馬 ・|八
# >> | 馬 ・ ・ ・ ・ ・ ・ ・ 馬|九
# >> +---------------------------+

座標のパース

Placeクラス経由で扱えばだいたいパース可

Place["4三"].name   # => "4三"
Place["4三"].name  # => "4三"
Place["43"].name    # => "4三"

内部では別の座標

Place["4三"].to_xy  # => [5, 2]

引数が配列だったときのみスルー

Place[[5, 2]].to_xy # => [5, 2]

駒の情報取得例

pp Piece["飛"].to_h
# >> {name: "飛",
# >>  promoted_name: "龍",
# >>  basic_names: ["飛", "rook"],
# >>  promoted_names: ["龍", "ROOK", "竜"],
# >>  names: ["飛", "rook", "龍", "ROOK", "竜"],
# >>  key: :rook,
# >>  :promotable=>true,
# >>  basic_once_vectors: [],
# >>  basic_repeat_vectors: [nil, [0, -1], nil, [-1, 0], [1, 0], nil, [0, 1], nil],
# >>  promoted_once_vectors: # >>   [[-1, -1], [0, -1], [1, -1], [-1, 0], nil, [1, 0], [-1, 1], [0, 1], [1, 1]],
# >>  promoted_repeat_vectors: [nil, [0, -1], nil, [-1, 0], [1, 0], nil, [0, 1], nil]}

盤面テキストのパース

board = <<-EOT
+---------------------------+
| ・v桂 ・ ・ 馬 ・ ・v桂v香|
|v飛 ・ ・ ・ ・ と ・ ・ ・|
| ・ ・ ・ 全v歩 ・v玉 ・ ・|
| ・ ・ ・ ・ ・ ・v桂 ・v金|
| ・v歩 ・ ・ ・ 銀v歩v歩v歩|
|v歩 ・ 歩v角 ・ ・ ・ ・ ・|
| ・ 歩 銀v歩vと ・ ・ ・ ・|
| 歩 ・ 玉 香 ・ ・ ・ ・ 香|
| 香 桂 ・ ・ ・ ・ 飛 ・ ・|
+---------------------------+
EOT
BoardParser.parse(board)
# => {
  white: {
    "8一桂", "2一桂", "1一香", "9二飛", "5三歩", "3三玉", "3四桂", "1四金",
    "8五歩", "3五歩", "2五歩", "1五歩", "9六歩", "6六角", "6七歩", "5七と",
  },
  black: {
    "5一馬", "4二と", "6三全", "4五銀", "7六歩", "8七歩", "7七銀", "9八歩",
    "7八玉", "6八香", "1八香", "9九香", "8九桂", "3九飛",
  },
}

KIF形式の盤面表示と盤面の駒の確認

container = Container::Basic.start
container.piece_plot
puts container.board

container.board["5五"]      # => nil
container.board["8八"].name # => "▲8八角"
container.board["2八"].name # => "▲2八飛"
container.board["5九"].name # => "▲5九玉"
# >>   9 8 7 6 5 4 3 2 1
# >> +---------------------------+
# >> |v香v桂v銀v金v玉v金v銀v桂v香|一
# >> | ・v飛 ・ ・ ・ ・ ・v角 ・|二
# >> |v歩v歩v歩v歩v歩v歩v歩v歩v歩|三
# >> | ・ ・ ・ ・ ・ ・ ・ ・ ・|四
# >> | ・ ・ ・ ・ ・ ・ ・ ・ ・|五
# >> | ・ ・ ・ ・ ・ ・ ・ ・ ・|六
# >> | 歩 歩 歩 歩 歩 歩 歩 歩 歩|七
# >> | ・ 角 ・ ・ ・ ・ ・ 飛 ・|八
# >> | 香 桂 銀 金 玉 金 銀 桂 香|九
# >> +---------------------------+

5五将棋の例

Dimension.change([5, 5])
container = Container::Basic.start
soldiers = ["5五玉", "4五金", "3五銀", "2五角", "1五飛", "5四歩"]
container.players.each do |player|
  _soldiers = soldiers.collect{|s|
    s = Soldier.from_str(s)
    s.merge(place: s[:place].white_then_flip(player.location))
  }
  player.soldier_create(_soldiers)
end
container.piece_box_clear
p container
# >> 1手目: ▲先手番
# >>   5 4 3 2 1
# >> +---------------+
# >> |v飛v角v銀v金v玉|一
# >> | ・ ・ ・ ・v歩|二
# >> | ・ ・ ・ ・ ・|三
# >> | 歩 ・ ・ ・ ・|四
# >> | 玉 金 銀 角 飛|五
# >> +---------------+
# >> ▲先手の持駒:
# >> ▽後手の持駒:

container.execute("2四銀")
container.execute("4二銀")
container.execute("3四角")
container.execute("3二角")
container.execute("2三銀")
container.execute("4三銀")
container.execute("1二銀")
container.execute("同金")
container.execute("同角")
p container
# >> 10手目: ▽後手番
# >>   5 4 3 2 1
# >> +---------------+
# >> |v飛 ・ ・ ・v玉|一
# >> | ・ ・v角 ・ 角|二
# >> | ・v銀 ・ ・ ・|三
# >> | 歩 ・ ・ ・ ・|四
# >> | 玉 金 ・ ・ 飛|五
# >> +---------------+
# >> ▲先手の持駒:歩 金
# >> ▽後手の持駒:銀

NegaMax法のログの見方

3x3の盤面で対角線上に歩がある場合の駆け引き

Bioshogi.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
Dimension.change([3, 3]) do
  container = Container::Basic.new
  container.board.placement_from_human("▲3三歩 △1一歩")
  puts container
  pp Diver::NegaAlphaDiver.run(player: container.player_at(:black), depth_max: 1)
end
# >> 1手目: ▲先手番
# >>   3 2 1
# >> +---------+
# >> | ・ ・v歩|一
# >> | ・ ・ ・|二
# >> | 歩 ・ ・|三
# >> +---------+
# >> ▲先手の持駒:
# >> ▽後手の持駒:
# >>    0  試指 ▲3二歩(33) (1/2)
# >> 葉 1      試指 ▽1二歩(11) (1/2)
# >> 葉 1      評価 ▽1二歩(11)    +0
# >> 葉 1      試指 ▽1二歩成(11) (2/2)
# >> 葉 1      評価 ▽1二歩成(11) +1100
# >> 葉 1      確定 ▽1二歩成(11) +1100 候補:[▽1二歩成(11)(1100) ▽1二歩(11)(0)]
# >>    0  評価 ▲3二歩(33) -1100
# >>    0  試指 ▲3二歩成(33) (2/2)
# >> 葉 1      試指 ▽1二歩(11) (1/2)
# >> 葉 1      評価 ▽1二歩(11) -1100
# >> 葉 1      試指 ▽1二歩成(11) (2/2)
# >> 葉 1      評価 ▽1二歩成(11)    +0
# >> 葉 1      確定 ▽1二歩成(11)    +0 候補:[▽1二歩成(11)(0) ▽1二歩(11)(-1100)]
# >>    0  評価 ▲3二歩成(33)    +0
# >>    0  確定 ▲3二歩成(33)    +0 候補:[▲3二歩成(33)(0) ▲3二歩(33)(-1100)]
# >> {:hand=>"▲3二歩成(33)",
# >>  :score=>0,
# >>  :level=>0,
# >>  :reading_hands=>["▲3二歩成(33)", "▽1二歩成(11)"]}
  • 自分(先手)には「3二歩」「3二歩成」の二通りの手があることがわかり、どっちにするか問題。
  • それぞれ指したとき、後手側になってみていちばん良くなる手を探す。
  • 自分にとってはそれは負なのでマイナスとする
  • 「3二歩」のとき後手は「1二歩」「1二歩成」の二通りを考えていて「1二歩成」の方が良いとわかる。+1100点。
  • 自分にとってはそれは負なので「▲3二歩」なら -1100 点。
  • 同様に「▲3二歩成」なら 0 点。
  • -1100 になる手と、0点になる手なら当然0点になる「▲3二歩成」を指した方がいいという結果になる

ミクロコスモスの棋譜を読む

puts Parser.file_parse("microcosmos.kif").to_bod
# >> 後手の持駒:飛二 角 銀 桂二 香 歩三
# >>   9 8 7 6 5 4 3 2 1
# >> +---------------------------+
# >> | ・ ・ ・ と と と と 杏 ・|一
# >> | ・v金 ・ ・ ・ ・ ・ ・ ・|二
# >> | ・v桂 ・ ・v歩v歩v歩v歩 ・|三
# >> | ・ ・ とv銀v金vと ・ ・ ・|四
# >> |v馬v圭 香 ・ ・ ・ ・ 銀 ・|五
# >> | ・ 歩 歩 ・ ・ ・ と ・ 香|六
# >> | ・ ・ ・ ・ ・ ・ ・ ・ ・|七
# >> | ・ ・ ・ ・ とv銀 歩 ・ ・|八
# >> | ・ ・ ・ ・ ・ ・ 金 金v玉|九
# >> +---------------------------+
# >> 先手の持駒:なし
# >> 手数=1525 ▲2九金打 まで
# >>
# >> 後手番

詰みチェック

container = Container::Basic.new
container.placement_from_preset("平手")
brain = container.opponent_player.brain

require 'active_support/core_ext/benchmark'
Benchmark.ms { brain.iterative_deepening(depth_max_range: 0..0).none? }           # => 18.85599992237985
Benchmark.ms { container.opponent_player.create_all_hands(legal_only: true).any? } # => 0.674000009894371
Benchmark.ms { container.opponent_player.create_all_hands(legal_only: true).to_a } # => 19.73800011910498

仕様

棋譜サフィックス語の解釈

コマンド意味詳細
右の方のを選択移動元を指定座標より右で絞る(龍馬は例外で指定座標を無視し左右の方向)
左の方のを選択移動元を指定座標より左で絞る(龍馬は例外で指定座標を無視し左右の方向)
下の方のを上げる移動元を指定座標より下で絞る
上の方のを引く移動元を指定座標より上で絞る。下げるから “下” と書いてしまいがちなので注意
横一列の中から選択移動元を指定座標のY座標で絞る
縦一列の中から選択移動元を指定座標のX座標で絞る

もっと簡単に

    ↓引く

                   右の方にあるやつ

●    ← 寄せる

         ↑もち上げる
↑
直

棋譜の表記

表記意味
7六歩(77)7七の歩を7六に移動
7六歩7六歩(77) の省略形
2二角成2二に角が移動して成った
5五飛打5五に持駒の飛車を打った
同歩1手前の座標に歩を移動

主な例外

例外意味どんなときに起きる?
BioshogiErrorすべての例外の親
MustNotHappenありえない処理ブログラムがバグっている
AmbiguousFormatError指定座標に移動できる駒が多すぎる初手 “▲5八金”
SyntaxDefactとりあえず表記が違う駒の配置時に “4二銀成” とした
PieceNotFound指定の名前の駒が存在しない龍のつもりで蛇と書いた
HoldPieceNotFound打を明示したのにその駒を持っていない飛車を持ってないのに初手 “▲55飛打”
HoldPieceNotFound2打の省略型かと思ったがそれも違う飛車を持ってないのに初手 “▲55飛”
PieceAlredyExist自分の駒の上に自分の駒を初期配置配置時に2連続で “9七歩”
AlredyPromotedすでに成っている“5五” の龍を “5一飛成”
BeforePlaceNotFound「同」に対する座標が不明初手 “同歩”
RuleError反則系例外の親二歩など
CommonError黙認できる例外の親いまのところ二歩と手番間違いの2つだけ
SamePlaceDifferent座標と「同」を同時に指定したが一致しない“同歩” だけでいいのに “2四同歩” と場所を明示したとき、その前の指し手が “2四○” でない
ConsumedTimeMinusError消費時間がマイナスになっているCSAの読み込みのとき不整合が起きやすい。1分の持ち時間なのに1手1分で2手指したときなど
FileFormatErrorファイルのフォーマットがおかしいKIFファイルとして画像ファイルを読ませた
KeyNotFound手合割・囲い・戦法などのあるはずのデータがない手合割で「二枚落ち」と書くところを「飛車角落ち」と書いた
BrainProcessingHeavy処理が重すぎる反復深化深さ優先探索でのタイムリミットが短かすぎて指し手を生成できない

反則系 (RuleError のサブクラス)

こちらはプログラムの不具合や、棋譜の入力ミスに起因するものが多いので例外を出す

例外意味どんなときに起きる?
NoPromotablePiece“成” “不成” は指定できない1三金不成、3三玉成
NotFoundOnBoard盤面に指定の駒がない2七に歩がないのに2六歩(27)とした
SoldierWarpError指定座標に移動できる駒に移動元の駒が含まれていない初手 “▲25歩(27)”
DeadPieceRuleError死に駒(行きどころのない駒)を作った▲1一桂
PromotedPiecePutOnError成った状態で打とうとした5五龍打
PromotedPieceToNormalPiece成駒を成ってない状態に戻そうとした5五龍を5六飛
SamePlayerBattlerOverwrideError自分の駒の上に自分の駒を指した初手 “8八飛(28)”
ReversePlayerPieceMoveError相手の駒を動かそうとした▲の手番で初手 “3四歩”
MovableBattlerNotFound指定座標に移動できる駒が一つもない平手で初手 “▲3四歩” や ▲22角成

一部の例外を抑制できる系の反則

これらは棋譜にでてくるので別扱いにする

例外意味どんなときに起きる?
DoublePawnCommonError二歩歩がある縦列に歩を打った
DifferentTurnCommonError手番違い平手で初手△3四歩

表示座標系

987654321
9一1一
3三1三
7七
9九1九

コード座標系

012345678
00,08,0
1
26,28,2
3
4
5
62,6
7
80,88,8

棋譜のパース

  • “7六歩” の場合 “7六” と “歩” に分離する
  • “2二角成” の場合 “2二” と “角” と “成” に分離する
  • 同銀の場合、同がどこを差しているのか、前の座標を見る
  • “5八金右” の場合、5八から見て右下にある金が斜め上に上がったという意味なのでこの解釈が難しい
  • “4八” に金があった場合、”5八金右” は真横の金なのか、斜め下の金なのか、どっちだろう
  • ネット上にある棋譜はだいたい “7六歩(77)” の形式になっていて7七にあったことを明示しているのでがんばって推測しなくてもいい

棋譜ファイルの形式についての考察

KIFフォーマット

# ----  Kifu for Windows V6.22 棋譜ファイル  ----
開始日時:2000/01/01 00:00:00
終了日時:2000/01/01 01:00:00
棋戦:(棋戦)
持ち時間:(持ち時間)
手合割:平手  
先手:(先手)
後手:(後手)
手数----指手---------消費時間--
*対局前コメント
   1 7六歩(77)   ( 0:10/00:00:10)
*コメント1
   2 3四歩(33)   ( 0:10/00:00:20)
   3 6六歩(67)   ( 0:10/00:00:30)
   4 8四歩(83)   ( 0:10/00:00:40)
*コメント2
   5 投了         ( 0:10/00:00:50)
まで4手で後手の勝ち
  • 移動元が明記されているためプログラム的に扱いやすい
  • 例えば “5八金右” は “5八金(49)” と表わすので「右」の方にある金を探さなくても済む
  • ヘッダーは KI2 と共通でよい
  • ヘッダーとコンテンツを分けるセパレーターは /^手数.*/ らしい。基本、これがあるかどうかで KIF or KIF2 の判別ができそう
  • コメントは 直前の指し手 に結び付いている
  • しかし対局前のコメントは 結び付く指し手がない ため別データ扱いと考える方がよさそう
  • 「投了」は指し手とは別に管理した方がよい。指し手に「投了」が入ってくるとプログラムが複雑になるため
  • 「投了」の横の時間がある場合、それはどれだけ考えて投了したかを表す情報なので、見ないといけない。
  • 「投了」がないと将棋山脈ではKIFと見なされない。なお激指は「投了」を入れてくれない。
  • アスタリスクで始まるコメント部分には何を書いてもいいというのを利用して一手目の上に開始前メッセージがあるのがおかしい。結び付く手がない。開始前メッセージはヘッダーに入れる仕様だとよかった。
  • コメントには「#」と「*」の2つがあり、「#」はプログラム用のコメントで「*」は中継記者のコメント
  • 手合割の値の最後に謎の全角スペース2つあるのは謎。とくに気にしなくてもよいみたい
  • 棋譜部分の手数番号の前のインデントはなくてもよい
  • 激指(定跡道場4)は最後の指し手が「中断, 投了, 持将棋, 千日手, 詰み, 切れ負け」以外になっていると読み込めない。ここで何か判断しているようでもないので、この行は不要かもしれない。ただ投了までの時間を入れるにはこの行がいる。
  • 手数は必ず 1 から始まらないといけないっぽい (1以外から始まると多くのソフトがエラーになる)
  • 局面図を入れるときは、BOD を埋める。ただし「手数=数字」を入れると、エラーになるソフトもある
  • 局面図を入れるときの手番は「後手番」の行だけを入れる。※もともとBODに含まれている
  • 局面図を入れるときの手番と図面の間の空行は KIF のときは無くてよい (Kifu for Windows のフォーマットがそうだった)

KI2フォーマット

開始日時:2000/01/01 00:00
終了日時:2000/01/01 01:00
表題:(表題)
棋戦:(棋戦)
戦型:(戦型)
持ち時間:(持ち時間)
場所:(場所)
掲載:(掲載)
立会人:(立会人)
副立会人:(副立会人)
記録係:(記録係)
Web Page:(Web Page)
通算成績:(通算成績)
先手:(先手)
後手:(後手)

*対局前コメント
▲7六歩    △3四歩
*コメント1
▲6六歩△8四歩
*コメント2
まで4手で後手の勝ち
  • “5八金右” “同歩” など人が書いた風の棋譜になっている
  • ヘッダーとコンテンツを分けるセパレーターは 最初の空行 (2021-11-10 ではなかった)
  • 指し手は横に何個並んでもいいっぽい
  • 指し手のセパレータは 空白ではない 。くっついている場合もあるので三角マークの前で区切る
  • なお ではなく の場合もあるので ▲△▼▽ の4つに対応すること
  • 投了 がない (2021-11-10 ことはない)
  • ”#” もない(?)
  • まで○手で○手の勝ち は必要みたい (2021-11-10 ない場合もある )

Kifu for iPhone のフォーマット

# Kifu for iPhone V4.50 棋譜ファイル
開始日時:2021/11/04 22:09:41
棋戦:R対局 早指し2(猶予1分)
▲7六歩△3四歩▲2六歩△4四歩
▲4八銀△5四歩▲投了
  • ずっとヘッダーとコンテンツを分けるセパレーターは最初の空行と信じていたがなんと空行がなかった
  • なので空行の有無で分けてはいけない
  • また投了 投了 がないと思われていたが、あった

英語表記対応表

日本語英語
pawn
bishop
rook
lance
knight
silver
gold
king
成ったpromoted
盤面board
座標place
相対座標vector
先手black
後手white
対局室container

コマンドラインツール

% bioshogi --help
Commands:
  bioshogi convert         # 棋譜フォーマット変換
  bioshogi help [COMMAND]  # Describe available commands or one specific command
  bioshogi input_match    # 入力
  bioshogi piece           # 駒一覧
  bioshogi versus          # CPU同士の対戦

Options:
  [--debug], [--no-debug]
  [--quiet], [--no-quiet]

参考リンク集

ライセンス