From a84908b7e8a0e2423afd6b836eccf27a420270b4 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Wed, 20 Sep 2023 19:56:52 +0900 Subject: feat(blog/nuldoc): change content format from DocBook to NulDoc --- .../posts/2022-04-09/phperkaigi-2022-tokens.ndoc | 559 ++++++++++++++++++++ .../posts/2022-04-09/phperkaigi-2022-tokens.xml | 563 --------------------- 2 files changed, 559 insertions(+), 563 deletions(-) create mode 100644 vhosts/blog/content/posts/2022-04-09/phperkaigi-2022-tokens.ndoc delete mode 100644 vhosts/blog/content/posts/2022-04-09/phperkaigi-2022-tokens.xml (limited to 'vhosts/blog/content/posts/2022-04-09') diff --git a/vhosts/blog/content/posts/2022-04-09/phperkaigi-2022-tokens.ndoc b/vhosts/blog/content/posts/2022-04-09/phperkaigi-2022-tokens.ndoc new file mode 100644 index 00000000..c13b0c55 --- /dev/null +++ b/vhosts/blog/content/posts/2022-04-09/phperkaigi-2022-tokens.ndoc @@ -0,0 +1,559 @@ +--- +[article] +title = "PHPerKaigi 2022 トークン問題の解説" +description = "PHPerKaigi 2022 で私が作成した PHPer チャレンジ問題を解説する。" +tags = [ + "conference", + "php", + "phperkaigi", +] + +[[article.revisions]] +date = "2022-04-09" +remark = "公開" + +[[article.revisions]] +date = "2022-04-16" +remark = "2問目、3問目の解説を追加、1問目に加筆" +--- +
+
+ はじめに +

+ 本日開始された PHPerKaigi 2022 の PHPer + チャレンジにおいて、弊社 + デジタルサーカス株式会社 の問題を + 3問作成した。この記事では、これらの問題の解説をおこなう。 +

+

+ リポジトリはこちら: https://github.com/nsfisis/PHPerKaigi2022-tokens +

+
+
+ 第1問 brainf_ck.php +

+ ソースコードはこちら。実行には PHP 8.1 以上が必要なので注意。 +

+ + (fn($x) => $f(fn(...$xs) => $x($x)(...$xs)))(fn($x) => $f(fn(...$xs) => $x($x)(...$xs))); + $id = \spl_object_id(...); + $put = fn($c) => \printf('%c', $c); + $mm = fn($p, $n) => new \ArrayObject(\array_fill(+!![], $n, $p)); + + $👉 = fn($m, $p, $b, $e, $mp, $pc) => [++$mp, ++$pc]; + $👈 = fn($m, $p, $b, $e, $mp, $pc) => [--$mp, ++$pc]; + $👍 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, ++$m[$mp]]; + $👎 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, --$m[$mp]]; + $📝 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, $put($m[$mp])]; + $🤡 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { + +!![] => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { + $b => $loop(++$pc, ++$n), + $e => $n === +!![] ? ++$pc : $loop(++$pc, --$n), + default => $loop(++$pc, $n), + })($pc, -![])], + default => [$mp, ++$pc], + }; + $🎪 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { + +!![] => [$mp, ++$pc], + default => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { + $e => $loop(--$pc, ++$n), + $b => $n === +!![] ? $pc+![] : $loop(--$pc, --$n), + default => $loop(--$pc, $n), + })($pc, -![])], + }; + $🐘 = fn($p) => $z(fn($loop) => fn($m, $p, $b, $e, $mp, $pc) => + isset($p[$pc]) && $loop($m, $p, $b, $e, ...($p[$pc]($m, $p, $b, $e, $mp, $pc))) + )($mm(+!![], +(![].![])), $p, $id($🤡), $id($🎪), +!![], +!![]); + + $🐘([ + $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, + $🤡, + $👉, $👍, $👍, $👍, + $👉, $👍, $👍, $👍, $👍, $👍, + $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, + $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, + $👈, $👈, $👈, $👈, $👎, + $🎪, + $👉, $👍, $👍, $👍, $👍, $👍, $📝, + $👎, $👎, $📝, + $👉, $👎, $👎, $👎, $📝, + $👉, $👎, $👎, $👎, $📝, + $👎, $👎, $📝, + $👎, $📝, + $👈, $📝, + $👉, $👉, $👎, $👎, $📝, + $👍, $👍, $👍, $👍, $👍, $👍, $👍, $📝, + $👈, $👎, $👎, $👎, $👎, $📝, + $👈, $📝, + $👉, $👍, $👍, $📝, + $👉, $👎, $📝, + $👈, $📝, + ]); + ]]> + +

