検索
ニュース

美人サンタよりコードがお好き?JavaScriptエンジニアの変態プレイが炸裂(1/2 ページ)

12月20日、CodeIQと@ITは合同で「リアル版:サンタのためのコードゴルフコンペ&クリパ」を開催した。クリスマスを目前に、エンジニアが集う!

PC用表示 関連情報
Share
Tweet
LINE
Hatena

 12月20日、ITエンジニアのための実務スキル評価サービス「CodeIQ」と@ITは合同で「リアル版:サンタのためのコードゴルフコンペ&クリパ」を開催した。イベントは、CodeIQであらかじめ出題されていた「サンタのためのコードゴルフ」との連動企画。クリスマスを目前に、エンジニアが集うガチイベントの模様をレポートする。

イベントの様子
参加者Twitter

 「コードゴルフ」とは、ゴルフがカップインまでの打数を競うように、プログラムの文字数を削り、なるべく短くプログラムを書く遊びのことをいう。スポーツのゴルフでは、打数が少なければ少ないほど高得点が得られるが、コードゴルフでは、ソースコードの文字数が少なければ少ないほど良いとされる。従って、最短コードを書いた人が勝者となる。イベントでは、「サンタのためのコードゴルフ」問題の出題者であるクロノス・クラウンの柳井政和氏によるコードレビューが行われた。

 今回出題された問題は、アスキーアニメ。4枚のアスキーアートでアニメーションさせる問題である。テーマは、「配列」と「ループ処理」だ。

問題とポイント

漫画

 というわけで、サンタ見習いの智美ちゃんが振られた仕事を、こっそりと手伝ってあげて下さい。サンタ見習いの智美ちゃんのコードは、下記に掲載しています。JavaScriptを使って、このコードゴルフにチャレンジしてください。

 以下の関数yourCode()に書かれているコードと同じ文字列を返す処理を、なるべく短い文字数で書いてください。短いほど評価が高いです。ただし、関数外から直接答えを取ったり、通信で答えを得るなどした場合は、ランキング外になってしまいますので、行わないでください。


