放っておくと進まない仕事を進めるために

はてなは 7 月決算なので期のふりかえりをやっていたんだが、今期は「放っておくと進まない仕事を進めるために、時間を確保する」ことで進むようになった期だった。ちなみに前期は「放っておくと進まない仕事を進めるために、締切を設定する (強制力を持たせる)」ことで進めようとしていた期だった。

放っておくと進まない仕事とは、優先順位が低い仕事のこと。やってもやらなくても直ちに影響はない仕事をどうやったら進められるのかというのは長年ずっと課題だったが、やっと自分の中である程度答えが見えてきたかもしれないので記しておく。書き出してみたら至って普通なんだけど、以下をやっている。

  1. 見積もる
  2. 締切を作る
  3. 締切を宣言する
  4. 時間を押さえる

見積もる

チームで見積もりを行い、まずはタスクの重さや実現方法についての共通認識を持つ。

タスクを分解する、とも言い換えられる。やるべきなんだけど気が重い仕事はとにかく分解しまくる。それでも手が付かないならもっと分解する。最終的には「作業ディレクトリに cd する」まで分解する。

なお、この 5 秒で終わる状態になっていてもまだやらないことが経験により分かっている。これは自分が内心で cdだけやっても成果が出ないことを知っているからだろう。

だったら分解する意味は無いのか?と思うかもしれないがそんなことはなく、リストを人に見せておくと「 cd した?」という問い掛けが発生するので、作業に取りかかるキッカケを増やす役に立つ。僕のことは赤ちゃんだと思ってマイクロマネジメントして欲しい。

締切を作る

締切が無いから際限なく延びるので、まずはタスクの締切を設定する。ここで無理な締切を置いても結局実行できない (何度も痛い目を見た) ので、前段でちゃんと見積もられている必要がある。

締切を作ってタイムラインに載せることで、後ろにも仕事が詰まっていることを実感させ、本当の締切 *1 や裏の締切 *2 が実際には存在しないことを可視化する。

また締切を置くことで、他人からも遅延が見えるようになる。これも他人にマイクロマネジメントして貰うコツだと思う。

締切を宣言する

締切を作ったところで、守る気が無かったら意味が無い。脳内の優先度を上げる策として、前々期は昼会の中に「id:onk が今日終わらせるタスクを宣言する」というコーナーを作った。

この脳内優先度ってヤツが厄介で、ADHD 特性を持っていると、新しいイベントが常に優先度最高で割り込んできてしまう。なんとか対抗するために 一貫性の原理 を使い、自ら宣言することで、強い気持ちで優先度を高く保ち続けようとした。

「今日中にコレとコレが終われば」と言い聞かせることで、帰る/寝る前に本当に終わっているかを見返すことが可能になり、進捗するという算段だったのだが、この作戦を採る段階ではまだ「睡眠時間を犠牲にすればなんとかなるんじゃないか」のような甘い考えが傍らにあるので、次の「時間を抑える」も行う必要がある。

f:id:onk:20200810013524p:plain
当時のふりかえりを見るとボロクソに言われている

時間を押さえる

これはカレンダー上で作業予定を入れてすべてをブロックしてしまうと言う作戦。

新しいイベントが常に優先度最高で割り込んでくるなら、Slack を落とすことで刺激を減らせば良い。反応できないことを宣言するためにカレンダーも埋めておく。

ただこれだけで進むかというとやっぱりダメで、Slack を落としてもすぐに起動してしまう (完全に手癖) ので、更にペア作業時間として相互監視を行うことで進捗するようになる。完全にペア作業じゃなくても、配信することで緊張感が生まれるようだ。コロナ禍によりさぎょいぷが流行ったのは良い効果があった。

一人でもなんとかできるようになりたくて、リズムを作れば行けるはずだと思ってポモドーロは毎年何度も挑戦しているんだけど、まだ上手くいってない。何らかの強制力が閾値を超えないのだろう。例えば Blurred のようなツールと組み合わせてどんどん刺激を下げていくと、どこかで閾値を超える日が来ると思って模索はし続けている。

イベントにする

もう一つ、「イベントにする」という作戦にも触れておきたい。具体的には開発合宿とかバグ退治 Day とかがコレに当たる。これは僕の特性というよりも「チームで優先度が上がらない仕事」に対応する方法。

ロードマップに載せるほど優先度が上がりきらないが、やりたいタスクというのはどうしても存在する。

