Sassで見出しデザインの@mixinを作ろう!

こんにちは、@pompom0c0 です。 この記事は Enigmo Advent Calendar 2018 の18日目の記事です。 17日目の記事は @natten の はじめてのキーボード設計におけるアンチパターン でした。

今日はBUYMA内で使用している見出しデザインついて紹介して行こうと思います。

前置き

今回この記事を書こうと思った経緯

  • Sassの@mixinを実践でどんな風に使っているか知ってほしい!
  • 使う見出しデザインがパターン化されているので、デフォルトであると便利だと思ったから

想定読者

  • LPの実装をするデザイナーの方
  • @mixinは自分から書いたことがない人。

今回扱わないこと

  • FLOCSSの設計について
  • @extend@contentでの書き方紹介

mixinのおさらい

  • 別で定義したスタイルを@includeでなんども呼び出すことができます。
  • 引数を指定して、関数的な使い方もできます。
@mixin hoge($color: #fff, $size: 1rem) {
  color: $color;
  font-size: $size;
}
.ttl__1 {
  @include hoge(#000, 2rem);
}
.ttl__2 {
  // 引数は初期値のを使用
  @include hoge();
}
.ttl__1 {
  color: #000;
  font-size: 2rem;
}
.ttl__2 {
  color: #fff;
  font-size: 1rem;
}

引数を使えば初期値で出力するだけでなく、「今回はもう少し違った雰囲気で見せたいな...」という際には値を変えれば デフォルトでCSSを設定していても、出力結果を冗長させることなく記述することができます。

あとはよくある見出しデザインをSassでまとめて記述しておきます。

よく使う見出しデザインSass

縦線

@mixin line--ver($color: #000, $size: 30px) {
  &::after {
    content: '';
    width: 1px;
    margin-top: 1em;
    margin-bottom: .5em;
    display: inline-block;
    // 線の色と長さを引数にする
    height: $size;
    border-right: solid 1px $color;
  }
}

横線

@mixin line--under($color: #000, $size: 1px) {
  position: relative;
  &::before {
    content: '';
    position: absolute;
    left: 50%;
    bottom: -15px;
    display: inline-block;
    width: 60px;
    -moz-transform: translateX(-50%);
    -webkit-transform: translateX(-50%);
    -ms-transform: translateX(-50%);
    transform: translateX(-50%);
    // 線の色と長さを引数にする
    height: $size;
    background-color: $color;
  }
}

文字横に線

@mixin line--side($color: #000, $size: 1px) {
  border-left: solid $size;
  border-right: solid $size;
  width: 45%;
  margin: 0 auto 1em;
  padding-bottom: 0;
  border-color: $color;
}

文字下に蛍光ペンでマーキング

@mixin line--maker($color: #ffc63b, $size: .2rem) {
  background: linear-gradient(transparent 60%, $color 60%);
  padding: 0 $size;
}

コンテンツの幅の分だけ色がついてしまうので、マーキングしたい箇所にのみspanタグでclass指定してください。

<div class="framework__ttl">
  <h2><span class="ttl">タイトル</span></h2>
</div>

鍵カッコ

@mixin mark--quo($color: #000, $size: 1px) {
  position: relative;
  padding: 1rem;
  &amp;::before, &amp;::after {
      content:'';
      width: 20px;
      height: 30px;
      position: absolute;
      display: inline-block;
  }
  &amp;::before {
      border-left: solid $size $color;
      border-top: solid $size $color;
      top: 0;
      left: 0;
  }
  &amp;::after {
      border-right: solid $size $color;
      border-bottom: solid $size $color;
      bottom: 0;
      right: 0;
  }
}

まとめ

久しぶりに使う見出しデザインを実装する直前になって 「あ、これどうやって書くんだっけ・・・」とググるのは生産的じゃないな〜と思ってまとめてみました。 @mixin使ってるけど引数までは使ってない!使いこなしてみたい!って方に参考になればと思います。

参考サイト

Sass(SCSS)のmixin, extendなどまとめ Sassで@mixinを作る時に知っておきたい基礎知識 より素早くCSSコーディングするための、Sass(SCSS)のmixinスニペット集

はじめてのキーボード設計におけるアンチパターン

こんにちは。Enigmo インフラグループの @natten です。 この記事は Enigmo Advent Calendar 2018 の17日目の記事です。 16日目の記事は @enigmo7 の デザインパターンとリファクタリング でした。

本日の記事は技術寄りの話ではなく趣味の世界、自作キーボードのお話です。

キーボードを設計するモチベーション

2015年登場のErgoDoxに端を発する(諸説あります)メカニカルキーボード自作のムーブメントは国内でも盛んになる一方で、今年はHelixやErog42, Mint60にCrkbdなどなどエポックメイキングなプロジェクトが多く登場しました。 自分もご多分に漏れずこの潮流に飲み込まれてしまい、キーボードを購入するために技術書典に足を運んだり、キーキャップ一式を購入してみては「こんなプラスチックの塊が2万円?!」と愕然とする日々です。

さて、しばらくキーボードを買ったり作ったり積んだりしていると、不思議なもので「自分でもキーボードを設計してみようかな?」という気持ちが芽生えてきます。 様々なキーボードのキー配置に触れているうちに好みのキー配列や指の可動域、多用するキーのポジションがわかってきて自分に最もフィットしたキーボードは自分にしか作れない、という結論になりました。

とにかくキーボードを設計する

じゃあ実際にキーボードを設計してみましょう、ということで教則本を買います。

Crkbdを設計した @foostan さんによる、自作キーボード設計ノウハウ集です。 KiCADの使い方からケースの設計、PCBの発注と自作キーボード作成に必要な作業の一通りがわかりやすく記述された良書です。

こちらを斜め読みして、とにかく勢いとお試し感覚で設計してできあがった基板の実物がこちらになります。

https://pskbd.booth.pm/items/1044084

自作キーボードの世界に明るくない方はなんだこれ、となるでしょう。 じゃあ自作キーボード界の人ならどうかというと、同じくなんだこれ、という感想になります。 パーツを仮置きしてみると不思議がさらに加速していきます。

はみ出るように不自然に配置されたProMicroスペース、分離型でもないのにリバーシブル基板、キーと平行するように伸びるUSBケーブル。 なんでこんなことになってしまったのか、と冷静に振り返ってみると思い当たる節が次から次へと。

といったわけで、今回は

こんなキーボードができたよ!すごいだろ!

ではなく、

こんなキーボードができてしまった!こんなことしなければよかった!

という点をご紹介します。

キーボード設計アンチパターン

コンセプトを山盛りにする

  • 3列キーボードとか4列キーボードに追加できる、数字キー/ファンクションキー代わりの1列キーボードが欲しいな。数字キーだから0〜9で10キーかな
  • キースイッチテスターってただのアクリルベース土台タイプが多いけど、打鍵感だけじゃなくて接点の違いとかも確認しようと思うと実際のキー入力が必要になるよね。ホットスワップタイプにすれば実用的なテスターとしても使えるんじゃない?
  • 普段は使わないけど気まぐれに光らせたいときがあるなー。表面実装のLEDはハードルが高いから、とりあえずUnderGlowのLEDテープが取り付けられるようにパッドだけ用意しとこ
  • モゲMicro怖いからProMicroソケット化したいし、もしものときに取り外せるようにトッププレートで覆わないようにしておいたほうがいいかな

ダメなところ

  • そんなにいっぱい並立できません
  • 机上の空論なので、実際に基板を作るとコンセプト間で対立・矛盾が発生する

改善方法

  • 各コンセプトの要素(実用,趣味,利便,実験,コストなど)のバランスを見直す。特に実験方向にステータスを振れば振るほど破綻する可能性が高くなる
  • コンセプトはなるべく絞ると設計しやすい。反面、凡庸なものになってモチベーションが下がることもあるので、少しだけ背伸びするのも良い

何も考えずにリバーシブル基板にする

今後左右分離型のキーボードを作ることになるだろうし、多くの分離型キーボードと同じようにリバーシブル基板にしよう! 気分や環境によって選べるのは利点だよね?

ダメなところ

  • MCU周辺が無秩序なビアだらけになって汚い
  • リバーシブルにしたばかりに配線がめちゃくちゃ面倒になる
  • そもそも意味がない

改善方法

  • 作ろうとしているキーボードの用途や量産体制、目的がリバーシブル基板に見合っているかよく考える
  • 実例が多いからという雑な理由でリバーシブルにするより、片面できれいな配線方法を模索するのも一つの手です

基板以外の構成パーツについて深く考えない

いろんなキーボードのソースデータからフットプリントのパーツも流用して基板の設計ができたぞ! 実際にフットプリントを置いて配線してみると、どうにもならない箇所があったから多少妥協して当初のイメージとは違う形になったけど、実物が見たいからとりあえず発注しよう。リセットスイッチとかスペーサーはあとで検討して調達すればいいや

ダメなところ

  • なんとなく流用したパーツの入手性やコスト、サイズ感を意識していない
  • 平面上の寸法しか見ていないと、サンドイッチタイプのケースで物理的な干渉が発生することが多い

改善方法

  • 実際にキーボードとして組み立てる場合のパーツ構成と具体的な寸法を書き出してみる
  • 参考にしたキーボードが手元にあったら採寸して、パーツ配置の意図を読み取る
  • 使用する可能性のあるパーツは検証用に一通り手元に揃えておく

二言目には未完成とか言い出す

最低限は形になったけど、ダメな点もいっぱいあるしGithubで公開するのはちょっと気が引けるな…マサカリ飛んできたら心が折れちゃうかもしれないし、しばらく手元で温めようかな

ダメな点

そういうこと言ってるやつは永久に完成させられない

改善方法

とにかくプロジェクト名を決めてGithubに空リポジトリを作れ、Twitterとか自作キーボードDiscordでガンガン進捗を上げてバンバン叩かれよう

アンチパターンを振り返って

よくよく振り返ってみると、どれもキーボード設計に限ったことではありませんね。 プロダクト開発やシステム設計でもありがちな話ばかりです。

  • コンセプトを山盛りにする
    • →理想を高く掲げすぎず、実装する要素を精査する
  • 何も考えずにリバーシブル基板にする
    • →一般事例に惑わされず、自分の目的を見直す
  • 基板以外の構成パーツについて深く考えない
    • →細部にとらわれず、最終的なビジョンと全体像を考えて念入りに準備をする
  • 二言目には未完成とか言い出す
    • →初手から完璧を求めず、スモールスタートでもいいから人の目に晒して育てる

何かを利用・享受する側だけではなく、作り手の側に回り実際に手を動かしてみると、趣味の世界からでも得るものがあるという学びでした。 自分と同じようにはじめてキーボードを設計してみようかな、と考えている方の参考になれば幸いです。


Enigmoでは何かを作り出すことに熱中できる仲間を募集しています。 Adevent Calendarの記事ラインナップからもわかるように、バラエティに富んだメンバーがお待ちしていますので、自作キーボードを一緒にワイワイやってくれる方の ご応募お待ちしております。

明日の記事の担当は@pompom0c0です。お楽しみに。

デザインパターンとリファクタリング

この記事はEnigmo Advent Calendar 2018の16日目です

デザインパターンリファクタリング

こんにちは。iOSチームでエンジニアをやっています

今チームでは、プロジェクトの進行と並行してリファクタリングを行なっています

対象プロジェクトは、MVVMのデザインパターンを多用しています

そのプロジェクトをどうリファクタリングしていったかをツラツラと書いていこうかと思います

TL;DR

  • デザインパターンって、設計パターン。うまく対処するためにどう設計していけばいいかをまとめたものです
  • デザインパターンを各画面で分けよう
  • 簡単な実装なのに、MVVMを利用したら複雑になってしまった、であれば、設計を間違えている

リファクタリング

リファクタリングについては、短期間で見ると、ビジネスとしてぶっちゃけ一円にもなりません。 しかし、リファクタリングをする、しないでは、未来のプロジェクトの進行速度に影響していきます。

なぜリファクタリングをしたのか

  • メンテ、新規開発がしづらい
  • 数行いじると全然違う画面でエラーがでてしまう
  • どの画面がどのViewControllerなのかわからない
  • Swiftらしいコードに
  • MVVMなのに、UIViewControllerがふとっている

着手前

[アプリ設計]

  • RxSwift / RxSwift Community の様々なライブラリを使って MVVM を 試してみた実装
  • iOS5の時代のライブラリをそのまま使い続け、iOSの新しい機能が実装されていない

[構成]

  • Managers
    • サーバAPIへアクセスする
    • レスポンスをModelへパースする
    • パースしたModelの一部を、インスタンス変数で管理
    • シングルトン
  • ViewController
    • 別ViewControllerに遷移
    • Userのアクションへのリアクション
  • View
    • SnapKitによるレイアウト実装
  • ViewModel
    • Managerにデータを要求、受け取ったデータを管理
    • UIViewControllerにアクセスしてUIの更新等を行う
  • Model
    • データ

[アプリ設計]

[構成]

  • Components
    • Model
    • データ
    • ViewController
    • 別ViewControllerに遷移
    • Userのアクションへのリアクション
    • Delegate / DataSourceの実装
    • Storyboard / Xib
    • レイアウト実装
    • View
    • iPhoneのサイズによるFontサイズの調整など
  • Network
    • APIClient
    • サーバに要求する
    • Responseを生成する
    • Response
    • Codableによって APIをモデル化

比較

Before

After

やったこと

  1. Componentsというディレクトリを作ってその中に各画面毎のfileをいれるようにしました
  • その画面に関係するfileが明確になり、関係ないものは使わないようにチームで心がけるようになりました
  • 各Componentでデザインパターンを変更できるようにしたので、あった設計をできるようにしました
  • 簡単な画面については、コード量が少ない設計に変更
  1. APIClient / JSONデコーダー ともに複数あったので、新しいAPIClientを作成し、Codableを使うように変更 -> 古いものはまとめて削除。Objective-Cの時のような、json["key"] as? Intのような実装をなくしました

  2. ソースコードに対するコメント / BTS なぜその実装になったのか等記載がない -> 現状を知り着手しやすいように、複雑な処理になる部分は、シーケンス図 / コメントで動作を記載

  3. 一行直すだけで、関係ないと思ったところでエラーが起こる -> Component間で扱うデータを減らし、非結合にしてComponent間の影響を減らしました

  4. Manager / Utility クラスは、シングルトンで実装 -> シングルトン実装のクラスを極力減らす。シングルトンにするとpropertyをつけたくなる人がいるので避けます

  5. UIはSnapKitのみで実装 -> Storyboard / Xib で実装、IBInspectable, IBDesignableを使い、GUIで状況を把握しつつ実装  デザイナーが作ってくれているレイアウトに沿った物を作れる ( SnapKitだけの時、cornerRadiusなど漏れが発生していた )

まとめ

よかったこと

  • コードの削除をかなりできた 着手前と今の差分:

    2467 files changed, 142326 insertions(+), 271177 deletions(-)
  • すべての画面に、MVVMをあてようとして無理している部分が多々あり結果、読みにくいコードになっていたのを直せた

  • レビューする際に、コードよりも、Storyboard / Xib で見た方がわかりやすかった Before:

    override func viewDidLoad() {
    super.viewDidLoad()
    .... 40行ほど
    }
    
    After:
    override func viewDidLoad() {
    super.viewDidLoad()
    imageView.image = UIImage(named: ... ) // 1行のみ
    }
    

  • ドキュメント作成した事で効率改善が行えた

    • デザイナーとの画像受け渡しは、Xcodeから直接やってもらえることになった
    • ドキュメント自体もレビューされるので、その際に共有できた
    • レビューする側も、どういう事をしたいロジックなのか理解できた

リファクタリングを行う時、考慮すること

  • チーム全員なので、非エンジニアにも理解してもらわないといけない
  • リファクタしたいところを共有しておく
  • 企画・ディレクター案件がある際、その画面対応の際にまとめて行う
  • ディレクターには、案件を画面毎にまとめるように整理してもらう

e.g. - ホームが重いので改善する作業中に、haptic feedbackの導入や、お気に入りのハートを他の画面と同期するように仕組みを入れました - 検索画面を改修する際に、保存した検索条件に起因する部分もまとめて、書き直し整理を行ったりしました

Chainerでまるばつゲームを学習させてみた

はじめに

エニグモ サーバーサイドエンジニアの @gugu です。 この記事はEnigmo Advent Calendar 2018の15日目です。

日頃はBUYMAの機能改修を行っていますが、弊社では月末のプレミアムフライデーは業務と関係ない開発を行って良い日となっています。 そこで、前から興味のあった機械学習で何か作ってみようと思いました。

Chainerを使って「まるばつゲーム」を学習させてみたので、簡単にやったことを書こうと思います。

github.com

※ちょくちょくリファクタするかもです。

本題に入る前に

私のスペック

  • 日頃はRailsでサーバーサイドの開発
  • pythonはラズパイでLチカをしたぐらい
  • はじめての機械学習

仕様など

  • みんな知っているまるばつゲームです。
  • 学習させるコンピュータは常に先手
  • 0〜8の計9マス。どこに打つかを学習させます。(もう既に打たれたマスにも打つことが可能。当然ルール違反なので負け)
     |     |
  0  |  1  |  2
     |     |
-----+-----+-----
     |     |
  3  |  4  |  5
     |     |
-----+-----+-----
     |     |
  6  |  7  |  8
     |     |
  • 盤面のデータは誰も打ってないマスは0、○は1、☓は2の値が入ります。
     |     |
     |     |  ○
     |     |
-----+-----+-----
     |     |
  ☓  |  ○  |  ○
     |     |
-----+-----+-----
     |     |
     |  ☓  |  ☓
     |     |

      ↓ ↓ ↓

     |     |
  0  |  0  |  1
     |     |
-----+-----+-----
     |     |
  2  |  1  |  1
     |     |
-----+-----+-----
     |     |
  0  |  2  |  2
     |     |

配列だと[0, 0, 1, 2, 1, 1, 0, 2, 2]

  • コンピュータと戦わせて学習させます。(完全ランダムに打つ。打たれているマスには打たない。不毛な戦いになるので。)
  • 盤上の状態を入力して、出力結果のうちの最大値のマスに打ちます
     |     |
     |     |  ○
     |     |
-----+-----+-----
     |     |
  ☓  |  ○  |  ○
     |     |
-----+-----+-----
     |     |
     |  ☓  |  ☓
     |     |

のとき入力値は[0, 0, 1, 2, 1, 1, 0, 2, 2]
出力結果がもし[0.1, 0.2, 0.4, 0.2, 0.1, 0.3, 0.9, 0.1, 0.5]だったら、数値が最大のマス6に打ちます。

  • 勝敗結果から打ったマスが好手だったのか悪手だったのかを教えます

Chainerの基礎知識

ニューラルネットを定義

from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L

class MyChain(Chain):
    def __init__(self):
        super(MyChain, self).__init__(
                # 9-20-9の3層(隠れ層の20はなんとなく。)
                l1 = L.Linear(9, 20),
                l2 = L.Linear(20, 9)
        )

    def __call__(self, x):
        # 伝播(sigmoidでも良いがleaky_reluの方が結果が良いような気がする。)
        h = F.leaky_relu(self.l1(x))
        o = self.l2(h)
        return o

初期設定

# ニューラルネット
model = MyChain()

# 確率的勾配降下法(Stocastic Gradient Descent)を使用
opt = optimizers.SGD()
opt.setup(model)

打つ場所を決める

# 盤上の状態データをfloat32に形成する(chainerがfloat64には対応していないため)
x = Variable(np.array([input_data], dtype=np.float32))

# 勾配を0に初期化(chainerのお決まりごと)
model.zerograds()

# 入力xを変換し出力yへ
y = model(x)

# 出力の最大値を打つ
y.data.argmax()

学習させる

# 教えるデータをfloat32に形成する
t = Variable(np.array([teacher_data], dtype=np.float32))

# 出力yと教えるデータtとの差分を算出(平均二乗誤差)
loss = F.mean_squared_error(y, t)

# 逆伝播
loss.backward()

# 最適化
opt.update()

上記の基礎知識を使ってまるばつゲームを学習させていきます。

学習方法

強化学習はよく理解していないので、基礎知識のみで自己流に学習させます。

# result: 勝敗結果
#   ドロー:0
#   勝ち:1
#   負け:2
#   既に打たれたマスに打った:3
def learn(self, result):
    for i, y in enumerate(models):
        # marks[i]: 打ったマス番号
        # y.data[0]: 出力値
        teacher = self.teacher(result, marks[i], y.data[0])
        t = Variable(np.array([teacher], dtype=np.float32))

        # 出力yと正解tとの差分を算出(平均二乗誤差)
        loss = F.mean_squared_error(y, t)

        # 逆伝播
        loss.backward()

        # 最適化
        opt.update()

    # 学んだらリセット
    del models[:]
    del marks[:]
def teacher(self, result, mark, model):
    data = []
    # draw, win
    if result == Settings.WIN:
        for i in range(9):
            if i == mark:
                # 打ったマスに値を与える
                data.append(1)
            else:
                # 打ってない箇所は現状維持
                data.append(model[i])
    # lose or same place
    elif result == Settings.LOSE or result == Settings.SAME_PLACE:
        for i in range(9):
            if i == mark:
                data.append(-1)
            else:
                data.append(model[i])
    # draw
    else:
        for i in range(9):
            if i == mark:
                data.append(0)
            else:
                data.append(model[i])
    return data

学習方法説明

勝った場合

     |     |
     |     |  ○
     |     |
-----+-----+-----
     |     |
  ☓  |  ○  |  ○
     |     |
-----+-----+-----
     |     |
  ○  |  ☓  |  ☓
     |     |

最後の一手の出力結果が[0.1, 0.2, 0.4, 0.2, 0.1, 0.3, 0.9, 0.1, 0.5]で、マス6に打った場合、

  • 出力結果: [0.1, 0.2, 0.4, 0.2, 0.1, 0.3, 0.9, 0.1, 0.5]
  • 教えるデータ: [0.1, 0.2, 0.4, 0.2, 0.1, 0.3, 1, 0.1, 0.5]

とマス6の値を1にします。

同じように3手目が[0.3, 0.1, 0.9, 0.7, 0.5, 0.3, -0.3, -0.8, 0.5]で、マス2に打っていた場合

  • 出力結果: [0.3, 0.1, 0.9, 0.7, 0.5, 0.3, -0.3, -0.8, 0.5]
  • 教えるデータ: [0.3, 0.1, 1, 0.7, 0.5, 0.3, -0.3, -0.8, 0.5]

とマス2の値を1にします。

※これを1手目まで繰り返します。 ※上記は勝った例なので1にデータを変換しましたが、負けた場合は-1に、ドローは0にデータになります。

学習させてみた

※データは最後から100戦の勝率

100戦

win: 0.04
lose: 0.04
draw: 0.0
same_place: 0.92

1,000戦

win: 0.22
lose: 0.01
draw: 0.0
same_place: 0.77

10,000戦

win: 0.61
lose: 0.06
draw: 0.0
same_place: 0.33

100,000戦

win: 0.81
lose: 0.08
draw: 0.02
same_place: 0.09

強くなってる!

VS 人間

実際に100,000戦したコンピュータと戦ってみました。 ※コンピュータ:○、 人間:☓

     |     |
  0  |  1  |  2
     |     |
-----+-----+-----
     |     |
  3  |  ○  |  5
     |     |
-----+-----+-----
     |     |
  6  |  7  |  8
     |     |

      ↓ ↓ ↓

     |     |
  0  |  1  |  x
     |     |
-----+-----+-----
     |     |
  ○  |  ○  |  5
     |     |
-----+-----+-----
     |     |
  6  |  7  |  8
     |     |

      ↓ ↓ ↓

     |     |
  ○  |  1  |  x
     |     |
-----+-----+-----
     |     |
  ○  |  ○  |  x
     |     |
-----+-----+-----
     |     |
  6  |  7  |  8
     |     |

      ↓ ↓ ↓

     |     |
  ○  |  1  |  x
     |     |
-----+-----+-----
     |     |
  ○  |  ○  |  x
     |     |
-----+-----+-----
     |     |
  6  |  7  |  x
     |     |

まともに戦えるが、弱い。。

学習方法を変えてみた

どう変えたか?

「既に打たれたマスに打った」で負けた場合は、最後の一手のデータを調整するように変更します。

ソース

# result: 勝敗結果
#   ドロー:0
#   勝ち:1
#   負け:2
#   既に打たれたマスに打った:3
def learn(self, result):
    for i, y in enumerate(models):
        # 既に打たれたマスに打った and 最後の一手
        if result == 3 and i == len(models) - 1:
            # marks[i]: 打ったマス番号
            # y.data[0]: 出力値
            teacher = self.teacher(result, marks[i], y.data[0], True)
        else:
            teacher = self.teacher(result, marks[i], y.data[0], False)

        t = Variable(np.array([teacher], dtype=np.float32))

        # 出力yと正解tとの差分を算出(平均二乗誤差)
        loss = F.mean_squared_error(y, t)

        # 逆伝播
        loss.backward()

        # 最適化
        opt.update()

    # 学んだらリセット
    del models[:]
    del marks[:]
def teacher(self, result, mark, model, last_flg):
    data = []
    # draw, win
    if result == Settings.WIN:
        for i in range(9):
            if i == mark:
                # 打ったマスに値を与える
                data.append(1)
            else:
                # 打ってない箇所は現状維持
                data.append(model[i])
    # lose
    elif result == Settings.LOSE:
        for i in range(9):
            if i == mark:
                data.append(-1)
            else:
                data.append(model[i])
    # same plase
    elif result == Settings.SAME_PLACE:
        if last_flg == True:
            for i in range(0, 9):
                if i == mark:
                # 最後に打ったマスだけを`-2`に調整する。
                    data.append(-2)
                else:
                    data.append(model[i])
        else:
            for i in range(0, 9):
                data.append(model[i])

    # draw
    else:
        for i in range(9):
            if i == mark:
                data.append(0)
            else:
                data.append(model[i])
    return data

学習させてみた

10,000戦

win: 0.96
lose: 0.04
draw: 0.0
same_place: 0.0

コンピュータ相手だと9割以上勝てるようになって、打たれているマスには打たなくなりました。

VS 人間 (2)

100,000戦したコンピュータともう一度戦ってみました。 ※コンピュータ:○、 人間:☓

     |     |
  0  |  1  |  2
     |     |
-----+-----+-----
     |     |
  3  |  ○  |  5
     |     |
-----+-----+-----
     |     |
  6  |  7  |  8
     |     |


     |     |
  0  |  ○  |  2
     |     |
-----+-----+-----
     |     |
  3  |  ○  |  x
     |     |
-----+-----+-----
     |     |
  6  |  7  |  8
     |     |


     |     |
  0  |  ○  |  ○
     |     |
-----+-----+-----
     |     |
  3  |  ○  |  x
     |     |
-----+-----+-----
     |     |
  6  |  x  |  8
     |     |


     |     |
  x  |  ○  |  ○
     |     |
-----+-----+-----
     |     |
  3  |  ○  |  x
     |     |
-----+-----+-----
     |     |
  ○  |  x  |  8
     |     |

強い!まだ最強とまでは言えませんが、2つリーチを作れることを覚えたようです。

まとめ

機械学習はハードルがものすごく高いイメージでしたが、Chainerの基礎的な関数を駆使すれば初心者の私でも簡単な機械学習を作成できることがわかりました。 今後は強化学習DQNなど)をちゃんと勉強して、負けないレベルに修正できたらと思います。