+ この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。 +

+
+ 解説 +
+ 絵文字 +

+ まず目につくのは大量の絵文字だろう。 PHP + は識別子に使用できる文字の範囲が広く、絵文字も使うことができる。 +

+
+
+ プログラム全体 +

+ Brainf*ck のインタプリタとプログラムになっている。 Brainf*ck + とは、難解プログラミング言語のひとつであり、ここで説明するよりも + Wikipedia の該当ページを読んだ方がよい。 +

+

+ https://ja.wikipedia.org/wiki/Brainfuck +

+

+ なお、brainf*ck プログラムを普通の書き方で書くと、次のようになる。 +

+ + + + + + > + + + + + + > + + + + + + + + + + + + + > + + + + + + + + + + + < < < < - + ] + > + + + + + . + - - . + > - - - . + > - - - . + - - . + - . + < . + > > - - . + + + + + + + + . + < - - - - . + < . + > + + . + > - . + < . + ]]> + +

+ 実行結果はこちら: https://ideone.com/22VWmb +

+

+ それぞれの絵文字で表された関数が、各命令に対応している。 +

+
    +
  • $👉: >
  • +
  • $👈: <
  • +
  • $👍: +
  • +
  • $👎: -
  • +
  • $📝: .
  • +
  • $🤡: [
  • +
  • $🎪: ]
  • +
+

+ , (入力) に対応する関数はない + (このプログラムでは使わないので用意していない)。 +

+

+ なお、$🐘 はいわゆる main 関数であり、プログラムの実行部分である。 +

+
+
+ 絵文字の選択 +

+ おおよそ意味に合致するよう選んでいるが、$🤡$🎪 + は弊社デジタルサーカスにちなんでいる。 また、$🐘 は PHP + のマスコットの象に由来する。 +

+
+
+ strict_types +

+ declare 文の strict_types に指定できるのは、01 + の数値リテラルだが、 0x00b1 のような値も受け付ける。 今回は、PHP + 8.1 から追加された、0O または 0o から始まる八進数リテラルを使った。 +

+
+
+ URL +

+ ソースコードのライセンスを示したこの部分だが、 +

+ + + +

+ 完全に合法な PHP のコードである。 https: 部分はラベル、// + 以降は行コメントになっている。 +

+
+
+ リテラルなしで数値を生成する +

+ ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。 + PHP では、型変換を利用することで任意の整数を作り出すことができる。 +

+ + + +

+ []! を適用すると true が返ってくる。それに + + を適用すると、bool から int ヘの型変換が走り、1 が生成される。10 + はさらにトリッキーだ。まず 10 を作り、. で文字列として結合する + ('10')。これに + を適用すると、string から int + への型変換が走り、10 が生まれる (コード量に頓着しないなら、1 を 10 + 個足し合わせてももちろん 10 が作れる)。 +

+

+ また、error_reporting に指定しているのは -1 である。 これは、! + によって文字列を false にし、+ によって false0 + にし、さらにビット反転して -1 にしている。 +

+
+
+ if 文なしで条件分岐 +

+ 三項演算子ないし match 式を使うことで、if + を一切書かずに条件分岐ができる。 また、&& / || も使えることがある。 + 遅延評価が不要なケースでは、[$t, $f][$cond] + のような形で分岐することもできる。 +

+
+
+ whilefor 文なしでループ +

+ 不動点コンビネータを使って無名再帰する + (詳しい説明は省略する。これらの単語で検索してほしい)。 ここでは、一般に + Z コンビネータとして知られるものを使った ($z)。 +

+

+ 実際のところ、$🤡$🎪$🐘 は、一度 Scheme (Lisp の一種) + で書いてから PHP に翻訳する形で記述した。 +

+

+ なお、PHP は末尾再帰の最適化をおこなわない (少なくとも今のところは) + ので、 あまりに長い brainf*ck + プログラムを書くとスタックオーバーフローする。 +

+
+
+
+
+ 第2問 riddle.php +

+ ソースコードはこちら。実行には PHP 8.0 以上が必要なので注意。 +

