Raccoon Tech Blog [株式会社ラクーン 技術戦略部ブログ]

株式会社ラクーン 技術戦略部より、tipsやノウハウなど技術的な話題を発信いたします。

RubyKaigi2015レポート

メリークリスマス!

技術開発部のおおはらです。

本日12月25日はクリスマスでもありますが、Ruby2.3のリリース日でもあります!

毎年クリスマスにRubyはバージョンアップリリースを続けており、 開発や利用が盛んであることはRubyエンジニアにとって喜ばしい限りです。

ちなみに私が開発を担当しているコレックはRuby on Railsを使用しています。

さて、先日開催されたRubyKaigi2015に参加してきましたので、そのレポートをお送りしたいと思います。

RubyKaigi2015

1日目

Ruby3 challenges

  • Yukihiro "Matz" Matsumotoさん(rubyの作者)の基調講演
  • Rubyコミュニティのスローガン「MINASWAN」の紹介
  • Ruby2.3.0の新機能の紹介
    • did_you_mean gemがデフォルトでバンドルされる
      • タイポした時に正しいと思われる変数名やメソッド名などを教えてくれる
    • grep_vメソッド
      • パターンにマッチしないものを集める(grep -v)
    • digメソッド
      • 深い階層の要素(例:array[0][1][2])をエラー無く取得
    • indented here document
      • sql = <<~SQLとするとインデントの浅いところに合わせて先頭を削除
    • safe navigation operator(ぼっち演算子) &.
      • &.が膝を抱えて座って、石を寂しそうにながめている人に見えることからぼっち演算子と命名。面白くて覚えやすい!
      • user && user.accountuser&.accountのように簡潔に書ける。 railsのtry!と同じ。
    • Ruby3でStringがimmutableになることへの移行パス(マジックコメント)
  • Ruby3の進捗
    • Ruby2.0と比較して3倍の速度向上を目標
    • そのためにconcurrency(並行性)やJITコンパイラの導入を検討中
  • http://togetter.com/li/911263

Compiling Ruby scripts

  • 笹田耕一さん。Rubyコミッタ。Heroku社でのMatzチームの一員。
  • 性能改善のためにRubyスクリプトの実行前コンパイルを検討中
  • 作成したISeq(InstructionSequence)シリアライザ、デシリアライザの効果
    • 高速起動
    • メモリ削減
    • コンパイル済みコードを別プロセスで共有できる
  • Ruby2.3のsample/iseq_load.rbで試せる
  • 2015_RubyKaigi2015.pdf

Saving people from typos

  • Yuki Nishijimaさん。Pivotal Labs社。kaminari gemのメンテナ
  • Ruby2.3から同梱されるようになったdid_you_mean gemの仕組みの解説
  • spell checkerとmonkey patchで構成
  • spell checker
    • dictionary, control mechanism, optimizationで構成
  • dictionary
    • Symbol.all_symbolsなどで辞書を作成、エラータイプで辞書を分類
  • control mechanism
    • ミスタイプをLevenshtein距離、ミススペルをJaro-Winkler距離+Prefix bonuxで評価
  • monkey patch
    • NameError#to_sをオーバーライドして出力
    • 例外が発生した場所を特定するためにRuby2.3以前はC拡張を使っていたが、Ruby2.3から追加されたNameError#receiverが使えるようになった
  • http://togetter.com/li/911325

Time flies like an arrow; Fruit flies like a banana: Parsers for Great Good

Fast Metaprogramming with Truffle

  • Kevin Menardさん。Oracle社のエンジニア。
  • JRubyとTruffleを使うとメタプログラミングによる実行効率が落ちない
    • 例:method_missing の実行効率が落ちない
  • http://togetter.com/li/911356

High Performance Template Engine: Guide to optimize your Ruby code

  • Kohei Suzukiさん、Takashi Kokubunさん。両名ともクックパッド社。
  • Suzukiさんはfamlテンプレートエンジンを作成
  • benchmark-ips gemでベンチマークし、stackprof, rblineprof gemでプロファイリング、コード改善
  • slimテンプレートエンジンと同じバックエンドのtempleを使用
  • クックパッド社でfamlを本番投入している
  • KokubunさんはHamlitテンプレートエンジンを作成
  • stringを先につなげて高速化
  • いらない機能・オプションを削って高速化
  • Famlより高速だが互換性を一部捨てた
  • Q.同じ会社の同じ部署にいるなら、統合して互換性を失わないモードをオプションで提供しないのはなぜか
    • A. Hamlitは自分の勉強のために作りたかったから。作ったついでにベンチマークで勝ちたかった。
  • https://speakerdeck.com/k0kubun/high-performance-template-engine

TRICK 2015: The second Transcendental Ruby Imbroglio Contest for RubyKaigi


2日目

Keynote

  • KOSAKI Motohiroさん。Rubyコミッタ、Linuxカーネルコミッタ。
  • Linuxデバッグツールの紹介
    • Ftrace
      • Linuxのイベントをトレース
    • Perf tools(パフォーマンスツール群)
      • perf top 実行中のプロセスの関数ごとのtop
      • perf trace 全プロセスが呼んだシステムコールを記録
    • System Tap
      • デバッグスクリプト言語
      • 言語仕様があまり良くない(loopがないなど)
      • CとRubyでシステムコールが取れる
  • http://togetter.com/li/911612

The history of testing framework in Ruby

  • Kouhei Sutouさん。クリアコード社の代表。
  • /¥Atest.+unit¥z/iにマッチするtest unitの違いがわかるようになる話
  • Ruby1.3
    • testsupp
  • Ruby1.4
    • RubyUnit
  • Ruby1.6
    • Lapidary
    • Test::Unit(RubyUnit + Lapidary)
  • Ruby1.8
    • Test::Unit
      • rubyが標準でバンドル。require 'test/unit'することからtest/unitとも表現される
      • メンテナが交代したが、test/unitが複雑でメンテできなくなった
  • Ruby1.9
    • Rubyはtest/unitをバンドルしなくなった
      • test-unit gemとしてリリース(メンテナがSutouさん)
    • minitest
      • 作者はtest/unitのメンテナ)
      • Ruby開発者がminitestのラッパーとして新しくtest/unitを作成してrubyにバンドル
  • Ruby2.1
    • minitest5で互換性のない変更が入った
    • ruby自身のテストにはminitest4を使用
    • ユーザには最新のminitestを提供
  • Ruby2.2
    • minitest5をバンドル
    • 再びtest-unit gemをバンドル(for user)
    • ruby自身のテストにはフォークしたminitest4とtest/unitを使う
  • http://slide.rabbit-shocker.org/authors/kou/rubykaigi-2015/