優先度が上がらないのは他の重要度が十分に会話されているタスクに押しのけられてしまうからで、本当は「重要度を説明する」が正しい進め方だと思う。しかしそんなコスト高いことはやってられないので、すべてサボって信頼だけで進めたい。そこで開発合宿という「なぜか分からないが成果が出ることが分かっている」という信頼貯金を使って技術的スパイクを実施し、プロジェクトチーム内からプロトタイプを作るコストを軽減する。

イベントにすることで非日常感を作れ、周りが協力的になるので一石二鳥。

最近のはてな社内は、イベント仕立てをうまく使うチームが増えてきたように思う。個人の危機感をチームのタスクに変えていく術として、みんなの引き出しに収まったということなのだろう。

*1:バッファをすべて食い潰した締切

*2:輪転機が稼働する直前のこと。本当の締切の後に設定される

久々に sinatra app を作った

「いつもの」が結構ありそうなので書いておく。

app.rb ペラ 1 でツラくなったときの対策はだいたい sonots パイセンの ちっちゃくはじめておっきく育てる sinatra アプリの作り方 に書いてあって、これは今でも有効なので読んでおくと良いです。

ディレクトリ構成

REPO
├── app.rb
├── bin/
├── config/
│  ├── database.yml
│  ├── initializers/
│  └── locales/
├── config.ru
├── Gemfile
├── Gemfile.lock
├── helpers/
├── models/
├── public/
└── views/
  • sinatra らしさをなるべく残してある
    • 例えば config/boot.rb を用意するかは非常に悩んだのだけれど、起点は app.rb であって欲しい
  • models/, helpers/ は分けた
    • app.rb に直接書いていると 5 model ぐらいしか無くても数百行になってさすがに見通しが悪いので
  • config/initializers/ を置いた
    • 置き場所に迷ったものを放り込みやすくて便利

もう一声大きくなったらもっと Railsディレクトリ構造に近づける *1 けど、一旦こんな感じで。

セキュリティ対策

erubi を使う

XSS 対策のために escape: true を使いたい。

https://github.com/jeremyevans/erubi/blob/1.9.0/lib/erubi.rb#L57

erubi gem を入れた上で

class App < Sinatra::Base
  set :erb, escape: true
  ...

しておくと <%= ... %> がデフォルトで escape される。

String ごとに html_safe フラグを付けられるわけではないので ActiveSupport::SafeBuffer ほど便利ではないが、無いより 100 億倍良い。

これは完全に余談なんだけど Padrino は SafeBuffer が導入されていて羨ましい。

ところで Erubi には「escape が優先される」と書いてあるので escape: true って使ったんだけど、検索しても 1 件も引っかからないのな。