+ + + +

+ さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。 + トークンを得るためには、ソースコードを読み、定数 N + を特定する必要がある。 +

+

+ ここでは、私の想定解を解説する。 +

+
+ 読解 +

+ まずはソースコードを読んでいく。 +

+ + + +

+ 数値からなる $token があり、各要素をループしている。 +

+ + + +

+ まずは排他的論理和 (xor) を取り、 +

+ + + +

+ 二進数に変換して、 +

+ + + +

+ 0 を空白に、1 を # にし、 +

+ + + +

+ 5文字ごとに区切ったあと、改行で結合している。 +

+
+
+ ヒント +

+ 次に、ソースコードに書いてあるヒントを読んでいく。 +

+
    +
  • N それ自体は、42 や 8128 といったような特別な意味を持たず、ランダムに決められている
  • +
  • $token の各要素は、1文字を表す
  • +
  • 1文字は 5x5 のセルからなる
  • +
  • 出力されるのは、完全な PHPer トークンである
  • +
+

+ ここで、PHPer トークンは必ず # 記号から始まることを思いだすと、 + $token の最初の数字 0x14B499C は、変換の結果 # + になるのではないかと予想される (なお、このことは、リポジトリの README + ファイルに追加ヒントとして書かれている)。 +

+
+
+ 解く +

+ ここまでわかれば、あと一歩で解ける。すなわち、0x14B499C# + に変換されるような N を見つければよい。 +

+

+ N は高々 +

+ + + +

+ なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。 +

+ + + +

+ この一連の変換に対する逆変換を考えると、次のようになる。 +

+ + + +

+ これを実行すると、N が得られる。 +

+
+
+
+ 第3問 toquine.php +

+ ソースコードはこちら。 +

+ + vzcybqr(', ', $j), neenl_puhax(neenl_znc(sa($k) => fcevags('0k' . pue(37) . '07K', $k), $kf), 10)); + cevags($f, $g, fge_ebg13("<<<'Q'\a{$f}\aQ"), vzcybqr(",\a", $jf)); + Q; + $s = str_rot13($s); $xs = [ + 0x0AFABEA, 0x1F294A7, 0x1F2109F, 0x1F294A7, 0x0002800, 0x1F2109F, 0x0117041, 0x1F294A7, 0x1FAD6B5, 0x1F295B7, + 0x010FC21, 0x1FAD6B5, 0x1151151, 0x010FC21, 0x1F294A7, 0x1F295B7, 0x1FAD6B5, 0x1F294A7, 0x1F295B7, 0x1F8C63F, + 0x1F8C631, 0x1FAD6B5, 0x17AD6BD, 0x17AD6BD, 0x1F8C63F, 0x1F295B7, + ]; + $t = null.false; for ($i = 0; $i <= intdiv(__LINE__-035,6); ++$i) if (!isset($xs[$i])) break; else + $t .= implode("\n", str_split(str_replace(['0','1'], [' ','##'], sprintf(chr(37) . '025b', $xs[$i])), 012)) . "\n\n"; + $ws = array_map(fn($w) => implode(', ', $w), array_chunk(array_map(fn($x) => sprintf('0x' . chr(37) . '07X', $x), $xs), 10)); + printf($s, $t, str_rot13("<<<'D'\n{$s}\nD"), implode(",\n", $ws)); + ]]> + +

+ コメントにもあるとおり、次のようにして実行すれば答えがでてくる。 +

+ + + +

+ 実際にはもう少しパイプで繋げなければならない。 +

+
+ 解説 +
+ プログラム全体 +

+ コメントにもあるとおり、これは quine (風) のプログラムになっている。 + Quine + とは、自分のソースコードをそっくりそのまま出力するようなプログラムのことである。 +

+

+ このプログラムは、実行すると自身とほとんど同じプログラムを出力する。 + 異なるのはトークンになっている部分のみである。 +

+
+
+ トークン +

+ $xs がトークンに対応している。変換のロジックは riddle.php + とほぼ同じなので省略する。 +

+
+
+ 状態保持 +

+ トークンの何文字目まで出力したかを、ソースコードを変えずに (quine + なので) 覚えておく必要がある。 + このプログラムでは、トークンが出力されるとソースコードがだんだんと長くなっていくのを利用して、__LINE__ + から情報を取得している。 +