Redashがバージョンアップ(v5)して便利になったこと&不便になったこと

エニグモ データ活用推進室 @kmt47 です。 この記事はEnigmo Advent Calendar 2018の14日目です。

概要

redashがv5にバージョンアップしました。(少し時間経ちますが) この記事では、ユーザ目線でredash v5の新機能を紹介します。 v3→v4へのバージョンアップと比べると、機能的な追加は少なめ&追加された機能の便利度も低めといった印象です。 また、v4で追加になった超便利機能「表示形式(format)」設定において、v5からパーセント表示の仕様が変更になっています。 それでは、上手に新機能を使って、redash作成を効率化していきましょう!

クエリ編(表、グラフ)

クエリを「お気に入り登録」できるようになった(便利度:★★)

クエリを「お気に入り登録」できるようになりました。

「お気に入り登録」したクエリは、redashのトップページに「Favorite Queries」として表示されます。 ※ redashのトップページとは、画面の最上段のリダッシュのマーク(💭)をクリックしたときの画面 また、クエリ一覧画面(Queries)では、右側の「☆Favorites」で絞り込むことができます。 小技として、最上段の「Queries」の右の下のボタンをクリックすると、「お気に入り登録」したクエリが選択できるようになっています。

「お気に入り登録」の方法は簡単で、クエリの画面でクエリ名の左の☆をクリックするだけです。 登録されたクエリは、★ が黄色になります。 もう一度 ★ をクリックすると、登録が解除されます。

