お正月は親戚誘ってボドゲしよう

この記事は ボドゲ紹介 Advent Calendar 2018 - Adventar の8日目です。

昨日の記事はこちら

chocoxina.hatenablog.com

お邪魔者は自分も持っているものの、いきあたりバッテラはやったことないですね。

「酒でも飲みながらやるゲーム」だ。

この手のゲーム大好きですw

お正月ボドゲの定義

まず、最初に今回ワタシがおすすめしたいお正月ボドゲについてです。

昔だったら、人生ゲームやトランプといった簡単なゲームを遊んだ

ボドゲに慣れていない人のほうが圧倒的に多いので、以下の要点が満たせるゲームが良いと思います。

  1. 事前のルール説明ではなく「1回やったら分かる」系なゲーム
    • お酒飲んでてもルールが分かるの大事
  2. 1ゲームが短時間で終わるもの
    • 「じゃあそろそろ帰るね」となりやすいように1回のゲームが短い or 途中で抜けてもあまり支障がないのもいいですね
  3. ゲーマー的な実力より運の要素多め
    • ボドゲに慣れている慣れていないがあまりでない、不完全性の高いゲームのほうが公平度が高まってよいです

といったゲームをいくつか紹介してみようと思います。

Lift it!

頭にクレーン付けて、カードに指定された形に積み上げるというルール。

このゲーム、本来はすごろくのように外周を進んでいくというものなのですが、そのルールより指定時間で何枚のお題をクリアーできるか?みたいなものがルール説明不要で良かったです。

f:id:pokotyamu:20170101220013j:plain
プレイ風景

また、二人一組でクレーンを操ることもできるので、従兄弟対決なんかもできたりします。 コンポーネントが大きいので、若干持ち運びが大変なのが難点なぐらいで小さい子供でも楽しめるゲームです!

ゆびリンピック

f:id:pokotyamu:20181208010401j:plain
ゆびリンピック

2020年はオリンピックですね。 そんなオリンピック種目を指だけで表現しようというゲーム。 親番プレーヤーとその左隣の人が、引いた指だけを使って表現する(使えるのは手首から上だけ)のを他の人に当ててもらうものです。 なお、そのとき一緒にやる人とは会話や打ち合わせ禁止です。

f:id:pokotyamu:20181208010449j:plain
カード紹介

メジャーなスポーツは結構簡単なんですが、 人差し指だけで近代五種を表現してくださいって言われると、結構難易度高いです(ちゃんとボードに登場する競技は載っているので頑張ればわかるようになっている)。

とにかくわちゃわちゃする感じなので Lift it 同様賑やかに盛り上がれると思います!

バイバイレミング

f:id:pokotyamu:20181208210859j:plain
バイバイレミング

大富豪とかで盛り上がれるならこれは受け入れやすいかと。 3~99 までの数値がかかれたカードの中から10枚配られて、親が出した数値の列で割り切れるカードだったら出せる。それを繰り返して、先に手札を全部無くせたら勝ちといったルールです。

まず、レミングがかわいい!一枚一枚イラストが違うし、ちょっとずつ増えていくのもかわいい! カードに割り切れる数値が書いてあるから酔っててもなんか大丈夫!

f:id:pokotyamu:20181208211619j:plain
こだわりを感じる

そして、遊び方なんですが最新の拡張 Up/Down チップがかなりオススメで、流れた列の昇順/降順が毎回変わるというものです。これによって、初期手札が最強だったら勝てるというのが少し不安定になります。なので、「普通ルール → Up/Down チップあり → ドラフト」

f:id:pokotyamu:20181208010559j:plain
Up/Down チップ拡張

みたいな感じでちょっとずつルールを増やしていくと、一気にルールをすんなり理解してくれるようになります。

まとめ

今回はお正月で遊べそうなボドゲをご紹介しました。 他にも大喜利系とかとにかく盛り上がる系も楽しくていいですよね。

さて、次のお正月は何持っていくかなぁ〜

明日は、id: isami_va さんの「セーフハウスもしくはパーティゲーム」 です!お楽しみに〜

告知

毎週水曜日の 19:30~ 株式会社フィードフォースでボドゲ部やってるので、お時間あれば遊びに来てください!

Rails Developers Meetup 2018 Day 3 Extreme 参加レポ

この記事何?

2018年07月14日にやってた Rails Developers Meetup 2018 Day 3 Extreme に参加したレポート

気になったパートだけ抜粋してメモ書きと感想。

Rails DM 1-B (曖昧さを受け入れて開発していく方法)

新規サービスの立ち上げの時にエンジニアリングにフォーカスした個人的な考え。 意思決定など。

曖昧さ

ソフトウェア開発で事前に要件や仕様がハッキリと決まっていることはまず無い。 曖昧さがゼロになるのは、ソフトウェアを実現したときだけ 熟考によって作るものがどんどん変わる

しかし、リリース日はハッキリしている。

作るものを Fix すればいいじゃないか! (静的なモックを作る) けど、変わる時は変わる。

考えがまとまるのを待っているとエンジニアは暇だし、時間がもったいない。 一度決めたけど、他にいい案が出たらできる限り採用したい。

作りながら考える、考えながら作るをチームでやっている

作っている途中に仕様が変わったら変わったで受け入れる。 小さくどんどん作って見せるイメージ

Docker イメージでサーバー定義を書けば、 PR・マージするとデプロイできる。ぐらいの爆速感大事。 サーバー構築どれでやる? デプロイ環境を構築する技術は大事。