+
+
+ ROT 13 +

+ Quine は、素朴に書くとプログラムの一部が 2回記述されてしまう。 + これがあまり美しくないので、toquine.php では、ROT 13 + 変換を使って難読化した。 +

+

+ それにしてもなぜこんなものが標準ライブラリに……。 +

+
+
+
+
+ おわりに +

+ 解いていただいたみなさん、また、難易度調整につきあっていただいた社内のみなさん、ありがとうございました。 +

+

+ 今回は直前に作りはじめたのもあり、3問だけかつ使い古されたネタばかりになってしまいましたが、 + 来年は 5問、より面白い問題を持っていきます。 +

+

+ 実はもう作りはじめているので、どうか来年もありますように……。 +

+
+
diff --git a/vhosts/blog/content/posts/2022-04-09/phperkaigi-2022-tokens.xml b/vhosts/blog/content/posts/2022-04-09/phperkaigi-2022-tokens.xml deleted file mode 100644 index 898d7ea9..00000000 --- a/vhosts/blog/content/posts/2022-04-09/phperkaigi-2022-tokens.xml +++ /dev/null @@ -1,563 +0,0 @@ - -
- - PHPerKaigi 2022 トークン問題の解説 - - PHPerKaigi 2022 で私が作成した PHPer チャレンジ問題を解説する。 - - - conference - php - phperkaigi - - - - 2022-04-09 - 公開 - - - 2022-04-16 - 2問目、3問目の解説を追加、1問目に加筆 - - - -
- はじめに - - 本日開始された PHPerKaigi 2022 の PHPer - チャレンジにおいて、弊社 - デジタルサーカス株式会社 の問題を - 3問作成した。この記事では、これらの問題の解説をおこなう。 - - - リポジトリはこちら: https://github.com/nsfisis/PHPerKaigi2022-tokens - -
-
- 第1問 brainf_ck.php - - ソースコードはこちら。実行には PHP 8.1 以上が必要なので注意。 - - - (fn($x) => $f(fn(...$xs) => $x($x)(...$xs)))(fn($x) => $f(fn(...$xs) => $x($x)(...$xs))); - $id = \spl_object_id(...); - $put = fn($c) => \printf('%c', $c); - $mm = fn($p, $n) => new \ArrayObject(\array_fill(+!![], $n, $p)); - - $👉 = fn($m, $p, $b, $e, $mp, $pc) => [++$mp, ++$pc]; - $👈 = fn($m, $p, $b, $e, $mp, $pc) => [--$mp, ++$pc]; - $👍 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, ++$m[$mp]]; - $👎 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, --$m[$mp]]; - $📝 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, $put($m[$mp])]; - $🤡 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { - +!![] => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { - $b => $loop(++$pc, ++$n), - $e => $n === +!![] ? ++$pc : $loop(++$pc, --$n), - default => $loop(++$pc, $n), - })($pc, -![])], - default => [$mp, ++$pc], - }; - $🎪 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { - +!![] => [$mp, ++$pc], - default => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { - $e => $loop(--$pc, ++$n), - $b => $n === +!![] ? $pc+![] : $loop(--$pc, --$n), - default => $loop(--$pc, $n), - })($pc, -![])], - }; - $🐘 = fn($p) => $z(fn($loop) => fn($m, $p, $b, $e, $mp, $pc) => - isset($p[$pc]) && $loop($m, $p, $b, $e, ...($p[$pc]($m, $p, $b, $e, $mp, $pc))) - )($mm(+!![], +(![].![])), $p, $id($🤡), $id($🎪), +!![], +!![]); - - $🐘([ - $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, - $🤡, - $👉, $👍, $👍, $👍, - $👉, $👍, $👍, $👍, $👍, $👍, - $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, - $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, - $👈, $👈, $👈, $👈, $👎, - $🎪, - $👉, $👍, $👍, $👍, $👍, $👍, $📝, - $👎, $👎, $📝, - $👉, $👎, $👎, $👎, $📝, - $👉, $👎, $👎, $👎, $📝, - $👎, $👎, $📝, - $👎, $📝, - $👈, $📝, - $👉, $👉, $👎, $👎, $📝, - $👍, $👍, $👍, $👍, $👍, $👍, $👍, $📝, - $👈, $👎, $👎, $👎, $👎, $📝, - $👈, $📝, - $👉, $👍, $👍, $📝, - $👉, $👎, $📝, - $👈, $📝, - ]); - ]]> - - - この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。 - -
- 解説 -
- 絵文字 - - まず目につくのは大量の絵文字だろう。 PHP - は識別子に使用できる文字の範囲が広く、絵文字も使うことができる。 - -
-
- プログラム全体 - - Brainf*ck のインタプリタとプログラムになっている。 Brainf*ck - とは、難解プログラミング言語のひとつであり、ここで説明するよりも - Wikipedia の該当ページを読んだ方がよい。 - - - https://ja.wikipedia.org/wiki/Brainfuck - - - なお、brainf*ck プログラムを普通の書き方で書くと、次のようになる。 - - - + + + - > + + + + + - > + + + + + + + + + + + + - > + + + + + + + + + + - < < < < - - ] - > + + + + + . - - - . - > - - - . - > - - - . - - - . - - . - < . - > > - - . - + + + + + + + . - < - - - - . - < . - > + + . - > - . - < . - ]]> - - - 実行結果はこちら: https://ideone.com/22VWmb - - - それぞれの絵文字で表された関数が、各命令に対応している。 - - - $👉: > - $👈: < - $👍: + - $👎: - - $📝: . - $🤡: [ - $🎪: ] - - - , (入力) に対応する関数はない - (このプログラムでは使わないので用意していない)。 - - - なお、$🐘 はいわゆる main 関数であり、プログラムの実行部分である。 - -
-
- 絵文字の選択 - - おおよそ意味に合致するよう選んでいるが、$🤡$🎪 - は弊社デジタルサーカスにちなんでいる。 また、$🐘 は PHP - のマスコットの象に由来する。 - -
-
- strict_types - - declare 文の strict_types に指定できるのは、01 - の数値リテラルだが、 0x00b1 のような値も受け付ける。 今回は、PHP - 8.1 から追加された、0O または 0o から始まる八進数リテラルを使った。 - -
-
- URL - - ソースコードのライセンスを示したこの部分だが、 - - - - - - 完全に合法な PHP のコードである。 https: 部分はラベル、// - 以降は行コメントになっている。 - -
-
- リテラルなしで数値を生成する - - ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。 - PHP では、型変換を利用することで任意の整数を作り出すことができる。 - - - - - - []! を適用すると true が返ってくる。それに + - を適用すると、bool から int ヘの型変換が走り、1 が生成される。10 - はさらにトリッキーだ。まず 10 を作り、. で文字列として結合する - ('10')。これに + を適用すると、string から int - への型変換が走り、10 が生まれる (コード量に頓着しないなら、1 を 10 - 個足し合わせてももちろん 10 が作れる)。 - - - また、error_reporting に指定しているのは -1 である。 これは、! - によって文字列を false にし、+ によって false0 - にし、さらにビット反転して -1 にしている。 - -
-
- <literal>if</literal> 文なしで条件分岐 - - 三項演算子ないし match 式を使うことで、if - を一切書かずに条件分岐ができる。 また、&& / || も使えることがある。 - 遅延評価が不要なケースでは、[$t, $f][$cond] - のような形で分岐することもできる。 - -
-
- <literal>while</literal>、<literal>for</literal> 文なしでループ - - 不動点コンビネータを使って無名再帰する - (詳しい説明は省略する。これらの単語で検索してほしい)。 ここでは、一般に - Z コンビネータとして知られるものを使った ($z)。 - - - 実際のところ、$🤡$🎪$🐘 は、一度 Scheme (Lisp の一種) - で書いてから PHP に翻訳する形で記述した。 - - - なお、PHP は末尾再帰の最適化をおこなわない (少なくとも今のところは) - ので、 あまりに長い brainf*ck - プログラムを書くとスタックオーバーフローする。 - -
-
-
-
- 第2問 riddle.php - - ソースコードはこちら。実行には PHP 8.0 以上が必要なので注意。 - - - - - - さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。 - トークンを得るためには、ソースコードを読み、定数 N - を特定する必要がある。 - - - ここでは、私の想定解を解説する。 - -
- 読解 - - まずはソースコードを読んでいく。 - - - - - - 数値からなる $token があり、各要素をループしている。 - - - - - - まずは排他的論理和 (xor) を取り、 - - - - - - 二進数に変換して、 - - - - - - 0 を空白に、1 を # にし、 - - - - - - 5文字ごとに区切ったあと、改行で結合している。 - -
-
- ヒント - - 次に、ソースコードに書いてあるヒントを読んでいく。 - - - N それ自体は、42 や 8128 といったような特別な意味を持たず、ランダムに決められている - $token の各要素は、1文字を表す - 1文字は 5x5 のセルからなる - 出力されるのは、完全な PHPer トークンである - - - ここで、PHPer トークンは必ず # 記号から始まることを思いだすと、 - $token の最初の数字 0x14B499C は、変換の結果 # - になるのではないかと予想される (なお、このことは、リポジトリの README - ファイルに追加ヒントとして書かれている)。 - -
-
- 解く - - ここまでわかれば、あと一歩で解ける。すなわち、0x14B499C# - に変換されるような N を見つければよい。 - - - N は高々 - - - - - - なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。 - - - - - - この一連の変換に対する逆変換を考えると、次のようになる。 - - - - - - これを実行すると、N が得られる。 - -
-
-
- 第3問 toquine.php - - ソースコードはこちら。 - - - vzcybqr(', ', $j), neenl_puhax(neenl_znc(sa($k) => fcevags('0k' . pue(37) . '07K', $k), $kf), 10)); - cevags($f, $g, fge_ebg13("<<<'Q'\a{$f}\aQ"), vzcybqr(",\a", $jf)); - Q; - $s = str_rot13($s); $xs = [ - 0x0AFABEA, 0x1F294A7, 0x1F2109F, 0x1F294A7, 0x0002800, 0x1F2109F, 0x0117041, 0x1F294A7, 0x1FAD6B5, 0x1F295B7, - 0x010FC21, 0x1FAD6B5, 0x1151151, 0x010FC21, 0x1F294A7, 0x1F295B7, 0x1FAD6B5, 0x1F294A7, 0x1F295B7, 0x1F8C63F, - 0x1F8C631, 0x1FAD6B5, 0x17AD6BD, 0x17AD6BD, 0x1F8C63F, 0x1F295B7, - ]; - $t = null.false; for ($i = 0; $i <= intdiv(__LINE__-035,6); ++$i) if (!isset($xs[$i])) break; else - $t .= implode("\n", str_split(str_replace(['0','1'], [' ','##'], sprintf(chr(37) . '025b', $xs[$i])), 012)) . "\n\n"; - $ws = array_map(fn($w) => implode(', ', $w), array_chunk(array_map(fn($x) => sprintf('0x' . chr(37) . '07X', $x), $xs), 10)); - printf($s, $t, str_rot13("<<<'D'\n{$s}\nD"), implode(",\n", $ws)); - ]]> - - - コメントにもあるとおり、次のようにして実行すれば答えがでてくる。 - - - - - - 実際にはもう少しパイプで繋げなければならない。 - -
- 解説 -
- プログラム全体 - - コメントにもあるとおり、これは quine (風) のプログラムになっている。 - Quine - とは、自分のソースコードをそっくりそのまま出力するようなプログラムのことである。 - - - このプログラムは、実行すると自身とほとんど同じプログラムを出力する。 - 異なるのはトークンになっている部分のみである。 - -
-
- トークン - - $xs がトークンに対応している。変換のロジックは riddle.php - とほぼ同じなので省略する。 - -
-
- 状態保持 - - トークンの何文字目まで出力したかを、ソースコードを変えずに (quine - なので) 覚えておく必要がある。 - このプログラムでは、トークンが出力されるとソースコードがだんだんと長くなっていくのを利用して、LINE - から情報を取得している。 - -
-
- ROT 13 - - Quine は、素朴に書くとプログラムの一部が 2回記述されてしまう。 - これがあまり美しくないので、toquine.php では、ROT 13 - 変換を使って難読化した。 - - - それにしてもなぜこんなものが標準ライブラリに……。 - -
-
-
-
- おわりに - - 解いていただいたみなさん、また、難易度調整につきあっていただいた社内のみなさん、ありがとうございました。 - - - 今回は直前に作りはじめたのもあり、3問だけかつ使い古されたネタばかりになってしまいましたが、 - 来年は 5問、より面白い問題を持っていきます。 - - - 実はもう作りはじめているので、どうか来年もありますように……。 - -
-
-- cgit v1.2.3-70-g09d2