Turbo Rails with Rust

The worst Ruby codes I've seen in my life

Ruby for one day game programming camp for beginners

  • Ippei Obayashiさん。京大マイコンクラブのメンバー。東北大助教授。
  • 京大マイコンクラブの新入生歓迎イベントとして1日ゲームプログラミングを開催
  • プログラミング初心者である参加者の8割ほどが、動くゲームを完成
  • 成功の原因を考察
    • 参加者と同数のクラブのメンバーがマンツーマンで教えた
    • 1日という締め切りがあること
      • できることの見積がしやすい
      • 型を教えない、グローバル変数で頑張る
      • インストールを簡単にする工夫(ゲームのスターターキット)
  • http://togetter.com/li/911699

Ruby meets Go

Building CLI Apps for Everyone

making robot with mruby

LT

Update Early, Update Often

Automating View Internationalization in Ruby on Rails

  • Rails2から3に変わるときにデフォルトでhメソッド(html_escape)が実行されるのと同じように、I18nのtメソッドやlメソッドがデフォルトで実行されても良いのでは?という提案

A new testing framework Rgot

Building an Unbreakable MRI-based Embedded Computer Appliance

Do you trust that certificate?

How I debugged debugger

Padrino Travel Guide

What I learned by implementing a Ruby VM in Erlang

Rubygemsで作るお手軽データ分析基盤 〜あるいは 私はどうやって他人の褌で相撲を取ったか〜

  • gem選びの基準はシンプル、読みやすい、プルリクの送りやすさ
  • プルリクを送る時に、自分たち固有の機能か、汎用な機能かを切り分けないとマージされにくい

Rationalを最適化してみた

The Mythical Creatures of Summer of Code

  • Rails Girls Summer of Codeの紹介

3日目

Writing web application in Ruby

Refinements - the Worst Feature You Ever Loved

  • Paolo Perrottaさん。Metaprogramming Rubyの著者。
  • Ruby2.1から正式に導入されたRefinementsの解説
  • モンキーパッチは便利だが影響範囲が大きい。その範囲を制限するのがRefinements。
  • あるmodule内で、拡張したいクラスをrefineの引数として定義し、usingキーワードを記述したレキシカルスコープでのみ有効となる
module A
  refine String do
    def shout
      upcase + "!"
    end
  end
end

Class C
  using A
    "hello".shout # => "HELLO!"
  end
end

C.class_eval do
  "hello".shout # => NoMethodError
end

Discussion on Thread between version 1.8.6 and 2.2.3

Plugin-based software design with Ruby and RubyGems

  • Sadayuki Furuhashiさん。トレジャーデータ社の設立者。Fluentdの作者。
  • Fluentd
    • ログの集約ツール
    • RubyGems.orgのgemを使ったプラグインベースのアーキテクチャ
  • Embulk
    • CSVなどのデータ変換・投入ツール
    • RubyGems.orgのgemとJRubyを使ったプラグインベースのアーキテクチャ
  • 課題
    • Ruby VMのバージョン衝突
  • http://www.slideshare.net/frsyuki/pluginbased-software-design-with-ruby-and-rubygems

Request and Response

  • Aaron Pattersonさん。Ruby,Rails,Rackのコミッタ。講演は日本語(すごい)
  • Railsではリクエストをパースするために数十のrack middlewareを呼び出している
  • 開発環境高速化のためにhttp2を使えるライブラリを書いてみた
  • Rack3ではAPIが多分変わる
  • http://togetter.com/li/912368

Actor, Thread and me

  • Masatoshi SEKIさん。Rubyコミッタ。
  • アクターモデルのおさらい
  • 要素としては、アクター、メッセージ、エーテル(メッセージングシステム)
  • アクターは他のアクターにメッセージを送る。自分の都合の良い時に自分宛てのメッセージを受け取る
  • アクターが返事待ちになってシステムが止まることがある
  • https://speakerdeck.com/m_seki/actor-thread-and-me-rubykaigi2015

Keynote


初めてRubyKaigiに参加したのですが、今年はRuby3リリースに向けてどのように高速化していくか、という話題が多かったです。

Rubyそのものの実装の話が多かったので、C言語未経験の自分にとっては少し難解な部分もありました。

Rubyのカンファレンスではありますが、Ruby以外の言語や設計、テスト手法などに触れることができ、幅広く勉強できたと思います。

来年は9月8日~10日に京都国際会館で開催されるようなので今から楽しみです!

iptablesを利用してNATサーバを構築

こんにちは、えのもとです。

弊社では本番リリース直前検証環境として、ステージング環境を利用しています。
この環境が古くなってしまったため、環境再構築を実施することになりました。

再構築する上で、今後のこともあるので再構築しやすい環境に切り替えることにしました。
構築上でポイントとなったのは以下の2つです。
  • 本番環境はVMで稼働しているので、そのバックアップを活用する
  • サーバ設定(ネットワーク設定など)はできる限り変更したくない
これらを達成するため、閉鎖されたネットワークを構築しその中に環境を構築することにしました。そこで問題となるのは閉鎖された環境内外との通信なのですが、iptablesを利用してnatサーバを構築することで解決しました。今回はこのiptablesの検証の様子を紹介します。

NATとiptablesについて

今回環境を構築するに当たり、以下を参考にしました。

@IT:第10回 IPパケットの構造とIPフラグメンテーション
@IT:natテーブルを利用したLinuxルータの作成

要点だけ羅列すると以下のような内容になります。
  • パケット内IPヘッダに送信元アドレスと宛先アドレスが記載されている。
  • 送信元アドレスを変換するNATのことをSNAT
    宛先アドレスを書き換えるNATのことをDNATと呼ぶ。
  • iptablesのSNATはPOSTROUTINGチェインで、DNATはPREROUTINGチェインで行う。


検証内容と環境の紹介


存在しないIPアドレス宛てのパケットをiptablesでnatし、別のIPアドレスで受けとることができるか検証しました。検証には以下のような環境を利用しました。

セグメント
  1. A(192.168.100.0/24)
  2. B(192.168.200.0/24)