良く利用するクエリは「お気に入り登録」しておくことで、トップページから簡単にアクセスできるようになります。

クエリに「タグ」を付けることができるようになった(便利度:★★)

クエリに任意の「タグ」を付けることができるようになりました。

「タグ」を付けることで、クエリ一覧画面(Queries)の右側のリストから選択したタグで絞り込むことができます。

「タグ」を付ける方法は、クエリの画面の最上段にマウスをナビゲーションすると「+Add tag」のボタンが表示されます。 そのボタンを押すとポップアップ画面が表示されるので、既に登録済みのタグを選択、または新たなタグを入力して付与することができます。 1つのクエリにタグは複数付けることができます。 付けたタグを削除したい場合は、タグの右に表示される鉛筆マークをクリックして、削除したいタグの「×」を押してください。

パラメータの作成ボタンが追加になった(便利度:★★)

これまでは、パラメータを作成するときは、クエリ入力画面に波括弧を二つ {{ }} 入力して、その間にパラメータ名を入れていたかと思います。 こんな感じです。→ {{ hoge }}

v5では、パラメータ追加ボタンが追加されました。 クエリ入力画面の下の {{ }} ボタンがそれです。

ボタンをクリックすると「Add Parameter」というポップアップが表示されます。 「Keyword」は、クエリに埋め込むパラメータの名前、「Title」はパラメータの入力エリアの名前です。 「Type」でパラメータのデータ型を選ぶことができます。

