2020年7月16日木曜日

TFT 10.14 Peeba Comp

こちらのガイドの自分用まとめです。

難しいですが完成すると非常に強く、プレイしていて面白い構成です。
SET1のドラゴンガーディアンソーサラーを思い出します。

最終構成

アイテム

優先度: ベルト > グローブ > ヘラ
  • ニーコにタンクアイテム(※)
  • ソルにモレロが必須
※ なんでもいいが回避アイテムはジン対策になるので環境的に○

オプショナル

  • フローズンハート → フィズ/エコー
  • アイオニックスパーク → フィズ/エコー
  • プロテクター → ソル
  • スタガ → ソル
  • ショウジン → ジャンナ/ルル

プレイ方法

とにかくレベル9を目指します。
  • 3-6まではとにかく柔軟にプレイしHPを高く保つ
  • 4-1くらいからHPを失いながらお金を貯めることになる (周りが構成だしてくるので)
  • 5-1前後もしくはHP20-30くらいになったらLv9へ
  • Lv9でオールインして構成を出す
レベル9に行けそうになかったらLv8でワンチャンオールインしましょう。

移行方法

バード

最高。

ヴァンガード/ミスティック

カシオペア→ソルに自然に移行できるので理想的です。

シャコキャリー

雑に強いのでアイテム倉庫にして使えます。

プロテクター/スターガーディアン

ニーコを中心に構築すると自然に移行できます。

2019年11月11日月曜日

TFT 9.22 SET2 構成メモ

よく見る構成をメモ。

グレイシャル/ポイズン/レンジャー/バーサーカー

基本構成

https://lolchess.gg/builder/set2?deck=6befa760045d11eaa75cfdfe878420a0
レンジャー2体足して4レンジャーが王道。
前衛/後衛/CCのバランスが良くて安定感がある。キャリーの選択肢も広くて組みやすい。

ポイズン/プレデター

基本構成

https://lolchess.gg/builder/set2?deck=d0829f10045d11eaa593c3b375ab5cfd
5体でコア構成が揃うのでレベル4/5でのハイパーリロールで☆3を作るのが強い。

オーシャン/メイジ

基本構成

https://lolchess.gg/builder/set2?deck=5f6ed0c0048311eab273b5888ff152bb
メイジキャップがないと構成が窮屈。
ブランド以外が低コストなのでできればレベル4/5でリロールして☆3を作りたい構成。

シャドウ

構成例 (シャドウ/インフェルノ/サモナー )

https://lolchess.gg/builder/set2?deck=b8955a70045f11eabe36d3c1b56a1ca1
4体でシャドウを発動させて、好きな構成を盛る。
サモナー/ブレードマスター/バーサーカーあたりがよく合うがシャドウ自体が強いのでお好みでOK。

2019年10月16日水曜日

TFT 9.20 レベル8ラッシュガイド

試してみたらパワーを感じたので書きます。
Reddit記事の意訳・要約です。(この記事)
https://www.reddit.com/r/CompetitiveTFT/comments/dhbcft/how_to_rush_level_8_and_abuse_it/

一行で

レベル8まで耐えて節約して高コストユニット盛り盛りの超強い構成を一気に完成させる。

最終的な構成

ナイト4/ドラゴン/ガーディアン/ソーサラー3 を目指します。
具体的には、
https://lolchess.gg/builder?deck=08f594e0edb711e9a9119f5014f5248d
こんな感じです。

この構成の強いところはパンテオンとオレリオン・ソルが非常にタフになるところです。
ドラゴンとガーディアンとナイトによってスキルと通常攻撃の両方を大きく軽減します。

高コストユニットを集める

この構成には コスト5が3体、コスト4が3体が必要 なので一見集めるのが非常に困難に感じると思います。
しかし、パッチ9.20でレベル8のときのコスト5ユニットの出現率が1.6倍(5%→8%)になったので、レベル8でまとまったリロールを行うことで集めることが可能になりました。

ゲームの流れ

レベル8まで極力リロールせず、お金を節約し続けてください。
50G以上のお金は基本的に経験値に使用し、レベル8を急ぎます。

目安としては狼ラウンド(3-6)のあと2ラウンドほど待って、レベル8にあげてリロールを開始します。
このときHPは30以上、ゴールドは50G以上あるのが理想ですが、状況が悪ければもう少し早くリロールを開始してください。

序盤 (クルーグ/2-6まで)

