Skip to content

Commit

Permalink
Publish spoilers for IOCCC 2001
Browse files Browse the repository at this point in the history
  • Loading branch information
mame committed Apr 4, 2021
1 parent fe50158 commit d78cd86
Show file tree
Hide file tree
Showing 61 changed files with 5,082 additions and 0 deletions.
28 changes: 28 additions & 0 deletions 2001/anonymous.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
id: 2001/anonymous
year: 2001
order: 1
authors: "Gavin_Barraclough"
orig_url: "https://www.ioccc.org/2001/anonymous.c"
hint_url: "https://github.com/ioccc-src/winner/blob/main/2001/anonymous.hint"
title: "IOCCC 2001: Most likely to amaze"
award_ja: "もっとも仰天させそう"
---

## 動作

x86バイナリを任意のCPUの上で動かすためのバイナリ変換器。

残念ながら、動かせていない。
かなりデバッグしないと動かすことはできなさそう。
バイナリに詳しいプログラマに助けてほしい。

## 解説

anonymous.hintには実装方法が書かれていないが、コードを読む限り、x86のELF実行バイナリのサブセットをC言語コードに変換し、gccでコンパイルしてネイティブコードにするというものだと思う。

anonymous.hintにも「Hello worldやそれに近いプログラムしか動かせない」とあり、動かせるバイナリに強い前提があると思われる。
あいにく、実行確認が行われたバイナリ(anonymous.hintの中で"ten"と呼ばれている)が添付されておらず、現代のgccでビルドして得られるバイナリの形式には対応できていないのだと思う。
一応、当時のOS環境であるDebian 2.2 (potato)を作って試したが、動作を再現できなかった。

コードには"x86 Dynamic Binary Translator"というメッセージが埋め込まれている。
73 changes: 73 additions & 0 deletions 2001/bellard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
id: 2001/bellard
year: 2001
order: 2
patch: true
authors: "Fabrice_Bellard"
orig_url: "https://www.ioccc.org/2001/bellard.c"
hint_url: "https://github.com/ioccc-src/winner/blob/main/2001/bellard.hint"
title: "IOCCC 2001: Best abuse of the rules"
award_ja: "ルールの最高の悪用"
---

## 動作

セルフホスト可能なC言語のサブセットのコンパイラ。
次は、添付されているbellard.otccex.cから一部だけ抜粋したコードfib.cを動かす例。

```
$ gcc -m32 -rdynamic bellard.c -o bellard -ldl
$ cat fib.c
fib(n) {
if (n <= 2)
return 1;
else
return fib(n-1) + fib(n-2);
}
main(argc, argv) {
int n;
n = atoi(*(int *)(argv + 4));
printf("fib(%d) = %d\n", n, fib(n));
return 0;
}
$ ./bellard fib.c 10
fib(10) = 55
```

`./bellard fib.c 10`はfib.cをコンパイルして実行している。
見かけ上は、C言語インタプリタのようになる。
セルフホストは最後に示す。

## 解説

C言語のソースコードを読んで機械語(x86)に変換し、その機械語を実行する。
ただし、実行可能ファイルを出力するのではなく、メモリ内に命令列を構築し、その中の`main`関数をcallして実行する。
たとえば`./bellard foo.c`と実行すると、foo.cをメモリ内にコンパイルし、その結果を実行するので、見かけの挙動はC言語インタプリタのようになっている。
実行ファイルにするにはELFフォーマットを作る必要があるので、コードサイズ制限に合わなかったか。

コード形状による難読化はないが、サイズ制限に収まっているだけでもすごい。

