TOP/拡張スクリプト/カラムと並び替えのチュートリアル

カラムと並び替えのチュートリアル

チュートリアル

ここでは実例を使って独自のカラムを作る方法について説明します。

拡張スクリプトを使うようにする。

まずは設定を参照して拡張スクリプトを使用するように設定します。 設定したスクリプトファイルにプログラムを書いていきます。

分散、標準偏差のカラムと並び替え

分散と標準偏差を表示するカラムを作ります。 分散とは簡単に言うと各項目の平均値からの差の二乗の平均値で、標準偏差はその平方根になります。 詳しくはインターネットなどで調べてください。

共通関数の作成

分散の計算はカラムを作る際と並び替えの際に使用するので分散の値を計算する関数を作ります。 関数の場所は何処でもいいですが、Organizer のテーブルに登録します。 関数名は calculate_variance_of_decks_by_entry とします。 分散を求めるのは「メインボードの稲妻」や「サイドボードの強迫」の分散値になります。 なので、それらを表す Entry を引数にした関数になります。

また Entry は通常のカードではない場合(土地合計など)がありますので、 通常のカードの場合のみ分散を計算するようにして、それ以外の場合は null を返すようにします。

::Organizer.calculate_variance_of_decks_by_entry <- function( entry )
{
  local decks_count = decks.len().tofloat();
  if ( entry.card.is_normal() ) {
    if ( decks_count == 0.0 ) {
      return 0.0;
    }
    
    local average = ::Organizer.get_sum_of_decks_card_count_by_entry( entry ).tofloat() / decks_count;
    return ::Organizer.get_sum_of_decks_board( entry.board_type, function( board ) {
      local tmp = average - board.get_count_of( entry.card );
      return tmp * tmp;
    } ) / decks_count;
  }

  return null;
}

並び替え用クラスの作成

関数を用意したのでクラス等を用意していきます。

まずは分散の並び替え用クラスを作ります。これはカラムをクリックした際の並び替えに使用されます。 Sorter クラスを継承したクラスを作ります。 名前は VarianceSorter とします。

class VarianceSorter extends Sorter {
}

コンストラクタを作りそこで自身の名前を設定します。

class VarianceSorter extends Sorter {
  constructor() {
    this.name = "分散";
  }
}

次に並び替え用の関数 compare_for_sort を作ります。 この関数は引数として Entry を二つ受け取ります。

昇順の場合この関数は、第一引数が大きいなら正の整数、等しい場合は 0、 そして第一引数が小さいなら負の整数を返すようにします。 降順の場合はその逆になります。 この場合は降順にします。

また、それぞれのボードだけで並び替える場合は Sorter に用意してある compare_same_board_normal_only を使うと便利になります。

class VarianceSorter extends Sorter {
  constructor() {
    this.name = "分散";
  }

  function compare_for_sort( x, y ) {
    return Sorter.compare_same_board_normal_only( x, y, function( x, y ) {
      local tmp_x = ::Organizer.calculate_variance_of_decks_by_entry( x );
      local tmp_y = ::Organizer.calculate_variance_of_decks_by_entry( y );
      return tmp_y - tmp_x;
    } );
  }
}

これで並び替え用の VarianceSorter クラスは完成となります。

カラム用のクラスの作成

次に、各分散値を求めてセルに入力する用のクラスを作ります。 Processor クラスを継承したクラスを作ります。 名前は VarianceProcessor とします。

class VarianceProcessor extends Processor {
}

コンストラクタを作りそこで自身の名前と、カラムをクリックした際の並び替えとして 先ほど作成した VarianceSorter クラスのインスタンスを設定します。

class VarianceProcessor extends Processor {
  constructor() {
    this.name = "分散";
    this.sorter = ::VarianceSorter();
  }
}

次に、セルの内容を入力するための関数 process_function を作ります。 と言っても分散求める関数を既に作ってあるのでそれを呼び出すだけです。

class VarianceProcessor extends Processor {
  constructor() {
    this.name = "分散";
    this.sorter = ::VarianceSorter();
  }

  function process_function( entry ) {
    return ::Organizer.calculate_variance_of_decks_by_entry( entry );
  }
}

これでカラム用の VarianceProcessor クラスは完成となります。

メニューへの登録

クラスが完成したので、そのインスタンスをアプリケーションに登録します。

Organizer には初期化時に参照されてメニューを構成する変数が用意されています。 カラムの場合は processors 変数が、並び替えの場合は sorters 変数に配列が 設定されているので、その配列にインスタンスを追加します。

::Organizer.processors.append( ::VarianceProcessor() );
::Organizer.sorters.append( ::VarianceSorter() );

以上で完成となります。 Sylvan Analyzer を起動してメニューに分散があるかどうか確認してください。