適切なケースを選ぶためには結局ケース・バイ・ケース 事例がオープンなんで、他の人の事例を参考にしてみる。

素早く実装≠雑に設計

将来どうなるかわからないので、その時の手持ちの情報で必要十分なものを設計する。

マネージドの知識・経験があったとしても設計や実装にはコストがかかる。

画面変更をする時に GraphQL 使っていれば呼び出し方変えるだけでよかったりするので、いい感じに変更に強くなる。 チームがアーキテクチャに合わせる。

Rails DM 2-B Octocatは技術的負債の夢を見るか?

技術的負債とは? なぜ技術的負債=>害なのか? - モチベーションが低下する - チームの健康性の低下 - 離職・採用難

種類 - クソコード(今日はこれ) - スケールしないアーキテクチャ

ケース1 独自 DSL/フレームワークをアプリケーションコードに入れる

バグフィックスや機能追加のワークアラウンドが積み重なって、見通しが辛い。 開発者が気軽に入れやすくなる設計 属人性上がる

ケース2 責務が曖昧なクラスを取り入れる

疑似サービスクラス的なコード。 結合度が上がり続けて、インスタンスの相互参照が止まらない。 触りたくないし、触るとバグるという事象が発生。 ビジネス的にコア要素だったりする。

要因

時間不足 技術力不足

技術的に価値があると、負債が溜まりやすい

  • 仕様がいっぱいある
  • 変更が多い
  • 度々触る機会がある

サービスを走らせ続ける限り、コードの変更は避けられない。

技術的負債を避けるのではなく、向かい合う

チームで地図を共有する Rails はレールに乗り続けることで、地図が共有される。 地図のない世界もあるし、持たない人は探す・知ってる人から書く 持ってる人はどんどん共有してこう。

5-B オブジェクト指向設計実践ガイドこれだけは実践しとこうガイド

【前提】限られた時間とリソースの中 【目的】最小限のコストで、最大限変更に強いコードを書く

実用的な設計とは、未来を推測するものではなく、未来を受け入れるための選択肢を保護するもの

  • TRUE 原則
  • 単一責任
  • コメント

T 見通しが良い R 合理的 U 利用性が高い E 模範的

TRUE 原則から外れる場合は、コメントで補う。

常に TRUE 原則を意識する。 総力戦で変更に強いコードを作っていこう

常に意識TRUE原則を心がけ単一責任できないときはコメント

番外編

コピペは時として武器になる。 抽象コストを考えるコストが高くなるぐらいならコピペしちゃう。 コピペできないほど長いコード複雑なコードは書かない せいぜい3回目ぐらいまで。4回目以降は普通に抽象化しようね。 コピペを何回かくりかえすことで、どこにおいたら最適化が見えてくる

間違った抽象化よりはコピペしようね。

コードの合理性はプロダクトのフェーズ、チームによっても変わるのでよく話そう 議論の土台共通言語が必要。

ブルーハーツに学ぶ

あれもしたいこれもしたい
もっとしたいもっともっとしたい

限られた時間の中で
借り物の時間の中で
本物の夢を見るんだ

なるほど。かっこよい。

6-B IKUSEI on Rails

どうやって育てれば、どうやって育つか。

学ぶ方法は2つある。

  • 教えられて学ぶ
  • 発見して学ぶ

研修の目的としては、自ら課題を発見して学べるエンジニアになれるように育成する。

育成が最適化している。 研修の長さ・速度、パイロットプロジェクトレビュワーの人数。

パイロットプロジェクトとは?

プロダクトオーナーの要求にあうものを20日間で開発する。

設計や計画の時点では想定していなかったことが起こる。 優先順位を付けてオーナーと交渉するようになる。 機能の性能面を考えつつ話す。

プロダクトオーナーがわざと仕様を漏れを出すようにトラップを用意する。 あえてハマってもらう経験を積んでもらうのも大事。

先輩たちとプルリクエスト上で議論することは大事。 自分の理解度を確認できる。

先輩たち

良いところは良いと伝える

これってここに書くべきだっけ?という感じのレビュー 考えてもらうことも大事。 なぜこう書くか?というのをしっかり伝えた。

相手に伝わるプルリクエストとは?

定期的な 1on1 早いレスポンス

エンジニアのためのスライドデザイン実践講座

  • 伝わる・読まれるスライドレアウとの作り方。
    • トークの場にいなくても Web で読んでもわかりやすい
  • 簡単にできる。
  • 話の構成や喋りやすさには踏み込まない

Keynote テンプレート Azusa がベース。

表紙は OGP でも出るので、読みたくなる表紙。 自社やサービスがアピールできる。

タイトルをロゴっぽくする。 吹き出し便利(アクセント)

ビックリマークを明朝で少し斜め。勢いが出る。

カラーリングは MAX でも 4つぐらい。 主に2色。たまに白黒。

情報の整理。

フォームオブジェクトとの向き合い方

Rails のきれいな設計を最初からするのは難しいので、反復して作ると良さそう。

DB のテーブルにほぼ一致した構造のデータが POST されることはなかなか無い。 ユーザーが目にする情報単位と正規化されたデータ構造は異なる。

DB に永続化するだけでは済まないことがある。

差異を埋めるテクニック

accept_nested_attributes 辛い。 callback も辛い。

