前回の記事 では、ABC 記法の曲から五線譜を作りました。今回は、kintone に書いた将棋の棋譜(KIF 形式)を 1 手ずつ進めて、盤面図の PDF を作る帳票です。
ネタ記事です。棋譜は AI が自作した対局で、強い将棋ではありません。計算式で盤を進める作りは本気です。
できあがったのは、下の盤面図です。棋譜の読み取り・盤を進める計算・誤りの検出・盤の SVG まで、計算式だけで組んでいます。
この記事の見どころは、次の 3 つです。
- 棋譜を 1 手ずつ進める計算を、
ARRAY_FORのループの中で、LETで決めた盤の文字列をFSETで書き換えて作った。最初はループを使わず、LETの中で代入し直す形を 200 手ぶん並べていたが、開発者に教わって切り替え、式は約 1/5 になった(5.5・5.6、6.1、10.4) - 1 手は
REPLACERE2 回。評価回数は手の数にほぼ比例し、121 手の全局面で 2,625 回(上限 10 万回の 2.7%)(5 章) - 実機の PDF を見て「駒の枠が見にくい」と言われ、駒の枠なし版を作った(4 章)
元にしたのは、次のような依頼文です(要約)。
印刷屋プラグインで、将棋の棋譜から「盤面図」の PDF を作る帳票例を作って。
- kintone に書いた棋譜を 1 手ずつ進め、指定した 1 局面だけでなく、全局面も出す。棋譜は自作の対局か詰将棋(実在の対局を使うときは人が出典の棋譜を用意する。AI の記憶から棋譜を再現しない)
- アプリは 1 レコード = 1 局。対局名、先手・後手(架空)、日付、手合(平手だけ)、棋譜、表示する手数、開始・終了手数、メモ。テストレコードは、短い対局・長い対局(120 手前後)・成・不成・打・同の多い対局・投了で終わる対局・誤りを入れた棋譜
- 棋譜は KIF 形式の移動元付きの指し手(「7六歩(77)」「同 歩(23)」「5五角打」)。移動元の無い書き方は、できれば扱う。読み方を表に
- 盤面は計算式で。81 マスの固定長の文字列で持ち、1 手ごとに移動元と移動先だけを書き換える。移動元に駒が無い・自分の駒を取る・持ち駒に無い駒を打つ、は何手目で止まったかを出す。評価回数の上限(10 万回)に注意し、マスごとに関数を呼ばない
- ボタンは 3 つ(指定した手数の 1 図・全局面を 2 列 × 4 段の 8 図ずつ・範囲)。盤は 9×9 で、1 局面を 1 つの SVG に。駒は五角形と漢字、後手の駒は 180 度回す。直前の手の移動先に色。全局面の 1 図は約 90 × 65 mm
- 120 手前後の対局で、全局面(16 ページ)・PDF の大きさ・時間・評価回数を 150 dpi と 96 dpi で確かめる
この依頼文を AI に渡すところから始めました。チャットで頼んだことと結果は、次のとおりです。
| # | 頼んだこと | 結果 |
|---|---|---|
| 1 | 上の依頼文 | 依頼文のアプリを作る場所が〈スペースの URL〉のままだったので、AI が聞いてきた。前回と同じスペースと答えた。AI は、将棋の決まりを確かめる小さなプログラムを手元で作って見本の対局を指し、アプリ・レコード 5 件・計算式・帳票を作った |
| 2 | 「ループの中から外の LET の変数が見えない、は、どのような計算式? 変数名が異なれば見えるはず」 | AI の説明の誤りだった。小さな式で試し直し、「見えないのではなく、ループの中の LET で外と同じ名前に代入すると消える」と直した(6.1) |
| 3 | (作業フォルダーに移したセッションで)続けて | 印刷屋の MCP で作業フォルダーに保存し、検査とプレビュー(5 件 × 3 ボタン)で式のエラー 0 を確かめた(6.6) |
| 4 | (実機の PDF 7 本を見せる) | ページ数がプレビューと同じこと、見た目を確かめた |
| 5 | 「駒の枠が全体を見にくいものにしている。駒の枠なしバージョンを」 | 駒の五角形を描かず、字を少し大きくした版を、別の設定ファイルで作った(4 章) |
| 6 | (枠なしの実機の PDF 10 本を見せる) | ページ数は同じで、PDF は 1 割ほど小さくなった(7 章) |
| 7 | 「qiita 記事を書いて」 | この記事 |
| 8 | 「計算式エンジンの制約で工夫した計算式となったが、計算式エンジンの改善すべき点を提案して」「記事に改善案として追記」 | AI がエンジンのコードを読み、手元で動かして確かめ、改善案を出した(10 章) |
| 9 | 「ループの前に LET で変数を定義して、ループ内でセットすると、次のループでも参照できる」 |
FSET で書き換える形を 3 つのボタンに当てはめて試し、出力が同じで式が約 1/5 になることを確かめた(10.4)。設定もこの形に切り替えた(5.6) |
使ったのは、印刷屋プラグイン Ver.8、Claude Code(Claude デスクトップアプリの Code タブ)、印刷屋 MCP 0.2.0(資料・検査・プレビュー。中核は tools 2.1.0)です。アプリの作成とレコードの登録は、kintone の MCP サーバーで AI が行いました。
将棋の用語(簡単に)
| 用語 | 意味 |
|---|---|
| 筋・段 | 盤の縦の列と横の行。筋は右から1〜9(算用数字)、段は上から一〜九(漢数字)。「7六」は 7 筋の 6 段目 |
| 先手・後手(▲・△) | 先に指す側(盤の下側)と後に指す側(上側)。図では後手の駒を逆さに描く |
| 手数・局面 | 何手目か。局面は、ある手を指した後の盤と持ち駒のようす。開始局面は 0 手目 |
| 持ち駒 | 取った相手の駒。自分の駒として盤に打てる |
| 成・不成・成駒 | 相手の陣(奥の 3 段)に入るなどしたときに、駒を裏返して働きを変えるのが成、しないのが不成。成った駒は と(歩)・杏(成香)・圭(成桂)・全(成銀)・馬(角)・龍(飛) |
| 打 | 持ち駒を盤に置く手 |
| 同 | 直前の手と同じマスに指す手(「同 歩」) |
| 投了 | 負けを認めて対局を終えること |
| 手合・平手 | ハンデ(駒落ちなど)の種類。平手はハンデなし |
| KIF 形式 | 棋譜のテキスト形式の 1 つ。「1 7六歩(77)」のように、手数・移動先・駒・移動元を書く |
1. アプリと見本の棋譜
アプリは「盤面図(棋譜)」です。1 レコードが 1 局です。
| フィールドコード | 型 | 中身 |
|---|---|---|
対局名 |
文字列 1 行(60 字まで、必須) | 帳票の上に出す |
先手・後手
|
文字列 1 行(30 字まで) | 名前(架空) |
対局日 |
日付 | |
手合 |
ドロップダウン | 平手だけ |
棋譜 |
文字列(複数行) | KIF 形式の指し手 |
表示手数 |
数値 | 指定した手数のボタンで使う。空なら最終手 |
開始手数・終了手数
|
数値 | 範囲のボタンで使う。空なら開始局面・最終手 |
メモ |
文字列(複数行) | 局面の説明。指定した手数の図の下に出す |
盤面図PDF |
添付ファイル | PDF の保存先 |
練習対局のレコードの画面です。上の 3 つが印刷屋のボタンです。
見本の棋譜は 5 件です。
| # | 対局 | 手数 | 確かめること |
|---|---|---|---|
| 1 | 例会 第 1 局(中飛車・30 手まで) | 30(指しかけ) | 短い対局。消費時間付きの KIF |
| 2 | 例会 第 2 局(角換わり・121 手) | 121 + △投了 | 長い対局。全局面 122 図・16 ページ。表示手数 50、範囲 41〜56 |
| 3 | 練習対局(成・不成・打・同の多い将棋) | 78 | 同 9・成 10・不成 2・打 14。表示手数 27、範囲 21〜36 |
| 4 | 例会 第 3 局(四間飛車・投了まで) | 64 + ▲投了 | KIF ファイルの見出し(開始日時・手合割・先手・後手)と「まで64手で後手の勝ち」の行をそのまま貼った |
| 5 | 誤りを入れた棋譜(エラー表示の確認用) | 30 | 1 局目の第 17 手の移動元を、1 手目で動いた歩のマスに書き換えた |
棋譜は、AI が手元で作った小さな将棋のプログラムで指した自作の対局です。プログラムは合法手を作ります(王手放置・二歩・打ち歩詰め・行き所の無い駒の決まりも)。初期局面から 1・2・3 手の局面の数が、既知の値(30・900・25,470)と合うのを確かめてから使いました。出だしだけ台本(四間飛車・角換わり・中飛車)で、あとは 1 手読みと取り合いの読みで指したので、大駒をただで渡すような手もあります。
2. 棋譜の読み方
KIF 形式の、移動元付きの指し手を読みます。
手数----指手---------消費時間--
1 7六歩(77) ( 0:01/00:00:01)
2 3四歩(33) ( 0:02/00:00:02)
3 2二角成(88) ( 0:01/00:00:02)
4 同 銀(31) ( 0:03/00:00:05)
5 4五角打 ( 0:05/00:00:07)
| 書き方 | 読み方 |
|---|---|
| 指し手の行 | 行の頭(空白の後)が手数の番号の行だけを読む。奇数が先手(▲)、偶数が後手(△)。見出し(開始日時: など)、* のコメント、「まで…の勝ち」、消費時間は読み飛ばす |
| 位置 | 筋は全角か半角の数字、段は漢数字か半角の数字。「7六」→ 筋 7・段 6 |
| 同 | 直前の手の移動先(全角の空白はあってもなくてもよい)。同が続いても、さかのぼって最初の位置 |
| 駒の名前 | 歩 香 桂 銀 金 角 飛 玉(王も玉)、成駒は と・成香・成桂・成銀・馬・龍(1 字の 杏・圭・全、竜 も読む) |
| 成・不成 | 「成」は成る(歩香桂銀角飛だけ)。「不成」は成らない(書かなくても同じ) |
| 打 | 持ち駒を打つ(歩香桂銀金角飛だけ。移動元を書かない) |
| 移動元 | 「(77)」の 2 桁(全角の数字・括弧も読む)。打つ手のほかは必ず要る |
| 終わり | 投了・中断・詰み・千日手・持将棋・反則勝ち・反則負け・切れ負け・入玉勝ち。そこから後は読まない |
移動元の無い書き方(▲7六歩、右・左・直・上・引・寄)には対応していません。移動元を決めるには、移動先に動ける同じ駒を探し(飛車・角・香は途中のマスが空いているかも)、2 つ以上なら右・左などで選ぶ必要があります。1 手を REPLACERE 2 回で進める今の形(5 章)に入れるには、移動先と駒ごとの「動ける元のマスと途中のマス」の表が要り、設定の大きさに入りません。読めない手として止め、誤りの文に「移動元の「(77)」の無い書き方」と出します。
3. 帳票
3.1 3 つのボタン
| ボタン | 中身 | ファイル名 |
|---|---|---|
| 盤面図(指定した手数) | 表示手数の局面を A4 縦に 1 図。下にメモと指し手の一覧 |
盤面図_<対局名>_<N>手.pdf(空なら _最終手.pdf) |
| 盤面図(全局面) | 開始局面から最終手まで、2 列 × 4 段の 8 図ずつ | 盤面図_<対局名>_全局面.pdf |
| 盤面図(範囲) | 開始手数〜終了手数を 8 図ずつ |
盤面図_<対局名>_<開始>-<終了>手.pdf(空なら 0・最終手) |
どれも A4 縦・150 dpi で、PDF は 盤面図PDF に保存します。指定した手数の一覧は、出した手を黄色の太字、後の手を灰色にします。
3.2 図
- 全局面・範囲の 1 図は 560 × 408px(約 95 × 69 mm)、盤は 344 × 340px(約 58 × 58 mm。1 マス約 5.9 mm)。指定した手数の盤は 760 × 752px(1 マス約 13 mm)
- 盤の上に後手、下に先手の持ち駒を 1 行で(「▲持駒 角 桂 歩二」)。図の下に「第 N 手 ▲7六歩まで」。最初の図は「開始局面」、最後の図には「△投了」など
- 直前の手の移動先を黄色に。筋(9〜1)を盤の上、段(一〜九)を盤の右に
- 各ページの上に対局名・先手・後手・日付・手合、下にページ番号
3.3 誤りの文
棋譜に誤りがあると、そこで止め、1 ページ目の上の赤い枠に書きます。図は止まる前の局面まで出します。
| 誤り | 文 |
|---|---|
| 移動元に自分の駒が無い | 第 17 手 ▲5五歩: 移動元(57)に▲の歩が無い。第 16 手までの局面を出しています |
| 移動先に自分の駒 | 移動先(69)に自分の駒がある |
| 持ち駒に無い駒を打つ | 持ち駒に角が無い |
| 打つ先に駒 | 打つ先(56)に駒がある |
| 相手の玉を取る | 相手の玉を取る手 |
| 読めない手 | 「7七金」が読めない(KIF の指し手の形でない。…) |
| 1 手目が同 | 同…: 前の手が無いので、同の位置が分からない |
範囲の開始が終了より後のとき・表示手数が最後の手より後のとき・200 手を超えたときは、灰色の注記を出します。
4. 駒の枠(あり・なし)
最初の版は、依頼文どおり駒を五角形の枠と漢字で描きました。実機の PDF を見せたところ、「駒の枠が全体を見にくいものにしている。駒の枠なしバージョンを」と言われ、枠なし版を作りました。
駒は、SVG の <defs> に 1 回だけ定義して、マスごとに <use> で置いています。枠なし版は、五角形を描かず、字を 1 マス 10 に対して 7 → 8 に大きくして、マスの中ほどに置きました(明朝の字面の中心は、字の下端から字の大きさの約 0.38 倍上なので、y = 5 + 0.38 × 8 ≒ 8)。
<!-- 枠あり -->
<path id='z' d='M5 .7 8.7 2.1 9.4 9.4H.6L1.3 2.1Z' fill='none' stroke='#222' stroke-width='.35'/>
<g id='P'><use href='#z'/><text x='5' y='8.1'>歩</text></g>
<!-- 枠なし(svg の font-size を 7 → 8 に) -->
<g id='P'><text x='5' y='8'>歩</text></g>
<!-- 後手の駒は、どちらも先手の駒を 180 度回す -->
<g id='p' transform='rotate(180 5 5)'><use href='#P'/></g>
計算式・CSS・ほかの定数は同じで、違うのは HTML 欄の定数の SVG の部品と、設定の説明文だけです。作業フォルダーには別の設定ファイル(盤面図(枠なし).json)として置きました。ボタンの名前は同じなので、アップロードすると枠あり版と入れ替わります。
5. 計算式の作り
5.1 流れ
1 つのボタンは帳票の行 1 つです。HTML 欄には式が読む定数の JSON(正規表現と表、SVG の部品。15 KB)を置き、式が全ページを組みます。式は 1 ボタン約 3,400〜4,200 字(上限 2 万字)です(最初の形は約 1.6 万字。5.5)。
- 棋譜を、1 手 1 つの字句に変える(まとめて
REPLACERE9 回) - 盤を 1 手ずつ進め、局面の履歴を作る(1 手
REPLACERE2 回 × 手の数。5.6) - 履歴の全局面を、まとめて HTML と SVG にする(
REPLACERE10 回) - 図を 8 つずつページに包む(
REPLACERE1 回)
5.2 盤と持ち駒の文字列
盤は 81 マス × 3 字の文字列です。1 マスは「筋段の数字 2 字 + 駒 1 字」で、並びは 1 段目の 9 筋から 1 筋、2 段目…(盤の図と同じ)。駒は英字 1 字で、先手は大文字・後手は小文字、成駒は「次の字」(歩 P → と Q、角 B → 馬 C、飛 R → 龍 D など)、空きは . です。
§91l81n71s61g51k41g31s21n11l92.82r72.62.52.42.32.22b12.93p83p…
持ち駒は |▲飛角金銀桂香歩|△飛角金銀桂香歩 の、駒の字の後ろに数(漢数字。0 枚は何も無し)を付けます。例: |▲飛角一金銀桂香歩三|△…
5.3 指し手の字句
棋譜は、まとめて 9 回の REPLACERE で、1 手 1 つの字句にします。
#手数 手番 移動先 移動元 元の駒 後の駒 表示
#5▲2288BC2二角成 … 88 の角(B)が 22 で馬(C)になる
#38▲3500歩P3五歩打 … 打つ手は移動元 00、元の駒は持ち駒の字
「同」は、前の字句の移動先を後ろ読み(lookbehind)でさかのぼって入れます。同が続いても 1 回の REPLACERE で済みます。
5.4 1 手は REPLACERE 2 回
状態は「直前の手・残りの手・盤・持ち駒・表」をつないだ 1 つの文字列です。
@直前の手 #手 #手 … §盤|▲持ち駒|△持ち駒§§表
-
引く(
gなし。先頭の手だけ): 移動元・移動先に今ある駒、取った駒の持ち駒の字、その今の数と新しい数(表で 1 増やす・減らす)を、先頭の手に[…]で書き足す -
書き換える(
g): 選択肢 9 つの正規表現 1 つで、移動元を空きに、移動先を後の駒に、持ち駒を新しい数に、先頭の手を「直前の手」に変える。正しい手でなければ(移動元の駒が違う、移動先が自分の駒か相手の玉、打つ先に駒、持ち駒が無い)、先頭の手に!を付けて残りの手を消す(そこで止まる)
マスを書き換える選択肢は、「後ろ読みで文字列の頭まで戻り、そこから先読みで先頭の手を見る」形です。
(\d\d)[A-Z](?<=…)(?<=^(?=@[^#§]*#\d+▲\d\d(?:\8)(.).\[…)[\s\S]*)
5.5 最初の形: ループを使わずに 200 手並べる
計算式には、reduce のような、前の回の結果を次の回に渡す専用の関数はありません。最初は、ループの中の LET で外の変数に代入し直す形も使えなかったので(6.1)、1 つの LET の中で同じ変数に代入し直す形(関数の資料の LET(x,1,y,2,x,x+y,x) と同じ)を、手数ぶん並べました。記事を書いた後で、ループの中で FSET を使う形(5.6)に切り替えました。出力は同じです。
t, 状態, h, "¶" & t,
x, DIC_ITEM(DIC("p", LET(u, t, g, "",
u, REPLACERE(u, qa, ra), u, REPLACERE(u, qb, "g", rb), g, g & "¶" & u,
…(同じものを 50 手ぶん)…
u & "⁂" & g)), "p"),
t, REPLACERE(x, "⁂[\s\S]*", ""),
h, h & REPLACERE(x, "^[^⁂]*⁂", ""),
…(この 4 行を 4 回 = 200 手)…
-
uが今の状態、gがそこまでの局面の履歴(¶でつなぐ) - 50 手ずつ
DIC_ITEM(DIC("p", …), "p")で包むのは、REPLACEREが 1 つの式で 100 個を超えると崩れるため(6.2) - 手が無くなった後の
REPLACEREは何も変えないので、履歴の重なりは後で 1 回のREPLACEREでまとめて消す
5.6 今の形: ループの中で FSET で書き換える
記事を書いた後で、開発者から「ループの前に LET で変数を定義して、ループ内でセットすると、次のループでも参照できる」と教わり、この形に切り替えました。セットは FSET(変数, 値) です(10.4)。
t, 状態, h, "¶" & t,
u, t,
lp, ARRAY_FOR(MIN(200, LENGTH(REPLACERE(mv, "[^#]", "g", ""))), k,
ARRAY(FSET(u, REPLACERE(u, qa, ra)),
FSET(u, REPLACERE(u, qb, "g", rb)),
FSET(h, h & "¶" & u))),
t, u,
-
u(今の状態)とh(局面の履歴)は、ループの前のLETで決めておき、ループの中でFSETで書き換える - 回す数は、棋譜の手の数(字句の
#の数。200 まで)。手が無くなった後に空回りしない - 3 つの
FSETはARRAYの中に並べる(前から順に 1 回ずつ評価される) - ループの中の
REPLACEREは字面で 2 個なので、DICで包む必要が無い
式は 1 ボタン 1.6 万字から 3,400〜4,200 字になりました。見本の 5 件 × 3 ボタンの 15 通りで出力は 1 字も同じで、盤の計算も 4 局すべてで Python の局面と一致しました(5.8)。
5.7 図は局面ごとに関数を呼ばない
局面の履歴(最大 201 局面)を、まとめて REPLACERE 10 回で HTML にします。空きのマスを消す → 直前の手の移動先に印 → 駒のマスを <use> に → 印のマスに色の四角 → 持ち駒を「歩二」の形に → 局面ごとに図の HTML に、の順です。
駒の位置は、筋段の数字の後ろに 0 を付けて座標にしました。76P は translate(-70 60) になり、盤の外側の translate(90 -10) と合わせて x = 90 − 10 × 筋、y = 10 × 段 − 10 です。正規表現は計算できませんが、置き換えの文字列 translate(-$010 $020) だけで位置が決まります($01 は 1 つ目の組。後ろに 0 が続いても混ざらないよう 2 桁で書く)。
図を 8 つずつページに包むのは、((?:[^¤]*¤){1,8}) の 1 回です。
5.8 評価回数と時間
印刷屋の zip に入っている計算式エンジンを Node で読み込み、評価回数と時間を数える道具を AI が作って測りました(手元の Mac)。
今の形(5.6)です。
| レコード | 指定した手数 | 全局面 | 範囲 |
|---|---|---|---|
| 1(30 手) | 1,028 回・0.03 秒 | 965 回・0.16 秒 | 993 回・0.17 秒 |
| 2(121 手) | 2,708 回・0.23 秒 | 2,625 回・0.62 秒 | 2,659 回・0.28 秒 |
| 3(78 手) | 1,903 回・0.09 秒 | 1,829 回・0.32 秒 | 1,873 回・0.14 秒 |
| 4(64 手) | 1,681 回・0.07 秒 | 1,599 回・0.34 秒 | 1,627 回・0.31 秒 |
| 5(誤り。第 16 手まで) | 1,076 回・0.03 秒 | 1,007 回・0.10 秒 | 1,035 回・0.10 秒 |
評価回数は手の数にほぼ比例し、いちばん多い 121 手でも上限の 2.7% です。最初の形(5.5)は、手が無くなった後も 200 手ぶんの REPLACERE を必ず通るので、棋譜の長さに依らず約 3,300〜3,400 回でした(関数の呼び出しは 540〜570 回、うち 400 回が 200 手ぶんの REPLACERE)。時間はどちらも正規表現の処理が多くを占め、全局面(2 局目)は 0.6 秒台で大きくは変わりません。
盤の計算が正しいかは、4 局(30・121・78・64 手)で、式の出した局面(81 マス + 持ち駒)を Python で進めた局面と 1 局面ずつ比べ、全部一致するのを確かめました(最初の形と今の形の両方)。
6. つまずき
この章の計算式エンジンの性質は、印刷屋 Ver.8 で試したことです。版が変われば変わることがあります。
6.1 ループの中の LET で外の変数が消える
最初は、ループの中の LET で外の変数に代入し直せば、と試して「getVal FIELD CODE NOT FOUND: s」になりました。AI は「ループの中から外の LET の変数が見えない」と説明しましたが、開発者に「変数名が異なれば見えるはず」と言われて試し直すと、言い方の誤りでした。
| 式 | 結果 |
|---|---|
LET(s, "x", ARRAY_FOR(3, k, s & k)) |
["x0","x1","x2"](見える) |
LET(s, "x", ARRAY_FOR(3, k, LET(t, s & k, t))) |
見える |
LET(s, "x", ARRAY_FOR(3, k, LET(s, CONCAT(s, k), s))) |
エラー |
LET は最後に、自分が代入した名前を値の表から消します。ループの中の LET で外と同じ名前に代入すると、1 回目の終わりに外の s も消えます。そこで、ループをやめて、1 つの LET の中で代入し直す形(5.5)にしました。
後で、開発者から「ループの前に LET で変数を定義して、ループ内でセットすると、次のループでも参照できる」と教わりました。ループの中の LET ではなく、FSET(変数, 値) で書き換えます(10.4)。
6.2 100 個の上限
5.5 の形で REPLACERE が 101 個になると、「getVal FIELD CODE NOT FOUND: s」(変数が無い)になりました。前回の記事 の 6.1 と同じしくみ(エンジンは 1 つの名前につき左から 100 個まで呼び出しを先に取り出し、101 個目からは字のまま残す)です。50 手(REPLACERE 100 個)ずつ DIC_ITEM(DIC("p", LET(…)), "p") で包むと、300 手でも動きました。
6.3 正規表現の書き出し
正規表現は、名前付きの組((?<名前>…)、\k<名前>)で書いて、番号の組に直す道具で出しました。
-
持ち駒が増えない: 取った駒の字と数を、省略できる先読み
(?:(?=…(…)))?で取ろうとして、いつも空でした。JavaScript は空に一致した繰り返しの回を捨てます(前回の記事と同じ)。外側を先読みにして、中で字を進める(?=(?:…(…))?)の形にしました -
打った駒が盤に出ない:
\k<l3>00(組の後ろに 00)を\500と書き出していて、JavaScript が別の数として読みました。後ろ参照は(?:\5)、置き換えの文字列は$05の 2 桁で書くようにしました -
指し手の一覧が字句のまま: Python の文字列に
\\\\dと書いて、式の中が\\d(バックスラッシュの字)になっていました
6.4 SVG と html2canvas
印刷屋の PDF は、帳票の HTML を html2canvas で画像にしたものです。盤の崩れと字の下が切れるのは、印刷屋と同じ html2canvas 1.4.1 で描いた画像で AI が気づきました。
- 盤が崩れる: SVG に width・height を付けていなかったので、ブラウザーで開くと正しいのに、html2canvas では盤の一部を大きく描きました。20 倍の大きさ(1960 × 1940)を付けて、img で縮めました
-
後手の玉が出ない: 駒の五角形の id を
kにしていて、後手の玉の<g id='k'>と重なりました。五角形の id をzに -
持ち駒の行の字の下が切れる:
overflow: hiddenの行で、字の下半分が描かれませんでした。overflow を外し、ページの見出しと余白を詰めて 1 図を 408px に収めました
6.5 表示手数 0 が最終手になる
IF(表示手数 = "", …) は、数値の 0 でも真になりました(0 と "" が等しい)。ISBLANK にしました(範囲の開始・終了、ファイル名の式も)。
6.6 設定の保存
- 最初のセッションを、フォルダーを選ばずに始めてから作業フォルダーに移ったので、印刷屋の MCP が作業フォルダーを見つけられませんでした(「設定項目 print-craft.workspaceRoots の行が見つからない」)。作業フォルダーで動く別のセッションを作って保存しました
- 保存は、1 つ目のボタンを
pcraft_save_settings、2・3 つ目をpcraft_update_buttonで足しました(最初の形の設定)。ツールは設定の全文を受け取るので、AI が手元の道具の出力を書き写します。1 手ぶんの同じ塊(61 字)を 50 個 × 4 並べる部分で、塊の数を誤りました(50 個のところが 49 個、4 つのDICが 5 つ)。保存したファイルを手元の出力と比べて見つけ、JSON のエスケープ(\u0075はuと同じ値)を 5 個ごと・DICごとの目印にして書き直すと一致しました
7. 実機の PDF
印刷屋の画面(Ver.8)で、枠あり版 7 本・枠なし版 10 本の PDF を作りました。どれも最初の形(5.5)の設定で作ったものです。今の形(5.6)は、手元のエンジンとプレビューで出力が同じことを確かめました(実機の PDF はまだ作っていません)。どれも A4 縦、1 ページが 2480 × 3508 の JPEG(150 dpi の 2 倍)です。
| レコード | ボタン | ページ | 枠あり | 枠なし |
|---|---|---|---|---|
| 1(30 手) | 全局面 | 4 | 2.64 MB | 2.32 MB |
| 1 | 範囲(開始・終了とも空) | 4 | 2.32 MB | |
| 1 | 指定した手数(空 → 最終手) | 1 | 0.30 MB | |
| 2(121 手) | 全局面 | 16 | 9.48 MB | 8.46 MB |
| 2 | 範囲 41〜56 | 2 | 1.15 MB | |
| 2 | 指定した手数 50 | 1 | 0.39 MB | |
| 3(78 手) | 全局面 | 10 | 6.11 MB | 5.47 MB |
| 3 | 範囲 21〜36 | 2 | 1.28 MB | 1.14 MB |
| 3 | 指定した手数 27 | 1 | 0.37 MB | 0.34 MB |
| 4(64 手) | 全局面 | 9 | 5.43 MB | 4.81 MB |
| 5(誤り) | 全局面 | 3 | 1.53 MB |
- ページ数は、どれも手元のプレビューと同じでした
- 図 8 つのページは 1 ページ約 0.6 MB。線と字の多いページなので、五線譜(1 ページ 140〜250 KB)より大きくなります。枠なし版は、描く線が減るぶん 10〜12% 小さくなりました
- 見た目(駒の位置と向き、成駒、持ち駒の数、直前の手の色、図の並び、最後の図の「△投了」、誤りの赤い枠)は、手元で描いた画像と見比べて同じでした(画素では比べていません)
依頼文の「150 dpi と 96 dpi で確かめる」は、手元で、印刷屋と同じ手順(ページごとに html2canvas の scale 2 → JPEG 質 0.5 → jsPDF)で PDF を作って比べました。2 局目の全局面(16 ページ)は、150 dpi で 9.5 MB・描画 1.9 秒、96 dpi で 4.9 MB・1.4 秒です。96 dpi でも駒の字は読めましたが、ボタンは 150 dpi にしました。
8. 対応していないこと・気を付けること
- 移動元の無い書き方(▲7六歩、右・左・直・上・引・寄)、平手以外の手合、局面からの開始(詰将棋など)、変化(分岐)は扱わない
- 駒の動き方(その駒が移動元から移動先へ動けるか)や、二歩・王手放置などの反則は確かめない。止めるのは 3.3 の誤りだけ
- 200 手まで(超えたら注記を出し、第 200 手まで)
- 駒の字の書体は、SVG の中で Hiragino Mincho ProN・Yu Mincho・MS Mincho の順。PDF を作る PC に入っている書体で描かれる。Windows の PC では確かめていない
- 実在の対局の棋譜を使うときは、出典と使ってよいかを、使う人が確かめる
9. 設定ファイル
設定ファイルは 3 ボタンで 96 KB(枠なし版は 93 KB。normalize を通した形)あり、記事に載せるには大きいので、GitHub のサンプルに置きました(項目定義と見本のレコードも)。枠なし版が settings.json、枠あり版が settings-frame.json です。
印刷屋の設定画面のアップロードで取り込めます(アプリ番号は読み替えてください)。項目のフィールドコードは、1 章の表と同じにします。
10. 計算式エンジンの改善案
この帳票と前回の五線譜で回り道をした所を、計算式エンジンのコードを読み、印刷屋 Ver.8 の zip のエンジンを手元で動かして確かめました。今後の改善案としてまとめます(数字は手元の Mac での試しです)。
10.1 値の表のキーを数えずに作る
エンジンは関数の結果などを値の表に入れ、キーを表の大きさ(Object.keys(…).length)で作っています。表は評価の間ずっと増えるので、表を数える時間も増え、全体の時間が呼び出し回数の 2 乗で増えます(前回の記事の 6.2)。キーを連番にしたエンジンの写しで比べました。
| 式 | いま | キーを連番に |
|---|---|---|
JOIN(ARRAY_FOR(1000, k, IF(k > 0, "a", "b")), "") |
0.31 秒 | 0.04 秒 |
同じものを CONCAT の中に 10 個 |
11.8 秒 | 0.21 秒 |
| 19 個(評価回数の上限 10 万回で止まる) | 44 秒で止まる | 0.37 秒で止まる |
| 盤面図の全局面(2 局目) | 0.67 秒 | 0.61 秒 |
どれも出力は同じでした。盤面図の差が小さいのは、時間の多くが正規表現にかかるためです。
10.2 LET の変数を、入れ子の範囲で持つ
LET の変数は項目と同じ表に入り、LET の終わりに消されます(前の値には戻しません)。そのため、次のことが起きます。
| 式 | 結果 |
|---|---|
LET(s, "a", t, LET(s, "b", s), CONCAT(t, "/", s)) |
エラー(s。内側の LET が外の s を消す。ループが無くても起きる) |
CONCAT(手合, "/", LET(手合, "xyz", 手合), "/", 手合) |
エラー(手合。項目と同じ名前の変数を使うと、LET の後で項目が消える) |
変数を入れ子の範囲(スコープ)で持ち、抜けるときに前の値に戻せば、どちらも動きます。ループの中から外と同じ名前に代入し直す形(6.1)も書けるようになります。当面は、LET の変数名を、外の変数名や項目コードと重ねないようにします。
10.3 式を 1 回だけ解析する
いまは評価のたびに式の文字列を書き換えて解析し、文字列の字面は 1,000 個まで、関数は名前ごとに左から 100 個まで取り出します。101 個目は字のまま残り、囲むループより先に評価されます(前回の記事の 6.1、この記事の 6.2)。ループの中身は、回るたびに解析し直します。この帳票の文字列の字面は 1 ボタン 580〜610 個で、上限 1,000 に近いです。
式を一度だけ木にして評価すれば、上限とこの順番の問題が無くなり、ループの中の解析も要らなくなります。すぐできるのは、上限を超えたときに黙って字のまま残さず、エラーで止めることです。
10.4 前の回の結果を次の回に渡す(FSET で書ける)
記事を書いた後で、開発者から「ループの前に LET で変数を定義して、ループ内でセットすると、次のループでも参照できる」と教わりました。セットは FSET(変数, 値) です。LET の変数は項目と同じ表に入るので(10.2)、項目に値を入れる FSET で書き換えられ、LET のように消されません。
LET(s, "x", a, ARRAY_FOR(3, k, FSET(s, CONCAT(s, k))), s) → "x012"
5.5 の 200 手ぶんの並びを、この形(5.6)に置き換えて比べました。
| 5.5 の形(200 手を並べる) | FSET のループ | |
|---|---|---|
| 式の字数(全局面のボタン) | 15,665 字 | 3,371 字 |
| 評価回数(全局面。1 局目 30 手 / 2 局目 121 手) | 3,301 / 3,323 回 | 965 / 2,625 回 |
| 時間(2 局目の全局面、手元) | 0.6〜0.7 秒 | 0.6 秒前後 |
見本の 5 件 × 3 ボタンの 15 通りで、出力は 1 字も同じでした。印刷屋の検査(normalize)はエラー・警告 0、プレビューは式のエラー 0 です。DIC で 50 手ずつ包む必要も無くなります(ループの中の REPLACERE は字面で 2 個)。この帳票の設定は、この形に切り替えました(5.6)。
試して分かった注意があります。
-
FSETは、関数の説明ではBUTTONで使うもの(クリックしたときに項目に値を入れる)です。LETの変数を書き換えられるのは、変数が項目と同じ表に入っているからです -
CONCATの中に置くと 2 回評価されます(LET(s, "", a, CONCAT(FSET(s, s & "a")), s)はaa)。ループの本体に直接置くか、ARRAYの中に置きます(ARRAYの中は 1 回ずつ、前から順) - 数の 0 を入れると空になります(
LET(n, 5, a, FSET(n, 0), n)は空、"0"なら 0)。足し算で数える変数は、1 回目が 0 だとそこで空になり、以後も空のままです
改善案は次のとおりです。
- この書き方を資料(関数の説明・帳票レシピ集)に載せる。10.2 の範囲の直しをするときは、この書き方が動き続けるようにする
- 変数を書き換える関数(
SET(変数, 値)など)を用意するか、LETの変数にFSETを使ってよいと明記する。あわせて、CONCATの中での 2 回の評価と、0 が空になる扱いを直す - 前の回の結果を名前で読める関数(例:
ARRAY_SCAN(手の配列, u, m, 開始局面, 1 手進める式)が各回の結果の配列を返す)があれば、さらに短く書けるが、必須ではなくなった
10.5 エラーの文と評価回数
エラーは「getVal FIELD CODE NOT FOUND: s」「traverse tree check count > 100000 over.」だけで、範囲の外の変数なのか、101 個目なのか、上限なのかが分かりません。エラーに関数名・変数名と原因を入れ、評価回数と時間をプレビュー(印刷屋の画面と authoring の preview)に出せば、作る人が自分で遅い所を探せます。今回と前回は、それを数える道具を手元で作りました。
10.6 そのほか
-
ARRAY_MAPの要素が 0 だと空になる(JOIN(ARRAY_MAP(ARRAY(0,1,2), x, i, CONCAT("[", x, "]")), ",")は[],[1],[2]) - 数値の項目が 0 のとき、
表示手数 = ""は真(数値に換算できれば数値で比べる、という仕様どおり)。資料に「数値の項目が空かはISBLANKで見る」と書く(6.5) - 設定: この帳票は 3 つのボタンが同じ 15 KB ほどの定数の JSON を持っています(設定 93 KB のうち約 51 KB)。共通 CSS のように、ボタンどうしで共有できる定数があれば、設定が小さくなります
気を付ける点
- ループで前の回の結果を使うときは、ループの前に
LETで変数を決め、ループの中でFSET(変数, 値)で書き換える(CONCATの中に置かない。数の 0 は空になる。10.4)。ループの中のLETで外と同じ名前に代入すると、外の変数が消える - 同じ名前の関数が 1 つの式で 100 個を超えるときは、
DIC_ITEM(DIC("p", 式), "p")で包んで分ける -
LETの変数名は、項目コードと同じにしない(LETの後で、その項目が式から消える。10.2) - 状態は 1 つの文字列にまとめ、1 手を「引く」「書き換える」の
REPLACERE2 回にする。図も、局面ごとに関数を呼ばず、履歴の全体をまとめてREPLACEREで変換する - 置き換えの文字列で座標を作る(数字の後ろに 0 を付ける)と、正規表現だけで位置が決まる
- html2canvas で描く SVG には width・height を付ける。
overflow: hiddenの行は字の下が切れることがある - 数値の項目が空かは
ISBLANKで見る(= ""は 0 でも真)
まとめ
- KIF 形式の棋譜から、盤面図の PDF(指定した手数・全局面・範囲)を作る帳票を作った。棋譜の誤りは、止まった手と理由を赤い枠で知らせる
- 盤を進める計算は、最初は
LETの中で状態の文字列にREPLACEREを代入し直す形を 200 手ぶん並べた(評価回数は棋譜の長さに依らず約 3,300〜3,400 回)。後で教わった、ループの中でFSETで書き換える形に切り替え、出力は同じまま式が約 1/5、評価回数は手の数に比例(121 手で 2,625 回)になった(5.6、10.4) - 実機の PDF を見て、駒の枠なし版を作った。PDF も 1 割ほど小さくなった
- 回り道をした所を、計算式エンジンの改善案にまとめた。値の表のキーを連番にするだけで、
ARRAY_FOR1,000 回を 10 個並べた式が 11.8 秒から 0.21 秒になった(手元の試し。10 章) - 設計・見本の対局・計算式・確かめ・不具合の直しは AI に任せた。人が行ったのは、作りたいものを頼むこと、説明の誤りを指摘すること、実機で PDF を作って見せ、直してほしいところを伝えることだった
シリーズ「rex0220 印刷屋プラグイン - AI(Claude Desktop)に〜」:
- チャットで帳票を作らせる
- 帳票のデザインを作り込ませる
- 見積書から注文書・請求書を作らせる
- 組織図を設計させてから、アプリを作らせる
- QRコード付きの資産ラベルを作らせる
- 写真付きの点検報告書を作らせる
- ガントチャートアプリの工程表を作らせる(関連レコード一覧に対応した Ver.8)
- 休日アプリから年間カレンダーを作らせる
- 作文を原稿用紙(400 字詰め)の PDF にさせる
- 「あなたが生まれた日の太陽系」カードを作らせる
- 業務データから「番付表」を作らせる
- 番付表用の相撲字風の書体を作らせる
- ABC 記法の曲から「五線譜」を作らせる
- 将棋の棋譜から「盤面図」を作らせる(この記事)
関連リンク:





