詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

記録をスプレッドシートに保存できるスピードキューブ用タイマーアプリを作った

これです。

https://motemen.github.io/futimer/

そしてリポジトリはこちら。

GitHub - motemen/futimer: Web-based speedcubing timer

数ヶ月前からスピードキューブをはじめている。バラバラの状態に(スクランブル)されたルービックキューブをいかに早く揃えられるか、という競技で、自分はようやく40秒切れるようになったくらい。はじめた頃の自分から見ると驚異的な数字だけど、いま自己評価すると、脱初心者できたかな〜っという段階。

社内でもやや流行っている。スピードキューブの何が面白いのかというと、少なくとも自分がやり続けているのは、「圧倒的成長を感じられること」。やってみる前はルービックキューブを揃えることなんて想像もつかなかったし、習得するだけでも相当な苦労なのだろうと思っていたけれど、実は広く使われる手順というものがあって、これに従うとじつに意外なことに、ひと晩ふた晩も集中すれば何も見ずにキューブを揃えられるようになる。頭じゃなくて、手で覚えるのがミソ。

ソフトウェアエンジニアとして10年近くやっていると、自分の学習プロセスについてある程度のパターンができてしまっていて、新しいことに接しても想像がついてしまうところがあると思うのだけれど、スピードキューブをやることはその範囲外のことで、あらためて学びのプロセスというものを客観視するいい機会になる。それに、見た目以上に学習曲線が急なこともあって、楽しい。

そういうわけで揃えるのに3分くらいかかっていた最初期から、plusTimer という Android アプリを使ってソルブ時間を記録していたのだけど、データのエクスポートが簡単にはできず、成長記録と分析という観点ではもっと扱いやすい Google スプレッドシートに置きたい! と思うのが人情なわけで、PWA で自作することにした。スマホのホーム画面に置いていて、自分のレベルでは普通に使えてます。

シートに書くと、こんなグラフを楽しめる。

アプリの使い方の説明は特にしません。不親切!

特徴

  • Google スプレッドシートに記録を保存できる
    • 唯一にして最大の特徴
    • 保存ボタンでセッションをローカルに記録して、同期ボタンでセッションをシートに保存するイメージ
    • 複数デバイス間の同期には対応していない。スプレッドシートは追記のみ
  • 試しに録画機能もつけてみている。PC など広い画面でアクセスすると、右下のパネルから選べます。手前からの撮影にはならないけど……
  • 公式スクランブラの TNoodle のコード(を JS 化したもの)を使っている。そのぶん少し重い。Web Worker 化できたらいいんだけど
    • これを利用してる関係でライセンスは GPLv3
  • インスペクションのカウントダウンや、DNF の記録とかはとりあえず作ってない

ご意見ご感想お待ちしております。

ハッカーズチャンプルー2018で、『ソフトウェア開発と私』という発表をしました #hcmpl

6/30 に行われたハッカーズチャンプルー2018で、『ソフトウェア開発と私』という話をしてきました。もともと id:anatofuz さんのお誘いで、ネタは Perl やコミュニティに関する話であればなんでも、ということだったのですが、考えているうちにこれは自分の話になるな、ということで路線変更して、自分のことをふり返った内容にしてみました。面白いと思ってもらえたようでホッとしてます。

ソフトウェア開発と私 / Software Development and I - Speaker Deck

以前インタビューを受けたことがありましたが、その前後で考えたことや、言いきれなかったことをどこかでまとめたいと思っていて、いい機会でありました。それでもまだ全部まとまってるわけではないんですが。あと口頭での内容がメインなので、スライドだけでは分かりづらいところもあるかと思います。

ハッカーズチャンプルー

ハッカーズチャンプルーは初参加でした。沖縄のコミュニティが集まって、それぞれが面白いとおもう発表を用意する、という趣向ですが、たしかに内容の幅が広く、かつ遠すぎず、という感じで楽しめました。ゲストのトークの合間に地元コミュニティからの LT が挟まっているのも楽しくて、すべてがメインコンテンツという感じ。

会場がひとつでワントラックだったのと、小さなスクリーンにツイッターのハッシュタグを映しているのがよくて、一体感もあったと思う。運営の中の人たちからもいろんな話が聞けて、楽しかった。沖縄の人たちは沖縄好きなんだなーってのが伝わってきました。

沖縄旅行記

ここからが本編。

行きの便が那覇空港に着いて、スマホの機内モードをオフにした途端に台風情報が飛び込んできて、それが帰る予定の日曜昼に直撃するってんで、あーどうしようかなと思いつつも、ことの重大さがよく分かっておらず、便の振り替え1泊増やして月曜に帰るかも〜などと会社に間抜けな連絡をしていました。結果的には2泊追加して火曜日の便でいま帰ってます……。