最も有用な設計原則。 プログラムのプレゼンテーション層とその他の機能をうまく分けると良い。

入力とその他で分ける。

params から値を取り出し、 Hash や文字列として扱う。 http 由来のデータも先に Hash の中に含めちゃう。

フォームオブジェクトでやること(入力)は PORO として実装できる。 インターフェースが安定するので、ロジック部分を変化/洗礼する自由度が得られる。

入力とロジックを分けよう。

ビュー/コントローラー/AR::B の三層構造で賄えない場合、薄い層をもう一層作る。 Web 入力値とロジックに必要な入力の境界として扱えるようにする。

ナイーブかつテストが書きやすいレイヤからはじめて、反復しながら変化していけるようになるといい!

メモは取れなかったけどすごかったやつ

感想

今回もボリューム満タンな感じで圧倒的なインプットでした。 オブジェクト指向これだけは実践しようガイドとか読み返したくなるぐらいに面白かったし、自分の理解できる世界が広がってるなぁという風に感じれた。

あと、赤塚さんの話は毎回おもしろい! スライドづくりの参考にしよう!!!

そして、 youtube にあげてもらえるの復習だったり、裏番組見れたりで良いなぁという気持ち。

また Rubykaigi で悩んだ登壇者としゃべるミッションは無事に達成できました :tada: 個人的には1人だったのも良かったかも。 多分知り合いがいるとそこと話しちゃうけど、それがないとしっかり話すしかなくなる。 そんな感じなんやろうなぁ〜

レビューを受ける前に僕が気をつけていること2017

昨日、スターウォーズの前夜祭でエピソード8見てきました! えーすけです!!!

ラストジェダイ最高だったんでみんなエピソード1から全部見て行きましょう!!!! ネタバレすると、スクリーンで動くポーグがひたすらにかわいい。

f:id:pokotyamu:20171215102558j:plain:w300

この記事は feedforce Advent Calendar 2017 - Adventar の 15日目の記事です!

昨日は、スケちゃんの

GraphQL API をスキーマファーストで開発したいけどスキーマと実装で乖離を起こしたくない - Feedforce Developer Blog

でした! GraphQL 楽しそう!!!


今回の記事は、コードレビューについてです(サムネ画がポーグなのは全く関係ありませんw)。 最近行った勉強会でレビューについて考える機会があったので、普段の業務の中で日々受けて自分なりにこうしてるよというのをいい感じにまとめれたらなと思っています😀

ちなみにその勉強会はこちら

techplay.jp

前提

今の開発チームはバックエンド2人・インフラ1人・フロントエンド2人・デザイナー1人で開発しています。 自分はバックエンドチームとして、先輩と2人で頑張ってます。

今回の話は、基本的にバックエンドの先輩とのやり取りで得たことがほとんどです。

事前準備

そもそも自分がタスクに取り掛かる上で、

  • 自分が何を作るべきかを理解しておく
  • 0 コメを充実させる

この2点は必ず意識しています。

何を作るべきがを理解する

ここで言う理解には、仕様の理解と機能がどんな使われ方をされるか?の理解とがあります。

仕様については基本的には、ミーティングの中でやりとりをしているんですが

ここらへんは再度自分がやる前に漏れがないかを確認しておくと、スムーズに開発が出来ると思います。

また、機能の使われ方については、実際にユーザーにどう使われるものかのイメージを持っておくことで、気付けるテストケースもあるので非常に重要です。 どう使われるか知らずに作るより、自分が作るものは自信をもって説明できるようにしておきたいですよね。

0 コメの充実

Github の PR を作る時にまず書くのが description いわゆる 0コメ部分だと思います。 自分は、まずは空コミットで push して、やることリスト的な感じで書いていきます。

{対応する issue 番号(あれば)}

## レビュー開始条件
- [ ] 関連 PR がマージされて rebase 済み
- [ ] CI 通ってる
   ...

## やったこと
- [ ] Schema の更新
- [ ] User モデルの追加
    ...


## やらないこと

## 実装方針(新規機能とかの時)

まずはレビュー開始条件。 これは主に自分用の TODO みたいな形で使っています。

次にやったこと/やってないこと。 ボリューム感が多くなりそうだったら、やってないことに書いて、 PR として分ける必要がありそうだなぁとかはここで妄想しています。また、やったことがふわっとしか書けなかったら、自分のタスクへの理解が足りないので、この時点で一旦相談しています。

最後に実装方針。0コメ書き始める前に確認しておいたポイント、ミーティングで決まった仕様や、今回のざっくりの実装方針をまとめていきます。細かい実装のことというより、ざっくりと処理の流れを書いて頭の整理をしています。

f:id:pokotyamu:20171215113455p:plain:w600
0コメサンプル

ルフレビュー

0コメ書いて実際に実装が一段落付いて、レビュー条件が全部クリアできたら、レビューに出す前にセルフレビューをしています。 今年度1番頑張ってやっているのがこのセルフレビューです。

ルフレビューで何をするかですが、一通り眺めるのではなく、他の人が書いたコードの気持ちで実際にレビューしていましょう。 セルフレビューでは3つのことを意識しながらやっていきます。

  • コメントで自分の気持ちを先出し
  • テストケースは網羅できてる?
  • 自分の認識を明文化していく

一つずつ説明していきます。

コメントで自分の気持ちを先出し

ここでこんな気持ちで実装したんや!というのも合わせて、コメントに書いていきます。

f:id:pokotyamu:20171215012111p:plain:w600
コメントサンプル

特にクラスや変数の命名はかなりレビューコメントを受けるポイントだと思うので、自分の場合

  • こんな意味を込めて、xxxx にしました
  • 似たような機能の gem が xxxx なので、xxxx にしました
  • 他の候補として oooo も考えましたが、xxxx にしました

今感じのコメントを付けることが多いです。 なんでこれをしておくかというと、「こっちの名前の方がいいんじゃない?」ってコメントに対して、コメントするより、自分がこうしたいからこうみたいな気持ちを先に伝えておいたほうが言いやすさがあると思っているからです😌

もちろん、パイセンの提案の方がよいなぁと思ったら、そっちにしますし、それ以上の場合は口頭で話すようにしています。

ちなみに、こんな感じのコメントは先輩にも書いてもらうように依頼しています。 これは完璧に勉強目的です🙏ありがたや〜😭

テストケースは大丈夫?

次に見るポイントは、テストケースが網羅出来ているかです。

  • Context の切り方大丈夫?
  • 境界値でちゃんとテストできてる?
    • 以上/以下/より大きい/未満/nil/型違い ...etc
  • 正常系、異常系の書き漏れがない?
  • 重複しているテストはない?
    • Model と Controller で同じことをテストしてない?
  • スタブが変な感じになってない?
    • ちゃんとバグがあった時にも気づけそう?

などなど。せっかく自動テストを書いているならば、テストケースも抜けがないように確認していきたいですね。

あとは、関数として共通化できる部分がないか? N+1 とかなってない? とか普段レビューで見るポイントはしっかり見ていきましょう 👀

自分の認識を明文化していく

※ここは筆者がやりたいけど、苦手なので結局どこまで理解できたの?って1番指摘を受けている部分です。

ルフレビューの時にやっておくといいのが、認識の明文化だと思います。 ここで一番やりたいのは、自分がやったことを整理して伝えることです。

新しい gem を導入したり、ベンチマークを取って検証してみた際に、

  • どんな使い方をするもの?
  • テストデータはどんなものを使った?
  • 結果はどうだった?

これらのことをまとめていきます。ポイントはそのコメントを見た人がコメントを追っていけば自分の環境でも再現できることが大事です。

更に発展させるとしたら、自分の認識が複数の状態を混在させてないか?という点も大事です。 特にバグ修正関連の PR を作る時はここ大事だと思います。

  • どんな事象が発生したからバグが起こっているのか?
  • そのバグの再現性は?(テストデータ込み)
  • ライブラリの問題なら、どの部分が悪そう?

こんなコメントをバシッとまとまって書いてあると修正 PR をレビューする側から見ても分かりやすいですね。 また、複数の問題が絡まっていそうな場合、一つ一つを独立した問題として検証が出来ると、相手にも分かりやすく伝わる気がします。

この問題の切り分けが難しいので、毎回先輩にはどんな感じで調査進めたかは聞くようにしていて、真似していきたいポイントです😇

[番外編]レビューする時に気をつけていること

レビュー受けるばかりではなく自分がすることも当然ながらあります。 むしろ、自分が1つ PR 作っている間に 2〜3個 PR が作られていることも稀によくあります。

自分がレビューする時に気をつけているのは

  • 自分だったらこう書くのになんでこう書くのだろう?
  • コードを読んで、自分の理解をまとめから聞く

この2点です。

自分だったらこう書くだろうな

自分が普段業務で使っている言語は Ruby/Python が多いので、人によって上手く書けるかが大きく変わってくる言語だと思っています(個人的に) 設計の仕方だったり、メソッドの使い方だったりで、自分の思った実装と違った場合、なんでこう書いたかを最近意識しています。

この時、実際にコードを書く必要は全くないと思います。 自分が知らなかったメソッドを使ってたら、 irb とか pry で実際に挙動を試してみることもやっています。

コードを読んで、自分の理解をまとめから聞く

これは、認識の明文化と同じ話にはなりますが、ただ単に「わからないんで教えて欲しい」だけだと、先輩も困るので、一旦自分の中で整理して「ここまでは分かったんですけど、ここが分からんので教えて欲しい」的な感じ話し始めると、先輩に質問するのも楽になりました。

自分は話しを整理するのが苦手なので、ひたすら付箋に自分の頭を書き出して整理するように心がけています。 やっぱ頭の中だけで考えるのと、視覚的にも整理が出来るのは大きく違いそうです。

ちなみに、先輩に話し始めて自分で話しているうちに自己解決する場面が結構あります。 人に話していく中で自分の理解が整理されて解決することは多々あるので、言葉の力って凄いなと日々思っています。

人に話して解決するのをテディベア効果とも言うらしく、テディベア持ってないんでスヌーピーに話しかけるしか😏

ベアプログラミング(テディベア効果) - 発声練習

まとめ

今回は、自分がレビューを受ける(する)時に気をつけていることをまとめてみました。

実際問題、自分がこれ全部毎回出来ているかは微妙なのですが、毎回意識してやっていくことで、レビューに対する苦手意識もなくなってきました。

レビューは相手がいてこその部分もあるので、お互いにどんな感じで指摘して欲しいとか言い合える関係や場所があるかも大事かと思います。 (実際、私もこんな感じで書いていただけるとレビューしやすいのでよろしくお願いします的なのを話しました)