序盤はとにかく低コストの星2ユニットを作ります。ついでにナイトも集めます。
理想はナイト4(モルデカイザー、ガレン、ダリウス、ポッピー)ですが、あまりこだわらずに星2ユニットを作って中盤に備えます。

中盤 (クルーグ後/3-1から)

最も重要で、一番面白いのがこの中盤です。
早いプレイヤーはこの辺りからお金を使って構成を強化し始めるので、HPを一気に失う恐れがあります。

そのため、お金を節約しつつも限られたリソースを最大限活用してHPを守ります。ここをどのように乗り切るかがポイントです。
(元記事で紹介されているのは以下の構成です。)

  • 4コストユニットのキャリーとナイト4
  • アサシン
  • エレメンタリスト
  • 4デーモン
終盤 (狼後/4-1から)

レベル8まで上げてリロールします。
リロール中は理想の構成にこだわりすぎず、柔軟に対応しましょう。

例えばカーサスがでなければ、別のソーサラーでもいいし、ファントムのためにキンドレッドでもいいです。ケイルがでなければ別のナイトで代用できます。

カイサやヤスオの星2を見つけたらそれを使っても強いです。この時点でコスト5の星2を持っているのはたぶんあなただけなので、シナジーが無くても使う価値があります。

リロールして構成が整ったら、HPに余裕があればもう一度エコノミーしてもいいですが、後がない場合は毎ラウンドリロールして少しでもユニットを星2にできないか試してください。

アイテムについて

特に重要なのは ユーミ騎士の誓い です。どちらかがあれば構成を完成させられます。(騎士の誓いを利用する場合の構成例)
https://lolchess.gg/builder?deck=a089d2d0edc511e98c3025d32449d700

アイオニックスパーク も可能なら入手したいアイテムです。防御力に優れた構成のため、とても有効に機能します。

回転寿司では ヘラ もしくは ムダニデカイロッド を狙うのがオススメです。特に中盤はHPが減ってくるはずなので必要なアイテムを狙うチャンスです。

中盤を乗り切るためにはアイテムはできるだけ積極的に使う必要があります。最終的な構成に多少マッチしなくてもいいのでアイテムを活用してください。

感想

このガイドを読んで早速4試合やってみました。結果は8-1-1-1でした。(プラチナ帯)

最初の一戦は要領がつかめず中盤でHPが無くなりましたが、中盤を上手く乗り切ると物凄く強いです。コスト5が本当に揃うのかなーと思いましたがたくさん出ました。

2017年5月17日水曜日

ASP.NET MVCでCQRSを利用してORMをうまく使う

以前(2013年頃)自分が設計した内容のメモです。
今はASP.NETの世界から離れて久しい。

はじめに

CQRS(コマンドクエリ責務分離)という考え方を使って、参照系と更新系でORMとSQLを使い分けていいとこ取りするという趣旨です。

サンプル

サンプルを作りました。
https://github.com/herara-ofnir3/cqrs-sample

CQRS(コマンドクエリ責務分離)とは

CQRSについて詳しくはぜひこちらをどうぞ。

[Greg Young流CQRS - Mark Nijhof]
http://d.hatena.ne.jp/digitalsoul/20100712/1278886009
※ 上記記事のサンプル
https://github.com/MarkNijhof/Fohjin

ORMを使いたい理由と使いたくない理由

更新処理がとても快適

煩雑なSQLの発行処理を書かずとも、エンティティの状態を変化させ、永続化してやればうまいこと更新してくれます。
特に one-to-many や many-to-many の関係をカスケードしてうまく処理してくれるのは、とても素晴らしいメカニズムです。

バッチ処理的な一括更新や大量データを高速に更新するのには向いていませんが、業務アプリケーションの通常の更新処理なら非常にマッチします。

参照系でしぬ

参照系の処理がやりにくいです。
普通なら簡単なSQLを投げてそのまま結果を表示するだけ、みたいな画面ですら、気にすることが多すぎます。

  • 遅延読み込みされるとまずい(N+1問題)から、これとこれをフェッチして...
  • 一回流してみて実際のSQLをチェックしないと...

みたいな調子です。
どうしてもデータの取得が冗長になりがちです。
特にHQLでSQLを再現していると泣けてきます。

よく言われるORMの問題点はこの参照系の問題に集中している気がします。
どう考えてもシンプルにSQL書いたほうがはやくて確実ですし。

ORMの弱点を補うためのCQRS

ORMの得意な更新系の処理だけORM使って、参照系はSQLを書けばいい、という考えに至りました。
そして、参照系と更新系の分割と一貫性のために、CQRSを適用しました。