x86しかサポートしていないので、`-m32`が必要。
値とポインタがともに4バイトであることを強く仮定したコードになっているので、`-m32`なしで動かすのはとても大変だと思う。
また、`calloc`関数で得たメモリに機械語を構築してcallするが、現代ではこのようなメモリは実行フラグが立っていない([実行保護](https://ja.wikipedia.org/wiki/%E5%AE%9F%E8%A1%8C%E4%BF%9D%E8%AD%B7)を参照のこと)ので、`mmap`でメモリ確保するようにした。

[作者自身が解説サイトを作っている](https://bellard.org/otcc/)ので興味あれば参照されたい。
最近、難読化されていないバージョンも公開された。
なお作者(Fabrice Bellard)はその後、この作品を元に[tcc](https://github.com/TinyCC/tinycc)を作っている。

残念ながら、現代の環境ではfibまでは動かせたが、bellard.otccex.cを完全動作させることができなかった。
詳しくは調べていないが、関数ポインタが動いていないような気がする。
腕に自信のあるバイナリアンに解析してもらいたい。

---

当時のOS環境であるDebian 2.2 (potato)をQEMUでセットアップしたところ、bellard.otccex.cだけでなくセルフホストまで動作確認できた。

{% include img.html src="2001-bellard.png" desc="セルフホストができている様子" %}

`./bellard bellard.otccex.c 10`はbellard.otccex.cをコンパイルして動かしているだけ。
`./bellard bellard.c bellard.otccex.c 10`はbellard.cをコンパイルしてコンパイラを作り、その新しいコンパイラでbellard.otccex.cをコンパイルして実行する。
`./bellard bellard.c bellard.c bellard.otccex.c 10`はコンパイラのコンパイルをもう一段階増やしている。

36 changes: 36 additions & 0 deletions 2001/cheong.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
id: 2001/cheong
year: 2001
order: 3
authors: "Raymond_Cheong"
orig_url: "https://www.ioccc.org/2001/cheong.c"
hint_url: "https://github.com/ioccc-src/winner/blob/main/2001/cheong.hint"
title: "IOCCC 2001: Best short program"
award_ja: "最高のショートプログラム"
---

## 動作

任意精度で整数の平方根を求める。

```
$ gcc -o cheong cheong.c
$ ./cheong 81
9
$ ./cheong 1024
32
$ ./cheong 012345678987654321
111111111
```

偶数桁でないといけない。奇数桁の場合は先頭に0をつける。

## 解説

コード形状は"√"の形になっている。驚きの小ささ。

基本的には[開平法](https://ja.wikipedia.org/wiki/%E9%96%8B%E5%B9%B3%E6%B3%95)で求めていると思う。
完全には理解できていないが、1つの`char`に20進数の1桁と10進数の1桁を重ねてもたせる(20で割った余りが前者、20で割った商が後者)とか、`main`関数の再帰でうまく扱えるように値が負の範囲になるように調整する(`argc`は通常起動時は正なので、それと区別する)とか、非常にトリッキーなコードになっている。
40 changes: 40 additions & 0 deletions 2001/coupard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
id: 2001/coupard
year: 2001
order: 4
authors: "Pierre-Philippe_Coupard"
orig_url: "https://www.ioccc.org/2001/coupard.c"
hint_url: "https://github.com/ioccc-src/winner/blob/main/2001/coupard.hint"
title: "IOCCC 2001: Most obfuscated sound"
award_ja: "もっとも難読化された音"
---

## 動作

現在時刻を音声合成してくれる。次のように実行してtime.wavを再生する。

```
$ gcc -o coupard coupard.c
$ ./coupard | sox -c1 -r8000 -tub - -c2 -r44100 -twav time.wav
```

生成された音声のサンプルはこちら。

{% include audio.html src="2001-coupard.mp3" desc="time.wav" %}

"The time is / eleven / hours / eleven / minutes / and / eleven / seconds"と発声しているのがわかるだろうか。

## 解説

現在時刻を"The time is HH hours MM minutes and SS seconds"と発声したPCMデータを出している。
糸電話から蓄音機の間くらいの音質とcoupard.hintに書いてあるが、個人的にはそんなレベルではなく聞き取りづらいので気合いが必要。

elevenが一番聞き取りやすいと思ったので、11ばかりにした。
40行目を次のように書き換えると上記の11:11:11を読み上げてくれる。

```
l=-k*(k<0),i++);e(21,4);g(11);e(22,1);g(11);e(23,1);e(25,0);g(11);e(24,0);
```

コードは普通。音声データを表示可能文字でうまいことエンコードしているのだと思う(詳細未解読)。
64 changes: 64 additions & 0 deletions 2001/ctk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
id: 2001/ctk
year: 2001
order: 5
patch: true
authors: "Chris_King"
orig_url: "https://www.ioccc.org/2001/ctk.c"
hint_url: "https://github.com/ioccc-src/winner/blob/main/2001/ctk.hint"
title: "IOCCC 2001: Worst Driver"
award_ja: "最悪なドライバー"
---

## 動作

ドライビングゲーム。

```
$ gcc -o ctk ctk.c
$ ./ctk
```

```
\ \
\ \
\ \
\ \
\ \
\ \
%%% \ \
%\|/% \ \
| \ \
| \ \
\ \
\ \
\ \
\ \
\ \
\ \
\ \
| |
%%% / /
%\|/% / /
| / /
| / @ /
/ /
/ /
```

`@`が自分。<kbd>b</kbd>で左に進み、<kbd>m</kbd>で右に進み、<kbd>n</kbd>でまっすぐに戻す。
<kbd>1</kbd>、<kbd>2</kbd>、<kbd>3</kbd>、<kbd>4</kbd>、でギアを変える(4が最速)。
道から外れたらゲームオーバー。なお、道は徐々に細くなっていく。

## 解説

[Apple II](https://en.wikipedia.org/wiki/Apple_II)[Print Shop Companion](https://en.wikipedia.org/wiki/The_Print_Shop#The_Print_Shop_Companion)に入っていた"DRIVER"というイースターエッグを真似たゲームとのこと。

当時とエスケープシーケンスの意味が変わっているようで、動かすのには苦労した。
現代では[CSI sequence](https://en.wikipedia.org/wiki/ANSI_escape_code#Escape_sequences)`ESC [`の2文字だが、当時は0x9fの1文字でもよかったらしい(現代ではUnicodeに乗っ取られている)。
また、一行挿入するシーケンスがカーソルのカラム位置を変化させない前提があるようだが、現代では左端に戻ってしまうので、調整した。

Linux consoleには独自のエスケープシーケンスがあるそうで、これを活用している。
`ESC [ 10 ; n ]`でビープ音の周波数を変えることでギアに合わせたビープ音を鳴らしたり、`ESC [ n q`でギアに合わせてNum LockやCaps Lockなどのランプを光らせたりする機能もあるとのこと。
どちらも再現できる環境がないので動作未確認。
143 changes: 143 additions & 0 deletions 2001/dgbeards.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
id: 2001/dgbeards
year: 2001
order: 6
authors: "Doug_Beardsley"
orig_url: "https://www.ioccc.org/2001/dgbeards.c"
hint_url: "https://github.com/ioccc-src/winner/blob/main/2001/dgbeards.hint"
title: "IOCCC 2001: Best AI"
award_ja: "最高のAI"
---

## 動作

負けるが勝ちのチェス。

```
$ gcc -DE=break -DF=char -DK=case -DP=int -DR=return -DI=0xFFFF -o dgbeards dgbeards.c
$ ./dgbeards
+---+---+---+---+---+---+---+---+
8| *R| *N| *B| *Q| *K| *B| *N| *R|
+---+---+---+---+---+---+---+---+
7| *P| *P| *P| *P| *P| *P| *P| *P|
+---+---+---+---+---+---+---+---+
6| | | | | | | | |
+---+---+---+---+---+---+---+---+
5| | | | | | | | |
+---+---+---+---+---+---+---+---+
4| | | | | | | | |
+---+---+---+---+---+---+---+---+
3| | | | | | | | |
+---+---+---+---+---+---+---+---+
2| P | P | P | P | P | P | P | P |
+---+---+---+---+---+---+---+---+
1| R | N | B | Q | K | B | N | R |
+---+---+---+---+---+---+---+---+
```

K(キング)の前のP(ポーン)を前に出してみる。
チェスでは左列から右列へabcdefgh、下行から上行へ12345678の番号が振られていて、どのマスからどのマスへ移動するかを並べて書く。
この場合は`e2e4`

```
e2e4
+---+---+---+---+---+---+---+---+
8| *R| *N| *B| *Q| *K| *B| *N| *R|
+---+---+---+---+---+---+---+---+
7| *P| *P| *P| *P| *P| *P| *P| *P|
+---+---+---+---+---+---+---+---+
6| | | | | | | | |
+---+---+---+---+---+---+---+---+
5| | | | | | | | |
+---+---+---+---+---+---+---+---+
4| | | | | P | | | |
+---+---+---+---+---+---+---+---+
3| | | | | | | | |
+---+---+---+---+---+---+---+---+
2| P | P | P | P | | P | P | P |
+---+---+---+---+---+---+---+---+
1| R | N | B | Q | K | B | N | R |
+---+---+---+---+---+---+---+---+
```

次は相手の番。
AIに打たせるなら`g`と打つ(ちなみに、自分の手番でも`g`でAIに代わりに動かしてもらえるし、相手のコマの動かし方を指定することもできる)。

```
g
Value 129
+---+---+---+---+---+---+---+---+
8| *R| *N| *B| *Q| *K| *B| *N| *R|
+---+---+---+---+---+---+---+---+
7| *P| *P| *P| *P| *P| | *P| *P|
+---+---+---+---+---+---+---+---+
6| | | | | | | | |
+---+---+---+---+---+---+---+---+
5| | | | | | *P| | |
+---+---+---+---+---+---+---+---+
4| | | | | P | | | |
+---+---+---+---+---+---+---+---+
3| | | | | | | | |
+---+---+---+---+---+---+---+---+
2| P | P | P | P | | P | P | P |
+---+---+---+---+---+---+---+---+
1| R | N | B | Q | K | B | N | R |
+---+---+---+---+---+---+---+---+
```

ポーンは斜めにコマを取れる。
このチェスでは取れるコマがあるときは必ず取らないといけない。
他のムーブはできない。

```
e4f5
+---+---+---+---+---+---+---+---+
8| *R| *N| *B| *Q| *K| *B| *N| *R|
+---+---+---+---+---+---+---+---+
7| *P| *P| *P| *P| *P| | *P| *P|
+---+---+---+---+---+---+---+---+
6| | | | | | | | |
+---+---+---+---+---+---+---+---+
5| | | | | | P | | |
+---+---+---+---+---+---+---+---+
4| | | | | | | | |
+---+---+---+---+---+---+---+---+
3| | | | | | | | |
+---+---+---+---+---+---+---+---+
2| P | P | P | P | | P | P | P |
+---+---+---+---+---+---+---+---+
1| R | N | B | Q | K | B | N | R |
+---+---+---+---+---+---+---+---+
g
Value 7
+---+---+---+---+---+---+---+---+
8| *R| *N| *B| *Q| *K| *B| *N| *R|
+---+---+---+---+---+---+---+---+
7| *P| | *P| *P| *P| | *P| *P|
+---+---+---+---+---+---+---+---+
6| | | | | | | | |
+---+---+---+---+---+---+---+---+
5| | *P| | | | P | | |
+---+---+---+---+---+---+---+---+
4| | | | | | | | |
+---+---+---+---+---+---+---+---+
3| | | | | | | | |
+---+---+---+---+---+---+---+---+
2| P | P | P | P | | P | P | P |
+---+---+---+---+---+---+---+---+
1| R | N | B | Q | K | B | N | R |
+---+---+---+---+---+---+---+---+
```

相手はb5にポーンを動かしてきたが、これはf1のB(ビショップ)で取れるので、それ以外の動きは許されない。

このようにコマを進めあって、合法ムーブがなくなったら(全部のコマが取られたら)勝ち。
なお、キングを取られても終わりではない。

## 解説

負けたらsegfaultする。
これがこのジャンルのプログラムのスポーツマンシップであろうとのこと。

`{``}``;`と空白からなる文字列リテラルは、IOCCCのサイズ制限を回避しつつテーブルを埋め込むためのテクニック。
Loading

0 comments on commit d78cd86

Please sign in to comment.