escape_html: true で検索するとめちゃくちゃ尊い PR が出てきた。(先月じゃーん

github.com

Rack::Protection を使う

デフォルトで使われているんだけど、remote_token であって authenticity_token ではないので置き換える。

set :protection, use: %i[authenticity_token], except: %i[remote_token]

remote_token は referrer が同じだったら通している。 https://github.com/sinatra/sinatra/blob/v2.0.8.1/rack-protection/lib/rack/protection/remote_token.rb#L17-L19

authenticity_token は (GET, HEAD, OPTIONS, TRACE 以外は) 常にチェックする。 https://github.com/sinatra/sinatra/blob/v2.0.8.1/rack-protection/lib/rack/protection/authenticity_token.rb#L98-L106

これで例え XSS があったとしても CSRF されなくなって安心。

ところでこの記事書こうと思ってついでに自社の過去事例を漁っていたら 14 年前の面白記述を見つけた。サードパーティ製ツールを大事にしている精神が感じられて最高でした。

diary.hatenastaff.com

SessionStore を Cookie から変更

クライアント側に構造体を保存しているとセッションの無効化が困難、みたいなアレです。

ritou.hatenablog.com

ユーザごとのログインセッションを任意に破棄できるようにしようと思うとログイン処理のたびに 1 レコード増えることになって、「素朴な Web アプリケーションが許される時代は終わったなぁ」って気がしますね。

よく使う extension, middleware

ほとんど Sinatra::Contrib だな。

Sinatra::ConfigFile

YAML を置いておくとアプリケーション内から設定にアクセスできるマン。 大枠で言うと Rails::Application.config_for と同じ使い心地です。

Sinatra::ContentFor

# layout.erb
<title><% if content_for?(:title) %><%= yield_content(:title) %> | <% end %>APP_NAME</title>
# users/show.erb
<% content_for :title, @user.name %>

みたいなヤツ。 title の例だけで無いとツラいのが分かるはず。

Sinatra::JSON

json @obj

すると JSON dump して content-type 付けて返してくれるヤツ。

Sinatra::Reloader

development で有効にすると開発が楽で幸せ。

configure :development do
  register Sinatra::Reloader
  also_reload "models/**/*"
  also_reload "helpers/**/*"
end

Rack::Flash

rack-flash3 gem でやってる。

redirect 時に notice を渡す書き方もしたいので、Padrino::Flash からパクっておきます。

https://github.com/padrino/padrino-framework/blob/0.15.0/padrino-core/lib/padrino-core/application/flash.rb#L179-L215

REPL

bin/console にこれを置いておくだけでだいぶ楽。

#!/usr/bin/env ruby
require "bundler/setup"
require "irb"
require File.expand_path("../app", __dir__)

def reload!
  Sinatra::Reloader.perform(App)
end

IRB.start(__FILE__)

reload!ActiveSupport::Dependencies と違って丁寧に remove_const とかをやっているわけではなくただ require し直しているだけだけど、model を読み込み直したいぐらいの用途なら足る。

initialize

ディレクトリ構成のところで言った config/initializers を一番最初に読み込ませている。

Dir[File.expand_path("config/initializers/*", __dir__)].sort.each {|f| require f }

ActiveRecord

以下を食わせている。前半は establish_connection 用。後半は timestamp 周り。

path = File.expand_path("config/database.yml", __dir__)
specs = YAML.safe_load(ERB.new(IO.read(path)).result, aliases: true)
ActiveRecord::Base.configurations = specs.stringify_keys
ActiveRecord::Base.establish_connection(env.to_sym)

Time.zone_default = Time.find_zone!("Asia/Tokyo")
ActiveRecord::Base.time_zone_aware_attributes = true
ActiveRecord::Base.default_timezone = :utc

前半は sinatra-activerecord gem で良いんだけど、メンテが滞ってるのを見て から使わなくなってしまった。薄いのでシュッと剥がせたし。最近は次に名乗り出てくれたメンテナが元気なので大丈夫かもしれない。

timestamp は Rails だと Railtie が良い感じにやってくれているが、生で ActiveRecord を使うときは自分で指定する必要がある。 https://github.com/rails/rails/blob/v6.0.3/activerecord/lib/active_record/railtie.rb#L69-L74

あとは models/ 以下に配置して、 ApplicationRecord も欲しいので

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end
require_relative "application_record"
class User < ApplicationRecord
  ...
end

のような感じにしてある。

ActiveRecord::RecordNotFound

ActiveRecord::RecordNotFound を 500 じゃなく 404 で返したいので以下を書く。

error ActiveRecord::RecordNotFound do |e|
  not_found
  # 404 HTML を返したかったらこう
  # send_file "public/404.html", status: 404
end

I18n

Time を出力するときに I18n.l を使うと楽なので YAML を読み込ませておく。

# config/initializers/i18n.rb
I18n.load_path << Dir[File.expand_path("config/locales/*.yml")]

RSS/Atom

builder gem を使え!

ログイン

current_userauthenticate_user! さえあれば事足りることが多いので、 omniauth だけを使っている。

# application_helper.rb
def current_user
  return nil unless session[:user_id]
  @current_user ||= User.find_by(id: session[:user_id])
end
# app.rb
get "/auth/github/callback" do
  ...

  redirect env["omniauth.origin"] || "/"
end

private

  def authenticate_user!
    return if current_user

    if request.safe?
      redirect "/auth/github?origin=#{CGI.escape(request.fullpath)}"
    else
      redirect "/auth/github"
    end
  end

テスト

request spec

rspec-request_describer gem にお世話になっています。send_request を書いておけば rack-test で使える。

routing がカオスになってきたらリクエストを投げた後に last_request.env["sinatra.route"] を確認することで、routing spec の真似事ができる。

RSpec.describe "/", type: :request do
  let(:app) { App }
  let(:send_request) { send(http_method, path, request_body, env).status }

  describe "GET /" do
    it {
      is_expected.to eq 200
      expect(last_request.env["sinatra.route"]).to eq "GET /"
    }
  }
}

fixture

https://github.com/rspec/rspec-rails/blob/v4.0.1/lib/rspec/rails/file_fixture_support.rb をパクってきて spec/support/ 以下に置いてます。

module FileFixtureSupport
  extend ActiveSupport::Concern
  include ActiveSupport::Testing::FileFixtures
  included do
    self.file_fixture_path = File.join(File.expand_path("..", __dir__), "fixtures", "files")
  end
end

RSpec.configure do |config|
  config.include FileFixtureSupport
end

database_rewinder

頑張れば use_transactional_tests 使えるかもしれないけど、読み解くのサボっているので愛用しています。

まとめ