レビューはたくさん受けると辛いこともありますが、よりよいプロダクトにしていくためには絶対に必要なことなので、来年は自分からもガンガン提案出来るように勉強頑張るぞ💪💪💪


明日は、今の私が所属しているプロダクトである dfplus.io の PM ガッキーさんが io リリースからの1年を振り返ってという記事の予定です!!!楽しみ!!!

世紀末で、ヒャッハー!アウトリブ!

この記事は、 ボドゲ紹介 Advent Calendar 2017 - Adventar の12日目です! 昨日は madeinxxx さんの リスボア の紹介でした! 1755年のポルトガルが舞台のゲーム!かつらを最も多く集めたら勝ちというのがいいっすねwww

さてさて、そんな僕が紹介するのは 2079年の核戦争後の世界を舞台にしたアウトリブです!!

アウトリブ 完全日本語版

アウトリブ 完全日本語版

プレイヤーは、世界中の優れた生存者を集めた「コンボイ」と呼ばれる集団に認められるために、クランを率いて人類最後の都市に移住するとめのリーダーとなります。


アウトリブはこんなゲーム

核戦争後の世界ともあって、資源は限られています。 その資源は、早い者勝ちで奪い合わなければなりません!

f:id:pokotyamu:20171212010618j:plain

フィールド上には、森、ダム、軍事基地、鉱山、遊園地、都市、タンカーにクランのメンバーを移動させることで、資源を獲得することが出来ます。

集めた資源は、自分のクランの設備を増やすか装備を作成することに使います!

自分のクランには7つの部屋があって、その部屋の空いてるスペースに生存者が埋まると強力なルームの効果を使うことが出来ます! 例えば、装備を作る資源が減ったり、毎ターン資源を発生させたり。

f:id:pokotyamu:20171211194709j:plain

ただ、問題が一つ。 人が増えるということは、やっぱり食料も必要になるんですね。

f:id:pokotyamu:20171211235656j:plain

食料は大きく分けて3つ

  • 動物を狩ることで得られるお肉
  • ダムから取れる水
  • 保存が効く缶詰

缶詰以外の食料に関しては、お肉だったら取りやすい代わりに毎ターン腐るので破棄しなきゃいけなかったり、お水も毎ターン2つしか持ち越すことが出来ません。

ア○リ○ラやス○ーン○イジのように木を食べることはできず、足りない食料の分生存者が死んでしまいます。

ルームを作るのを先にして効果を得るか? それとも装備を先に作って資源の効率を早くするか? そんなジレンマを楽しみながら進めていきます!

ちなみに装備一覧はこんな感じです。

f:id:pokotyamu:20171211194818j:plain

獲得できる資源が増えたり、生存者が増えたりなど効果は様々です。

ここが辛いよアウトリブ

ここまでの説明では、わりと普通のよくある資源や食料の先勝での取り合いでしたが、このゲームはプレイヤー間のやり取りも発生する場面があります。 このセクションでは、アウトリブならではの辛い部分を紹介していきます。

移動について

各クランには英雄コマと呼ばれる資源を調達する部隊が4体与えられています。 それぞれの英雄コマはパワー(3が2体、4、5が1体)を持っていてそのパワー分だけ移動先の資源を獲得することができるのですが、それらの移動は2マスまでの隣接する場所しか行くことが出来ません。 もちろん同じ場所に留まることも出来ません。

さらに、今自分がいるコマがある位置には行くことが出来ません。

この自分のコマがいるところには移動出来ないというのが悩みのタネで、この資源がほしいけどお前邪魔!!!!みたいなイライラを味わうことが出来ます😈

資源が足りない?奪うしかないじゃろ!

まぁ世紀末なんで、自分が足りない資源がなければ他のクランから奪うしかないですよね!!

f:id:pokotyamu:20171212000417j:plain

各クランの英雄コマには 移動が終わった英雄コマはアクティブな状態となります。 他のクランのアクティブな英雄コマがある場所に移動した時奪い合いが発生するんです。

もし、自分より低いパワーのクランの英雄がいる場合、そのパワー差の分だけ相手に資源を渡さなければなりません。

f:id:pokotyamu:20171212010636j:plain

この写真の場合は、黄色のパワー5に対して、オレンジはパワー4なので1つ、紫はパワー3なので2つ分の資源を渡さなければなりません。

これを防ぐためには、パワーの分だけの弾薬を使って追い返すことができます! ただ装備によっては、与えるプレッシャーを増やすものもあります。そうですバットですね!

f:id:pokotyamu:20171211194716j:plain

グレイソンさんほんま強そう。。。

まとめ

他にも毎ターン核の汚染が起こったり、動物が凶暴化したり、ルールを細かく説明すると、この記事では僕の文章力が持たないので、今回はざっくりアウトリブを紹介させていただきました!

ちなみに最終盤面はこれぐらいのカオスさでございます。

f:id:pokotyamu:20171212003542j:plain

まぁこのくらいの終盤になると、

「こいつ死んでも大丈夫なやつ」とか「こいつの分の食料はあいつから奪えばいいから」とか言い始めるので、みんな最高に世紀末してます。


もし、この記事で少しでも興味を持っていただけたら嬉しいです☆ 毎週水曜日に弊社フィードフォースでボドゲ会やっているので、もしお時間あれば一緒に遊びましょ〜 今週はゲムマの戦利品会な予定!

フィードフォース ボドゲ部 (@ff_boardgame) | Twitter