CQRSはイベントソーシングとセットで語られることが多いのですが、イベントソーシングはスルーします。
データアクセスがクエリ(参照系)とコマンド(更新系)で違うだけの実装です。

その他利点

実際やってみると色々いいところがありました。

良い作りに縛れる
アプリケーションに必要な参照/更新操作がクエリ/コマンドという形でいい感じにコード上に浮き上がってきます。
いわいる「変な作り」というのがやりにくくなります。
テストがしやすい (疎結合になる)

コマンドの発行は全てコマンドバスに投げるだけになるので、
コントローラが依存するのはコマンドバスといくつかのクエリだけとなります。
そのため、アンチパターンとしてありがちなファットコントローラに陥りにくくなります。

依存関係が絞られますし、トランザクションなどの煩雑な処理が隠蔽されるため、テストが非常に簡潔になります。
自然に結合度が下がり、構造がシンプルになります。
おかげでメンバが積極的にテストを書いてくれました。

更新処理のログが簡単にとれる

全てのコマンド(更新操作)はコマンドバスを通して発行されるので、更新処理を簡単に確実にログできます。(コマンドバスにログ処理を挟むだけ)
このあたりの機構にイベントソーシングを組み合わせると強力ですが、ログをとれるだけでも大きな恩恵を得られました。

運用・保守をする上で、現状のデータに至るまでの経緯が全て記録されているというのは、極めて有効でした。

2014年7月11日金曜日

Dapper で例外 "シーケンスに要素が含まれていません" が起きる

Dapper を使っていたらハマったので。(Dapper っていうより LINQ ?)

現象

以下のように Dapper で Single/First メソッドを使うと例外が起きることがあるようです。

var user = connection.Query<User>("select * from Users where UserName = @userName", new { userName = "hoge" }).Single();
System.InvalidOperationException: シーケンスに要素が含まれていません

クエリの結果が必ず1件あるにも関わらず、起きることがあります。また、常になるというわけではありませんでした。(環境にもよる...?)
私が遭遇したケースでは、数十回から数百回に一度程度再現しました。

解決策

原因と理由はよくわかりませんが SingleOrDefault/FirstOrDefault に変更すると発生しなくなります。

var user = connection.Query<User>("select * from Users where UserName = @userName", new { userName = "hoge" }).SingleOrDefault();

参考

2013年7月20日土曜日

IE10 の互換モードで jquery.validate.unobtrusive を使うとエラー

最近ハマったのでメモ。解決策は以下の記事を参考にしました。

エラーの内容

jquery.validate.unobtrusive を IE10の互換モードで利用するとこんなエラーが出ることがあります。

行: 2699
エラー: メンバーが見つかりません。

どうやらページ内に form があるだけで起こるようで、jQuery その他のスクリプトが全滅して全く動かなくなります。怖い。

なんとなく IE10 の互換モードのバグっぽい雰囲気ですが、時間がなかったためあまり詳しく調べることができませんでした。(いいわけ)

jQuery の以下の set メソッド呼び出しで、novalidate という属性を設定しようとしてエラーが発生しているようです。

nodeHook = jQuery.valHooks.button = {
  get: function( elem, name ) {
   var ret;
   ret = elem.getAttributeNode( name );
   return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
    ret.nodeValue :
    undefined;
  },
  set: function( elem, value, name ) {
   // Set the existing or create a new attribute node
   var ret = elem.getAttributeNode( name );
   if ( !ret ) {
    ret = document.createAttribute( name );
    elem.setAttributeNode( ret );
   }
   return ( ret.nodeValue = value + "" );
  }
 };

解決策

いろいろな記事を見て回ると、jQuery の中身を書き換えたりする方法もありましたが、ちょっと怖いので互換モードの指定で逃げました。

以下のように X-UA-Compatible で IE が利用可能な最新のレンダリングモードを指定します。

<meta http-equiv="X-UA-Compatible" content="IE=edge" />

おわりに

いろいろなところでこの問題がおきてテンション下がりました。

jquery.validate.unobtrusive は ASP.NET MVC のテンプレートに入っているし、form がページ内にあるだけでエラーが起こるので、結構影響大きいのではと思うのですが、全然日本語の記事がなかったので書いてみました。

2013年6月16日日曜日

値オブジェクトのためのカスタムモデルバインダー

値オブジェクト

単一の値を表すオブジェクトのことで、いわいる不変オブジェクトのことです。(イミュータブルってやつですね)
例えばこんな感じのヤツですね。