これぐらいの準備で割と Rails と似た書き味で書けるし、だいたい Sinatra::Contrib にあるから安心って感じですね。Padrino に標準装備されているものが多いのですごく悩ましい……。padrino-helpers に嫌な思い出があるだけで Padrino は悪くないので使えば良いのかもしれない。

デプロイしたのが先週なので、運用が始まったらもっと色々考えることが増えていきそうだと思う。例えばログ。(今は標準出力 && Rack::CommonLogger のままで一旦いいやって思って手を付けていない)

*1:GitHub を数時間泳いだところ、https://github.com/cesarfigueroa/acme がめちゃくちゃ良いので参考にしたい

MySQL の ORDER BY や GROUP BY に position を渡せるのを知って驚いたけど deprecated だった

ほぼタイトルママなんだけど

SELECT DATE(created_at), COUNT(1)
  FROM users
  GROUP BY 1;

ってクエリを見つけて、 GROUP BY 1 ってなんぞ……?という話。

MySQL のドキュメント には

[GROUP BY {col_name | expr | position}, ... [WITH ROLLUP]]

 

[ORDER BY {col_name | expr | position}

 

Columns selected for output can be referred to in ORDER BY and GROUP BY clauses using column names, column aliases, or column positions. Column positions are integers and begin with 1:

とあり、ORDER BY, GROUP BY は position を受け付ける。

これは SELECT 句に書いた列番号で指定できるってヤツなんだけど、なので最初の例で言うと GROUP BY DATE(created_at) と書く代わりに GROUP BY 1 と書けて便利。

でも

Use of column positions is deprecated because the syntax has been removed from the SQL standard.

とも書いてあって、はいナルホド〜

ので SQL99 で消えたのかな?知らんけど。

遠慮無く「やめましょう!」ってレビューできました。

Athena だと逆に推奨している

Top 10 Performance Tuning Tips for Amazon Athena | AWS Big Data Blog

  1. Optimize GROUP BY

...

sql SELECT state, gender, count(*) FROM census GROUP BY state, gender;

One other optimization is to use numbers instead of strings, if possible, within the GROUP BY clause. Numbers require less memory to store and are faster to compare than strings. The numbers represent the location of the grouped column name in the SELECT statement; for example:

sql SELECT state, gender, count(*) FROM census GROUP BY 1, 2;

RDBMS の実装に合わせて使い分けるということになるんだろうなぁ。

株式会社はてなに入社しました

株式会社はてなに入社しました

株式会社はてなに入社しました - hitode909の日記

マネージャとしても 2 年目になりました。 今年もよろしくお願いします。

gorogoro.rb を読んだ

gorogoro.rb とは

大江戸Ruby会議08でぺんさんが最後に再生していた頭のおかしい (褒め言葉) Quine。

コードはこちら

https://gist.github.com/tompng/45d93b3386b5986a94b9c3c8beecba69

実装を見ていく

アスキーアートプログラミング構文

まず見慣れた (毎年 Ruby 会議に参加しているうちになぜか見慣れてしまった) アスキーアートプログラミング構文が使われている。

eval((c=%{
  ...
}).split*'')

詳しくは あなたの知らない超絶技巧プログラミングの世界 の第 2 章を参照だけど、%{} の中がプログラムを好きな形に整形したもの。スペースや改行を取り除いた上で、セミコロンで改行してあげると人間が読める程度に復元できる。

Quine

c=(w*26+';eval((c=%{'+c+"}).split*'')").split$/

の辺りが Quine。

eval c= を文字列として再構成して、自身を埋め込んでいる。

メインループ

0.upto(90) {|tm|
  ...
  puts(...)
  sleep(0.05)
}

sleep を入れながら 91 回 puts している。つまりこのプログラム全体は 91 枚のパラパラ漫画。

tm (タイマーかな) を固定すると任意のコマを描画できる。

キャンバス

m = 96
...
cn = (1..m).map { [0] * m }
...
puts(
  (0..47).map {|i|
    m.times.map {|j|
      l, j = [cn[2*i+1][j], cn[2*i][j]]
      %['".:Y,L#])[3*l+j]
    } * ''
  } * $/
)

96 * 96 の 2 次元配列を変数 cn (多分キャンバス) に入れてあり、puts している。

(0..47).map {|i| ... } は最後に $/ つまり "\n"* (Array#join) しているので m.times.map {|j| ... } が空文字で join しているので

各画素は

%['".:Y,L#])[3*l+j]

で、3*l+j を出力してみると

000000000000000000000034100000011100000000000000000000000000000000000000000000000000000000000000
000000000000000000000041000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000440000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000004400000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000004100000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000044000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000044000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000033343000000000000000000000000000000000000000000000000000000000000000000000000000
000000000003010000000011330000000000000000000000000000000000000000000000000000000000000000000000
000000000000668888888866014300000000000000000000000000000000000000000000000000000000000000000000
000000001062200008888888660143000000000000000000000000000000000000000000000000000000000000000000
000000000066660008888888806014300000000000000000000000000000000000000000000000000000000000000000
000000400888888600288882008004400000000000000000000000000000000000000000000000000000000000000000
000000008888888800000000008004400000000000000000000000000000000000000000000000000000000000000000
000000302888888200688660688004400000000000000000000000000000000000000000000000000000000000000000
000000100888800008888888882034400000000000000000000000000000000000000000000000000000000000000000
000000010028860008888888820344100000000000000000000000000000000000000000000000000000000000000000
000000001300286602288822003441000000000000000000000000000000000000000000000000000000000000000000
000000000013300002200033444100000000000000000000000000000000000000000000000000000000000000000000
000000000000111444444441100000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000001430000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000044000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000004400000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000440000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000001430000003333333000000000000000000000000000000000000000000000000000000
000000000000000000000000000143034111111111443300000000000000000000000000000000000000000000000000
000000000000000000000000000034100666686666001443000000000000000000000000000000000000000000000000
000000000000000000000000000310062222288888860014300000000000000000000000000000000000000000000000
000000000000000000000000003106200000088888888000430000000000000000000000000000000000000000000000
000000000000000000000000004006888860028888880080040000000000000000000000000000000000000000000000
000000000000000000000000000088888888000222200080040000000000000000000000000000000000000000000000
000000000000000000000000000088888882000660000880040000000000000000000000334444444433000000000000
000000000000000000000000004028888220068888868880040000000000000000000344441100000011143000000000
000000000000000000000000001302888000688888888200400000000000000000004441006688888866001430000000
000000000000000000000000000030028600088888882034144330000000000000044400622222288888866013000003
000000000000000000000000000001300222066222003410000114433300000000444002060000088888880600334441
000000000000000000000000000000001033333333110000000000001114443333440068888860028888820060110000
000000000000000000000000000000000000000000000000000000000000000111440088888880000020000680000000
000000000000000000000000000000000000000000000000000000000000000000440088888880006666006880000000
000000000000000000000000000000000000000000000000000000000000000000143028882200688888888800100000
000000000000000000000000000000000000000000000000000000000000000000014302886000888888888000000000
000000000000000000000000000000000000000000000000000000000000000000000130028600088888820010000000
000000000000000000000000000000000000000000000000000000000000000000000001330022000020001000000000
000000000000000000000000000000000000000000000000000000000000000000000000001100000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

のようになっていて、例えば 0 に置換することで各画素を出力している。目を細めてみるとこれだけでも絵が見えますねw

':. を使って 1 画素を更に上下に分割していたり (そのために行の倍のサイズのキャンバスがある)、#: を使い分けることで色の濃さ(質感)を表現していたりする。

カメラ

x = 1.62 * t + 2
y = -1.44 * t - 2
z = 10 - 7 * t - 4.1 * [tm - 6, 0].max / 9

の辺りがカメラ。

x = 0, y = 0, z = 3 とかに固定すると、その場で画像がクルクル回る。

3D 描画

rn が数式を 3D 描画しているところ?で、ピタゴラスの定理的なものが見えるねフンフンナルホドとか言ってみるけどまだ理解していない。

rn = ->(x, y, z, th) {
  xx = n + m * x / z
  yy = n + m * y / z
  rr = 144 / z
  r1 = 2**2i
  r2 = E**th.i
  tr = ->(x, y, z) {
    x, y = ((x + y.i) * r1).rect
    x, z = ((x + z.i) * r2).rect
    [*((x + y.i) / r1).rect, z]
  }
  ([0, xx - rr].max.ceil..[m - 1, xx + rr].min).each {|ix|
    ([0, yy - rr].max.ceil..[m - 1, yy + rr].min).each {|iy|
      vx, vy, vz = tr[ix / n - 1, iy / n - 1, -2]
      cx, cy, cz = tr[-x, -y, z]
      vv = vx * vx + vy * vy + vz * vz * 3
      vc = vx * cx + vy * cy + vz * cz * 3
      h = 0.3
      d = vc * vc - vv * (cx**2 + cy**2 + cz**2 * 3 - 1 - h * h * 3)
      d < 0 && next

      x2 = cx - vx * s = (h / vz).abs + cz / vz
      y2 = cy - vy * s
      x2**2 + y2**2 > 1 && next

      ...
    }
  }
}

各オブジェクト

lambda が一つのオブジェクトになっていて、例えば雷は tn

tn = ->(i,j) {
  a = 1 - j/52
  b = j>40 ? 0 : 1
  j = n - j/2.0
  (j/2 - a*16) < i && i < (j - b*16)
}

これを描画するとこうなる。

                                                ,#######################
                                                #######################
                                               ,######################
                                               ######################
                                              ,#####################
                                              #####################
                                             ,####################
                                             ####################
                                            ,###################
                                            ###################
                                           ,##################
                                           ##################
                                          ,#################
                                          #################
                                         ,################
                                         ################
                                        ,###############
                                        ###############
                                       ,##############
                                       ##############
                                      ,#############,,,,,,,,,,,,,,,,
                                      #############################
                                     ,############################
                                     ############################
                                    ,###########################
                                    ###########################
                                                   ,##########
                                                   ##########
                                                  ,#########
                                                  #########
                                                 ,########
                                                 ########
                                                ,#######
                                                #######
                                               ,######
                                               ######
                                              ,#####
                                              #####
                                             ,####
                                             ####
                                            ,###
                                            ###
                                           ,##
                                           ##
                                          ,#
                                          #
                                         ,

gt が雷門。

gt = ->(x, y) {
  y -= 0.2
  i = n + x * 168
  j = 24 + y * 84

  # 屋根
  next(x * (-y)**0.5 * 16 % 1 < 0.5 ? 1 : 2) if y < -0.4 && y > -0.9 - x**6 && y > -0.4 / x**2
  # 梁
  next(x.abs * 2.6 % 1 > 0.6 || y > -0.35 || x.abs < 0.1 ? 2 : 0) if y >= -0.4 && y < -0.32 - x**4 / 10
  # 提灯の中の「雷門」って文字列
  next(2) if (0 <= j && j < n && 0 < i && (c[j][i] || w) != w)
  # 提灯
  next(1) if 2 * x**4 + y**4 < 0.01
  # 風神雷神
  x = (x.abs - 0.5).abs
  y < -0.4 || y > 0.55 || x > 0.2 ? 0 : x > 0.12 || y > 0.47 || (y - 0.2) % 0.5 < 0.1 ? y > 0.45 && x > 0.12 ? 2 : 1 : 2 * x * x + y * y < 0.01 + y % 0.05 / 5 ? 1 : 0
}

雷門は特に提灯の中の「雷門」文字部分の実装が意味分かんなくて、

next(2) if (0 <= j && j < n && 0 < i && (c[j][i] || w) != w)

なんと c つまりこのプログラム自身の文字列を参照している。

AA になっているので提灯の中に「雷門」が出現するのだよね。プログラムを左寄せにすると提灯の中も左に寄る。

     ::Y#L::##::##''""'''""''""''""'''""''""''""'''""''""''""''"""''""''""''"""::##::##::##Y
    L::##::##::Y##::##::##L::##::##::###::##::##:::##::##::##Y::##::##::L##::##::##Y:L##::##'
   #::##::Y##::##::Y#L::##::##L::##::##:::##::##:::##::##::L##::##::L##::##::##Y::##::##Y::##'
  "::##L::##::##L::##::###::##::Y##::##:::##::##:::##::##:::##::##Y::##::##Y::##::L##::##::L##'
 "::Y#L::##::Y##::##L::##::Y##::##L::##::###::##:::##::###::##::L##::##Y::##::L##::##Y::##::L#Y
"::Y##::##L::##::Y##::Y##::##L::##::Y##::###::##:::##::###::##Y::##:::##::###::##Y::##::L##::##Y
::Y##::Y##::##L::##L::##:::##::Y##::###::###::##:::##:::##::###::###::##Y::##:::##::L##::L##::##
:Y##::Y##::Y##::###::###::###::##L::##L::##:::##:::##:::##:::##:::##::L##::L##::L##::L##::##Y::#
Y##::Y##::Y##::Y##::Y##:::##:::##:::##:::##:::##:::##:::##Y::##Y::##Y::##Y::##Y::##Y::##Y::##Y::
##::Y##::Y##:::##L::##L::###::###::Y##:::##:::##:::##Y::###::###::L##::L##:::##Y::##Y::##Y::L##:
L::Y##::Y##L::##L::###::Y##:::##L::###::###:::##:::###::###:::##:::##Y::###::L##:::##Y::##Y::L##
::###::Y##L::###::Y##:::##L::###:::##L::###:::##:::###::L##:::###::L##:::##Y::###::L##Y::###::L#
:##L::Y##L::###::Y##L::###:::##L::###:::###:::##:::###:::##Y::###:::###::L##:::###::L##Y::L##:::
##L::Y##:::###::Y##L::Y##L::###:::###:::###::Y##:::###:::###:::##Y::L##Y::###:::###::L##Y::L##Y:
L::Y##L::Y##L::Y##L::Y##L::Y##L::Y##L::Y##:::###:::###:::###:::###:::###:::###:::###:::###:::##Y
::Y##L::Y##L::Y##L:::###:::###:::###:::###:::###:::###Y::L###:::###:::###:::###:::###:::L##Y::L#
Y##L:::###L::Y##L::Y###:::###:::Y###:::###:::###::::###:::###:::###Y::L###:::###:::L##Y::L##Y:::
########               ##########         #############        ###########               #######
,,######"""""""""""""""##########"""""""""#############""""""""###########"""""""""""""""######,
   :::::    ,,,,,,,,,,,LLLLLL,,,,,,,,,,,,,LLLLLLLLLLLLL,,,,,,,,,,,,,LLLLL,,,,,,,,,,,,    :::::
   ::::::::::::::::::::::::::      '':::YYYY::::::::::::::::''      ::::::::::::::::::::::::::
   :::::::::::::::::::::::::: ####:::::::::::::::::::::::::::::'    ::::::::::::::::::::::::::
   :::::::::::::::::::::::::: ##":::::::::::::::::::::::::::::::'   ::::::::::::::::::::::::::
   :::::...............:::::: ####YYYYYYYYYY:::::::::::::::::::::   :::::................:::::
   :::::               :::::: ####LL:::::::::::::::::::::::::::::   :::::                :::::
   :::::               :::::: ####YYYYYYYYYYYYYYYYYYYYYY::::::::::  :::::                :::::
   :::::   '':::::''   :::::: ###########LLLLL::::::::::::::::::::  :::::    '::::::''   :::::
   :::::   ':::::::'   :::::: ###LLLL:::::::::::::::::::::::::::::  :::::   '':::::::'   :::::
   :::::  .:::::::::.  :::::: ##LLLLL:::::::::::::::::::::::::::::  :::::  .::::::::::.  :::::
   ::::: ':::::::::::' :::::: #YYYYYYY::::::::::::::::::::::::::::  :::::  ':::::::::::  :::::
   :::::  '::::::::::  :::::: ####LLLLL:::::::::::::::::::::::::::  :::::   ::::::::::'  :::::
   :::::  ..::::::::.  :::::: ###Y::::::::::::::::::::::::::::::::  :::::  ..::::::::..  :::::
   :::::   :::::::::   :::::: ########LLLLL:::::::::::::::::::::::  :::::    ::::::::'   :::::
   :::::   .........   :::::: ########LLLLLLLLLLLLLLLLLLLLLLLLLLLL,,LLLLL,,,,,........   :::::
   :::::               :::::: ##LLLLLLL:::::::::::::::::::::::::::  :::::                :::::
   :::::               :::::: ####LLLLLLLLLLLL:::::::::::::::::::   :::::                :::::
   :::::''''''''''''''':::::: ####LLLLLLLLLLLLLLLLL::::::::::::::   :::::'''''''''''''''':::::
   :::::::::::::::::::::::::: #######:::::::::::::::::::::::::::.   ::::::::::::::::::::::::::
   :::::::::::::::::::::::::: ##########LLL::::::::::::::::::::.    ::::::::::::::::::::::::::
   :::::::::::::::::::::::::: ,,,, .::::::::::::::::::::::::..      ::::::::::::::::::::::::::
   :::::               ::::::           .................           :::::                :::::
   :::::               ::::::                                       :::::                :::::
   :::::               ::::::                                       :::::                :::::
   :::::               ::::::                                       :::::                :::::
   :::::               ::::::                                       :::::                :::::
   #####'''''''''''''''######                                       #####''''''''''''''''#####
   #####:::::::::::::::######                                       #####::::::::::::::::#####
   #####:::::::::::::::######                                       #####::::::::::::::::#####

他のオブジェクトも全部数式で表現されている辺りも十二分に凄いんだけど、その凄さが霞んでしまうぐらいビックリした。

読み終わった感想

いやー、意味分かんなかった。

頑張って読んだらそれなりに理解はできるんだけど、こんなオシャレにカメラを動かせるセンスはまったく無いなー。

今年観た映画2019

劇場で観た奴。

ちょうど 24 回行っていて、月 2 回ぐらい観てるんだなってのが分かる。

マレフィセントもアナ雪もターミネータースターウォーズも観なかったので、後半は失速したなー。シティーハンタールパン三世も行けてない……。

全部 TOHO シネマズ 二条で観ている。徒歩圏内だし、最悪タクれば 5 分後に着けるのがめちゃくちゃ便利。

HELLO WORLD は観た直後に外に出たら聖地 (伏見稲荷から二条駅まで戻ってきて先生に捕まるところ) が目の前にあって、ずっと世界に没入してる感じで体験がめちゃくちゃ良かった。

今年買ったもの2019

買った順。

  • バタフライボード2 A4 サイズ
    • かなり活躍した。持ち歩くホワイトボードはあると良い
    • 裏紙が無限にあるなら無くても良いなとは思う
    • nu board ホワイトボードマーカー 6mm 3色セット
    • 付属のホワイトボードマーカーだけだと細さが足りないので、細い色違いを買い足した
  • ニトリ セミダブルマットレス Nスリープ プレミアムP3-CR
    • 前のは無印で適当に買った奴だと思う (記憶無し)
    • 10 年ぐらい使っていたのでヘタっていて、買い換えた瞬間から寝ていて腰が痛くならなくなって最高
    • 惰眠をむさぼれるようになってしまった
  • LGエレクトロニクス 曲面ディスプレイ (34WK95C-W)
    • Nota に遊びに行ったところ、曲面ディスプレイを見かけて欲しくなったので購入
    • Retina ディスプレイではないので MacBook Pro と一緒に使ってるとちょっと違和感はある
    • スイーベルが無いので、他人と一緒にモニタを覗き込むときに不便
    • PC 1 台しか繋がないと実力を発揮できない感じがしている
  • カードケータイ KY-01L
    • 洗濯して壊したので 2 個目…… :sob:
  • AKRacing premium レイブン
    • 何かの広告?で「180度までリクライニングできる!」って見て、寝られるじゃんって思って購入
    • 普段使いのワーキングチェアとして、想像の 7000 倍ぐらい快適に活用している
      • 買ったけどイマイチだったね、となると想像していた
    • 社で椅子を何にしようか迷っている人が居ればオススメしたい
    • フットレストも買わないと 180° リクライニングの力は活かせないことが分かっている
  • Mistel MD650L BAROCCO
    • 買った 1 週間後ぐらいに UHK (去年の12月に購入) が届いたのでほぼ使ってない
    • 普段の指使いそのままで移行できるのがメチャクチャ良くて、ただ分割されただけになっている
  • Logicool MX ERGO
  • Ultimate Hacking Keyboard
    • Facebook 眺めていたら広告が流れてきたので購入
    • 届くまで 8 ヶ月ぐらい?今なら買ってすぐ届くっぽい
    • 出張時に持ち歩く程度には大好きになってる
    • そのうちどんな設定してるか記事書かないとだ。(バックアップ代わり
  • Titan Security Key
    • 発売されたってニュースを見て買った
    • YubiKey 5C を既に使っていたので切り替えるの面倒で放置してる……
    • タブレットが USB micro-B なので YubiKey 繋げなくて不便じゃんってよく思ってるので作業したいが、Type-C 対応のタブレットに切り替える方が早そう
  • Fitbit Versa 2
    • なんか発売されたってニュースを見て買った
    • 1 週間電池がもつのは最高 of 最高
    • ウェアラブルバイス着けるの初めてで、今まで計測していなかった心拍数グラフを見て「なるほどー」って言ってる
      • 二日酔いが収まった時間が如実に分かるのがすごく便利。酷いときは翌日の夕方まで酔い続けているらしい
  • AQUOS sense3
    • KY-01L は失くした
    • AQUOS zero2 にしたかったんだけどまだ売ってないので、軽さが同じぐらいのものに
    • KY-01L と違ってバッテリーが持つので、テザリング専用機として考えても、より便利になってしまった (知ってた……)
  • WALKMAN NW-A100TPS (40周年記念モデル)
    • なんかのニュースで見て買った。まだ届いていない
    • 買ってからよく見たけど、容量全然足りなくない??
    • 持ってる全曲持ち歩くスタイルとは合わなそうで、さて困ったぞとなっている
  • Garmin vivosmart 4
    • id:hitode909買っていて 羨ましかったので購入
    • Body Battery とストレスが測れるのが最高に良くて、チームでバッテリー残が高い方が仕事を受け持つスタイルを取っていきたい

こうして見返すと衝動買いが多いことが分かる。

机周りが一通り揃った (モニタ、椅子、キーボード、マウス) ので、来年は IYH することは減るはず、きっと、多分。実際 HHKB は買わずに耐えているわけだし。