function yourCode() {

var arrayMax = 4;
var resArray = new Array(arrayMax);
	// アニメ用の4枚のアスキーアートを返す
var w = 80;
var h = 40;
var illumination = 160;
// キャンバス用配列を初期化
var canvasArray = new Array(4);
for (var a = 0; a < arrayMax; a ++) {
	canvasArray[a] = new Array(h);
	for (var y = 0; y < h; y ++) {
		canvasArray[a][y] = new Array(w);
	}
}
// ツリーを作成
for (var a = 0; a < arrayMax; a ++) {
	for (var y = 0; y < h; y ++) {
		for (var x = 0; x < w; x ++) {
			canvasArray[a][y][x] = "_";
			var treeTop = Math.floor(y / 8) * 4;
			var treeW = Math.floor(y / 8 + 1) * 8;
			if (Math.abs(x - w / 2) < (y - treeTop) % treeW) {
				canvasArray[a][y][x] = "%";
			}
		}
	}
}
// イルミネーションを作成
for (var a = 0; a < arrayMax; a ++) {
	for (var i = 0; i < illumination; i ++) {
		var r = (a + i) * i * 49999 + 59999 & 0xFFFF;
		var x = 1 + r % (w - 2);
		var y = 1 + r % (h - 2);
		canvasArray[a][y][x] = "*";
		canvasArray[a][y][x - 1] = "-";
		canvasArray[a][y][x + 1] = "-";
		canvasArray[a][y - 1][x] = "|";
		canvasArray[a][y + 1][x] = "|";
	}
}
// 文字列化
for (var a = 0; a < arrayMax; a ++) {
	var arrayY = new Array(h);
	for (var y = 0; y < h; y ++) {
		arrayY[y] = canvasArray[a][y].join("");
	}
	resArray[a] = arrayY.join("\n");
}
// 戻り値を戻して終了
return resArray;

アスキーアートの出力結果

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***


 この基のコードには、あらかじめ冗長性を設けているという。短く書けるコードを、わざと長く書いているのだ。「大きなトラップが3つある」と柳井氏はいう。

  1. 不要な処理の削除
  2. ループの統合
  3. 多数の配列

1.不要な処理の削除

 最初のトラップは、「不要な処理の削除」である。例えば、

// キャンバス用配列を初期化
var canvasArray = new Array(4);
for (var a = 0; a < arrayMax; a ++) {
  canvasArray[a] = new Array(h);
  for (var y = 0; y < h; y ++) {
    canvasArray[a][y] = new Array(w);
  }
}

 このブロックは丸ごと削除できる。[x][y]の配列ではなく、[x + w * y]の配列にすれば、そもそも初期化は不要になるためだ。

2.ループの統合

 第2のトラップは、「ループの統合」。基のコードは、ループの統合が可能なように、本来ランダムに行うべき処理を一意に決まるコードで書いている。

var r = (a + i) * i * 49999 + 59999 & 0xFFFF;

 「勘の鋭い人は、線形合同法の変形だと気付くだろう」と柳井氏は解説を続ける。線形合同法とは、擬似乱数列を生成するアルゴリズムの一つである。

 例えば、Visual BasicのRnd関数は次のような構造になっている。

線形合同法の例
static long x=327680;
float Rnd(void)
{
  x=x*16598013+12820163&16777215;
  return x*(1.0/16777216.0);
}

 今回の問題では、この「線形合同法もどき」を利用し、星の表示場所を散らしている。従って、次の部分は丸ごと短くできる。

var r = (a + i) * i * 49999 + 59999 & 0xFFFF;

 ここは、どの順番で計算しても可能であるため、ソースコードの中に複雑に埋め込むことができる。あえてここを正しい乱数にしなかったのは、「計算順番は問わないので好きに計算する位置を変えてください」という出題者からのサインで問題を解くヒントになる。何度も登場するループは、乱数の処理順にとらわれずに統合できるため、柳井氏は「統合してね」というメッセージを問題の中に隠したそうだ。問題を見た瞬間にここまで分かるかどうかが、この問題の大きなハードルである。

3.多数の配列

 第3のハードルは、「多数の配列」だ。問題のコードでは、ネストした配列が出てくる。アスキーアートの4枚の配列は、まず木を書いて、その後に星を書いている。すなわち、4枚の同じ絵のレイヤーを作り、その後個別の絵を描くという処理だ。「『この処理をどこまで統合できるか』というところで、文字数の差が付くようにしている」と柳井氏は語る。

変態コード現る!

 今回のイベントには、1人の勇者がいた。2バイト文字も「1文字」として扱ったコードをとくとご覧あれ。

miau氏のコード(209文字)
for(var i=0,C,Z="";C="景爨癡爠刽孝ⱡ㴴ⱷ㴸ㄻ愭ⴻ剛慝
㵃⹪潩渨∢⤩筃㵛崻景爨椽㌲㌹ⱚ㴲?椭ⴻ⥃孩崽繩╷㽩╷㸴〭娦
椥眼㐰⭚㼢┢㨢弢㨨娫㵾椯眥㠿ⴱ㨳Ⱒ屮∩㭦潲⠻娼ㄶ〻䍛
椭睝㵃孩⭷崽≼∩椽⡡⭚⤪娫⬪㐹㤹㤭㔵㌷☶㔵㌵Ⱪ㵩┳㠪眫椥
㜸⵾眬䍛楝㴢⨢ⱃ孩ⴱ崽䍛椫ㅝ㴢ⴢ絒".charCodeAt(i++);)
Z+=String.fromCharCode(C>>8&255,C&25
5);return eval(Z)
整形コード
for(
    var i=0,C,Z="";
    C="景爨癡爠刽孝ⱡ㴴ⱷ㴸ㄻ愭ⴻ剛慝㵃⹪潩渨∢⤩筃㵛崻景爨椽㌲㌹ⱚ㴲?椭ⴻ⥃孩崽繩╷㽩╷㸴〭娦椥眼㐰⭚㼢┢㨢弢㨨娫㵾椯眥㠿ⴱ㨳Ⱒ屮∩㭦潲⠻娼ㄶ〻䍛椭睝㵃孩⭷崽≼∩椽⡡⭚⤪娫⬪㐹㤹㤭㔵㌷☶㔵㌵Ⱪ㵩┳㠪眫椥㜸⵾眬䍛楝㴢⨢ⱃ孩ⴱ崽䍛椫ㅝ㴢ⴢ絒"
        .charCodeAt(i++);
    )
        Z+=String.fromCharCode(C>>8&255,C&255);
return eval(Z)
取り出し後の元のコード(230文字)
for(var R=[],a=4,w=81;a--;R[a]=C.join("")){C=[];fo
r(i=3239,Z=23;i--;)C[i]=~i%w?i%w>40-Z&i%w<40+Z?"%"
:"_":(Z+=~i/w%8?-1:3,"\n");for(;Z<160;C[i-w]=C[i+w
]="|")i=(a+Z)*Z++*49999-5537&65535,i=i%38*w+i%78-~
w,C[i]="*",C[i-1]=C[i+1]="-"}R
取り出し後の整形コード
for(
    var R=[],
    a=4,
    w=81;
    a--;
    R[a]=C.join("")
    ){
        C=[];
        for(
            i=3239,
            Z=23;
            i--;
            )
                C[i]=
                    ~i%w
                    ?
                        i%w>40-Z
                        &
                        i%w<40+Z
                        ?
                            "%"
                        :
                            "_"
                    :
                        (
                            Z+=~i/w%8
                            ?
                                -1
                            :
                                3,
                            "\n"
                        );
                for(
                    ;
                    Z<160;
                    C[i-w]=C[i+w]="|"
                    )
                        i=(a+Z)*Z++*49999-5537&65535,
                        i=i%38*w+i%78-~w,
                        C[i]="*",
                        C[i-1]=C[i+1]="-"
    }
R
コードに夢中のエンジニア
コードに夢中のエンジニア

 柳井氏によるコードレビューの後、チーム対抗戦「ミニ・コードゴルフ コンペ」が行われた。まるで当たり前のようにサンタの帽子をかぶって、コードゴルフに打ち込む姿は貴重な光景だった。

 コンペが終わると、クリスマスパーティーに突入。しかし、集まったエンジニアたちは、いかにコードを短く書くかに夢中だった。ヘソ出しサンタがすぐそこにいるというのに、このありさまだ。

……。
……。
…………。
…………。
………………。
………………。

 こうして、「リアル版:サンタのためのコードゴルフコンペ&クリパ」は幕を閉じた。JavaScriptエンジニアは、ヘソ出しサンタよりコードが好きだった。

敗北サンタ(@IT 太田智美)
敗北サンタ(@IT 太田智美)

Copyright © ITmedia, Inc. All Rights Reserved.

       | 次のページへ
ページトップに戻る