gorogoronyan FC2

JavaScript: SGF ツリービューのサンプル

SGF ツリービュー

囲碁で SGF (Smart Game Format) と呼ばれるテキストデータがよく用いられます。 HTML や XML に似たデータ構造を持つフォーマットで ツリー分岐のデータが扱えます。
囲碁,SGF ファイルの処理

SGFデータは囲碁専用というわけではなくて、 ツリー構造を持つテキストメモ帳や ちょっとしたアウトラインプロセッサなどでも使えます。 将棋やチェスでも利用してみた。
将棋とチェスのボード

ul,li タグを使ったツリービュー (2022)

TestHTML_SGFTree_ul_li_nest_css_...
手のツリー表示のイメージ。 こちらは HTML のみで JavaScript の処理は入っていません。

表示のみのツリービュー

SGFツリーを HTML で表示
SGFツリーを HTML で表示
TestJS_SGF_TreeView2021.html
SGFテキストをパースして 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)

関連

inserted by FC2 system