予約していたホテルが風俗街のど真ん中にあって登壇前のナーバスな精神にさらにダメージを受け、引きこもろうかと思いましたが、前夜祭に参加しないわけにもいかないので、街へ出て、先にお土産を買ってから前夜祭へ。開始30分経っても半分しか人が集まっていなかったり、飛び入り LT が続いたりと楽しい会でした。あとこのあたりで沖縄の足はタクシーだなと気づく。

当日土曜日はまだ台風も来てないのに朝から豪雨で、どうなることかと思ったけど開始するころにはきれいに晴れてて何より。本編の話は上に書いているのでいいとして、問題は翌日曜日の飛行機。

ハッカーズチャンプルー楽しかった、明日は何があるか分からないから早起きしよう、と思って寝たところ、欠航の連絡が来ていたらしいと起きてから気づく。

チェックアウトのときに台風大変ですね、もう今日の夜もこの宿は埋まってますよとフロントで言われて、空港のカウンターでも、今日は飛ばないし月曜日の便まで埋まってます、火曜日もわずか、と聞いてからようやく焦りだしました。関空直通ではないけれど夕方の便が空いていたので取ってみたけれど、間もなく欠航し、これは追加泊決定だな、とあきらめてルートビアを飲みながらホテルを探すも、安宿しかなくとりあえずカプセルホテルが確保できたのみ。

ルートビア、たしかにサロンパスの味。サロンパス食べたことあるからわかる

カプセルかーやだなーと思いながら、ほかの登壇者の動向を探ってみると fujiwara さんの以下のツイートに気づいて、自分も真似してみるとリロードしてるうちにビジネスホテルが確保できた。

booking.com には Perl の神様がついている。ホテルで予約がちゃんと通っていることを確認して荷物を預けるとようやく沖縄を楽しむしかないと思えてきて、少し観光する余裕が出てきました。本場の台風たのしかった。また本州で会おうな。

あきらめて沖縄満喫してる

月曜日の日中はふつうに仕事。ハッカーズチャンプルー後夜祭が開かれるとのことで当然参加したけどこの人たちと何度飲んでるんだろと思って面白かった。発祥の店のタコライスを食べられたのに感激しました。

ハッカーズチャンプルー後夜祭してる #hcmpl

いまは朝イチの便で関空に向かってます。ここまで来るともう安心でしょう。いろいろと大変でしたが、最終的にはすべていい思い出です。

AWS AppSync で時刻のソースをエポックミリ秒として扱いたい

サーバレスで GraphQL したかったので AWS AppSync に入門してみた。スキーマを書いてデータソースとリゾルバを追加して……というフローでずいぶんさくさくと進むんだけど、タイトルに書いたとおり時刻をエポックミリ秒として扱おうとすると困ってしまった。

つまり以下のようなスキーマで、データソースに格納されたエポックミリ秒(例えば 1503932400000)を JSON にして返そうとすると:

type Entry {
  createdAt: Int!
}

こんな感じのエラーになってしまった。

{
  "data": {
    "getEvent": {
      "createdAt": null
    }
  },
  "errors": [
    {
      "path": [
        "getEvent",
        "createdAt"
      ],
      "locations": null,
      "message": "Can't serialize value (/getEvent/createdAt) : Expected type 'Int' but was 'Long'."
    }
  ]
}

なるほどね、と思ったものの GraphQL に Long 型はない。調べてみると、数値が 2147483648 以上になるとリゾルバの内部で Long 型として扱われるように見える。

これは型が Int のフィールドにローカルのリゾルバ(type が None のもの)をアタッチし、request mapping template を

{
    "version": "2017-02-28",
    "payload": 2147483648 ## または 2147483647
}

のようにして確かめることができる。

ミリ秒をあきらめてエポック秒にすればこの境界を越えないわけだけど、せっかくなのでこういう風に解決してみた:

createdAt フィールドが引数を取り、文字列型を返すようにする。

type Entry {
  createdAt(format: String, timezone: String): String!
}

上記 createdAt フィールドにローカルのリゾルバをアタッチし、request mapping template を以下のようにする。

#set($format = $util.defaultIfNull($ctx.args.format, "yyyy-MM-dd HH:mm:ss"))
#set($timezone = $util.defaultIfNull($ctx.args.timezone, "+09:00"))
{
    "version": "2017-02-28",
    "payload": "${util.time.epochMilliSecondsToFormatted($ctx.source.createdAtEpochMillis, $format, $timezone)}"
}

クライアント側からはパーズしやすいフォーマットで受け取るか、受け取った文字列をそのまま表示するなどすればよい。楽しいですね。

参考: Resolver Mapping Template Context Reference - AWS AppSync

はてなで一緒に働きませんか?