標準偏差

次に、標準偏差について作成します。 標準偏差は分散の値の平方根になります。

分散の時と同じように、まずは関数を作成します。 関数名は calculate_standard_deviation_of_decks_by_entry とします。

::Organizer.calculate_standard_deviation_of_decks_by_entry <- function( entry )
{
  local tmp = ::Organizer.calculate_variance_of_decks_by_entry( entry );
  if ( tmp != null ) {
    return ::sqrt( tmp );
  }
  return tmp;
}

各クラスも分散の時と同じように作成します。

class StandardDeviationSorter extends Sorter {
  constructor() {
    this.name = "標準偏差";
  }

  function compare_for_sort( x, y ) {
    return Sorter.compare_same_board_normal_only( x, y, function( x, y ) {
      local tmp_x = ::Organizer.calculate_standard_deviation_of_decks_by_entry( x );
      local tmp_y = ::Organizer.calculate_standard_deviation_of_decks_by_entry( y );
      return tmp_y - tmp_x;
    } );
  }
}

class StandardDeviationProcessor extends Processor {
  constructor() {
    this.name = "標準偏差";
    this.sorter = ::StandardDeviationSorter();
  }

  function process_function( entry ) {
    return ::Organizer.calculate_standard_deviation_of_decks_by_entry( entry );
  }
}

セパレータとサブメニューの登録

次に、メニューに登録するのですが少し工夫してセパレータとサブメニューを作ってみます。

セパレータは配列に SubMenu.Separator (中身はnull) を設定することで追加することが出来ます。

::Organizer.processors.append( SubMenu.Separator );
::Organizer.sorters.append( SubMenu.Separator );

サブメニューはサブメニュー用のクラスが用意されています。 サブメニューのコンストラクタの第 1 引数に名前、第 2 引数に配列を設定することで サブメニューを作成することができます。 サブメニューの名前を「統計」としてその中に分散と標準偏差を追加します。

::Organizer.processors.append( SubMenu.Separator );
::Organizer.processors.append( ::SubMenu( "統計", [
  ::VarianceProcessor(),
  ::StandardDeviationProcessor(),
  ] ) );

::Organizer.sorters.append( SubMenu.Separator );
::Organizer.sorters.append( ::SubMenu( "統計", [
  ::VarianceSorter(),
  ::StandardDeviationSorter(),
  ] ) );

Sylvan Analyzer を起動してメニューにセパレータとサブメニューと その中に分散と標準偏差が追加されている事を確認してください。

合計項目の処理

コレまでのチュートリアルをこなしても、 合計の値(土地合計など)の分散や標準偏差が出ない事に気づいたかもしれません。 これは、分散を計算する関数内で通常のカードの場合のみ分散を計算するようにしたからです。

なので、各エントリの場合分けをして計算すれば良いのですが、土地合計の場合、クリーチャー合計の場合、… といちいち記述していたのでは面倒くさいですね。 Sylvan Analyzer ではそういった合計項目の場合は TotalEntry クラスを継承した Entry が引数として渡されるので、そのように場合分けをすれば全ての合計項目に対応できるようになっています。

Organizer には読み込み済のデッキそれぞれに対して処理する関数がいくつか用意されています。 詳しくはリファレンスのOrganizerを参照して下さい。

TotalEntry クラスを継承したクラスには should_count 関数が用意されており、 その関数の引数に Card を指定するとそのカードを合計にカウントするかどうかを取得できます。

これらを利用して修正した分散を計算する関数は以下となります。

::Organizer.calculate_variance_of_decks_by_entry <- function( entry )
{
  local decks_count = decks.len().tofloat();
  if ( entry.card.is_normal() ) {
    if ( decks_count == 0.0 ) {
      return 0.0;
    }

    local average = ::Organizer.get_sum_of_decks_card_count_by_entry( entry ).tofloat() / decks_count;
    return ::Organizer.get_sum_of_decks_board( entry.board_type, function( board ) {
      local tmp = average - board.get_count_of( entry.card );
      return tmp * tmp;
    } ) / decks_count;
  }

  if ( entry instanceof TotalEntry ) {
    if ( decks_count == 0.0 ) {
      return 0.0;
    }

    local average = ::Organizer.get_sum_of_decks_board(
      entry.board_type, @( board ) board.count_total_of( @( card ) entry.should_count( card ) ) ).tofloat() / decks_count;

    return ::Organizer.get_sum_of_decks_board( entry.board_type, function( board ) {
      local tmp = average - board.count_total_of( @( card ) entry.should_count( card ) );
      return tmp * tmp;
    } ) / decks_count;
  }
  return null;
}

TOP/拡張スクリプト/カラムと並び替えのチュートリアル