仮想マシン
  1. vm01           eth0:【 192.168.100.1 】
  2. vm02           eth0:【 192.168.100.2 】 eth1:【 192.168.200.2 】
  3. vm03           eth0:【 192.168.200.3 】
  4. gateway_A   eth0:【 192.168.100.254 】
  5. gateway_B   eth0:【 192.168.200.254 】

eno_2015120901

vm01から存在しないIP【 192.168.100.3 】 宛にパケットを送り、
vm02でNATさせてvm03のIP【 192.168.200.3 】へ届けます。
vm01からするとIP【 192.168.100.3 】 が存在するように見えます。(赤線)
実際はvm02を経由してセグメントBにいるvm03へ届きます。(青線)

eno_2015120902


事前確認

iptablesに余計な設定があると誤作動の原因になるので、一旦どのサーバもクリアしておきます。

# iptables クリア
iptables -F
iptables -F -t nat
# iptables 確認
iptables -L
iptables -L -t nat
# iptables 保存
service iptables save

また、vm02はパケットを転送する必要があるので以下の設定を加えます。
vi /etc/sysctl.conf
#net.ipv4.ip_forward=0
net.ipv4.ip_forward=1

sysctl -p



ステップ 1 : vm01にルーティング追加

手始めにvm01から【 192.168.100.3 】宛のpingを実行し、その様子をvm01とvm02の両方でtcpdumpを使いパケットをキャプチャして確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
From 192.168.100.1 icmp_seq=2 Destination Host Unreachable
From 192.168.100.1 icmp_seq=3 Destination Host Unreachable
From 192.168.100.1 icmp_seq=4 Destination Host Unreachable

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:28:24.003479 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:25.003469 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:26.003479 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:28.004484 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46

vm01から【 192.168.100.3 】へパケットを送る場合、同一セグメント内なのでゲートウェイなどは経由せず直接通信することになります。直接通信するためには相手のMACアドレスを知る必要があります。ARPリクエストをブロードキャストへ送信しMACアドレスを探している様子が確認できました。

これを無理やりvm02へ送るようにvm01にスタティックルーティングを追加します。

# vm01
[root@vm01 ~]# ip route add 192.168.100.3 via 192.168.100.2 dev eth0
[root@vm01 ~]# ip route show
192.168.100.3 via 192.168.100.2 dev eth0
192.168.100.0/24 dev eth0  proto kernel  scope link  src 192.168.100.1
169.254.0.0/16 dev eth0  scope link  metric 1002
default via 192.168.100.254 dev eth0

再度vm01から【 192.168.100.3 】へpingを実行し確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
From 192.168.100.2: icmp_seq=2 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2: icmp_seq=3 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2: icmp_seq=4 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2 icmp_seq=2 Destination Host Unreachable
From 192.168.100.2 icmp_seq=3 Destination Host Unreachable
From 192.168.100.2 icmp_seq=4 Destination Host Unreachable

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:48:19.136230 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 1, length 64
13:48:20.135528 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 2, length 64
13:48:21.135501 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 3, length 64
13:48:22.135497 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 4, length 64

【 192.168.100.3 】宛のパケットがvm02へ届くようになりました。



ステップ2 : vm02にDNAT設定を追加

次に存在しない宛先IPアドレス【 192.168.100.3 】をvm03【 192.168.200.3 】変換するため、vm02のiptablesにDNATの設定を追加します。

# vm02
[root@vm02 ~]# iptables -t nat -A PREROUTING -d 192.168.100.3 -i eth0 -j DNAT --to-destination 192.168.200.3
[root@vm02 ~]# iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       all  --  0.0.0.0/0            192.168.100.3       to:192.168.200.3

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination


vm01から【 192.168.100.3 】へpingを実行し、vm02とvm03で確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:55:28.318556 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 1, length 64
13:55:28.318586 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 1, length 64
13:55:29.318498 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 2, length 64
13:55:29.318513 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 2, length 64
13:55:30.318483 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 3, length 64
13:55:30.318496 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 3, length 64
13:55:31.318515 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 4, length 64
13:55:31.318529 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 4, length 64

