詩と創作・思索のひろば

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

Fork me on GitHub

Baba Is You をクリアした

追加レベルパックはまだですが……。ルール改変型倉庫番パズルの Baba Is You の全ステージをクリアした。一晩やっても進捗がでない日もある、非常に難しいパズルだった。買ってからいちおうのエンディング(スタッフロールを見る)まで休み休み1年、そこからコンプリートまで9ヶ月ということで長く楽しめた。最初にやったステージは解法を忘れてるのでもう一周しても遊べそうだ。

長い戦いだったがやりきれたのは id:akiym が最後までやるべきとオススメしてくれたからだ……ありがとうございます! プレイ時間は70時間ほどだったらしいけど、ゲーム外で考えていた時間が長いのでもっともっと費やしている。

自分がやったのは Switch 版。時間をとらないのでアクセスしやすいスマホ版がオススメかもしれない。

下の方にややゲームの内容に触れた感想を書くので、先にもうちょっと一般的なことを書く。このゲームのステージに取り組むときのパターンは、最終的に大きく3種類に落ち着いたと思う。これらの方策を織り交ぜてやっていた。

終わりから考える。通常、BABAがFLAGに重なったらクリアなので、その状態に持っていくためにはどういう状態になっていなければならないか、ということを逆算して、その状態に持っていく。壁を動かしたいとかドアを開けたいとか。そしてこの「通常、」という留保のあるところが Baba Is You の面白いところ。これはやってみてほしい〜。

パーツから考える。ステージに無駄なルールはない(はずな)ので、これをどう使ったらいいのかから考えていく。ここにPUSH のルールがあるけど何を PUSH にしたらいい? → 列挙してみる、とか。

とにかく動かしてみる。そうは言っても頭の中で考えてもわからんことは多い。たくさん試行錯誤して動かしていると、検討できていなかった並びができたり、予想していなかった挙動を発見したり。リトライ性も高いので何度でもやる。

こうやって書いてみると、ゴールが動くことを含めて、完全に仕事ですね!

とまあこういう検討をスクリーンショット付きの Scrapbox のページに作り、自分と会話しながら可能性をつぶしたり拡げたりする。最初は無理だろこれ、としか見えなかったレベルも、「もしかしてこれができるのか?」「いやまさか……」「できたー!」となり、楽しい。

完全に自力でクリアできたわけではなく、以下のヒントページにかなりお世話になった。

Steam Community :: Guide :: Baba Is You の ヒント

少しずつヒントを開示してくれるし、直接的な解答はあまり教えてくれない、ちょうどいいヒント集。ありがとうございます。

好きな曲は以下かな〜。どのステージで流れてたかはおもいだせない。

目標は達成したので、ちょっと休んだらまたのんびり追加レベルパックでもやるつもり。こうなるともう余生です。

あとは思い出のステージでも語ろうと思います。ややネタバレになってきます。

A WAY OUT?

早い段階でこのフィナーレに到達できるので、「あっこれは全然解けそうにないし途中で得たアイテムとか技法を使って解くやつだな」と思っていたのだけど、ぜんぜんそんなことはなく普通に解けるステージだったので衝撃を受けた。

OUT AT SEA

なんかそもそも FLOWER GARDEN 全体が難しいんだが、これはメモに「は?」と書いてた思い出があり印象深い。気づいたときも「は?」だったが。しかしエンドゲームになってくるとこのテクも多用するという……。マップのステージで一番お気に入り。

FRAGILE EXISTENCE

最後までクリアしそびれていたレベル。どういうことかお分かりでしょう。

THE FLOATIEST PLATFORMS

バリエーションのあるレベルはいくつかあるが、これと FLOATY PLATFORMS のペアがいちばん好き。最初見たとき「??」となった。分かると楽しい。

PARADE

この!! レベルにめちゃくちゃ悩まされた!!! これが道を塞いでいたのでえーん前に進めないよとなっていたらアップデートが降ってきて別のところに移動された。これが解けなすぎて数ヶ月 Baba Is You を休んでいたし、PC の壁紙にして毎日睨んでいた。再開してから2日ほどで解けたので不思議。

GETTING TOGETHER

これは二度とやりたくない! ゲームを再開したとき、ああこれもやらないといけないのか……と暗い気分になったものの、実はクリア済だった。解法もちゃんとメモってあった。えらい。

BOOBY TRAP

初手を動かすのがとても上手になったと思う。最初に作ったのは BELT でした。。

コードレビューのときに見ているところ

あるときコードレビューするときにどういうところ見てるんですか? と訊かれてたしかに自分でもあまり言語化したことはなかったな、と気づいたので簡単に書いておく。