明日のアドベントカレンダーは、see_know さんです!大聖堂やったことないから楽しみ^^

Serverless Conf Tokyo 2017 に行ってきたぞ

先日の11月3日文化の日に Serverless Conf Tokyo 2017 に参加してきました!

tokyo.serverlessconf.io

モチベーション的には、最近業務的なところで、サーバーレスなシステムの実装をしているので、事例だったり AWS Batch の話が気になって参加しました。

LT の中で、『アウトプットしないのは知的な便秘。』 と言われたので、積極的にアウトプットしてこうな。

今回は、その中で特に気になったものとまとめての感想を書きたいと思います。

資料

まとめていた方がいらっしゃるので、引用させていただきます。 素晴らしいまとめ(-人-)

www.n-novice.com

会場の雰囲気とか

外人の方も多かった印象。

ブースにも外人の方が多くて、英語でガッツリ話してたのが印象的でした。

ブースで話をきくとバッチがもらえて、全部のブースを回ると Tシャツがもらえる仕組みは素敵だった。

以下、戦利品

f:id:pokotyamu:20171106012147j:plain

Step functionsとaws batchでオーケストレートするイベントドリブンな機械学習基盤

ピタゴラスイッチ部分の構成図をしっかり見せていただけたのはすごくありがたかった。 S3 にデータが上がったのをトリガーに Lambda を使って、 Step Functions を使って AWS Batch のステートを管理。

一つ一つの状態を変化させるのにも Lambda を使って、Dynamo DB の情報を update する、と言った流れが非常に分かりやすかったです。

ほんとに最小単位で関数を用意するんだなという発見でした。

The mind of Serverless as a Software

slideship.com

サーバーレス全般の話について分かりやすかった。 新しくサーバーレスでなんかするって人にとりあえず読んどけレベルな資料だと思いました。初心者僕が思ったんだから間違いないはず。

データの流れを一方通行にして、次の関数次の関数とどんどん渡していくことが大事。 そうなれば、一つの関数ごとに単体テストがかけるはず。

またサーバーレスに向いているのは、横にスケール(並列性)する分野。 向いていないのは、縦にスケール(処理速度)する分野。

ここらへんは今システム開発してて、納得感がありました。 単体テストを信頼して、ちゃんと通しでも E2E で確認というのが個人的にはしっくりきています。

まとめ

そもそも Serverless が出てきたのは、アプリ開発者がアプリにコミットできるようにってことだろうし、その仕組を知っておく意味で行く価値のあるイベントでした。

インフラ/バックエンド/デザイン/フロントエンド ここがすべてそれぞれの分野にコミットできるようなチームが組めるとホントに強いんだろうなと感じました。もちろん無関心は良くないと思うんですけどね。インフラもやってくぞ!!

また、今回のイベントで、Serveless な構成はビジネス面でスケールさせるためには、絶対に必要になってくる分野な気がしています。 特に開発スピードが求められる環境ならば、なおさらインフラのことを考えずに開発できることはメリットでしかないので、ここらへんも使いこなしたいな。

最近、Rails エンジニアーだけじゃなく、AWS 周りも色々触り始めるテラフォーマーになったので、有識者から学べるところをどんどん吸収してきたいと思いますので、今後共よろしくお願いしますん。

個人的には、 AWS が用意した、 Python の Serverless フレームワークchalice が気になってます!!!

github.com

Serverless フレームワークとどう違うんだろ?触ってみる!!!

Elasticsearch 先輩で価格周りを触りたい

やりたいこと

1000 JPY みたいな文字列をいい感じに数値検索の対象にできないかなという実験。

今回の実験は以下のデータに対して、実験方法の curl を実行し、正しく指定した範囲のデータが返ってくることができるか?ということを調べる。

{"index":{"_index":"sample_index","_type":"sample_type","_id":"1"}}
{"PRICE1":"3000 JPY"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"2"}}
{"PRICE1":"5000 JPY"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"3"}}
{"PRICE1":"100 JPY"}
$ curl -XGET 'localhost:9200/sample_index/_search?pretty' -H 'Content-Type: application/json' -d'
{
   "query" : {
        "range" : {
            "PRICE1": {
                "gte": 10,
                "lte": 3000
             }
        }
    }
}'

# id 1, 2 のみ検索結果にヒットすることを期待している。

自動マッピングに任せる

$ curl -X POST -H 'Content-Type: application/x-ndjson' 'http://localhost:9200/_bulk?pretty&refresh' --data-binary "@price.json"
$ curl -XGET 'localhost:9200/sample_index?pretty'
{
  "sample_index" : {
    "aliases" : { },
    "mappings" : {
      "sample_type" : {
        "properties" : {
          "PRICE1" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1500889167417",
        "number_of_shards" : "5",
        "number_of_replicas" : "1",
        "uuid" : "eZdsR51MRkeqfem48th0Rw",
        "version" : {
          "created" : "6000002"
        },
        "provided_name" : "sample_index"
      }
    }
  }
}

text 型で入ってるため、上手く数値の範囲検索を行うことが出来ない。

Numeric Detection

Dynamic field mapping | Elasticsearch Reference [5.5] | Elastic

text 型で入ってきた数値に関して、検索できるようにするぜという設定。 これによって、 以下のような text の中身が数値というデータに関しては、数値検索可能となる。

{"index":{"_index":"sample_index","_type":"sample_type","_id":"1"}}
{"PRICE1":"1000"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"2"}}
{"PRICE1":"10"}
$ curl -XPUT 'localhost:9200/sample_index?pretty' -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "sample_type": {
      "numeric_detection": true
    }
  }
}'
{
  "acknowledged" : true,
  "shards_acknowledged" : true
}
$ curl -X POST -H 'Content-Type: application/x-ndjson' 'http://localhost:9200/_bulk?pretty&refresh' --data-binary "@test-price.json"
$ curl -XGET 'localhost:9200/sample_index/_search?pretty' -H 'Content-Type: application/json' -d'
{
   "query" : {
        "range" : {
            "PRICE1": {
                "gte": 100,
                "lte": 1000
             }
        }
    }
}'
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "sample_index",
        "_type" : "sample_type",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "PRICE1" : "1000"
        }
      }
    ]
  }
}