# vm03
[root@vm03 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:55:28.381794 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 1, length 64
13:55:29.381711 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 2, length 64
13:55:30.381702 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 3, length 64
13:55:31.381730 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 4, length 64

vm03にvm01のPINGが届いていることが確認できました。



ステップ3 : vm02にSNAT(NAPT)設定を追加

vm01から送るpingは無事vm03へ届けることができたのですが、vm03がセグメントAの情報を持っていないため、送信元であるvm01に返信することができません。そのため、vm02へSNAT(NAPT)を追加します。

# vm02
[root@vm02 ~]# iptables -t nat -A POSTROUTING -o eth1 -s 192.168.100.0/24  -j MASQUERADE
[root@vm02 ~]# iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       all  --  0.0.0.0/0            192.168.100.3       to:192.168.200.3

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  192.168.100.0/24     0.0.0.0/0

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
64 bytes from 192.168.100.3: icmp_seq=1 ttl=63 time=0.235 ms
64 bytes from 192.168.100.3: icmp_seq=2 ttl=63 time=0.164 ms
64 bytes from 192.168.100.3: icmp_seq=3 ttl=63 time=0.164 ms

# vm02
[root@vm02 ~]# tcpdump -i eth0 src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
14:04:40.915680 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.915507 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.915500 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.915525 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 4, length 64

# vm02
[root@vm02 ~]# tcpdump -i eth1 src host 192.168.200.2
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
14:04:40.915714 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.915522 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.915513 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.915546 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 4, length 64

# vm03
[root@vm03 ~]# tcpdump -i any src host 192.168.200.2 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
14:04:40.978924 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.978721 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.978718 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.978754 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 4, length 64

無事にpingが返ってくることが確認できました。



まとめ

作成した環境を利用して、どんなサービスが利用できるのかも検証しました。

動作OK : ssh http https dns mysql
動作NG : ftp oracle

ipヘッダ以外でipのやり取りをするものについては正常に動作しないのですが、動作したものについては、特に問題もなく使えることが分かりました。

以上です。何かの役に立てばと思います。

クラウドソーシングサービス Upwork を使ってみる

こんにちは、たむらです。
今回は先日興味本位で利用してみた海外のクラウドソーシングサービス「Upwork」についてのアレコレを書いてみたいと思います。

◆ Upworkとは?

upwork_top

UpworkはアメリカにあるUpwork Global Inc.社の提供する世界最大規模のクラウドソーシング仲介サービスです。日本にもクラウドワークスなどの同類のサービスがありますがその海外版と考えれば分かりやすいかと思います。世界中の個人や企業がクライアントとなり、事務作業やシステム開発、デザインなどの様々な仕事を発注し、その仕事に対して世界中のフリーランサーが受注します。以前は「oDesk」という名前でしたが、2015年5月に「Upwork」に名前が変更されました。クライアントとフリーランサー双方の利便性を考えた画面や機能が充実しており、非常によく出来ているサービスになっています。幾つかその特徴を紹介してみましょう。

使い勝手の良いメッセージ機能

 フリーランサーとクライアントのやり取りは基本サイト上のメッセージ機能で行います。ファイル添付や外部リンク添付などの機能は勿論ですが、「雇用」や「成果物の受入れ」といった契約上のマイルストーンも自動的にこのメッセージ上にログが残っていくのでこのメッセージ機能だけで案件の流れのほぼ全てを追うことができる様になっています。

多様な報酬支払の方法

  クライアントからフリーランサーへの報酬の支払いは、(1)時給(2)固定報酬 の2つが選択できます。
  (1)の時給制の場合、そもそもフリーランサーの作業時間が管理なんかできるの?という疑問が湧きますが、次に述べる仕組みでその点はカバーしています。(2)の固定報酬は発注案件につき固定の報酬を決めるやり方です。仕事登録時には多くの利用者は時給制を選択してるよとアドバイスが出てきますが、固定報酬を選択しても普通にフリーランサーからのオファーは来ます。まずは試してみるということであれば固定報酬の方が安心でしょう。
  その他にも、マイルストーンにより報酬を分割して支払う仕組みがあったり、良い働きをしてくれたフリーランサーにボーナスを支払う仕組みがあったりします。

フリーランサーの作業を確認するための仕組み

  時給制で仕事を発注する場合はフリーランサーの作業状況確認ができないと不安ですが、その仕組みもUpworkには用意されています。
  フリーランサーのPCでは案件に携わっていた作業時間のスクリーンショットやキータイプ数、マウス移動量などが自動的に記録される様になっており、それをクライアントが確認できる様にすることで本当に案件に従事していたのかという時給制の不安を払拭することができます。

フリーランサーやクライアント双方の履歴・フィードバックの存在

  仕事の契約をする際は、報酬や案件内容の他に、フリーランサーとクライアントそれぞれに付けられた過去仕事実績の履歴とフィードバックも確認して選ぶことになります。フィードバックは仕事が完了した後にフリーランサーとクライアントが相互に評価したものになるのですが、この評価が面白い仕組みで簡単に言えば「ホンネとタテマエ」を入れられるようになっています。タテマエ側に入れたものは相手を含めて一般に公開されるもので、ホンネ側に入れたものは表面上は出てきませんが恐らく一覧順位などで生かされているのかと思われます。「今回のフリーランサーは仕事は良かったがコミュニケーションを取るのに苦労した。悪いレビューで彼の今後の仕事に支障をきたしたくないが、私はもう一緒には仕事をしたくないなぁ」というような時、どちらの感想も素直に記載することができます。

グローバルな利用を意識した分かりやすさ

 私も英語は決して得意ではないのですが、Upworkは色々な点で分かりやすさが実現されていると思います。仕事の登録時には細かく入力アドバイスが出てきたり、ヘルプやコミュニティもかなりの情報量があり、英語がネイティブじゃない人にも使いやすくなっています。英語のレベルがフィードバックの項目の一つになっていたり、メッセージ機能で相手側の現地時刻が表示されていたりするのも、世界的な規模で使われることを想定したものなのでしょう。

Upworkの利用料

  システムの利用料はクライアントからフリーランサーへの報酬の10%分になります。固定報酬10ドルで仕事の発注を行った場合、9ドルがフリーランサーの報酬、1ドルがUpworkへの支払いとなります。



◆ 実際にUpworkを使ってみる

 それでは実際に使ってみましょう。今回はクライアントとして仕事を発注してみます。
発注する案件は、社内で作成したあるWeb汎用フォームシステム、通称「fumi」のマスコットキャラクターを作ってもらうというものにしてみました。

1. まずはサインアップ
 https://www.upwork.com/ からサインアップします。登録時にクライアント側/フリーランサー側 及び会社アカウント/個人アカウントを選択した上で入力を行います。  Facebook等のアカウント認証を使うこともできます。

クライアント側かフリーランサー側を選びます
upwork_signup1
  upwork_signup2


メールでアカウント認証をした後、ログインしたら自分のプロフィール情報を入力しておきます。

upwork_profile

2. 決済方法の登録
 自分のプロフィールの他に、仕事を発注する前に決済方法の登録をしておいた方がスムーズです。アカウントメニューの[Setting]- [Billing Method]から登録します。決済方法はPaypalとクレジットカードが選べますが、クレジットカードの場合カード認証として登録後にUpworkからの少額請求(及びキャンセル)された金額を入力する必要があります。クレジットカード会社のWeb明細等でも海外からの請求だとすぐには確認できないケースが多いので注意が必要です。私はすぐに仕事を発注したかったのでカード会社に連絡して金額を教えてもらって対応しました。

upwork_billing1


3. 仕事登録
  さあ、では早速仕事を登録してみましょう。ヘッダの[Post a Job]等から仕事の登録を行います。

upwork_postjob2

 ヘルプ曰く、「気を惹けるタイトルにせよ!」とのことだったので、"Where is my mascot character like octcat!"というタイトルにしてみました。実際に気を惹けるのかなんて分かりませんが、まぁ勢いだけでいってみましょう(笑)。
 案件の詳細記入欄には、どんなマスコットキャラクターが欲しいのか、最終的には成果物として何が欲しいのか等をできるだけ細かく書き、あまり質疑が行わなくて済むようにしました。報酬金額はお試し利用なので、固定報酬制の$30としました。金額の相場観は一度フリーランサー側として他のクライアントが出している依頼を見ると参考になると思います。

[入力項目の補足]
・How would you like to pay?
 時給制(Hourly)にするか、固定報酬(Fixed Price)にするかを決めます。
・Desired Experience Level
 どんなレベルのフリーランサーを求めているのかを指定します。あくまで指標値としてフリーランサー側の応募過程で利用されるもののようです。
・Marketplace Visibility
 誰がこの仕事を検索できるかを指定します(誰でも/Upworkメンバーのみ/招待した人のみ)。
・Preferred Qualifications
 求めるフリーランサーの必須条件を指定できます。フィードバックのスコアだったり、受注履歴だったり、居住地だったりです。
・Cover Letter
 応募してくるフリーランサーにカバーレター(挨拶状)を求めるかどうかを指定します。フリーランサーを選択するための判断材料の一つになるので特に理由がなければ求めた方が無難です。


4. 応募を待つ
  仕事の登録をしたらフリーランサーからの応募を待ちましょう。私の案件の場合、4時間位で最初の応募がきて、1日後迄に3名の応募がありました。デフォルト設定だと応募がくると画面とメールでお知らせが届きます。通知は[Setting]-[Notification]で細かく設定できます。

応募してきてくれたフリーランサーの一覧upwork_freelancerlist


5. 応募者からフリーランサーを選び発注する
  仕事を発注するフリーランサーを応募者から選考します。選考する材料となるのは、「プロフィール」、「カバーレター」、「作業履歴(どれだけの仕事をこなしたか?どれだけ成功させたか?」、「ポートフォリオ」等になります。
  これはと思うフリーランサーを見つけたら発注前にメッセージを送ってみましょう。簡単な挨拶や、仕事の理解度ややる気を確認する程度でもどんな人かを判断する材料にはなります。またできるならば作業の段取りなどを認識合わせしておくと非常に良いと思います。

 今回はインド、ウズベキスタン、ロシアの方から応募があり、その中から選ばせてもらうことにしました。インドの方はUpworkでそれなりの件数の案件も行って良さそうでしたが、ポートフォリオのイラストがあんまり日本受けする様なものではなく見送りました。ウズベキスタンの方は選考材料となる情報が余りに少なく候補外に。最後のロシアの方ですが、まだUpworkでの仕事の経験は殆ど無かったのですが、カバーレターがやる気がありそうだったことと、ポートフォリオも無難なものだったのでメッセージを送ってみました。するとすぐ反応があり、すぐにでも取り掛かるよとのこと。そんな訳で今回はこのロシアの方に仕事を発注することにしました。

フリーランサーのプロフィール画面
upwork_overview

発注前に取ったコミュニケーション。大した内容じゃなくてもいいので一度は会話を交わした方が良いと思います
 upwork_sendmessage

発注画面
upwork_hire


 発注時にはマイルストーンの定義などの契約内容の設定の他に、フリーランサーが契約に応じてくれた場合に案件掲載を閉じるかの設定も行えます。この設定を有効にしている場合は受注したフリーランサー以外の応募者にお断りの連絡がUpworkから自動で通知されます。

 その後、ロシアのフリーランサーが受注承諾し、無事契約を結ぶことができました。


6. フリーランサーと仕事を進める
  フリーランサーとの質問や確認はメッセージを使って行なうことになります。私の今回の仕事ではマスコットキャラクターのイラスト2点を成果物として指定していたので、1点めのラフスケッチを出してもらう>フィードバック>着色>2点めの作成・・・という様な流れで進めました。

フリーランサーとのメッセージのやり取り。成果物はGoogleDrive等で貰っていました。
upwork_messages


7. 成果物の確認と受入れ
 最終的に契約時に約束した成果物が完成した時点で、フリーランサー側から「フリーランサー名 has submitted work for your approval!」というアクションにより成果物の引き渡し兼受入依頼が提示されます。これに対して「変更依頼(request changes)」または「受入(approve)」が選択できます。変更依頼は何かしかのミスや不足があり差し戻す際に使います。受入はこの仕事が問題なく完了できていると判断した場合に選択します。支払いもこのタイミングで行われます。マイルストーンを設定した契約の場合、次のマイルストーンを設定することもできます。

8. 契約の終了とフィードバックの送信
 フリーランサーとの契約を完了する場合は案件リスト脇の[Action]から[End Contract]を選択します。その後フリーランサーへのフィードバックを入力できます。特徴でも書いたようにホンネとタテマエを入力できる様な仕組みになっています。

upwork_endcontract_feedback2


ちなみに、下のキャラクターが今回作ってもらったマスコットです。「fumiという日本の女性にも使われる名前」、「Webフォームシステムのマスコット」というポイントから、宇宙の郵便配達人というコンセプトを思いついたそうです。全くのおまかせで作ってもらったのですが、予想できない面白いものを作ってくれました。今回の依頼は契約してから3日程で完了しています。

fumi-characterfumi-character_2_colored_normal_size


◆ Upworkの感想

リーズナブル

   固定報酬だと5ドルから仕事を依頼できます。勿論雇いたいフリーランサーのレベルによって報酬は異なりますが、日本国内だけで発注を考えた場合より、よりリーズナブルに仕事を依頼できる可能性が高いといえます。

時差を使った進捗効率

   フリーランサーの所在地にもよりますが、時差のある地域の場合だと、夕方に仕事の詳細を伝えて翌日の朝に成果物を確認できるといったことができます。リアルタイムにやりとりできずスピード感が上がらないことのトレードオフではありますが、依頼内容と仕事の段取りを事前に良く共有できていれば時差を有効に使って業務を進めることができます。

それなりに揉めた

   実は今回の契約で1点フリーランサーと揉めたことがありました。どんな内容かというと"milestone(マイルストーン)"という言葉についての認識齟齬があったためで、私はマイルストーンという言葉を「途中の節目」という意味で使っていたのですが、フリーランサー側は「最終的な成果物」という意味で使っていて話が非常にこじれました。片方が英語ネイティブであれば余り起き得ないことかもしれませんが、非ネイティブ同士だとこんな問題も起きえます。できるだけ仕事の契約前に使用している言葉の意図や、仕事の段取りを入念に共有し、気になるところがあればしっかり確認しておくことが重要です。

世界を身近に感じる

   やはりこれが一番面白く感じたところです。東南アジアやインド、東欧など本当に様々な国の人々がフリーランサーとして登録しています。行ったこともない国や地球の裏側に住む人と仕事ができる。これは非常に面白いです。勿論ビジネスとして使う場合には、ナレッジを蓄え、リスクを十分に考慮し発注する必要がありますが、今回の例で言えば飲み会1回分程度、最低報酬の$5ならたったランチ1回分程度の金額からグローバルビジネスを経験できることには色々な可能性を感じます。

◆ まとめ

 そもそも、今回なぜUpworkを使ってみようと思ったかというと、AWS Summit Tokyo 2015の大前研一氏のトークでoDeskの話が出たことと、トークの中で「(知っているだけではなく)使ってみるということ・体感することが重要」と仰っていたことに共感したからです。エンジニアの職種としても新しい言語やシステム基盤にどんどん触って体験することが必要だと思いますが、最近そういうことに自分が臆病になっているんじゃないかと思っていたところだったので「気付き」を与えてもらった感覚でした。

 そして使ってみて改めて思うことは、IT業界においても、グローバル化というものが本当に巨大な潮流となって足元で渦を巻いているのだということ。現在のシステム開発を取り巻く環境はどんどんオープン化し、どこにいても開発ができる状況になってきています。そういった意味でクラウドソーシングを利用したシステム開発は今後よりやりやすくなると考えられます。現状、日本ではまだまだ「言語の壁」・「文化の壁」が障壁として高く、グローバルなクラウドソーシングは普及しているとはいえません。ですがいずれそれらの問題が払拭された時には、日本に拠点を置きシステムをすべて内製している弊社のような環境でも、利用し得るケースが出てくるのかもしれません。
 今回のUpworkの利用を通して、そんなことを感じることができたのでした。体感して感想を持つことはやっぱり重要ですね。

 殆ど会社のことと関係ない内容になってしまいましたが、興味を持たれた方はまずは遊び感覚でUpworkを体感されてみてはいかがでしょうか?
それでは。

第3回技術部LT大会 開催

こんにちは、たむらです。

先日第3回技術部LT大会を開催しました。
昨年から部内研修として開催しているLT大会ですが、今回も個性溢れるテーマが取り上げられました。普段あまり接点のない部内メンバーの業務内容も伺えるものになっていて、メンバー間の相互理解にも一役買いそうな内容になっています。今回のLT大会では今年の新人が早速発表したり、来年度の内定者を招いたりと賑やかなイベントとなりました。

~発表されたLTタイトル~
「はじめてのSOAPへGo」
「NetBeansのプロファイラを使ってみた」
「PowerShellを使ってみた」
「ピュアSQLで数独を解いてみた」
「fumiのマスコットを作ってみた」
「「クエストフェーズ解決ミニアプリ」ではなぜRIot.jsを採用したか?」
「オラクルマスター12Cのブロンズを受験する為の基礎の参考書を読んでみた」
「あのAmazonも使っているApache FOPで請求書を作る」
「SDのSVNをGit&GitHubに移行したい件」
「ぼくと
の100日戦争」
「backlog APIとWebhookを使った家庭内タスク管理」
「ラクーンに入社して」
「俺のオンラインストレージ ~ ownCloud on AWS ~」

LT大会の様子
DSC_0796
DSC_0806
DSC_0814
DSC_0803

今回の結果は、
1位:「ぼくと○○○の100日戦争」
2位:「ピュアSQLで数独を解いてみた」
3位:「NetBeansのプロファイラを使ってみた」
となりました。

1位のネタは、スピーカーのエンジニアが、とあるスマホゲーム攻略アプリを作った際に実際に起きたエピソードで、会社オフィシャルの当Blogにはちょっと詳細を書けない様な内容でした(笑)。ただ、誰もが知っているスマホゲームで内容も技術的な知見を含むリアリティのあるトークだったことから、ダントツの1位となりました。
2位は「数独問題をバインド変数でインプットデータとして与えると、答えを返してくれるSQL」を作成するという、ちょっと宇宙人的な発想と超人的なSQLスキルを持ったスピーカーならではと言える内容でした。

次の開催は来年の1月頃を考えていますが、段々レベルが上がってきているので次回も楽しみです。

『レジスタンス:アヴァロン』を遊びやすくするDIY

開発の松尾です。
テックブログのネタを要求されたので、色々と考えてみまして、弊社が運営しているサービスとは全く関係のない、最近社内でランチタイムに遊んでいる人狼型カードゲーム『レジスタンス:アヴァロン』を遊びやすくするための、ちょっとしたDIYについて書いてみることにしました。

『レジスタンス:アヴァロン』とは

20150824_1
『レジスタンス:アヴァロン』は2013年にホビージャパンより日本語版が発売された、人狼系正体秘匿型推理ゲームです。ゲームマスターのような中立な立場のプレイヤーが必要ない、手軽な人狼系カードゲームといったところでしょうか。「アーサー王伝説」を下敷きにしたファンタジーな雰囲気がお気に入りです。

ゲームのルールがわからないと、後段で紹介するちっちゃなアプリたちが意味不明過ぎるシロモノに見えてしまうので、ゲーム内容とルールについて解説してみましょう。

『レジスタンス:アヴァロン』は5人~10人のプレイヤーで遊ぶことができるカードゲームです。経験上7,8人で遊ぶのが一番バランスがとれていて楽しいと思います。また、面白い特徴として参加人数の多寡によらず1ゲームがおよそ30分程度で決着します。昼休みにお弁当をつつきつつ気軽にプレイできるところが嬉しいですね。

20150824_2
最もオーソドックスに5人でプレイする場合は、画像に見える5枚のキャラクターカードを使用します。青いマークがアーサー王側のキャラクターであることを、赤いマークはモードレッド側のキャラクターであることを示しています。これらのキャラクターカードをシャッフルしてプレイヤーに一枚づつ配ります。各々のプレイヤーは原則として誰がどのキャラクターであるかは分からない状態でゲームを開始します。プレイヤーは自分に割り当てられたキャラクターが示す陣営に属し、その陣営の勝利条件を目指します。アーサー王側のキャラクターであれば各クエストを成功させることを目指し、モードレッド側のキャラクターであればアーサー王側に属するフリをしながら各クエストを失敗させるために頑張ります。

20150824_3
クエストと言っても内容は単純です。この画像はプレイヤーが5人の場合のクエストシートですが、「2、3、2、3、3」と並んでいるところが各クエストに参加するプレイヤーの人数を表しています。第1クエストの数字は「2」になっているので、5人で討議しつつ2人のプレイヤーを選出しなければなりません。クエストに参加するプレイヤーを選出するのはリーダー権を持つプレイヤーです。ゲーム開始時のリーダー権はジャンケンかなにかで任意のプレイヤーに付与し、そのプレイヤーの位置から時計回りに移動していきます。

20150824_4
リーダー権を持つプレイヤーがクエストに参加するメンバーを提案したら、プレイヤー全員による投票を行います。各々のプレイヤーが投票トークンを伏せて提出し、過半数強の承認が得られればクエストフェイズに移ります。5人プレイでは3人以上の承認が、6人プレイでは4人以上の承認が必要になります。また、提案が却下されるとリーダー権が次のプレイヤーに移動します。提案の却下が5回続くと自動的にアーサー王側陣営の敗北が決定します。クエストシートの投票トラックは提案が却下される度に数字を進めて、今が何度目の投票であるかを示しています。

※ちなみにラクーンのメンバーで遊んでいるときは、面倒くさいという理由もあって投票トークンは使わずに、承認の場合は一斉に挙手という手法を採用しています。「いっせぇーのっ、せっ」って感じで。

20150824_5
クエストに参加するプレイヤーが決定したらクエストフェーズに入ります。クエストに参加するプレイヤーは、任務を成功させるか、はたまた失敗させるかをカードで提出します。任務カードに各々の陣営のアイコンがあるように、アーサー王側のプレイヤーであれば絶対に「任務成功」のカードしか提出できません。しかし、モードレッド側のプレイヤーであれば、自身がモードレッド側であることを隠すために、あえて「任務成功」のカードを選択する自由があります。内容を隠して提出されたカードをシャッフルし一斉にオープンします。全ての任務カードが「任務成功」であれば、そのクエストではアーサー王側の勝利です。しかし、ひとつでも「任務失敗」が混じっていればそのクエストはモードレッド側の勝利となります。
※プレイヤーが7人以上になると「任務失敗」が2枚提出されないとアーサー王側の勝利となる特殊なクエストも存在します。
このように第1クエスト、第2クエストとゲームを進めていく中で、先に3回目的を達成した陣営が勝利します。誰が味方で誰が敵なのか、疑心暗鬼にかられつつ自分の陣営の勝利のために考え抜き議論していくのが、このゲームのもっとも面白いところです。
とは言え、全てのプレイヤーが他のプレイヤーについて情報をまったく知らないままだと単に運否天賦なゲームになってしまいます。人狼ゲームに特殊な能力を備えた役職があるように、『レジスタンス:アヴァロン』では特定のキャラクターに付与された能力が存在し、その能力によってゲーム開始時点の情報量に差をつけます。
それぞれの主要なキャラクターの能力は以下のように分かれます。

マーリン(アーサー王側)

  • 能力:ゲーム開始時点にモードレッドの手下をすべて知ることができる。
  • 戦略:アーサー王側が勝利を飾っても、モードレッド側の「暗殺者」にマーリンであることを見ぬかれると逆転負けになるルールがあるため、身分がバレないように行動しつつ、うまくアーサー王陣営を勝利に導く。

アーサーの忠実なる家来(アーサー王側)

  • 能力:なし
  • 戦略:敵も味方もさっぱり分からない状態で開始。疑心暗鬼に負けず知力を尽くす。

モードレッドの手下(モードレッド側)

  • 能力:ゲーム開始時点で味方がどのプレイヤーなのかを知っている。
  • 戦略:モードレッド陣営であることがバレないようにしつつ、クエストに自分か仲間が含まれるように誘導する。

暗殺者(モードレッド側)

  • 能力:ゲーム開始時点で味方がどのプレイヤーなのかを知っている。また、アーサー王側が勝利した場合に、マーリンだと思われるプレイヤーを指名して正解すると暗殺によるモードレッド側の逆転勝ちになる。
  • 戦略:基本はモードレッドの手下と同様。しかし、負けた場合にマーリンを指名する責任があるため、誰がマーリンであるのかに常に気を配る必要がある。

何人でプレイした場合でも絶対に「マーリン」と「暗殺者」は含まれます。キャラクターがマーリンであれば、自分がマーリンとバレないように、時にはわざと任務を失敗させるなどのテクニックを駆使して、頑張って味方を勝利へ誘導しなければなりません。モードレッド側のキャラクターであれば、陣営は少数派ながら、味方が誰かはわかっていることを活かしつつ、アーサー王陣営に属しているフリをしながら、うまく任務を失敗させる必要があります。このようなキャラクターによる情報量の差がゲームに深みと戦略性を与えています。もっとも情報量の少ない「アーサーの忠実な家来」で疑心暗鬼にかられる中で知力を絞るも良し、何食わぬ顔でモードレッド陣営であることを隠しつつ任務の失敗に邁進するなど、ゲームのたびに自分の立ち位置や戦略ががらりと変わっていくのが『レジスタンス:アヴァロン』の醍醐味だといえるでしょう。

『レジスタンス:アヴァロン』を10倍遊びやすくするDIY

さて、こんな楽しい『レジスタンス:アヴァロン』ですが、ゲームマスターのいない人狼型ゲームであるという特性から、ゲームの開始時に少々やっかいな儀式が必要になります。具体的には、各々のキャラクターの情報量をコントロールするために、全員で目をつぶり、モードレッド陣営の味方の確認フェーズやマーリンによるモードレッドの手下確認フェーズを設けなければなりません。

以下は、ゲームのマニュアルより引用した、全員が目を閉じた状態で行うこの儀式を進行するためのセリフの例です。

「皆の者は目を閉じ、自分の手を握り拳にして前に差し出せ」

「モードレッドの手下は目を開き、周囲を確認してだれが邪悪のしもべであるか確認せよ」

「モードレッドの手下は目を閉じよ」

「すべてのプレイヤーは目を閉じ、自分の手を握り拳にせよ」

「モードレッドの手下は親指を立て、マーリンがお前らを知られるようにせよ」

(以下略)

キャラクターの能力による情報量の差を実現するためには、この儀式を正確に執り行う必要があります。

『レジスタンス:アヴァロン』で遊ぶようになった当初は、自分で買って持ってきた責任から、わたし自身が目をつぶりつつ、上記のようなセリフを唱えていたのですが、やはりセリフや順序を間違ってしまうことが多々ありました。せっかくプレイヤーにキャラクターを割り当てても、この儀式に不備があるとキャラクターのシャッフルからやり直さなければなりません。ゲームを何度か遊ぶうちに段々とこの手順が面倒くさくなってくるのはいかんともしがたいところでした。

そんな時に天啓のごとくひらめきました。

OSXってたぶん喋れたよな!?

『レジスタンス:アヴァロン』オープニングスクリプト on OSX

$ say "松尾です"

さっそくググってコマンド名を調べ、サブノートMacのコンソールからsayコマンドを叩いてみます。多少不自然なイントネーションながらも女性の声で「まつおです」とはっきり喋るではないですか!この仕組さえあればあとは一気呵成に書くのみ。

#!/bin/sh
STORM="storm.mp3"
THUNDER="thunder.mp3"

function effect() {
  if [ -f "$1" ]; then
    afplay "$1" -t $2
  else
    sleep $2
  fi
}

say "「レジスタンス:アヴァロン」を開始します"
sleep 2
say "みな、目を閉じ、拳を前に出せ"
effect $STORM 4
say "モードレットの手下は、目を開け"
sleep 1
say "モードレットの手下は、仲間を確認せよ"
effect $THUNDER 5
say "モードレットの手下は、目を閉じよ"
sleep 2
say "マーリンは、目を開け"
sleep 1
(以下略)

概略このようなシェルスクリプトを書いてざっくりとデバッグ。afplayコマンドを使えばmp3ファイルの再生も可能だとわかったので、無料素材サイトより拾ってきた効果音を鳴らして雰囲気を盛り上げる仕掛けも施しました。上記のスクリプトの先頭部にある、変数STORMやTHUNDERに入っているファイルが存在すればそれを効果音として指定した秒数で再生します。ファイルがなければないで単に無音でsleepするという単純な仕組みです。

『レジスタンス:アヴァロン』では「マーリン」や「暗殺者」以外にも、マーリンが誰なのかを知ることができる「パーシヴァル」、マーリンからモードレッド陣営だと見抜けない「モードレッド」など、任意で追加できる特殊なキャラクターがいます。そういったパターンに全部対応できるように仕上げたスクリプトの完成版はgistに置いています。環境変数で特殊キャラクターの有り無しをコントロールすることも考えましたが、面倒臭いのでやめました。このちょっと頭の悪そうなシェルスクリプトで完成版です。不要だったらコメントアウトすれば良いという方針です。セリフを変えたければスクリプト内の日本語をいじりましょう。必ずしもsayコマンドが日本語を正しく区切って読んでくれるわけではないので、変な読み方をされたら「、(句読点)」で区切るのがオススメです。

※ちなみに上記のスクリプトには、『レジスタンス:アヴァロン』の拡張版『湖の騎士』での追加キャラクター「ランスロット」を使った場合の選択ルールにも対応していますが、大抵の場合は不要なため「ランスロット」と書いてある行はコメントアウトしちゃいましょう。

『レジスタンス:アヴァロン』クエストフェーズ解決ミニアプリ

さてこのように、『レジスタンス:アヴァロン』の難儀なポイントがひとつ解決しました。昼休みのたびに持ちだされて、オープンニングスクリプトだけを喋らされるMacbook Airに多少の憐憫の情が湧きますが、標準状態のWindowsでは逆立ちしても真似ができない仕事をあっさりと実現できたわけですので、きっと故スティーブ・ジョブスも草葉の陰で喜んでいるのではないかと妄想しつつ、もうひとつ煩雑な手順があることに気づきました。

それは、クエストフェーズの投票と開票が非常にセンシティブな手順である、という問題です。

具体的には、仮に3人がクエストを実行する場合、3人にそれぞれ「成功」「失敗」の2枚組のカードを割り当てて、選択したカードを回収してシャッフルの後オープンします。結果として1枚「失敗」のカードが含まれていても、誰が出したのか分からないようにする必要があるからです。また、選択されなかったカードについても回収してシャッフルしなければなりません。誰がどのカードを残していたかは、どのカードを選択したかの裏の結果に過ぎないため、これら情報についても慎重に取り扱う必要があります。

この手順の実行には少々緊張感が漂います。仮に、誰かの選択したカードが見えてしまったら最後、その瞬間からゲームバランスが崩壊し、せっかく進行したゲームが途中で台無しになってしまいます。

と、このような悩みを解決するためにエンジニアの下田が作ったのが、HTML5+JSのみで作られた、えーっと名前は無いので適当につけると「クエストフェーズ解決ミニアプリ」です。
20150825_1
スマホからここにアクセスするだけで簡単に起動します。ソースコードはgistに置いてあります

とりあえずクエストフェーズに入ったらこの画面より「START」をタップします。

20150825_2
クエストの「成功(SUCCESS)」か「失敗(FAILURE)」のどちらかを選んでタップします。タップする指の位置で投票内容がバレないように、ボタンの上下はランダムに入れ替わるようになっています。(わたしが「指の位置でバレそう」と呟いていたら翌日実装されていました。きっと小人さんの仕業でしょう。)

20150825_3
投票が完了するとこのような画面が表示されます。クエストの投票を完了していないプレイヤーが残っていたら、このままの状態で次のプレイヤーにスマホを手渡します。「CONTINUE」をタップすると次のクエスト投票が続行され、すべてのプレイヤーが完了したら「OPEN」をタップしてクエストの結果を確認します。

20150825_4
「成功」が2人、「失敗」が1人という結果が出ました。残念なことにクエストは失敗です。3人のプレイヤーにモードレッド陣営の邪悪なキャラクターが混じっていた模様です。

と、このような感じのミニアプリを使うことで、慎重にカードを取り扱う必要のあったクエストフェーズの煩雑さが一気に解決してしまいました。文明の利器の最たるものである現代のスマホ上で、インターネット技術の重要な基盤であるHTML5を用いて、単なる計数機を作ってしまったような罪悪感をちょっぴり感じてしまうのですが、これもまた一種のIoTではなかろうかと強弁して逃げ切ることにします。

テーブルゲームのDIY万歳

このように『レジスタンス:アヴァロン』にハマったがゆえに、エンジニアの余技でDIYしてしまったという、誰得な記事を書いてしまいました。実際に他部署の人間と『レジスタンス:アヴァロン』を遊んだ時に、急にMacが喋り出したり、スマホでクエスト投票ができたりする様を見て、ちょっぴり引かれた空気を感じてしまい、落ち込んだりもするけど、私は元気です。

この内容が役に立ちそうな局面がほとんど思いつかないのですが、快適に『レジスタンス:アヴァロン』を遊んでみたいという奇特な方であれば喜んでいただけるのではないかと思っています。

それではみなさん、素敵なテーブルゲームライフを!


記事検索