なお、これまで通りクエリ入力画面で直接追加することもできます。

パーセント表示の仕様が変更になった(便利度:マイナス☆☆☆☆)

この記事「Redashがバージョンアップ(v4)して便利になったこと」にも記載しましたが、v4で追加された最大の便利機能「数値の表示形式(フォーマット)をredashで設定できるようになった」のパーセント表示に関する仕様が変更になりました。

例えば、前年比を表示する場合、 「売上(今年) 120万円」÷「売上(去年) 100万円」の結果に対して、表示形式で「0.0%」を指定すれば、 120 ÷ 100 = 1.2 で 「120.0%」と表示されていました。

しかし、v5では、このままでは「1.2%」となってしまいます。 つまり、v4では自動的に100倍されましたが、v5でクエリで100倍する必要があります。 個人的な意見としては、v4の仕様の方が良い(正しい)と思います。 Excelでも1.2の値をパーセント表示にしたら120.0%になりますよね。それを敢えて100倍するなんて。

パーセント表示を使っていたクエリは、v5へのバージョンアップによって、全て修正が必要になりました。

ダッシュボード

ダッシュボードを「お気に入り登録」できるようになった(便利度:★★)

使い方はクエリと同じなので、詳細は省略します。

ダッシュボードに「タグ」を付けることができるようになった(便利度:★★)

