こもろぐ @tenkoma

What We Find Changes Who We Become -- Peter Morville著『アンビエント・ファインダビリティ 』

広告:本ブログで紹介している書籍等商品の紹介でAmazonアソシエイトを利用していることがあります。

PHPカンファレンス福岡2019 マネクラからの挑戦状 Webサイトコードゴルフ スコア60230(非公認)の解説

PHPカンファレンス福岡2019内企画 マネクラからの挑戦状、開催時間内にはほとんどやってませんでしたが、後日やってみて、スコア60230を出せたので解説してみます。開催時間外なので非公認スコアです。

(記事公開後、 @m3m0r7)さんと相談しながら縮めた結果、60300→60230へとさらに70短縮できました。

PHPカンファレンス福岡2019 マネクラからの挑戦状 Webサイトコードゴルフ

スコア60230(PHPファイル(480文字)+画像ファイル)

まずは入念にレギュレーションをチェック

プログラム一式をマネージドクラウドインスタンスにデプロイ後、オンライン上に用意されたDiffツールを使って、ブラウザレンダリング結果が一致しているかチェックします。合格したら、make check コマンドでスコアを計測する、という流れです。また以下のレギュレーションが存在します。

  • PHPで作成されたサイト https://phpconfuk-codegolf-php.lolipop.io/ (正解サイト)のソースコードをお渡しします(このリポジトリです)。
  • サイトの 表示を一切変えずに Webサイトを構成する全コード全ファイルの合計ファイルサイズを小さくして、あなたのロリポップ!マネージドクラウドPHPプロジェクト にデプロイしてください。
  • 最も小さいファイルサイズになった人が優勝です
  • コンテナ内の /var/www/html 内にWebサイトを表示するのに必要な全てのコード、およびファイルを設置してください
    • /var/www 等への設置は禁止です
  • Makefile内の check タスク内のコマンドの変更は禁止です

レギュレーション

プログラムがやっていること

エントリーポイントとなる index.php を見てみましょう。

コアとなるロジックは、

  • クエリストリングのキーと値の組み合わせを tokens テーブルに保存。
  • 値降順(第一ソート順), キー降順(第二ソート順)でとりだし、3番目の値の文字列を取得し逆さにする
  • HTMLテンプレートに文字列を埋め込んで表示

という感じでした。

vendors ディレクトリ、データベースアクセスを取り除く

初期のスコアは400万を超えていますが、その大部分が vendors ディレクトリであることがわかります。 プログラムを把握した上であらためてレギュレーションを確認してみると、データベースに関してはなんら記述がないので、データベースへ保存するコードを除去できます。 よって、受け取ったクエリストリング配列を処理して、レンダリングに必要な文字列を返すコードと、その他のHTMLを全てindex.php に入れれば、大分スコアが減らせそうです。そうして出来たコードにインデント・スペースを補完したコードが以下。

<?
    $a=$_GET;
    krsort($a);
    arsort($a);
    echo strrev(array_values($a)[2])
?>

最初のforeach で配列を整形してる部分をなくせないかな、と思いましたが思いつかなかったです。 ユーザー定義のソート関数ではキーと値のソートルールが同じだったので、$i = $a[1] != $b[1];$itrueなら値、false ならキーでソートされます。

コードゴルフなのに、そこそこ読みやすいコードになってしまいましたね。

short open tag

short open tagは将来的に廃止されそう(PHP: rfc:deprecate_php_short_tags)ですが、PHP 7.3.2ではまだ動作しますし、マネクラの環境でもデフォルトで使えます。タグとforeach キーワードの間にスペースが無くても動きます。

不要なファイルを削除する

これで、 vendors も削除できますし、 model/token.php も不要になりました。表示に必要なファイルは、 index.php, mc.png の2つになったので、改変不可な make check ファイルを含む Makefile を入れて、3ファイル以外の全てのファイルを削除しましょう。 .env, .htaccess が無くてもDiffチェッカーはパスします。

HTMLドキュメントを短くする

残るはHTMLを短くするのみです。PNGファイルは、名前を変える以外の変更はしていません。

無駄なCSSを取り除く。

lolipop-mc-codegolf-challenge/home.liquid at master · pepabo/lolipop-mc-codegolf-challenge · GitHub

CSS定義でファイルサイズがかなり大きくなってます。 まず、HTMLにはクラス属性が1個も定義されてなかったので、セレクタにクラスを含むものは安全に削除できます。 使ってないタグを含むセレクタの定義も削れます。 結果以下が残りました。

body {
    height: 100vh;
    font-family: Helvetica;
    letter-spacing: .04em
}
table {
    height: 100%
}
* {
    border: none;
    padding: 0;
    margin: 0;
    font-weight: 400;
    color: #465560;
    text-align: center
}
td {
    height: 50%;
    width: 5%
}

font-family は、レンダリングに使われている物だけをのこしました。画面に表示されている文字列要素が h2 だけだったので、 * に統合することができました。

表示に影響しないタグを削除していく

<html><header><meta><body> などを削ってもチェッカーにパスしました。

表示に影響しない閉じタグを削除していく

一箇所 </table> が残りましたが、それ以外は削除できます。 </style> については、ドキュメントの最後に移動すると、削除できます。 ところどころにある &nbsp; も不要です。

属性値を"で囲まない

はい。業務で真似してはいけない。

DOCTYPE宣言

<!DOCTYPEhtml>

宣言内のスペース不要でした。

pngファイル名をmc.pngm

5文字縮まりましたね。拡張子なんていらなかった。

まとめ

これで解説は終わりです。 全般的に業務で役に立たない知識です。

没テクニック

テーブルレイアウトをやめてdivにする

挑戦しましたが、Diffが出てしまうので断念。

PNGファイルサイズを縮める

PNGファイルからアルファチャンネルを削除すれば、スコアを縮められるかと思ってGIMPでやってみましたが、Diffチェッカーに引っかかって失敗しました。サイズは8,000ほど縮まりそうだったので残念です。

レギュレーションを守った上で最高のスコアは

なんと0になります。スコアチェッカーのコマンドは以下の通りですが、 *.git/*, Makefile, .env が除外されているため、.git/ ディレクトリ以下に index.php, mc.png を移動して、シンボリックリンクを作れば、レギュレーションを完全に守った上で0が達成できます。 チェッカーで除外するのではなく、デプロイ時に除外すればこの穴はできないので、運営側で意図した穴なのかな、と思います。

@ssh -p ${SSH_PORT} ${SSH_USER}@${SSH_HOST} 'find /var/www/html -type f -not -iwholename "*/.git/*" -not -name "Makefile" -not -name ".env" | xargs cat | wc -c'