Tokenize

あとは、 100 JPY となっている部分を 100JPY に分けたら 100 に対する検索にヒットするようになりそうなので、やってみる。

$ curl -XPOST 'localhost:9200/_analyze?pretty' -H 'Content-Type: application/json' -d'
{
  "tokenizer": "whitespace",
  "text": "100 JPY"
}
'

{
  "tokens" : [
    {
      "token" : "100",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "JPY",
      "start_offset" : 4,
      "end_offset" : 7,
      "type" : "word",
      "position" : 1
    }
  ]
}

空白で、トークンを区切る whitespace が使えそうなので、これでトークンを 100JPY に分けることはできそう。

{"index":{"_index":"sample_index","_type":"sample_type","_id":"1"}}
{"analyzer": "whitespace","PRICE1":"3000 JPY"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"2"}}
{"analyzer": "whitespace","PRICE1":"5000 JPY"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"3"}}
{"analyzer": "whitespace","PRICE1":"100 JPY"}

というわけで、実験データに analyzer を足した物を実験データとして再度やり直した。

しかし、挙動が安定しない 意図通りの挙動になってくれない感じバグっぽい気もしている。 またフォーラム行きかしら。。。

ちなみにこんな感じ↓ 100000 以上について指定している。

$ curl -XGET 'localhost:9200/sample_index/_search?pretty' -H 'Content-Type: application/json' -d'
{
   "query" : {
        "range" : {
            "PRICE1": {
                "gte": 100000
             }
        }
    }
}'

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "sample_index",
        "_type" : "sample_type",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "analyzer" : "whitespace",
          "PRICE1" : "5000 JPY"
        }
      },
      {
        "_index" : "sample_index",
        "_type" : "sample_type",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "analyzer" : "whitespace",
          "PRICE1" : "3000 JPY"
        }
      },
      {
        "_index" : "sample_index",
        "_type" : "sample_type",
        "_id" : "3",
        "_score" : 1.0,
        "_source" : {
          "analyzer" : "whitespace",
          "PRICE1" : "100 JPY"
        }
      }
    ]
  }
}

そもそもトークンが上手くいっていない感じだな。。。うーん。。。わからん。。。。

$ curl -XGET 'localhost:9200/sample_index/_search?pretty' -H 'Content-Type: application/json' -d'
{
   "query" : {
         "term": { "PRICE1": "JPY"}
        }
    }
}'

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 0,
    "max_score" : null,
    "hits" : [ ]
  }
}

【追記】

johtani さんからアドバイスを頂いた。

アプローチの仕方が間違っていた。 やっぱり、数値にアプリ側で変換してインデックス貼ってあげるのが楽な気がする。

Elasticsearch 先輩で日付を触りたい

やりたかったこと

日付の取り回しについて調べてみた。 具体的には、以下のようなデータを想定する。

  • 20170713
  • 2017/07/13
  • 2017-07-13
  • 2017年07月13日
  • 2017713
  • 2017/7/13
  • 2017-7-13
  • 2017年7月13日

これらが不特定なカラムに入っている時に、 Elasticsearch で、いい感じに受け取るにはどうすればいいか?を調べてみた。

自動的にやってくれるもの

Elasticsearch は自動的に型を判断していい感じにマッピングしてくれる機能がある。 そこで使われる日付のフォーマットはこちらに一覧されている。

format | Elasticsearch Reference [5.5] | Elastic

というわけで、以下の実験データを何もマッピングされていない index に突っ込んでみる