使い方はクエリと同じなので、詳細は省略します。

ダッシュボードの変更時にグチャってならなくなった。(便利度:★★★★)

v5でもっとも良くなった点は、ここだと思います。 v4のユーザであれば「グチャって」いう意味が分かると思いますが、 v4では、ダッシュボードのクエリのサイズ、特に高さを変更していると、クエリのサイズが崩れて(「グチャって」)、取り返しのつかないことになった経験がある方も多いと思います。 その為、一度変更したら、保存して、また変更したら保存して、といったように崩れても元に戻れる対策を講じながら作業を行う必要がありました。クエリのサイズを自由に変更できる機能はv4で追加された便利な機能なのですが、ちょっと不具合があったようです。 それがv5では修正されています。ちょっと変更するごとに保存して、「グチャって」なったら修正をキャンセルして、といった作業から解放されたのは非常に助かりました。

まとめ

v5へのバージョンアップでは、便利機能の追加は少なめでした。 v4では、かなり便利な機能が多く追加されていたので、偶数バージョンのv6に期待します。

Org-modeを半年くらい使ってみた

Org-modeを半年くらい使ってみた

Enigmo Advent Calendar 2018の12日目の記事です。

こんにちは、エンジニアの@t4kuです。半年ほどorg-modeを使ってメモや、日々のタスク管理を行ってきたのでやってみた感想を共有しようと思います。