バージョン情報 (e.g: "1.2", "11.101") を単一の値として扱う
public class Version
{
    public Version(int major, int minor)
    {
        Major = major;
        Minor = minor;
    }

    public int Major { get; private set; }

    public int Minor { get; private set; }

    public override string ToString()
    {
        return string.Format("{0}.{1}", Major, Minor);
    }
}

Major, Minor といったプリミティブ値を Version という型にまとめ、状態を変えれないようにすることで扱いがシンプルになります。

これはオブジェクト指向プログラミングのプラクティスとしてよく言われるものですが、ASP.NET MVC で不変オブジェクトを扱うと困ることがあります。

困る例

例えばこんなURLを使いたいと思って、アクションを用意するとします。

/Home/SomeAction?version=1.2
public ActionResult SomeAction(Models.Version version)
{
    ViewBag.Version = version; // version は null で、ModelState にもエラーが入る
    return View("SomeView");
}

当然 version 引数にはクエリパラメータで指定した "1.2" が Major と Minor に入ってきてほしいと思うわけですが、これはうまくいきません。

それもそのはずで、Version という型にどのように値を入れていいか、モデルバインダが知らないからです。

カスタムのモデルバインダを作る

せっかく値オブジェクトをコツコツ作っても、モデルバインダで使えないのでは寂しいので、カスタムのモデルバインダを作りましょう。

上記の Version 型の場合はこんな感じのモデルバインダを作ります。

public class VersionBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueKey = bindingContext.ModelName;
        var valueResult = bindingContext.ValueProvider.GetValue(valueKey);
        string rawValue = null;

        if (valueResult != null)
            rawValue = valueResult.AttemptedValue;

        if (string.IsNullOrEmpty(rawValue))
            return null;

        Version result;

        if (Version.TryParse(rawValue, out result))
        {
            return result;
        }
        else
        {
            bindingContext.ModelState.AddModelError(
                valueKey,
                string.Format("'{0}' は無効な値です。", rawValue)
                );

            return null;
        }
    }
}

実際の文字列からの変換は TryParse パターン とかで適当に実装します。

上記のように作ったモデルバインダを、Application_Start 時に登録します。
App_Start ディレクトリに、以下の様な起動用クラスをまとめるといいと思います。

public class BinderConfig
{
    public static void RegisterBinders(ModelBinderDictionary binders)
    {
        binders.Add(typeof(Models.Version), new VersionBinder());
    }
}

これを Global.asax の Application_Start メソッドで呼んであげます。

protected void Application_Start()
{
    // その他いろいろな起動設定...
    BinderConfig.RegisterBinders(ModelBinders.Binders);
    // その他いろいろな起動設定...
}

これで Version 型の値がしっかりモデルバインディングされます。

これたくさん作るの面倒だよね?

モデルバインディングしたい値オブジェクトがたくさんあったら、一つ一つカスタムのモデルバインダを実装するのは恐ろしく面倒です。

コードも定形なのでスーパークラスにまとめると楽です。

public abstract class ValueObjectBinder<T> : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueKey = bindingContext.ModelName;
        var valueResult = bindingContext.ValueProvider.GetValue(valueKey);
        string rawValue = null;

        if (valueResult != null)
            rawValue = valueResult.AttemptedValue;

        if (string.IsNullOrEmpty(rawValue))
            return null;

        T result;

        if (TryParse(rawValue, out result))
        {
            return result;
        }
        else
        {
            bindingContext.ModelState.AddModelError(
                valueKey,
                GetParseErrorMessage(rawValue)
                );

            return null;
        }
    }

    protected abstract bool TryParse(string input, out T result);

    protected virtual string GetParseErrorMessage(string rawValue)
    {
        return string.Format("'{0}' は無効な値です。", rawValue);
    }
}

さっきの Version 型の場合、コレを使うとこうなります。

public class VersionBinder : ValueObjectBinder<Version>
{
    protected override bool TryParse(string input, out Version result)
    {
        return Version.TryParse(input, out result);
    }
}

これならまあ作ってもいいかなってレベルじゃないでしょうか。もし大規模なアプリケーションでこれでも大変なら、T4 とか使う手もありそうです。

まとめ

これで値オブジェクトをたくさん使えるよ。

TFT 10.14 Peeba Comp

こちらのガイドの自分用まとめです。 https://www.reddit.com/r/CompetitiveTFT/comments/hraunp/tft_1014_break_the_meta_new_peeba_comp_set_35/ 難しいですが完成すると非常に強く、プレ...