変更意図が要求に沿っているか

  • そもそも実現しようとしていることが、ユーザやプロダクトオーナーの要求に沿っているか。モデリングや実装のコンテキストを自分でも把握しておく。
  • 関連する別の変更やイシューなど、自分が知っていて相手が知らない有意義な情報があったらコメントする。

モデリングが妥当か

  • モデルによって意図が表現できているか。仕事が適切な粒度で明確に切り分けられているか。意図のない共通化がなされていないか。
  • わかりやすい名前がつけられているか。ここが混乱していると何かがよくないサイン。既存のコードがすでに……ということもある。そういう場合は改善できそうな道筋について議論できるとベター。
  • 仕事にあったインタフェースになっているか。テストを書いたときに素直なコードになっていると望ましい。例外の取り扱いや、アウトプット先を変えたときに無理なく使えそうか、とかも考える。

実装が妥当か

  • プロダクションでの性能が意識されているか。逆に無意味に性能を追ってコードが読みづらくなっていないか。
  • アルゴリズムに誤りがないか。とくに外部・外界とのやりとりを伴う場合、エッジケースがテストされているか。
  • 異常が発生したときにデバッグ・調査しやすいか。エラー(ログ)に必要な情報が含まれているか。
  • 既存のコードが今回の意図に合わせて変更されているか。

読み手の負荷が高くないか

  • コメントが書かれているか。ドキュメントやドキュメントへのリンクがあるか。
  • 読みづらいコードになっていないか。やむなくそうなっている場合はコメントがあるか。Pull Request のコメントで補足される場合があるけど、そういうときはコメントに書いてもらうようにする。
  • 議論の最中で出てきた未来の仕事が TODO とか FIXME となってコメントに残されているか。
  • この言語では/チームでは/プロダクトではしない書き方がないか。このへんは完全に知識の問題で、逆に教えてもらうチャンスでもある。機械がやってくれてもいいところ。

ざっと書き出したものなのでこれが全てではないと思います。

意図を理解したら、変更を順に見ていき、必要に応じて実行フローの前後を確認して、違和感があったり理解できないところにコメントをつけていくような流れ。

また、指摘や質問するだけではなく「ここはこう読み取りました」というコメントを残すこともある。これは認識を揃えるため。コードレビュー初心者で何をしたらいいかわからない、という人はこの「俺はこう思ったっす」を書いていくところから始めたらよいと思う。

gotest.tools でテストスナップショットをインラインに記録する

テキストや何らかのデータ構造(プログラムを含む)を生成するタイプのコードを書いているときはスナップショットテストが便利で、

  1. とりあえず一度スナップショットを生成してみる
  2. 生成されたものを人間が検分する
  3. 以降はスナップショットが壊れないように・意図したとおりに変更されるようにコードを書いていく

みたいなことをしている。このようにテストスナップショットには、「いったいこのコードで何がアウトプットされるのか?」に対するある種の自動ドキュメンテーションの機能がある。こういうものが欲しくなることは自分の書いたコードに限らずほかにもあって、たとえばクエリビルダのようなブラックボックスをテスト中で用いる際の sanity check 兼ドキュメントとしても有用。

まあ、ドキュメントとして使うことを意識するなら、(それが小さくすむなら)コードの近くにあったほうがありがたい。それでテストスナップショットをコード中に置くようなツールを書こうと思って調べてたら、すでにそういうものがあった。前にも紹介した gotest.tools というやつだ。

これには golden というスナップショットテストの仕組みがあり、testdata/ 以下に置いたファイルとの比較をしてくれるのだけど、それだけではなくわりと一般的に提供される assert.Equal のような関数にも実はスナップショット的に expected を更新してくれる機構がある。こちらはテスト中で値が比較されたとき、expectedXXX と名付けられた変数がその引数に含まれていたら、その変数自身を元のコードで書き換えてしまう、ということをやっている。なかなか大胆。

わかりづらいと思うので実際のコードで説明すると、

func TestBuild_Mutation(t *testing.T) {
    s, err := Build(...)

    expectedMutation := `mutation CreateReviewForEpisode($ep: Episode) {
  createReview(episode: $ep) {
    stars
    commentary
  }
}`
    assert.Equal(t, string(s), expectedMutation)
}

https://github.com/motemen/go-graphql-query/blob/7e5bb57182f75951f0bb5ce96293835637760d7e/query_test.go#L143-L170

以上のコードのうち expectedMutation := の文の右辺は go test -update によって生成されたものだ。こういった感じで、アウトプットが小さい場合や読み手にその場で説明したいような場合にはコード中に埋め込めると便利ですね。

もともとトップレベルの変数の更新しか対応してなかったのを PR 送って関数内の変数にも対応してもらった。まだタグが切られてないので、これを使うにはハッシュを指定して go get する必要がある。文字列以外のデータ構造にも対応できるといいんだけど、そこまでやるなら別ライブラリかな~。

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