org-modeとは何か?

org-modeとはemacs上で動作するアウトライナーです。

アウトライナーは有名なところでいうとMacアプリではOmnioutliner や webアプリでもworkflowyなどがあります。

workflowlyについてはこちらの紹介記事がわかりやすいです(丸投げ) http://goryugo.com/20180412/dynalist-workflowy/

課題やタスクのブレークダウンなど考えをまとめたりするのに org-modeではこのようなツリー構造をプレーンテキストで書いておけば 鞍上いい感じに表示してくれます。

markdownでも同じようなことができますが、ノードを移動したりインデントを変えたりするのが面倒なのでそういう用途でmarkdownを使う人はいないと思います。

また、スケジュール機能やTODOやタグやクロック機能もあるのでこれだけで 見積もりや振り返りがプレーンテキストで完結します。

org-modeのここがいい

自分が使っていて特によいと思った機能です。

テーブル表記の入力が楽

勝手にフィールドの幅を調整してくれたりなかなか便利だなと思いました。

体験すると、qiitaのmarkdownでtableを書くことが苦行というかほとんど罰ゲームに感じるようになってきました。

画像や数式が差し込める

プレーンテキストでありながら画像も入れれるので、gitなどで履歴管理しつつ最低限わかりやすい ビジュアルをキープできるので、プログラマのメモとしてはいいバランスだと思います。