{"index":{"_index":"sample_index","_type":"sample_type","_id":"1"}}
{"DATE1":"2017年07月13日"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"2"}}
{"DATE2":"2017/07/13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"3"}}
{"DATE3":"2017-07-13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"4"}}
{"DATE4":"2017.07.13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"5"}}
{"DATE5":"20170713"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"6"}}
{"DATE6":"2017年7月13日"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"7"}}
{"DATE7":"2017/7/13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"8"}}
{"DATE8":"2017-7-13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"9"}}
{"DATE9":"2017.7.13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"10"}}
{"DATE10":"2017713"}
$ curl -s -X GET 'http://localhost:9200/sample_index/' | jq .
{
  "sample_index": {
    "aliases": {},
    "mappings": {
      "sample_type": {
        "properties": {
          "DATE10": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE2": {
            "type": "date",
            "format": "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
          },
          "DATE3": {
            "type": "date"
          },
          "DATE4": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE5": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE6": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE7": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE8": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE9": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    },
    "settings": {
      "index": {
        "creation_date": "1499935832320",
        "number_of_shards": "5",
        "number_of_replicas": "1",
        "uuid": "Tf1HqJSfQzmoIPVKrMgnQQ",
        "version": {
          "created": "5040299"
        },
        "provided_name": "sample_index"
      }
    }
  }
}

結果から分かる通り、YYYY/MM/DD の形のみ受け付けている

そして、 format | Elasticsearch Reference [5.5] | Elastic にある通り、07月のように0埋めしないと自動ではマッピングしてもらえない。

なので、 DATE5~10 のフォーマットは、事前に何かをしてやるとかしないと、難しそう。

フォーマットを自分で定義してみる

次に自分で mapping を指定してデータの登録を行ってみる。

{
  "mappings": {
    "sample_type": {
      "properties": {
        "DATE1": {
          "type":   "date",
          "format": "yyyy年MM月dd日"
        },
        "DATE3": {
          "type":   "date",
          "format": "yyyy-MM-dd"
        },
        "DATE4": {
          "type":   "date",
          "format": "yyyy.MM.dd"
        },
        "DATE5": {
          "type":   "date",
          "format": "yyyy年M月dd日"
        }
      }
    }
  }
}

登録は次の用に行う。

curl -X PUT 'http://localhost:9200/sample_index' --data-binary @mapping.json | jq .

これを行った後、 DATE1~5について再度データの挿入をしてみた。

$curl -X GET 'http://localhost:9200/sample_index/' | jq .
{
  "sample_index": {
    "aliases": {},
    "mappings": {
      "sample": {
        "properties": {
          "DATE1": {
            "type": "date",
            "format": "yyyy年MM月dd日"
          },
          "DATE3": {
            "type": "date",
            "format": "yyyy-MM-dd"
          },
          "DATE4": {
            "type": "date",
            "format": "yyyy.MM.dd"
          },
          "DATE5": {
            "type": "date",
            "format": "yyyy年M月dd日"
          }
        }
      },
      "sample_type": {
        "properties": {
          "DATE1": {
            "type": "date",
            "format": "yyyy年MM月dd日"
          },
          "DATE2": {
            "type": "date",
            "format": "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
          },
          "DATE3": {
            "type": "date",
            "format": "yyyy-MM-dd"
          },
          "DATE4": {
            "type": "date",
            "format": "yyyy.MM.dd"
          }
        }
      }
    },
    "settings": {
      "index": {
        "creation_date": "1500369904641",
        "number_of_shards": "5",
        "number_of_replicas": "1",
        "uuid": "EN99Zmc7TuK-7ePO1S9T6A",
        "version": {
          "created": "5040299"
        },
        "provided_name": "sample_index"
      }
    }
  }
}

これで、データ型での登録ができるようになった。 しかし、これでは、予め決められているカラムにしかデータの取込が出来ないため、デフォルトとして登録することを考えるもやり方が分からない。。。

Dynamic template は index の正規表現に則って、決まった index に対してマッピングを当てはめるみたいなことはできるが、不特定のカラム名に対して割り当てるのは難しい。

Dynamic templates | Elasticsearch Reference [5.5] | Elastic

うーん。。。

フォーラムで質問して見るかしら。。。

(追記 7月20日) フォーラムで質問したら、大谷さんから回答が返ってきた。

不特定カラムに対して日付のフォーマットを指定する - Elastic In Your Native Tongue / 日本語による質問・議論はこちら - Discuss the Elastic Stack

6.0.0-alpha2 だと試せるそうなので、試し見た。

$ curl -X PUT -H 'Content-Type: application/json' http://localhost:9200/my_index -d '
{
  "mappings": {
    "my_type": {
      "dynamic_date_formats": ["yyyy年MM月dd日", "yyyy/MM/dd", "yyyy.MM.dd", "yyyy-MM-dd"]
    }
  }
}' | jq .
{
  "acknowledged": true,
  "shards_acknowledged": true
}
$ curl -X POST -H 'Content-Type: application/json' http://localhost:9200/_bulk --data-binary @test.json 
$ curl -X GET http://localhost:9200/my_index | jq .
{
  "my_index": {
    "aliases": {},
    "mappings": {
      "my_type": {
        "dynamic_date_formats": [
          "yyyy年MM月dd日",
          "yyyy/MM/dd",
          "yyyy.MM.dd",
          "yyyy-MM-dd"
        ],
        "properties": {
          "DATE1": {
            "type": "date",
            "format": "yyyy年MM月dd日"
          },
          "DATE2": {
            "type": "date",
            "format": "yyyy/MM/dd"
          },
          "DATE3": {
            "type": "date",
            "format": "yyyy-MM-dd"
          },
          "DATE4": {
            "type": "date",
            "format": "yyyy.MM.dd"
          }
        }
      }
    },
  以下略
  }
}

意図通り、 date 型でのデータの保存ができるようになった。 ポイントは、事前に dynamic_date_formats を指定することで、予め想定される date を確定することだった。 これを template 化しておくことで、任意の index に対して、任意のカラムの日付を割り当てることができそう。

Dynamic field mapping | Elasticsearch Reference [master] | Elastic

日付が扱えるとどんな検索ができるか?

Range Query | Elasticsearch Reference [5.5] | Elastic

  • ○日から○日の範囲内
  • 今から何日以内
  • この日から何日以内

といった検索が行えそう

まとめ

文字列をトークン化するとか、予め決められたデータを処理することに対しては、 Elasticsearch は有効打にできそうだけど、日付周りは、フォーマットの関係に一工夫必要だった。