JavaScript: SGF ツリービューのサンプル
SGF ツリービュー
囲碁で
SGF (Smart Game Format)
と呼ばれるテキストデータがよく用いられます。
HTML や XML に似たデータ構造を持つフォーマットで
ツリー分岐のデータが扱えます。
囲碁,SGF ファイルの処理
SGFデータは囲碁専用というわけではなくて、
ツリー構造を持つテキストメモ帳や
ちょっとしたアウトラインプロセッサなどでも使えます。
将棋やチェスでも利用してみた。
将棋とチェスのボード
ul,li タグを使ったツリービュー (2022)
TestHTML_SGFTree_ul_li_nest_css_...手のツリー表示のイメージ。 こちらは HTML のみで JavaScript の処理は入っていません。
表示のみのツリービュー
TestJS_SGF_TreeView2021.htmlSGFテキストをパースして HTML の ul, li タグのツリーで表示する。
SGFファイルの処理
(補足) チェスや将棋ではわざわざ SGFファイルを使う必要はありません。
SGFのオブジェクトをそのまま JSON
シリアライズしたデータファイルを扱う方が楽です。
JavaScript: ツリー構造のデータを扱う (2)
編集機能を持たせたツリービュー
JavaScript: ツリー構造のデータを扱う (3)◎クラス構成 + TreeView 表示のみのツリービュー + EditableTreeView ツリー編集機能を持つツリービュー + SGFTreeView SGF データ用のツリービュー ◎ EditableTreeView ツリーのノードの項目名の変更やカット&ペーストなどの編集機能を付ける。 ◎ SGFTreeView 囲碁、連珠、オセロ、将棋、チェスなどの SGF ツリービュー。 手のツリーデータを表示、編集。
SGF データの例
補足
こちらは SGF テキストのフォーマットの簡単な説明です。
ツリーの分岐のある複雑な SGF テキストは囲碁でしか使っていないので、
囲碁以外の他のアプリケーションで使う必要はありません。
オセロなど他のゲームで使用するときには SGF オブジェクトを
JSON テキスト化した JSON ファイルを使った方が簡単です。
JavaScript: JSON シリアライズ
単純な SGF ファイルの説明
下のサンプルは ノード名 N[] と コメントテキスト C[] のみの SGF テキストです。 CA[] はテキストの文字コードを表しています。 SGF テキストは HTML の ul, li タグの代わりに ; や () の記号を使っていると考えると分かりやすいです。 多少、混乱する部分もありますが・・・。
◎SGFデータ例1 (;CA[utf-8]N[ルートノード] (;N[ノード名1]C[コメント1]) (;N[ノード名2]C[コメント2,解説説明,サンプルテキスト] (;N[ノード名2-1]C[コメント2-1]) (;N[ノード名2-2]C[コメント2-2]) ) (;N[ノード名3]C[コメント3]) )上のSGFデータをツリー表示すると、例えばこのような表示になります。
ノードの中身はとてもシンプルです。
◎ Node の例 class Node { constructor(){ this.properties = {}; //ノードに関するデータを格納する連想配列。 this.nodelist = []; //子の Node を格納する配列。 } } properties には、例えば let node = new Node(); node.properties.N = "ノード名"; node.properties.C = "コメントテキスト"; のように Node に関するデータを格納します。
◎補足 ・JavaScript の properties = {}; は、Java, C# でいう Hashtable, Dictionary です。キーは string, 値は string やシリアライズ可能な 配列, Hashtable (Dictionary) になります。 ・properties には、データファイルに保存するデータだけセットします。 アプリケーションで一時的に付けるプロパティはセットしません。 例えばツリービューであれば、Node に対応する li 要素は properties にはセットしません。JavaScript: ツリー構造のデータを扱う (1)
将棋,チェスに利用した例
上の SGF に黒の手、白の手などの情報を足すと棋譜データになります。
◎SGFデータ例2 (注意) move の記述は将棋用のフォーマットがないので適当に作った。 (追記2021/12) そのうち、USI (将棋), UCI (チェス) の表記法と 揃うように変更する予定です。また SGF テキストを使わず、SGF の JSON に置き換える予定です。 (;GM[8]CA[utf-8]SZ[9]N[ルート]C[ルートのコメント]AW[51玉][61金][41金][31銀][71銀][21桂][81桂][11香][91香][13歩][23歩][33歩][43歩][63歩][53歩][73歩][83歩][93歩][22角][82飛]AB[69金][49金][59玉][29桂][89桂][19香][99香][79銀][39銀][17歩][27歩][37歩][47歩][57歩][67歩][77歩][87歩][97歩][88角][28飛] ;B[27歩-26歩] ;W[33歩-34歩] ;B[39銀-48銀]N[ノード名1] ;W[82飛-32飛]N[ノード名2] ) ・;文 1つノード、1つの手を表す。文の中に N[],C[] やラベル LB[] や時刻 DT[] などの 情報も入れられる。 AB[][]...、AW[][]... はあらかじめ置いておく駒。B[],W[] と AB[]AW[] を同じ ノード内に混在させない(と思う)。 ・GM[] データの種類を表す。1は囲碁、3はチェス、8は将棋 ・SZ[] 盤の大きさ。将棋では9, チェスでは8, 囲碁では 19,13,9など ・;B[move] 黒の手。こちらも N[],C[],LB[],DT[]なども入れられる。 ・;W[move] 白の手。上と同じ。上の SGF をツリー表示すると、例えばこんな感じになります。 こちらは 1手進むごとに ul でインデントしている例。
手の分岐があると、 それぞれの手以下のツリーを () で括って並列に並べます。 下のSGFデータは途中で手の分岐がある例です。
◎SGFデータ例3 (;GM[8]CA[utf-8]SZ[9]N[詰め将棋]C[将棋のサンプルデータ] ;N[001]AW[13馬][16玉][39角]AB[27飛][28龍] ( ;B[27飛-17飛]N[正解] ;W[39角-28角] ;B[17飛-16飛] ) ( ;B[28龍-17龍]N[失敗] ( ;W[16玉-25玉] ;B[27飛-25飛] ) ( ;W[39角-17角] ) ) ( ;B[27飛-25飛]N[失敗]C[王手になってない。] ;W[39角-28角] ) )上の SGF を表示すると、例えばこんな感じになります。
◎SGFデータ例4 ・取った駒を置いたり(B[00_-52金])、ラベル文字を入れたり(LB[][][]...)。 (;GM[8]CA[utf-8]SZ[9]N[詰め将棋]C[ルートのコメント] ;N[002]AW[51玉]AB[53歩]ABC[金] ;B[00_-52金]N[正解]LB[61:↖][62:←][42:→][41:↗] )
将棋とチェスは似た処理も多いので共通化しやすいです。
チェスの表示例。手のツリー表示も、 手数の少ない詰め将棋や詰めチェスでは使いやすいですが、 手数の多い対戦用の棋譜になると不便です。 とても深いフォルダにあるファイルを開くのと同じ話になります。 1手ずつクリックして次の手を開く操作になり面倒です。 囲碁でも単純なツリー表示とは別に 変化図表示という変則のツリー表示を使います。
SGFデータ、エラーになるデータの例
・B と W と AB,AW,AE は排他的 1つのノードの中に同時に現れない。 B と W は排他。下の説明 1) B と AB,AW,AE も排他。下の説明 2) W と AB,AW,AE も排他。下の説明 2) ◎1) 1つのノードの中に B と W が同時に現れないこと。 B : 黒の手 W : 白の手 ・正常なデータ ;N[黒の手]B[27歩-26歩] 黒27歩を26に移動 ;N[白の手]W[22角-88馬] 白22角を88に移動、馬に成る ・エラーデータの例 ;N[ノード名]B[27歩-26歩]W[22角-88馬] (誤り)B と W が同時に現れる。 1つのノードで 2手分の情報がある。 ◎2) AB, AW, AE は B や W がないノードにだけ入れられる AB : 黒の駒を置く AW : 白の駒を置く AE : 指定した座標の駒を削除 ・正常なデータ N[テーマ図]AB[17歩]AW[13歩]AE[55] B や W がないノード。 ・エラーデータの例 N[黒の手]B[27歩-26歩]AB[17歩] (誤り)B と AB が同時にある。 N[黒の手]B[27歩-26歩]AW[13歩] (誤り)B と AW が同時にある。 N[黒の手]B[27歩-26歩]AE[17] (誤り)B と AE が同時にある。 N[白の手]W[33歩-34歩]AB[17歩] (誤り)W と AB が同時にある。 N[白の手]W[27歩-34歩]AW[13歩] (誤り)W と AW が同時にある。 N[白の手]W[27歩-34歩]AE[17] (誤り)W と AE が同時にある。 ◎3) AE, AB, AW はノード内で同じ座標のデータが2つ以上存在しないこと ・同じ座標のデータが 2つ以上あると処理の順序依存が起こる。 処理の順番によって結果が変わる。 ・エラーデータの例1 ;N[テーマ図]AB[33銀]AW[33歩] 33のデータが2つある。どちらを先に処理するかによって結果が変わる。 1)AB[33銀] 2)AW[33歩] だと 33は白歩になる。 1)AW[33歩] 2)AB[33銀] だと 33は黒銀になる。 ・エラーデータの例2 ;N[テーマ図]AB[33銀]AE[33] 33のデータが2つある。 1)AB[33銀] 2)AE[33] だと 33は駒が削除される。 1)AE[33] 2)AB[33銀] だと 33は黒銀になる。
セキュリティ関連の注意点
HTML と JavaScript を使ったアプリケーションでは セキュリティの話がついてまわります。
表示する文字のエスケープ
SGF データを扱う場合も SGF の内容が安全なデータであれば問題ないですが、 出所不明の SGF データを扱う場合には注意が必要です。 SGF データのコメントテキストやノード名、ラベルなどの文字列に JavaScript コードが混ざっていると不正な処理が行われる可能性があります。 そのため、出所不明のデータテキストを HTML 上で表示する場合、 適切にエスケープすることが必要です。
◎ 誤り例 まずい処理: innerHTML にテキストをセットする。 div.innerHTML = "<span>" + comment + "</span>"; //このような処理はダメ comment に HTMLタグがあり、onclick("func()"); のような処理が 入ってるとまずい。 セキュリティを気にする Webアプリケーションでは innerHTML に直接文字列を代入する処理を使用しません。 ◎ 正しい例 1) div.textContent = comment; 2) const text = document.createTextNode(comment); div.appendChild(text); 1),2) はどちらも同じです。 comment 文字列は必ずエスケープされます。
将棋やチェスで SGF を扱う場合
将棋やチェスではSGFツリーのオブジェクトをそのまま JSON で保存するのが便利です。
将棋やチェスでは SGF ファイルを使っていないので SGF
ファイルのテキストフォーマットを気にする必要がありません。
SGF で保存する場合は SGF形式のテキストファイルにせず、
JavaScript の JSON でツリーオブジェクトを保存するのが便利です。
データファイルの読み書きが JSON シリアラズ・デシリアライズの
1行の記述で済みます。
専用のパーサーやフォーマッターの処理を用意する必要がありません。
JavaScript: ツリー構造のデータを扱う (1)