Latex記法で書いたものは数式が表示されます。

ソースコードが実行できる

org-babelという拡張があるのでソースコードブロックで書いたものを評価して、結果を表示できます。

※ob-ipythonというjupyterに繋ぐ拡張が必要ですが ob-ipython org-babel integration with Jupyter for evaluation of (Python by default) code blocks

スケジュール機能(アジェンダ)

ノードにスケジュールを設定しておくと、アジェンダコマンドを利用してその日にスケジュールされたタスク一覧(アジェンダビュー)を表示することができます。

※実際のファイルが出せないのでテキトーなタスクなのでわかりにくくてすいません

アジェンダビューはスケジュール日別に出したり、deadlineごとに出したり、タグごとに出したりいろいろカスタマイズできますが、自分の場合は、オペレーション系のタスクとプロジェクトごとのタスクごとに一覧化するようにして、一日毎の作業を管理する別のorgファイルにコピーします。

名称未設定2.png

一日のタスクを直列に並べると、あんまり余計なことを考えずにただこなしていけるような気がします。

クロック機能が便利

各ノードにTODOステータスやスケジュールを設定するだけでなく、実際に作業をする時にクロックインすると 時間を記録してくれます。また、任意の期間でレポートを作成できます。

f:id:enigmo777:20200415200414g:plain

活用法

何も考えずに単体のorgファイルをそのまま使っててもいいのですが、自分の場合は下記のように Dropbox配下のディレクトリを分けてメモと予定/振り返りを管理しています。

orgファイル間は簡単にリンクを貼って辿れるので、アジェンダファイル(Agenda/work.org)内のトピックから必要なファイルにリンクを張っておけば、だいたい事足ります。

どのようにファイルをオーガナイズするかやどういう単位で分割するかということについては深遠なテーマで、半年くらい使った素人ではまだキャッチアップできない(というか一生できる気がしない)のですが、下記のyoutubeシリーズはすごく勉強になりました。

org-mode tutorials

半年くらい使ってみた感想

何をどこに書くべきかが決まってきてキーバインドにも馴染んでくると、フローを壊さずに開発してるときもも打ち合わせしてるときも、アイデアをためておけるので、何かのインタラプションがあっても、安心して忘れられる他、ググる回数やブラウザで遷移する回数が減ったきがします。

また、テキストなのですべてgitで管理できるので、週次ごとにプルリクエストを作るようにすると diffを見れば振り返るのが一目瞭然です。

