棋書のルートマップをExcel VBAで作る
こんにちは。
今回は将棋&プログラミングの話です。
棋書を読んでいると「第〇図からの指し手②」と何ページか前の分岐に戻ることが多々ありますね。こういうのが何度もあると今どの辺の変化を追っているんだったかわからなくなってしまいます。全体像を掴みたいので棋書にルートマップを付けてくれたら助かるのですが、そういうのが付いている本は見たことがありません。レイアウトの問題とかもあるし難しいのでしょうか。フローチャート作成ソフトなどを探してみましたが、直接使えそうなのも見つけられませんでした。そこでプログラミングの勉強も兼ねて自分で作ってみることにしました。
作るもの
棋書はだいたい下図の形式になっていますね。
※図は後手四間飛車のすべて(井出隼平)の最初のページ
これらの情報からこんなルートマップ↓を作ります。
1ページにつき1つのテキストボックスを生成して樹形図風に繋ぎ分岐を表します。
作り方のイメージ
(1)Excelシートにページ番号、図番号などを入力する
(2)Excelシート or VBAで並べ方とか繋ぎ方を記述する (悩み所さん)
(3)VBAでテキストボックスとコネクタを描画する (調べればわかりそう)
といった考えの下、(1)と(3)を書けるだけ書いてから(2)を試行錯誤していくという流れで作成しました。
この記事では完成形だけ説明します。
ページ番号、図番号などを入力
まずはExcelシートに情報(①ページ番号②~~からの指し手③このページの指了図)を入力していきます。1章分入力します。
※ここからは角交換四間飛車の新常識(古森悠太)の第3章を例に使用します
並べ方・繋ぎ方
意外とシンプルにできました。
直前のページから続く場合は「前ページの右」に配置すればいいので簡単ですね。
横位置:前ページの右
縦位置:前ページと同じ
分岐に戻る場合は「前ページの右の下」になります。
横位置:前ページの右
縦位置:前ページの下
これを記述するには分岐に戻るページを示すフラグ(True or Falseか1 or 0)があれば良さそうですね。
VBAでもExcelシートでも記述できそうですが、Excelシートの方が思い通りに処理できているか確認しやすいのでExcelシートに記述しました。
D列:どのページから繋がるかを図番号を基に入力
Index関数とMatch関数、Vlookup関数などが使えそうですが、1つのページに複数図があったり、「p〇〇途中図からの指し手」とかあったりすると対応できませんでした。
E列:D列に該当する行番号 (後の処理がわかりやすくなるのでここで計算)
F列:改行フラグ (直前のページから連続:0、分岐前のページに戻る:1)
G列:改行フラグの累積数 (テキストボックスを配置する行番号に使用)
2行目:便宜的に必要。(「初手から~」とか「基本図から~」の分岐元になる)
F列の改行フラグを基にG列で行番号を作ったので、VBAの記述が多少楽になります。(ExcelシートとVBAの両方が少しずつ複雑になる悪手ともいえる)
小まとめ
横位置:前ページの右
縦位置: 行番号(ExcelシートのG列)を参照
テキストボックスとコネクタを描画
あとはVBAで記述するだけですね。VBAのコードはこのようになります。
以下各箇所の説明です。コードの配色がおかしい…(^ω^;)
サイズや間隔は容易に変えられるように定数にしておきます。
また、Excelシートの列番号を渡しておきます。
'定数
'1つ目の位置(左端・上端)
Const left_pos_0 = 680
Const top_pos_0 = 40
'大きさ(幅・高さ)
Const txt_width = 80
Const txt_height = 50
'間隔(左端・上端の)
Const left_diff = 100
Const top_diff = 100
'参照列番号
Const page_col = 1 'page
Const previous_fig_col = 2 '前の図
Const result_fig_col = 3 '結果図
Const previous_row_col = 5 '前ページ
Const vert_loc_col = 7 '縦位置
変数txt()はテキストボックス描画用。要素数未設定の配列で宣言し、Excelシートの行数を取得してからReDimしました。
'変数
Dim last_row As Integer '最終行
Dim i As Integer 'For文のカウント用
Dim txt() As Object 'テキストボックス
Dim con As Shape 'コネクタ
Dim left_pos As Integer '横位置(左端)
Dim top_pos As Integer '縦位置(上端)
'最終行取得
last_row = Cells(Rows.Count, 1).End(xlUp).Row
'txtの要素数=行数
ReDim txt(last_row)
※すべてのテキストボックス個別で変数を生成しているのでメモリ効率が悪そうです。もっと良い書き方があるかもしれませんが数十ページ分の動作では問題なかったのでこのままにします。
1つ目のテキストボックスだけ先に別で生成します。
'txt(1)は未使用
'1つ目のテキストボックス追加(引数: 方向, 左端, 上端, 幅, 高さ)
Set txt(2) = ActiveSheet.Shapes.AddTextbox(msoTextOrientationHorizontal, left_pos_0, top_pos_0, txt_width, txt_height)
txt(2).TextFrame.Characters.Text = Cells(2, result_fig_col).Value
配列の番号はExcelシートの行番号と合わせました。
テキストボックスの書き方は調べたらすぐにでてきました。(色々なサイトで説明されているので省略)
初期位置とサイズは定数で宣言しているので引数で渡します。
表示する文字列はExcelシートから任意に設定します。ここではCells(2, result_fig_col) = "C2" の値を表示します。(C2:基本図と書いてあるセル)
2つ目以降はFor文でテキストボックスとコネクタを追加していきます。
'2つ目以降
For i = 3 To last_row
'位置決め
left_pos = txt(Cells(i, previous_row_col).Value).Left + left_diff '前ページの右
top_pos = top_pos_0 + Cells(i, vert_loc_col) * top_diff '指定の縦位置
'テキストボックス追加
Set txt(i) = ActiveSheet.Shapes.AddTextbox(msoTextOrientationHorizontal, left_pos, top_pos, txt_width, txt_height)
txt(i).TextFrame.Characters.Text = "p" & Cells(i, page_col) & " " & Cells(i, previous_fig_col) & "~" & vbLf & "…" & Cells(i, result_fig_col)
'コネクタ
Set con = ActiveSheet.Shapes.AddConnector(msoConnectorStraight, 1, 1, 1, 1)
With con
.ConnectorFormat.BeginConnect txt(Cells(i, previous_row_col)), 4 '4: 右
.ConnectorFormat.EndConnect txt(i), 2 '2: 左
.Line.ForeColor.RGB = RGB(200, 200, 200) '線の色
End With
Next
テキストボックスの位置決めは先述した通りです。
(再掲)
横位置:前ページの右
縦位置: 行番号(ExcelシートのG列)を参照
記述内容はこうなります。
横位置(left_pos) = 前ページの横位置 +横間隔(left_diff)
縦位置(top_pos) = 初期位置 + 行番号 * 縦間隔(top_diff)
横位置は相対的な記述、縦位置は絶対座標的な記述になってしまいセンスがイマイチですが一応書けました。
テキストボックスに表示する文字列も任意に決めます。ここでは「ページ番号+第〇図~ (改行) …第〇図」としました。
コネクタはBeginConnectとEndConnectにそれぞれ前ページとこのページのテキストボックスを渡します。", 4"とか", 2"で繋ぐ位置を指定することができ、4と2を設定すると「BeginConnectの右」と「EndConnectの左」が繋がります。(1:上、2:左、3:下、4:右 反時計回りですね)
実行して完成です。
あとは棋書を読み進めながら手動で符号とかコメントとか形勢を書くこともできます。サイズや間隔を大きく設定すれば書き込む空白が増えます。VBAで生成した後はテキストボックスとコネクタとして操作できるので一部だけ位置調整したり書式変更することも可能です。
何かのお役に立てば幸いです。