あと副産物ですが、普段プログラミングをする際はvimを使っているのですが、org-modeのためだけにemacsを使うようになり少しemacsの良さがわかってきました。そして両方の宗教を理解することで、世界平和に少し貢献できるような気がしてきました。

参考

React DnDでスマホでもドラッグアンドドロップ

Enigmo Advent Calendar 2018の12日目の記事です。

注意: この記事のサンプルコードで使われている各ライブラリのバージョンは下記になります。

react 16.4.0
react-dnd 4.0.2
react-dnd-html5-backend 4.0.2
react-dnd-touch-backend 0.5.1

React DnD

Reactでドラッグアンドドロップでの並び替えを実装する際によく使われるのがReact DnDというライブラリです。 このライブラリではHTML5Drag and Drop APIを利用してドラッグアンドドロップを実現していますが、このAPI自体がスマートフォンなどのタッチデバイスには対応しておらず、スマホでそのままドラッグアンドドロップを実装することができません。

TouchBackend

React DnDを使う際、ドラッグアンドドロップしたいコンポーネントDragDropContext という HOC(Higer Order Component) に渡します。 この DragDropContext の最初の引数に渡すのは通常、 HTML5Backend というバックエンドモジュールです。

import HTML5Backend from 'react-dnd-html5-backend'
import { DragDropContext } from 'react-dnd'

class YourApp {
    /* ... */
}

export default DragDropContext(HTML5Backend)(YourApp)

前述した通りタッチデバイスの場合はこの HTML5Backend は使えません。 しかしタッチデバイス対応した TouchBackendというものがあるのでそちらを使います。

import HTML5Backend from 'react-dnd-html5-backend'
import TouchBackend from 'react-dnd-touch-backend';
import { DragDropContext } from 'react-dnd'

const isTouchDevice = () =&gt; {
 /* タッチデバイス判定 */
}

class YourApp {
    /* ... */
}
export default DragDropContext(isTouchDevice() ? TouchBackend : HTML5Backend)(YourApp)

これだけでタッチデバイス対応ができました。 しかし、 HTML5Backend のようにいい感じにプレビューされません。

HTML5Backendではちゃんとプレビューされている


TouchBackendではプレビューされていない!

ChromeのDevToolsでスマートフォンをエミュレートして録画しているためマウスカーソルが表示されています。

DragLayer

React DnD にはDragLayerという、ドラッグ時のプレビュー表示をカスタマイズできるAPIがあります。 これを使うことでタッチデバイスでもいい感じのプレビューを表示することができます。

利用側のサンプルコードは以下です。

import React from 'react'
import DragLayer from 'react-dnd/lib/DragLayer'
import TouchBackend from 'react-dnd-touch-backend';
import { DragDropContext } from 'react-dnd'

function collect(monitor) {
  const item = monitor.getItem()
  return {
    currentOffset: monitor.getSourceClientOffset(),
    previewProps: item &amp;&amp; item.previewProps,
    isDragging:
      monitor.isDragging() &amp;&amp; monitor.getItemType() === 'IMAGE'
  }
}

function getItemStyles(currentOffset) {
  if (!currentOffset) {
    return {
      display: 'none'
    }
  }

  const x = currentOffset.x
  const y = currentOffset.y
  const transform = `translate(${x}px, ${y}px) scale(1.05)`

  return {
    WebkitTransform: transform,
    transform: transform,
  }
}

class PreviewComponent extends React.Component {
  render() {
    const { isDragging, previewProps, currentOffset } = this.props
    if (!isDragging) {
      return null
    }

    return (
      <div>
        {/*...*/}
      </div>
    )
  }
}

const DragPreview = DragLayer(collect)(PreviewComponent)


class YourApp {
  render() {
    return (
      <div>
        {/* ... */}
        
      </div>
    )
  }
}

export default DragDropContext(TouchBackend)(YourApp)

かんたんに解説

DragLayer の引数 collect 関数ではDragLayerMonitorのオブジェクトが渡されます。 monitor.getItem()DragSource にアクセスすることができ、 任意で渡した props(今回の場合は previewProps という名前で渡していますが、どんな名前でも渡すことができます) にアクセスできます。 また、 monitor.isDragging で実際にドラッグされているか判定することができます。 同一画面の他のコンポーネントでもドラッグアンドドロップするために、 DragDropContext が複数ある場合は monitor.getItemType() でどのコンテキストなのかを判定するとよいでしょう。 プレビューがタッチした部分に追従するように monitor.getSourceClientOffset() を使ってオフセット座標を返しておきます。 collect 関数の返り値のオブジェクトはそのままプレビュー用のコンポーネントprops として受け取ることができます。 getItemStyles 関数では受け取った props.currentOffset を使ってCSSを調整しています。

DragDropContext に渡したコンポーネントDragLayer を描画することで、ドラッグ時にプレビューを表示することができます。


スマホでもプレビューができた!

ChromeのDevToolsでスマートフォンをエミュレートして録画しているためマウスカーソルが表示されています。

最後に

スマートフォンなどのタッチデバイスHTML5のようなドラッグアンドドロップを実現する方法を解説しました。 実際に実装する際は、TouchBackendのリポジトリ に完全に動作するサンプルがあるのでそちらも参考にしてみてください。

参考リンク

http://react-dnd.github.io/react-dnd/about https://github.com/yahoo/react-dnd-touch-backend